文章目錄
- 一、準備工作
- 二、軟件配置
- 三、移植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