CUBEMX+CANOPEN教程二:can功能代码完善

在教程一基础上进行can功能完善

主要工作:

  1. can筛选器配置
  2. can发送与接收的相关结构体构建
  3. can发送与接收等函数构建
  4. can festival移植
  5. 定时器配置

文章目录

  • 1. can筛选器配置
  • 2. can发送与接收的相关数据结构构建
  • 3. can发送与接收等函数构建
  • 4. can festival移植
  • 5. 定时器配置
  • 小结

1. can筛选器配置

在bsp_can.c文件中添加

void CAN_Filter_Init(void)
{
	CAN_FilterTypeDef CAN_FilterInitStructure;

	CAN_FilterInitStructure.FilterActivation       	= ENABLE; 
	CAN_FilterInitStructure.FilterBank				= 0;
	CAN_FilterInitStructure.FilterFIFOAssignment   	= CAN_FILTER_FIFO0;
	CAN_FilterInitStructure.FilterIdHigh           	= 0x0000;
	CAN_FilterInitStructure.FilterIdLow           	= 0x0000;
	CAN_FilterInitStructure.FilterMaskIdHigh        = 0x0000;
	CAN_FilterInitStructure.FilterMaskIdLow         = 0x0000;
	CAN_FilterInitStructure.FilterMode				= CAN_FILTERMODE_IDMASK;
	CAN_FilterInitStructure.FilterScale            	= CAN_FILTERSCALE_32BIT;
	CAN_FilterInitStructure.SlaveStartFilterBank	= 14;
	
	if(HAL_CAN_ConfigFilter(&hcan1, &CAN_FilterInitStructure) != HAL_OK)
	{
		_Error_Handler(__FILE__, __LINE__);
	}
	if(HAL_CAN_Start(&hcan1) != HAL_OK)
	{
		_Error_Handler(__FILE__, __LINE__);
	}
	if(HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
	{
		_Error_Handler(__FILE__, __LINE__);
	}
}

2. can发送与接收的相关数据结构构建

CAN1发送与接收数据帧头定义与标准库有所不同

/* bsp_can.c */
/* CAN1发送与接收数据帧头 */
CAN_TxHeaderTypeDef				CAN1_TxHeaderMessage;
CAN_RxHeaderTypeDef				CAN1_RxHeaderMessage;

/* CAN1发送与接收实际数据 */
CAN_Msgdata can1_tx_msg;
CAN_Msgdata can1_rx_msg;
/* bsp_can.h */
 typedef struct{
	Message m;
}CANOpen_Message;

typedef struct
{
	uint8_t Data[8];
}CAN_Msgdata;

其中Message为结构体在can.h文件中定义

/* can.h */
typedef struct {
  UNS16 cob_id;	/**< message's ID */
  UNS8 rtr;		/**< remote transmission request. (0 if not rtr message, 1 if rtr message) */
  UNS8 len;		/**< message's length (0 to 8) */
  UNS8 data[8]; /**< message's datas */
} Message;

#define Message_Initializer {0,0,0,{0,0,0,0,0,0,0,0}}

typedef UNS8 (*canSend_t)(Message *);

3. can发送与接收等函数构建

发送函数

/* bsp_can.c */
u8 canSend(CAN_HandleTypeDef *hcan,Message *m)
{
	unsigned char i;
	CAN_TxHeaderTypeDef TxMessage;
	
	TxMessage.StdId = (uint32_t)(m->cob_id);
	TxMessage.ExtId = 0x00;
	TxMessage.RTR = m->rtr; 							  
	TxMessage.IDE = CAN_ID_STD; 						  
	TxMessage.DLC = 8; 	
	TxMessage.DLC = m->len; 		
	for(i=0;ilen;i++)								  
	{
		can1_tx_msg.Data[i] = m->data[i];
	}
	if(HAL_CAN_AddTxMessage(&hcan1, &TxMessage, can1_tx_msg.Data, (uint32_t*)CAN_TX_MAILBOX0) != HAL_OK)
	{
		return 1;
//	 	printf("发送失败\r\n");
//		_Error_Handler(__FILE__, __LINE__);
	}
	return 0;
}

can接收队列挂起中断回调函数

/* bsp_can.c */
CANOpen_Message CAN1_Rx_m;
Message RxMSG = Message_Initializer;
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
	if(hcan->Instance == CAN1)
	{
		/* 得到can接收数据 */
		 HAL_CAN_GetRxMessage(hcan,CAN_RX_FIFO0, &CAN1_RxHeaderMessage, can1_rx_msg.Data);
		
		/* 处理can接收数据按照 canDispatch函数要求的数据格式*/
		 RxMSG.cob_id = CAN1_RxHeaderMessage.StdId;
		 RxMSG.len = CAN1_RxHeaderMessage.DLC;
		 RxMSG.rtr = CAN1_RxHeaderMessage.RTR;
		 memcpy(RxMSG.data,can1_rx_msg.Data,CAN1_RxHeaderMessage.DLC);
	}

//   AnalysisMessagefromDriver();
//	SEGGER_RTT_printf(0, "can master revcive data!!!!!!!!!!!!\n");
//	canDispatch(CANOpenMasterObject, &(RxMSG));
}

