IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> CANopen:移植CANFestival以及应用(基于CUBEMX HAL库)大礼包 -> 正文阅读

[嵌入式]CANopen:移植CANFestival以及应用(基于CUBEMX HAL库)大礼包

内容描述

网上很多讲CANopen协议的理论知识,但讲解如何实际运用的较少。本次项目是在F1上进行开发,也能快速移植用于其他类型芯片。贴出了网上较好的教程,并结合自己代码进行分享。

移植CANFestival

B站up主视频链接:
某位热心UP主的视频

此视频超级实用,还包括SDO PDO的配置和使用流程,适合入门观看

他还出了文档版
移植文档版

(此类教程较多,就不写了)

STM32配置

采用STM32F103ZET6开发板,使用CUBEMX配置生成项目

基础配置

(CUBEMX常用方法教程较多,此处不详讲,只贴出关键内容)

时钟配置:

请根据自己使用的板子配置时钟树
F1时钟树配置

重要配置:

定时器配置:

CANFestival需要一个定时器来模拟多个软件定时器
TIM1配置注意此分频系数,F1为72M ,分频后为1M,即1us计数一次。此计数时间和CANFestival代码设置有关,若1us一次则需要修改CANFestival的timerscfg.h文件中的值
TIMEVAL_MAX 也需要修改为5000
修改内容

CAN配置

根据通信需要设置波特率 此时波特率是500000bit/s
波特率配置设置接收中断
中断
设置GPIO,F1的板子默认这两个口,其他类型板子根据实际情况修改
GPIO设置
生成项目
注意 生成的项目中有can.h文件,这两个文件和CANFestival里面的can.h文件重名,可能会导致编译错误,可通过修改文件名解决。

代码详解:

此代码为基础代码,只确保CAN和CANopen通信,其他开发未完善

代码链接:

代码链接

将代码移植到其他开发板上流程:

此代码以正点原子的F103ZET6开发板为基础移植和编写,若想用于其他开发板,比如F7,需按照以下流程
1、根据自己的开发板配置CUBEMX并生成项目。注意CANFestival库和项目生成的文件同名,我将项目中的can.h改为了F1can.h ,注意其他文件的头文件声明也需要统一修改,将两个头文件进行区分。
2、移植CANFestival库
也可直接复制我项目中的CANFestival文件夹。
项目构成文件夹移植3、编写关键函数
CAN
为检验CAN硬件是否可用,项目中包含了CAN的代码。此部分代码来自正点原子HAL库版本
文件目录Core->UserCode主要三个函数:

(1)过滤器配置

void CAN_Config(void)
{
  CAN_FilterTypeDef  sFilterConfig;

  /*配置CAN过滤器*/
  sFilterConfig.FilterBank = 0;                     //过滤器0
  sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
  sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
  sFilterConfig.FilterIdHigh = 0x0000;              //32位ID
  sFilterConfig.FilterIdLow = 0x0000;
  sFilterConfig.FilterMaskIdHigh = 0x0000;          //32位MASK
  sFilterConfig.FilterMaskIdLow = 0x0000;
  sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;//过滤器0关联到FIFO0
  sFilterConfig.FilterActivation = ENABLE;          //激活滤波器0
  sFilterConfig.SlaveStartFilterBank = 14;
	
  //过滤器配置
  if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
  {
    while(1){}
  }

  //启动CAN外围设备
  if (HAL_CAN_Start(&hcan) != HAL_OK)
  {
    while(1){}
  }

  //激活可以RX通知
  if (HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
  {
    while(1){}
  }
  
  /*配置传输过程*/
  TxHeader.StdId = 0x321;
  TxHeader.ExtId = 0x01;
  TxHeader.RTR = CAN_RTR_DATA;
  TxHeader.IDE = CAN_ID_STD;
  TxHeader.DLC = 2;
  TxHeader.TransmitGlobalTime = DISABLE;
}

(2)发送 如要运用请根据需要更改,本函数只是测试 数据和结构较乱。

uint8_t CAN1_Send_Msg(uint8_t* msg,uint8_t len)
{	
    uint8_t i=0;
	uint32_t TxMailbox;
	uint8_t message[8];
    TxHeader.StdId=0X601;        //标准标识符
    TxHeader.IDE=CAN_ID_STD;    //使用标准帧
    TxHeader.RTR=CAN_RTR_DATA;  //数据帧
    TxHeader.DLC=len;                
	message[0] = 0x40;
	message[1] = 0x28;
	message[2] = 0x20;
	message[3] = 0x00;
	message[4] = 0x00;
	message[5] = 0x00;
	message[6] = 0x00;
    message[7] = 0x00;
    if(HAL_CAN_AddTxMessage(&hcan, &TxHeader, message, &TxMailbox) != HAL_OK)//发送
	{
		return 1;
	}
	while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan) != 3) {}
    return 0;
}

(3)接收

此接收函数未进行检验,但接收中断可用。

