CanFestival移植到STM32G4
文章目录
- 一、准备工作
- 二、软件配置
- 三、移植CanFestival
- 参考
一、准备工作
1、获取Canfestival源码
2、Python下载
3、wxPython下载
4、CanFestival字典生成
5、安装参考
Python2.7.15及wxPython2.8百度云盘下载地址:https://pan.baidu.com/s/1bRS403m4B31m4ovSJ-_HwA
提取码:38sn
二、软件配置
FDCAN 500K@2M配置
/** Bit timing & sampling* Tq = (BRP+1)/Fcan if DIV8 = 0* Tq = 8*(BRP+1)/Fcan if DIV8 = 1* TSync = 1.Tq* TSeg1 = (TSEG1+1)*Tq >= 3Tq* TSeg2 = (TSEG2+1)*Tq >= 2Tq* Bit Time = TSync + TSeg1 + TSeg2 >= 8Tq** Resynchronization:** Tsjw = (SJW + 1)*Tq* TSeg1 >= Tsjw + Tprop* TSeg2 >= Tsjw*/
可以通过 KVASER Bit Timing Calculator for CANFD 这个网站在线计算.
CANFD关键代码:
FDCAN_FilterTypeDef sFilterConfig2;
FDCAN_RxHeaderTypeDef RxHeader2;
FDCAN_TxHeaderTypeDef TxHeader2;void fdcan2_config(void)
{sFilterConfig2.IdType = FDCAN_STANDARD_ID;sFilterConfig2.FilterIndex = 0;sFilterConfig2.FilterType = FDCAN_FILTER_RANGE;sFilterConfig2.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;sFilterConfig2.FilterID1 = 0x00;sFilterConfig2.FilterID2 = 0x7FF;if (HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig2) != HAL_OK){Error_Handler();}sFilterConfig2.IdType = FDCAN_EXTENDED_ID;sFilterConfig2.FilterIndex = 0;sFilterConfig2.FilterType = FDCAN_FILTER_RANGE;sFilterConfig2.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;sFilterConfig2.FilterID1 = 0x00;sFilterConfig2.FilterID2 = 0x1FFFFFFF;if (HAL_FDCAN_ConfigFilter(&hfdcan2, &sFilterConfig2) != HAL_OK){Error_Handler();}/* Configure global filter on both FDCAN instances:Filter all remote frames with STD and EXT IDReject non matching frames with STD ID and EXT ID */if (HAL_FDCAN_ConfigGlobalFilter(&hfdcan2, FDCAN_REJECT, FDCAN_REJECT, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK){Error_Handler();}/* Activate Rx FIFO 0 new message notification on both FDCAN instances */if (HAL_FDCAN_ActivateNotification(&hfdcan2, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK){Error_Handler();}if (HAL_FDCAN_ActivateNotification(&hfdcan2, FDCAN_IT_BUS_OFF, 0) != HAL_OK){Error_Handler();}/* Configure and enable Tx Delay Compensation, required for BRS mode.TdcOffset default recommended value: DataTimeSeg1 * DataPrescalerTdcFilter default recommended value: 0 */HAL_FDCAN_ConfigTxDelayCompensation(&hfdcan2, hfdcan2.Init.DataPrescaler * hfdcan2.Init.DataTimeSeg1, 0);HAL_FDCAN_EnableTxDelayCompensation(&hfdcan2);HAL_FDCAN_Start(&hfdcan2);
}void HAL_FDCAN_ErrorStatusCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t ErrorStatusITs)
{if(hfdcan->Instance == FDCAN2) {MX_FDCAN2_Init();fdcan2_config();}
}//RxHeader1.DataLength => can_dlc
//0x00000 => 0
//0x10000 => 1
//0x20000 => 2
//0x30000 => 3
//0x40000 => 4
//0x50000 => 5
//0x60000 => 6
//0x70000 => 7
//0x80000 => 8
//0x90000 => 12
//0xA0000 => 16
//0xB0000 => 20
//0xC0000 => 24
//0xD0000 => 32
//0xE0000 => 48
//0xF0000 => 64
uint8_t dlc2len[]={0,1,2,3,4,5,6,7,8,12,16,20,24,32,48,64};
uint8_t RxData2[100];
uint8_t TxData2[64];
uint8_t can_dlc2len(uint32_t RxHeader_DataLength)
{return dlc2len[RxHeader_DataLength>>16];
}uint8_t cnt = 0;
uint8_t brs[] = {'-', 'B'};
uint8_t esi[] = {'-', 'E'};
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{if((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != 0) { HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader2, RxData2);if (hfdcan->Instance == FDCAN2) { usb_printf("fdcan2, ");} }usb_printf("0x%8X, %02d, %c, %c:",RxHeader2.Identifier, can_dlc2len(RxHeader2.DataLength), brs[RxHeader2.BitRateSwitch>>20 & 0x1],esi[RxHeader2.ErrorStateIndicator>>31 & 0x1]);for(cnt = 0; cnt < can_dlc2len(RxHeader2.DataLength); cnt++) {usb_printf(" %02X", RxData2[cnt]);}usb_printf("\n\r");}FDCAN_SendFailTypeDef fdcan2_send_fail = {0};
void fdcan2_transmit(uint32_t can_id, uint32_t DataLength, uint8_t tx_data[])
{TxHeader2.Identifier = can_id;TxHeader2.IdType = FDCAN_EXTENDED_ID;if(can_id < 0x800) { //exactly not rightTxHeader2.IdType = FDCAN_STANDARD_ID;}TxHeader2.TxFrameType = FDCAN_DATA_FRAME;TxHeader2.DataLength = DataLength;TxHeader2.ErrorStateIndicator = FDCAN_ESI_ACTIVE;TxHeader2.BitRateSwitch = FDCAN_BRS_ON;TxHeader2.FDFormat = FDCAN_FD_CAN;TxHeader2.TxEventFifoControl = FDCAN_NO_TX_EVENTS;TxHeader2.MessageMarker = 0; //marker++; //Tx Event FIFO Useif(HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan2, &TxHeader2, tx_data) != HAL_OK) {fdcan2_send_fail.flag = 1;memcpy(&fdcan2_send_fail.TxHeader, &TxHeader2, sizeof(FDCAN_TxHeaderTypeDef));memcpy(fdcan2_send_fail.TxData, tx_data, can_dlc2len(DataLength));}
}uint32_t count = 0;uint32_t cnt_100us = 0;uint32_t cnt_500us = 0;uint8_t tim4_flag = 0;
void canfd_test(void)
{for(uint8_t i = 0; i < 64; i++) {TxData2[i] = i;}if(count < 1000) {TxData2[0] = count >> 8 & 0xFF;TxData2[1] = count & 0xFF;++cnt_100us;cnt_500us = cnt_100us / 5;if(cnt_500us && (cnt_100us%5==0) ) {switch(cnt_500us) {case 1: fdcan2_transmit(0x123, FDCAN_DLC_BYTES_64, TxData2); fdcan2_transmit(0x124, FDCAN_DLC_BYTES_64, TxData2);fdcan2_transmit(0x125, FDCAN_DLC_BYTES_64, TxData2); break;case 4: fdcan2_transmit(0x12345678, FDCAN_DLC_BYTES_64, TxData2); break;case 5: fdcan2_transmit(0x12345679, FDCAN_DLC_BYTES_64, TxData2); break;case 6: fdcan2_transmit(0x1234567A, FDCAN_DLC_BYTES_64, TxData2); break;case 7: /* next send */ break;case 8: break;case 20: ++count; cnt_100us = 0; break; //10ms}} else { //fail retransmission onceif(fdcan2_send_fail.flag) {fdcan2_transmit(fdcan2_send_fail.TxHeader.Identifier, fdcan2_send_fail.TxHeader.DataLength,fdcan2_send_fail.TxData);fdcan2_send_fail.flag = 0;}}}}
通讯可收发即可。
三、移植CanFestival
步骤一:
在新建好的工程目录下新建文件夹Canopen,再在Canopen下新建文件夹driver、inc和src,再在inc文件夹下面新建stm32文件夹(我这里主要以移植到stm32为例说明,如果是移植到VC或其他平台下,这里也可以命名为其他名字,如vc)。
步骤二:
将CanFestival-3-10\src目录下的dcf.c、emcy.c、lifegrd.c、lss.c、nmtMaster.c、nmtSlave.c、objacces.c、pdo.c、sdo.c、states.c、sync.c、timer.c共12个文件拷贝到CanFestival\src目录下;
将CanFestival-3-10\include目录下的所有.h文件共19个文件全部拷贝到CanFestival\inc目录下,
再把CanFestival-3-10\examples\AVR\Slave目录下的ObjDict.h文件拷贝过来,一共20个;
将CanFestival-3-10\include\AVR目录下的applicfg.h、canfestival.h、config.h、timerscfg.h共4个头文件拷贝到canfestival\inc\stm32目录下;
将CanFestival-3-10\objdictgen\MySlave目录下的slave.c、slave.h拷贝到canfestival\driver目录下,并在该目录下新建stm32_canfestival.c文件。(这里的MySlave是自己新建的,用于保存通过对象字典编辑工具生成的eds等文件)
步骤三:
将CanFestival\src目录下的所有.c文件添加到工程;将canfestival\driver目录下的stm32_canfestival.c文件添加到工程;
如果实现的是从设备,再将canfestival\driver目录下的TestSlave.c文件添加到工程,如果实现的是主设备,则将TestMaster.c文件添加到工程;
步骤四:
将文件目录canfestival\inc、canfestival\inc\stm32、canfestival\driver等路径添加到工程包含路径。
步骤五:
在stm32_canfestival.c中包含头文件#include “canfestival.h”,并定义如下函数:
void setTimer(TIMEVAL value)
{
}
TIMEVAL getElapsedTime(void)
{return 1;
}
unsigned char canSend(CAN_PORT notused, Message *m)
{return 1;
}
可以先定义一个空函数,等到编译都通过了之后,再往里面添加内容,这几个函数都是定义来供canfestival源码调用的,如果找不到这几个函数编译就会报错。
步骤六:
通过以上几步,所有的文件都弄齐了,但是编译一定会出现报错,注释或删除掉config.h文件中的如下几行就能编译通过:
include <inttypes.h>
#include <avr\io.h>
#include <avr\interrupt.h>
#include <avr/pgmspace.h>
#include <avr\sleep.h>
#include <avr\wdt.h>
如果还有其他报错,那有可能是因为不同源码版本、不同平台、不同人遇到的错误也会不相同,这里的过程只能做一定的参考,不一定完全相同,需要根据编译出错提示来进行修改对应地方,一般都是有些函数没声明或者某个头文件没有包含或者包含了一些不必要的头文件而该文件不存在或者是一些变量类型不符合需定义之类的。
如果提示:
error: #3092: anonymous unions are only supported in --gnu mode, or when enabled with #pragma anon_unions
解决方法:
勾选GUN extensions。
步骤七:
解决了所有的编译错误后,接下来实现刚才定义的3个空函数:
/*** @brief Set timer for nex alarm* @param value: timer value* @retval None*/
void setTimer(TIMEVAL value)
{// 获取当前定时器值UNS32 timer = Bsp_Canopen_GetTimer();// 计算已经过去的时间elapsed_time += timer - last_counter_val;// 设置下一次定时器值last_counter_val = CANOPEN_TIM_PERIOD - value;// 设置定时器Bsp_Canopen_SetTimer(last_counter_val);
}/*** @brief Return the elapsed time to tell the Stack how much time is spent since the last call.* @param None* @retval The elapsed time*/
TIMEVAL getElapsedTime(void)
{uint32_t timer = Bsp_Canopen_GetTimer(); // Copy the value of the running timerif (timer < last_counter_val)timer += CANOPEN_TIM_PERIOD;TIMEVAL elapsed = timer - last_counter_val + elapsed_time;return elapsed;
}/*** @brief Can send* @param notused: not used* m: message point* @retval 0:successful -1:failed*/
unsigned char canSend(CAN_PORT notused, Message *m)
{// 调用fdcan2_transmit函数发送消息return fdcan1_transmit(m->cob_id, m->len, m->data);
}void CAN_DispatchRcv_Cbk(FDCAN_ReceivedFailTypeDef *RxMsg) {// 定义一个静态变量msg,用于存储接收到的消息static Message msg;// 如果接收到的消息标志位为1,则返回if (RxMsg->flag) {return; }// 如果接收到的消息是标准ID,则将消息ID赋值给msg.cob_idif (RxMsg->RxHeader.IdType == FDCAN_STANDARD_ID) {msg.cob_id = RxMsg->RxHeader.Identifier & 0x7FF; } else {// 如果接收到的消息是扩展ID,则将消息ID赋值给msg.cob_id,并将最高位设置为1msg.cob_id = RxMsg->RxHeader.Identifier | 0x80000000; }// 将消息类型设置为数据帧msg.rtr = 0;// 将接收到的消息长度赋值给msg.len//msg.len = (UNS8)can_dlc2len(RxMsg->RxHeader.DataLength);msg.len = (UNS8)RxMsg->RxHeader.DataLength;// 将接收到的消息数据赋值给msg.datamemcpy(msg.data, RxMsg->RxData, msg.len);// 禁用TIM4中断// HAL_NVIC_DisableIRQ(TIM4_IRQn);// 调用canDispatch函数,将接收到的消息传递给slave_DatacanDispatch(&slave_Data, &msg);// 使能TIM4中断// HAL_NVIC_EnableIRQ(TIM4_IRQn);
}/*** @brief Timer ISR* @param None* @retval None*/
void TIMx_Dispatch_Cbk(void)
{last_counter_val = 0;elapsed_time = 0;TimeDispatch();
}void CANopen_App_Init(CO_Data *d)
{ setNodeId(d, (UNS8)CAN_NodeID);setState(d, Initialisation);setState(d, Pre_operational);setState(d, Operational);
}
其中:
#define CANOPEN_TIM_PERIOD 0xffffextern void canfd_test(void);
extern void TIMx_Dispatch_Cbk(void);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM4) {TIMx_Dispatch_Cbk();}
}/*** @brief * @param None* @retval void*/
inline uint32_t Bsp_Canopen_GetTimer(void)
{return __HAL_TIM_GET_COUNTER(&htim4);
}/*** @brief * @param None* @retval void*/
inline void Bsp_Canopen_SetTimer(uint32_t value)
{__HAL_TIM_SET_COUNTER(&htim4, value);
}
步骤八
完善canfd收发函数
typedef struct {uint8_t flag;FDCAN_TxHeaderTypeDef TxHeader;uint8_t TxData[64];
} FDCAN_SendFailTypeDef;typedef struct {uint8_t flag;FDCAN_RxHeaderTypeDef RxHeader;uint8_t RxData[64];
} FDCAN_ReceivedFailTypeDef;FDCAN_FilterTypeDef sFilterConfig1;
FDCAN_RxHeaderTypeDef RxHeader1;
FDCAN_TxHeaderTypeDef TxHeader1;void fdcan1_config(void)
{sFilterConfig1.IdType = FDCAN_STANDARD_ID;sFilterConfig1.FilterIndex = 0;sFilterConfig1.FilterType = FDCAN_FILTER_RANGE;sFilterConfig1.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;sFilterConfig1.FilterID1 = 0x00;sFilterConfig1.FilterID2 = 0x7FF;if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig1) != HAL_OK){Error_Handler();}sFilterConfig1.IdType = FDCAN_EXTENDED_ID;sFilterConfig1.FilterIndex = 0;sFilterConfig1.FilterType = FDCAN_FILTER_RANGE;sFilterConfig1.FilterConfig = FDCAN_FILTER_TO_RXFIFO0;sFilterConfig1.FilterID1 = 0x00;sFilterConfig1.FilterID2 = 0x1FFFFFFF;if (HAL_FDCAN_ConfigFilter(&hfdcan1, &sFilterConfig1) != HAL_OK){Error_Handler();}/* Configure global filter on both FDCAN instances:Filter all remote frames with STD and EXT IDReject non matching frames with STD ID and EXT ID */if (HAL_FDCAN_ConfigGlobalFilter(&hfdcan1, FDCAN_REJECT, FDCAN_REJECT, FDCAN_FILTER_REMOTE, FDCAN_FILTER_REMOTE) != HAL_OK){Error_Handler();}/* Activate Rx FIFO 0 new message notification on both FDCAN instances */if (HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_RX_FIFO0_NEW_MESSAGE, 0) != HAL_OK){Error_Handler();}if (HAL_FDCAN_ActivateNotification(&hfdcan1, FDCAN_IT_BUS_OFF, 0) != HAL_OK){Error_Handler();}/* Configure and enable Tx Delay Compensation, required for BRS mode.TdcOffset default recommended value: DataTimeSeg1 * DataPrescalerTdcFilter default recommended value: 0 */HAL_FDCAN_ConfigTxDelayCompensation(&hfdcan1, hfdcan1.Init.DataPrescaler * hfdcan1.Init.DataTimeSeg1, 0);HAL_FDCAN_EnableTxDelayCompensation(&hfdcan1);HAL_FDCAN_Start(&hfdcan1);
}void HAL_FDCAN_ErrorStatusCallback(FDCAN_HandleTypeDef *hfdcan, uint32_t ErrorStatusITs)
{if(hfdcan->Instance == FDCAN1) {MX_FDCAN1_Init();fdcan1_config();}
}//RxHeader1.DataLength => can_dlc
//0x00000 => 0
//0x10000 => 1
//0x20000 => 2
//0x30000 => 3
//0x40000 => 4
//0x50000 => 5
//0x60000 => 6
//0x70000 => 7
//0x80000 => 8
//0x90000 => 12
//0xA0000 => 16
//0xB0000 => 20
//0xC0000 => 24
//0xD0000 => 32
//0xE0000 => 48
//0xF0000 => 64
uint8_t dlc2len[]={0,1,2,3,4,5,6,7,8,12,16,20,24,32,48,64};
uint8_t RxData1[100];
uint8_t TxData1[64];
uint8_t can_dlc2len(uint16_t data_length) {// 提取 DLC 的 4 位(例如:DLC 位于位 16-19)uint8_t dlc = (data_length >> 16) & 0x0F; // 确保 dlc 在 0-15 范围内return (dlc < 16) ? dlc2len[dlc] : 0; // 边界检查
}uint8_t cnt = 0;
uint8_t brs[] = {'-', 'B'};
uint8_t esi[] = {'-', 'E'};
extern void CAN_DispatchRcv_Cbk(FDCAN_ReceivedFailTypeDef *RxMsg) ;
// 接收回调函数
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs) {if (hfdcan->Instance == FDCAN1 && (RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE)) {FDCAN_RxHeaderTypeDef RxHeader1;uint8_t RxData1[64];FDCAN_ReceivedFailTypeDef RxMsg = {0};if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader1, RxData1) != HAL_OK) {Error_Handler(); // 自定义错误处理return;}// 提取数据长度//uint8_t data_len = can_dlc2len(RxHeader1.DataLength);uint8_t data_len = RxHeader1.DataLength;if (data_len > 64) {return; // 无效长度处理}// 填充接收消息结构体RxMsg.RxHeader.Identifier = RxHeader1.Identifier;RxMsg.RxHeader.DataLength = data_len;RxMsg.RxHeader.BitRateSwitch = (RxHeader1.BitRateSwitch) ? 1 : 0;RxMsg.RxHeader.ErrorStateIndicator = (RxHeader1.ErrorStateIndicator) ? 1 : 0;memcpy(RxMsg.RxData, RxData1, data_len);// 调试输出#ifdef CAN_DEBUGLogCanFrame(&RxMsg); // 封装调试函数#endif// 分发消息CAN_DispatchRcv_Cbk(&RxMsg);}
}FDCAN_SendFailTypeDef fdcan1_send_fail = {0};
uint8_t fdcan1_transmit(uint32_t can_id, uint32_t DataLength, uint8_t tx_data[])
{// 定义一个FDCAN_TxHeaderTypeDef类型的变量TxHeader1,并初始化为0FDCAN_TxHeaderTypeDef TxHeader1={0};// 判断can_id的高位是否为1,如果是,则表示为扩展ID,否则为标准IDif ((can_id & 0x80000000) == 0) { // 设置为标准IDTxHeader1.IdType = FDCAN_STANDARD_ID;// 将can_id的低11位赋值给TxHeader1.Identifiercan_id &= 0x7FF;} else { // 设置为扩展IDTxHeader1.IdType = FDCAN_EXTENDED_ID;// 将can_id的低29位赋值给TxHeader1.Identifiercan_id &= 0x1FFFFFFF;}// 设置标识符TxHeader1.Identifier = can_id;// 设置帧类型为数据帧TxHeader1.TxFrameType = FDCAN_DATA_FRAME;// 设置数据长度TxHeader1.DataLength = DataLength;// 设置错误状态指示器为活动状态TxHeader1.ErrorStateIndicator = FDCAN_ESI_ACTIVE;// 设置比特率切换为开启TxHeader1.BitRateSwitch = FDCAN_BRS_ON;// 设置为FD格式TxHeader1.FDFormat = FDCAN_FD_CAN;// 设置为不使用Tx Event FIFOTxHeader1.TxEventFifoControl = FDCAN_NO_TX_EVENTS;// 设置消息标记为0TxHeader1.MessageMarker = 0; //marker++; //Tx Event FIFO Use// 将数据添加到Tx Event FIFO队列中if(HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader1, tx_data) != HAL_OK) {// 如果添加失败,则设置发送失败标志fdcan1_send_fail.flag = 1;// 将TxHeader1的值复制到fdcan1_send_fail.TxHeader中memcpy(&fdcan1_send_fail.TxHeader, &TxHeader1, sizeof(FDCAN_TxHeaderTypeDef));// 将tx_data的值复制到fdcan1_send_fail.TxData中memcpy(fdcan1_send_fail.TxData, tx_data, can_dlc2len(DataLength));// 返回0return 0;} // 返回1return 1;
}uint32_t count = 0;uint32_t cnt_100us = 0;uint32_t cnt_500us = 0;uint8_t tim4_flag = 0;
void canfd_test(void)
{for(uint8_t i = 0; i < 64; i++) {TxData1[i] = i;}if(count < 1000) {TxData1[0] = count >> 8 & 0xFF;TxData1[1] = count & 0xFF;++cnt_100us;cnt_500us = cnt_100us / 5;if(cnt_500us && (cnt_100us%5==0) ) {switch(cnt_500us) {case 1: fdcan1_transmit(0x123, FDCAN_DLC_BYTES_64, TxData1); fdcan1_transmit(0x124, FDCAN_DLC_BYTES_64, TxData1);fdcan1_transmit(0x125, FDCAN_DLC_BYTES_64, TxData1); break;case 4: fdcan1_transmit(0x12345678, FDCAN_DLC_BYTES_64, TxData1); break;case 5: fdcan1_transmit(0x12345679, FDCAN_DLC_BYTES_64, TxData1); break;case 6: fdcan1_transmit(0x1234567A, FDCAN_DLC_BYTES_64, TxData1); break;case 7: /* next send */ break;case 8: break;case 20: ++count; cnt_100us = 0; break; //10ms}} else { //fail retransmission onceif(fdcan1_send_fail.flag) {fdcan1_transmit(fdcan1_send_fail.TxHeader.Identifier, fdcan1_send_fail.TxHeader.DataLength,fdcan1_send_fail.TxData);fdcan1_send_fail.flag = 0;}}}}
初始化
HAL_TIM_Base_Start_IT(&htim4);
fdcan1_config();
CANopen_App_Init(&slave_Data);
修改timerscfg.h
// The timer is incrementing every 10 us.
#define MS_TO_TIMEVAL(ms) ((ms) * 100U)
#define US_TO_TIMEVAL(us) ((us)/10)
根据实际修改,由于TIM4的时基100KHz(160Mhz/1600),也就是10us
经测试NMT的Boot_up (节点上线报文)、NMT 心跳报文 、NMT节点状态切换、SDO快速读、SDO快速写、SDO错误、PDO、节点守护均正常。
参考
【1】CanOpen协议【CanFestival】移植方法 支持VC、QT、STM32:https://bbs.21ic.com/icview-878522-1-1.html
【2】CANopen协议【CANFestival】移植方法:
https://www.cnblogs.com/ChYQ/p/5719469.html#:~:text=%E6%8E%A5%E4%B8%8B%E6%9D%A5%E5%BC%80%E5%A7%8B%E7%A7%BB%E6%A4%8D%EF%BC%9A%20%E6%AD%A5%E9%AA%A4%E4%B8%80%EF%BC%9A,%E5%9C%A8%E6%96%B0%E5%BB%BA%E5%A5%BD%E7%9A%84%E5%B7%A5%E7%A8%8B%E7%9B%AE%E5%BD%95%E4%B8%8B%E6%96%B0%E5%BB%BA%E6%96%87%E4%BB%B6%E5%A4%B9CanFestival%EF%BC%8C%E5%86%8D%E5%9C%A8CanFestival%E4%B8%8B%E6%96%B0%E5%BB%BA%E6%96%87%E4%BB%B6%E5%A4%B9driver%E3%80%81inc%E5%92%8Csrc%EF%BC%8C%E5%86%8D%E5%9C%A8inc%E6%96%87%E4%BB%B6%E5%A4%B9%E4%B8%8B%E9%9D%A2%E6%96%B0%E5%BB%BA%20stm32%E6%96%87%E4%BB%B6%E5%A4%B9%EF%BC%88%E6%88%91%E8%BF%99%E9%87%8C%E4%B8%BB%E8%A6%81%E4%BB%A5%E7%A7%BB%E6%A4%8D%E5%88%B0stm32%E4%B8%BA%E4%BE%8B%E8%AF%B4%E6%98%8E%EF%BC%8C%E5%A6%82%E6%9E%9C%E6%98%AF%E7%A7%BB%E6%A4%8D%E5%88%B0VC%E6%88%96%E5%85%B6%E4%BB%96%E5%B9%B3%E5%8F%B0%E4%B8%8B%EF%BC%8C%E8%BF%99%E9%87%8C%E4%B9%9F%E5%8F%AF%E4%BB%A5%E5%91%BD%E5%90%8D%E4%B8%BA%E5%85%B6%E4%BB%96%E5%90%8D%E5%AD%97%EF%BC%8C%E5%A6%82vc%EF%BC%89%E3%80%82
【3】STM32无系统移植CanFestival小白教程:https://blog.csdn.net/weixin_43072093/article/details/115245443
【4】CANOPEN 学习(一) CANFestival 字典工具 环境搭建:https://blog.csdn.net/mobei1983/article/details/110879850?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0-110879850-blog-115245443.pc_relevant_multi_platform_whitelistv1_exp2&spm=1001.2101.3001.4242.1&utm_relevant_index=3
【5】CanFestival字典生成:https://blog.csdn.net/lushoumin/article/details/92841982
【6】基于STM32F4的CANOpen移植教程(超级详细)_月落三千雪的博客-程序员秘密_canopen stm32:https://cxymm.net/article/qq_37662088/123261908
【7】驱动器使用 —— DS402状态切换(个人笔记):https://blog.csdn.net/weixin_43455581/article/details/103661372
【8】基于STM32F4的CANOpen移植教程(超级详细):
https://blog.csdn.net/qq_37662088/article/details/123261908
【9】CanFestival移植到STM32 F4芯片(基于HAL库):
https://www.iotword.com/32673.html
【10】B站CANopen视频
https://www.bilibili.com/video/BV1LF411E7KJ/?spm_id_from=333.1387.collection.video_card.click&vd_source=5f570a9f261c43941608688d2d31a4c5
【11】CANopen FD:
https://www.can-cia.org/can-knowledge/canopen-fd-the-art-of-embedded-networking
【12】STM32 CAN使用记录:FDCAN基础通讯:https://blog.csdn.net/Naisu_kun/article/details/132830048
【13】STM32G474_FDCAN的普通CAN模式使用(寄存器开发):https://shequ.stmicroelectronics.cn/thread-637235-1-1.html