注意以上代码最后三行的三个被注销函数,第一个是分析canopen网络主机接收数据的,实际项目应用;第二个是打印,不重要;第三个是canopen报文分析的,很重要;

4. can festival移植

分析源码可以知道,canopen实现的最重要的就是用定时器来控制canopen网络的报文发送和接受时序和裁决。在移植好can festival后主要就是讲定时器融合到festival源码的所用的定时器功能中去。

5. 定时器配置

定时为1ms

/* tim.c */
/* TIM4 init function */
void MX_TIM4_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig;
  TIM_MasterConfigTypeDef sMasterConfig;
  TIM_OC_InitTypeDef sConfigOC;

  htim4.Instance = TIM4;
  htim4.Init.Prescaler = 84-1;
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim4.Init.Period = 1000-1;
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  if (HAL_TIM_OC_Init(&htim4) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  sConfigOC.OCMode = TIM_OCMODE_TIMING;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_OC_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

在bsp_timer.c中实现几个函数,主要是提供给festival源码中调用

  1. 实现定时器设置自动重装载值
  2. 实现定时器得到当前计数值
  3. 实现定时器初始化函数
  4. 实现定时器比较输出中断回调函数
#ifdef CANOPEN_MY
#include 
#include "canfestival.h"
#include "timer.h"

#endif

#ifdef CANOPEN_MY
static TIMEVAL last_time_set = TIMEVAL_MAX;

/**
  * @brief  setTimer
  * @param  value:Set time value 0x0000-0xffff
  * @retval NONE
  */
void setTimer(TIMEVAL value)
{
	__HAL_TIM_SetAutoreload(&htim4, value);
}
/**
  * @brief  getElapsedTime
  * @param  NONE
	* @retval TIMEVAL:Return current timer value
  */
TIMEVAL getElapsedTime(void)
{
	uint16_t timer = __HAL_TIM_GetCounter(&htim4);
	

	return timer > last_time_set ? timer - last_time_set : last_time_set - timer; 	
}

/**
  * @brief  TIM4_start
  * @param  NONE
  * @retval NONE
  */
void TIM4_start(void)
{
		MX_TIM4_Init();
}
/**
  * @brief  initTimer
  * @param  NONE
  * @retval NONE
  */
void initTimer(void)
{
		TIM4_start();
}

其中#define CANOPEN_MY 已经在main.h文件中定义,定时器开启并不是在主函数中调用,而是在festival源码中调用开启定时器。

/* CANOpenObjDictConfig.c */
void Master_operational(CO_Data* d)
{
	UNS32 SyncTimePeriod = 2000;
	UNS32 size = sizeof(SyncTimePeriod);
	
	writeLocalDict( d, /*CO_Data* d*/
			0x1006, /*UNS16 index*/
			0x00, /*UNS8 subind*/ 
			&SyncTimePeriod, /*void * pSourceData,*/ 
			&size, /* UNS8 * pExpectedSize*/
			RW);  /* UNS8 checkAccess */
	stopSYNC(d);
	startSYNC(d);
	setTimer(100);
//	TIM_Cmd(TIM4, ENABLE); //20190408 FreeRTOS 移植 CANopen
	HAL_TIM_Base_Start_IT(&htim4);
	SEGGER_RTT_printf(0, "Master_operational\n");
}

在主函数初始化时调用canopen网络初始化,CanopenInit();此函数在festival源码中已有定义,定义如下:

/**
  * @brief  CanopenInit
  * @param  NONE
  * @retval NONE
  */
void CanopenInit(void)
{
	UNS32 i;
	
	initTimer();   
	CANOpenMasterObject->canHandle = CAN1;
	CANOpenMasterInit(CANOpenMasterObject);
	setState(CANOpenMasterObject,Initialisation);
	setNodeId (CANOpenMasterObject, 127);
//	canInit(CAN1,CAN_BAUD_1M);      
	canInit(); //20190408 FreeRTOS 移植 CANopen	
	for(i=0; i<65536; i++);
	for(i=0; i<65536; i++);
	for(i=0; i<65536; i++);
	for(i=0; i<65536; i++);
	for(i=0; i<65536; i++);
}

源码中的can初始化是可以配置波特率的,应为cubemx中已经配置好波特率,因此将canInit(CAN1,CAN_BAUD_1M);函数注销,自主实现canInit();函数,此函数定义如下:

u8 canInit(void)
{
	MX_CAN1_Init();
	CAN_Filter_Init();
	return 0;
}

此函数就是调用cubemx生成的can初始化函数,在加上自己实现的can过滤器初始化函数。

至此完成can festival移植已经完成,实现canopen通讯网络主机功能。

小结

你可能感兴趣的:(嵌入式)