Canfestival_移植以及修改移植后的各种bug

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

移植文件

我移植过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;
				}
    }
}

下一节讲心跳的配置以及RPDO和TPDO以及遇到的坑。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值