移植文件
我移植过H7和F7的。我以H7的来说明。H7的CAN有些配置和F7是不一样的。踩过的坑和在这里说一下。




把C文件添加到keil中,把头文件的路径也添加上。
我移植过程中遇到的问题有:
1.注释掉这些文件
2我是用keil,但是使用cpp来编译的,所以会有指针不匹配的现象
3乱码,这里很发现,虽然报错了,但是找了好久才发现的
4。用c++编译的,所以要加上这个,不然会找不到头文件
5.其他问题都是些小问题。网上也能找到。
2个定时器函数编写
这两个定时器函数是被canfestival用来做硬件定时器和软件定时器的计时。具体怎么计时。要分析下来的函数
/ 这个函数是在定时器中断中执行.
//此函数是执行设定好的回调函数.
//会先计算,进入中断到当前语句经过的时间overrun.
//overrun的作用是减少误差.
//再将overrun加上完全休眠时间,得到真正的完全休眠时间.
//在for中,如果真的休眠时间大于设定的
//row->val(从上次中断开始,还有多长时间进入回调)
//那么就使能回调函数,非周期性的回调执行后就会失能.
//周期性的回调会计算overrun对回调周期的影响,
//从新给row->val进行赋值.
//如果row->val小于next_wakeup,
//就更新next_wakeup为小的row-val.
//如果for中有的回调任务时间没有到.
//那么将对应的row->val减去真正的完全休眠时间. 作者:GXTon https://www.bilibili.com/read/cv12630004 出处:bilibili
void TimeDispatch(void)
{
TIMER_HANDLE i;
TIMEVAL next_wakeup = TIMEVAL_MAX; /* used to compute when should normaly occur next wakeup */
/* First run : change timer state depending on time */
/* Get time since timer signal */
UNS32 overrun = (UNS32)getElapsedTime();
TIMEVAL real_total_sleep_time = total_sleep_time + overrun;
s_timer_entry *row;
for(i=0, row = timers; i <= last_timer_raw; i++, row++)
{
if (row->state & TIMER_ARMED) /* if row is active */
{
if (row->val <= real_total_sleep_time) /* to be trigged */
{
if (!row->interval) /* if simply outdated */
{
row->state = TIMER_TRIG; /* ask for trig */
}
else /* or period have expired */
{
/* set val as interval, with 32 bit overrun correction, */
/* modulo for 64 bit not available on all platforms */
row->val = row->interval - (overrun % (UNS32)row->interval);
row->state = TIMER_TRIG_PERIOD; /* ask for trig, periodic */
/* Check if this new timer value is the soonest */
if(row->val < next_wakeup)
next_wakeup = row->val;
}
}
else
{
/* Each armed timer value in decremented. */
row->val -= real_total_sleep_time;
/* Check if this new timer value is the soonest */
if(row->val < next_wakeup)
next_wakeup = row->val;
}
}
}
/* Remember how much time we should sleep. */
total_sleep_time = next_wakeup;
/* Set timer to soonest occurence */
setTimer(next_wakeup);
/* Then trig them or not. */
for(i=0, row = timers; i<=last_timer_raw; i++, row++)
{
if (row->state & TIMER_TRIG)
{
row->state &= ~TIMER_TRIG; /* reset trig state (will be free if not periodic) */
if(row->callback)
(*row->callback)(row->d, row->id); /* trig ! */
}
}
}
这里给上别人的分析:

以下是两个定时器的函数,(也是抄别人的)
extern CAN_HandleTypeDef CAN1_Handler;
extern CanTxMsgTypeDef TxMessage;
#define CANOPEN_TIM_PERIOD 1000 //定时周期
/* 定时器TIM相关变量 */
static TIMEVAL last_counter_val = 0;
static TIMEVAL elapsed_time = 0;
void Canopen_Driver_Init(UNS16 CANOPEN_TIM_PERIOD_tmp)
{
setNodeId(&TestSlave_Data, Can_node_id); //设置从站nodeid==0x05
Call_Back_Init();
setState(&TestSlave_Data, Initialisation); //让canopen进入pre-op状态
setState(&TestSlave_Data, Operational);
//CANOPEN_TIM_PERIOD = CANOPEN_TIM_PERIOD_tmp;
}
//#include "timer.h"
/************************************************
函数名称 : setTimer (CANOpen接口函数)
功 能 : Set the timer for the next alarm.
参 数 : value --- 参数
返 回 值 : 无
*************************************************/
void setTimer(TIMEVAL value)
{
uint32_t timer = __HAL_TIM_GetCounter(&TIM3_Handler);; // Copy the value of the running timer
elapsed_time += timer - last_counter_val;
last_counter_val = CANOPEN_TIM_PERIOD - value;
__HAL_TIM_SetCounter(&TIM3_Handler, CANOPEN_TIM_PERIOD - value);
}
/************************************************
函数名称 : getElapsedTime (CANOpen接口函数)
功 能 : Return the elapsed time to tell the Stack how much time is spent since last call.
参 数 : 无
返 回 值 : (消逝的)时间
主要被源码用来查询距离下一个定时触发还有多少时间
*************************************************/
TIMEVAL getElapsedTime(void)
{
uint32_t timer = __HAL_TIM_GetCounter(&TIM3_Handler); // Copy the value of the running timer
if(timer < last_counter_val)
timer += CANOPEN_TIM_PERIOD;
TIMEVAL elapsed = timer - last_counter_val + elapsed_time;
return elapsed;
}
下面这个函数是硬件定时器中断时调用,TimeDispatch会处理软件定时器“们”。
void TIMx_DispatchFromISR(void)
{
last_counter_val = 0;
elapsed_time = 0;
TimeDispatch();
}
有的代码写的好像不用这么复杂的、、不过我没验证过。可能这个没有做一些判断溢出的处理。
像这个https://blog.youkuaiyun.com/weixin_44118217/article/details/121265571

1ms定时器的一些宏定义
上面的CANOPEN_TIM_PERIOD 是1000是因为我的定时器设置的周期arr是1000,我设置的频率是1K(1000),我的分频是240,我的定时器总线频率是240M,所以。我定时器实际时钟是1M,也就是1us,记住这个1us。很重要。
那么我要1ms。那就是计数1000个1us。

下面宏定义是timerscfg.h文件里面的。很重要的文件,用来软件定时器定时的。
配置错了。心跳和PDO计数发送等需要时间计算的都会不准确。
#ifndef __TIMERSCFG_H__
#define __TIMERSCFG_H__
#ifdef __cplusplus
extern "C" {
#endif
// Whatever your microcontroller, the timer wont work if
// TIMEVAL is not at least on 32 bits
#define TIMEVAL UNS32
// using 16 bits timer
//#define TIMEVAL_MAX 0xFFFF
#define TIMEVAL_MAX 1000
// The timer is incrementing every 1 us.
#define MS_TO_TIMEVAL(ms) ((ms) * 1000)
#define US_TO_TIMEVAL(us) ((us) / 1)
#ifdef __cplusplus
}
#endif
#endif
MS_TO_TIMEVAL(ms)和US_TO_TIMEVAL(us)跟我们定时器的计数频率有关。
我们这里是1M.那就是1us,1ms就等于1000*1us。
上面的TIMEVAL_MAX不能超过你设置的定时器的周期,根据我的实验发现,好像要等于定时器的周期,如果不等于,我的硬件定时器中断函数中加上TIMx_DispatchFromISR这个函数后,也就是TimeDispatch(),我中断周期就不是1ms了,但是我的心跳报文还是很准确,我配置的1S还是会1S发送

这个TIMx_DispatchFromISR会影响定时器的周期(1ms)。当我注释掉这个函数时,if(++i>1000)刚好是1S,加上这个函数之后要1分钟才打印,
在if外面的 // printf(“1\r\n”);,反而变成了1S打印一次,也就是说。这个函数把定时器周期从1ms变成了1S。,但是从报文来看。我的配置是没错的。1S发送一次心跳。

后来我把这个写成了FFFF,应该是1000.和我的最大周期一致才行、

给上可能是解释这个的网址(可能):
https://www.bilibili.com/read/cv12630004
Canfestival的需要的发送函数
H7踩过的坑:
就是发送的时候FDCAN1_TxHeader.TxFrameType的赋是FDCAN_REMOTE_FRAME
和F7的不一样的:一个是0x00000002U,,一个是0x20000000U
所以。移植要很小心。。