u8 CAN1_Receive_Msg(u8 *buf)
{
 	u32 i;
	u8	RxData[8];

	if(HAL_CAN_GetRxFifoFillLevel(&CAN1_Handler, CAN_RX_FIFO0) != 1)
	{
		return 0xF1;
	}

	if(HAL_CAN_GetRxMessage(&CAN1_Handler, CAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK)
	{
		return 0xF2;
	}
    for(i=0;i<RxHeader.DLC;i++)
    buf[i]=RxData[i];
	return RxHeader.DLC;
}

使用方法:
在生成项目的主函数中添加CAN_Config();
并通过CAN1_Send_Msg();函数发送数据。检验CAN功能是否正常。
(如果CAN无法进行通信,则难以进行CANOpen的开发)

CANOpen函数
CANFseitval移植时需要用户进行添加的函数都放在此文件中,方便移植。可在整理完CANFestival的文件加后直接添加此文件,完成整个移植。

#include "bsp_canopen.h"
#include "tim.h"
#include "bsp_can.h"
#include "F1can.h"
#include "can_driver.h"
#include "TestMaster.h"
//CANFestival移植  需要用户自己定义函数
//canSend、getElapsedTime和setTimer
// canSend、getElapsedTime和setTimer没有定义。这三个接口是需要用户移植程序的时候自己进行添加的。
/* 功能:	设置定时器触发时间
	 参数: 	定时器触发时间
	 返回值:无 
 */
extern CAN_TxHeaderTypeDef	TxHeader;      //发送
extern CAN_RxHeaderTypeDef	RxHeader;      //接收
void setTimer(TIMEVAL value)
{
	TIM2->ARR = TIM2->CNT + value;
}
 
/* 功能:	获取上次触发到现在的流逝时间
	 参数: 	无
	 返回值:获取计数器值
 */
TIMEVAL getElapsedTime(void)
{
	return TIM2->CNT;
}
//定时器中断 调用TimeDispatch函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

{
    if (htim->Instance == htim1.Instance)

    {
      TimeDispatch();
    }

}

unsigned char canSend(CAN_PORT notused, Message *msg)
{

	  uint16_t time = 0;
    uint32_t TxMailbox = msg->len;
		// uint32_t	i;
    // TxMessage.ExtId = 0x00;
    TxHeader.StdId = msg->cob_id;
    if(msg->rtr)
      TxHeader.RTR = CAN_RTR_REMOTE;
    else
      TxHeader.RTR = CAN_RTR_DATA;

    TxHeader.IDE = CAN_ID_STD;
    TxHeader.DLC = msg->len;
		while(( HAL_CAN_AddTxMessage(&hcan,&TxHeader,msg->data,&TxMailbox)!=HAL_OK) || time > 200)
    { 
			// printf("Send successfully!\r\n");
        time ++;
        return CAN_SEND_OK;
    }
			// printf("Send error!\r\n");
        return CAN_SEND_ERR;
	
}

/**
  * @brief  CAN接收完成中断(非阻塞) 
  * @param  hcan: CAN句柄指针
  * @retval 无
  */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef* hcan)
{
//  unsigned int i = 0;
	Message RxMSG ;
  HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxMSG.data);
  RxMSG.cob_id = (uint16_t)(RxHeader.StdId);
  if( RxHeader.RTR == CAN_RTR_REMOTE )
  {
     RxMSG.rtr = 1;    
  }
  else
  {
      RxMSG.rtr = 0; 
  }  
	RxMSG.len = RxHeader.DLC;
	canDispatch(&TestMaster_Data, &(RxMSG)); 
	/* 准备中断接收 */
}
void init_rxmes(void)
{
  /*把接收结构体清零*/
  TxHeader.StdId = 0x00;
  TxHeader.ExtId = 0x00;
  TxHeader.IDE = CAN_ID_STD;
  TxHeader.DLC = 0;
}


/**
  * 函数功能: CANopen,Node-ID初始化
  * 输入参数: notused:结构体 m:数据
  * 返 回 值: 无
  * 说    明: 无
  */
 s_BOARD MasterBoard = {"1", "1M"};
void InitNodes(CO_Data* d, UNS32 id)
{
	/****************************** INITIALISATION MASTER *******************************/
	if(strcmp(MasterBoard.baudrate, "none")){
		setNodeId(&TestMaster_Data, 0x00);

		/* init */
		setState(&TestMaster_Data, Initialisation);
		setState(&TestMaster_Data, Operational);
		masterSendNMTstateChange(&TestMaster_Data,0x01,NMT_Start_Node);
		
	}
}
static TimerCallback_t init_callback;
void StartTimerLoop(TimerCallback_t _init_callback) 
{
	init_callback = _init_callback;
	SetAlarm(NULL, 0, init_callback, 0, 0);
    HAL_TIM_Base_Start_IT(&htim1);
  
}
//CANOpen初始化
void CANOpen_Init(void)
{
  CAN_Config();
  init_rxmes(); 
  StartTimerLoop(&InitNodes);
  HAL_CAN_ActivateNotification(&hcan,CAN_IT_RX_FIFO0_MSG_PENDING); 	
}

4、将CANOpen_Init(); 放入主函数中,完成CANOpen的移植。
如果是使用PDO,则需要配置字典,while里面可为空。CANOpen会自己根据定时器去字典里面查找需要发发送的命令。可以查看文章前面UP主链接里的教程。

如果是使用SDO,则需要通过writeNetworkDict(); readNetworkDict();函数进行读写。具体操作看链接
SDO读写指令
CANFestival SDO详解

SDO指令的组成和函数的编写请根据实际应用进行编写。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-11-12 19:45:23  更:2021-11-12 19:46:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 8:26:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码