在教程一基础上进行can功能完善
主要工作:
- can筛选器配置
- can发送与接收的相关结构体构建
- can发送与接收等函数构建
- can festival移植
- 定时器配置
在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__);
}
}
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 *);
发送函数
/* 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报文分析的,很重要;
分析源码可以知道,canopen实现的最重要的就是用定时器来控制canopen网络的报文发送和接受时序和裁决。在移植好can festival后主要就是讲定时器融合到festival源码的所用的定时器功能中去。
定时为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源码中调用
- 实现定时器设置自动重装载值
- 实现定时器得到当前计数值
- 实现定时器初始化函数
- 实现定时器比较输出中断回调函数
#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通讯网络主机功能。