unsigned char canSend(CAN_PORT notused, Message *m)
{
uint32_t i;
uint8_t txbuf[8]={0,0,0,0,0,0,0,0};
FDCAN1_TxHeader.Identifier=m->cob_id;
//// //32位ID
FDCAN1_TxHeader.IdType=FDCAN_STANDARD_ID; //标准ID
FDCAN1_TxHeader.DataLength=FDCAN_DLC_BYTES_8; //数据长度
FDCAN1_TxHeader.ErrorStateIndicator=FDCAN_ESI_ACTIVE;
FDCAN1_TxHeader.BitRateSwitch=FDCAN_BRS_OFF; //关闭速率切换
FDCAN1_TxHeader.FDFormat=FDCAN_CLASSIC_CAN; //传统的CAN模式
FDCAN1_TxHeader.TxEventFifoControl=FDCAN_NO_TX_EVENTS; //无发送事件
FDCAN1_TxHeader.MessageMarker=0;
if(m->rtr==1)
{
FDCAN1_TxHeader.TxFrameType=FDCAN_REMOTE_FRAME; //远程帧
}
else
{
FDCAN1_TxHeader.TxFrameType=FDCAN_DATA_FRAME; //数据帧
}
for(i = 0; i < m->len; i++)
{
//printf("%x ",m->data[i]);
txbuf[i] = m->data[i];
}
//printf("\r\n");
if(HAL_FDCAN_AddMessageToTxFifoQ(&FDCAN1_Handler,&FDCAN1_TxHeader,txbuf)!=HAL_OK)
{
// printf("1\r\n");
return 1;//发送
}
return 0;
// uint8_t txbuf[8];
// uint32_t i;
}
CAN接收中断函数
主要Canfestival需要的里面的参数
H7踩过的坑:
H7接收到一帧数据后。是要判断帧种类的。。
不能直接赋值 RxMSG.rtr = FDCAN1_RxHeader.RxFrameType
这样是错误的。因为FDCAN1_RxHeader.RxFrameType是一个32位的0x20000000U。
之前就是因为这个。导致接收到数据,但是canDispatch却分析不出来。
因为RxMSG.rtr只有1和0.

extern "C"void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs)
{
u8 i=0;
//printf("1\r\n");
//RxFifo0ITs是一个中断的标志位,表示是什么中断,FDCAN 中断寄存器 (FDCAN_IR)
if((RxFifo0ITs&FDCAN_IT_RX_FIFO0_NEW_MESSAGE)!=RESET) //FIFO1新数据中断
{
// printf("xxxxx\r\n");
//提取FIFO0中接收到的数据
HAL_FDCAN_GetRxMessage(hfdcan,FDCAN_RX_FIFO0,&FDCAN1_RxHeader,Can1_APP.CanBus_RecvBuf);
//RXF0C.F0SA、RXF1C.F1SA 和 RXBC.RBSA,看手册
Can1_APP.Client_Id = FDCAN1_RxHeader.Identifier;
Can1_APP.CanBus_Recv_Len = FDCAN1_RxHeader.DataLength>>16;
/************Canfestival需要的********************************************/
RxMSG.cob_id =FDCAN1_RxHeader.Identifier;
RxMSG.len = FDCAN1_RxHeader.DataLength>>16;
if( FDCAN1_RxHeader.RxFrameType == FDCAN_REMOTE_FRAME )
{
RxMSG.rtr = 1;
}
else
{
RxMSG.rtr = 0;
}
memcpy(RxMSG.data, Can1_APP.CanBus_RecvBuf, FDCAN1_RxHeader.DataLength>>16);
canDispatch(&TestSlave_Data, &(RxMSG)); //canopen报文分析
/**************************************************************************/
HAL_FDCAN_ActivateNotification(hfdcan,FDCAN_IT_RX_FIFO0_NEW_MESSAGE,0);
// printf("%x\r\n",RxMSG.cob_id);
if(Can1_APP.Client_Id == Can_Recv_Client_ID)
{
// printf("%d\r\n",Can1_APP.CanBus_Recv_Len);
//
// printf("%x\r\n",Can1_APP.CanBus_RecvBuf[0]);
Can1_APP.Can_Recv_Flag =1;
}
}
}

本文详细介绍了在STM32平台上移植Canfestival的过程,包括移植文件、定时器函数编写、1ms定时器宏定义以及发送和接收函数的注意事项。在移植过程中遇到的指针不匹配、乱码、头文件找不到等问题,作者提供了解决方案,并分享了关于硬件定时器中断、软件定时器配置的经验,特别是1ms定时器周期的调整和CAN发送帧类型的坑。
3870

被折叠的 条评论
为什么被折叠?



