STM32CubeMX与HAL库学习--简单的CAN回环测试
前言
本人小白,最近看了CAN协议与STM32的bxCAN外设的一些资料,简单地做个CAN回环测试练习一下。这只是一个初学者的简单练习记录,学习的话还是要看对应的教程啦。
使用到的工具及版本: STM32CubeMX版本:6.3.0 HAL库:STM32CubeF4 Firmware Package V1.26.1 MDK-ARM:V5.32.0.0 开发板:野火的霸天虎开发板V2(主控芯片是STM32F407ZGT6)
大概的框图如下:
STM32CubeMX生成初始化代码
时钟、串口配置之类的略过。 关于CAN配置:
在NVIC那里打开CAN1 RX0中断允许,并设定一下优先级。
在MDK-ARM里编辑代码
CAN的设置尚未完成,CubeMX那里只是设置了模式,接下来设置筛选器。我配置的是列表模式,筛选了拓展ID 0x2233和标准ID 0。
static void CAN_Filter_Config(void)
{
CAN_FilterTypeDef CAN_FilterTypeDef;
CAN_FilterTypeDef.FilterBank=0;
CAN_FilterTypeDef.FilterMode=CAN_FILTERMODE_IDLIST;
CAN_FilterTypeDef.FilterScale=CAN_FILTERSCALE_32BIT;
CAN_FilterTypeDef.FilterIdHigh= ((((uint32_t)0x2233<<3)|
CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF0000)>>16;
CAN_FilterTypeDef.FilterIdLow= (((uint32_t)0x2233<<3)|
CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF;
CAN_FilterTypeDef.FilterMaskIdHigh= 0;
CAN_FilterTypeDef.FilterMaskIdLow= 0;
CAN_FilterTypeDef.FilterFIFOAssignment=CAN_FILTER_FIFO0 ;
CAN_FilterTypeDef.FilterActivation=ENABLE;
HAL_CAN_ConfigFilter(&hcan1,&CAN_FilterTypeDef);
}
发送或者接收数据都有对应的结构体对帧的ID、帧类别、数据长度等进行了描述(CAN_RxHeaderTypeDef、CAN_TxHeaderTypeDef),发送时需要根据数据特性配置发送结构体,接收则是把接收数据的特性存储到指定的接收结构体。 消息发送函数 获取接收消息函数
因为只是简单的测试,我就在初始化阶段把他们设置好。 用一个CAN_Config把CAN的功能完整。
__IO uint32_t CAN_RxFlag = 0;
CAN_TxHeaderTypeDef TxMes;
CAN_RxHeaderTypeDef RxMes;
uint8_t CAN_TxDate[9]="CAN LOOP";
uint8_t CAN_RxDate[9];
uint32_t TxMailbox;
void CAN_RxMesInit(CAN_RxHeaderTypeDef* RxMessage)
{
(*RxMessage).StdId = 0x00;
(*RxMessage).ExtId = 0x00;
(*RxMessage).IDE = CAN_ID_STD;
(*RxMessage).DLC = 0;
(*RxMessage).RTR = 0;
(*RxMessage).FilterMatchIndex = 0;
(*RxMessage).Timestamp = 0;
}
void CAN_TxMsgInit(CAN_TxHeaderTypeDef* TxMessage)
{
(*TxMessage).StdId=0x00;
(*TxMessage).ExtId=0x2233;
(*TxMessage).IDE=CAN_ID_EXT;
(*TxMessage).RTR=CAN_RTR_DATA;
(*TxMessage).DLC=8;
}
void CAN_Config(void)
{
CAN_Filter_Config();
CAN_TxMsgInit(&TxMes);
CAN_RxMesInit(&RxMes);
HAL_CAN_ActivateNotification(&hcan1,CAN_IT_RX_FIFO0_MSG_PENDING);
HAL_CAN_Start(&hcan1);
}
在CubeMX生成的CAN1初始化函数中调用自己写的CAN_Config(),完成CAN的设置并开启它。 FIFO0接收到数据的中断回调函数与CAN错误回调。
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef* hcan)
{
HAL_CAN_GetRxMessage(&hcan1,CAN_RX_FIFO0,&RxMes,CAN_RxDate);
if((RxMes.ExtId==0x2233) && (RxMes.IDE==CAN_ID_EXT) && (RxMes.DLC==8) )
{
CAN_RxFlag = 1;
}
else
{
CAN_RxFlag = 0;
}
}
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan)
{
printf("\r\nCAN出错\r\n");
}
main函数。
extern __IO uint32_t CAN_RxFlag;
extern uint8_t CAN_TxDate[8];
extern uint8_t CAN_RxDate[8];
extern CAN_TxHeaderTypeDef TxMes;
extern CAN_RxHeaderTypeDef RxMes;
extern uint32_t TxMailbox;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_CAN1_Init();
MX_NVIC_Init();
printf("CAN LOOP TEST\r\n");
HAL_CAN_AddTxMessage(&hcan1, &TxMes, CAN_TxDate, &TxMailbox);
while (1)
{
if(CAN_RxFlag)
{
CAN_RxFlag = 0;
printf("Transmit message:%s\r\n",CAN_TxDate);
printf("Receive message:%s\r\n",CAN_RxDate);
}
}
}
效果
其他
最初,我把发送数组和接收数组的长度设为8
uint8_t CAN_TxDate[8]="CAN LOOP";
uint8_t CAN_RxDate[8];
结果是这样: 不出意外的话这两个数组在内存中是这样的:
如果只是用来存储CAN数据这倒是没什么问题,但是用printf把它们作为字符数组输出的话就可能会像图中一样越界了。 所以我把这两个数组长度定义为9,0~7字节是数据,第8字节一直是0(编译器给它初始化为0),这样就可以防止printf输出越界。
后续
接下来,在原来的基础上增加一个按键,把按键按下次数转换为字符串存储到CAN_TxDate数组,并在按键按下的中断里通过CAN发送,其他基本与上文无异,我的实现步骤如下: 首先打开原来的CubeMX工程文件,找到按键的引脚,把它配置为外部中断 使能中断 然后就可以生成代码了,别忘了勾选保留用户代码 写在用户区域的代码会得以保留,写的时候注意一下位置,我前面while循环里的代码写错位置了,重新生成文件时就被删了。 正确的位置
while (1)
{
if(CAN_RxFlag)
{
CAN_RxFlag = 0;
printf("Transmit message:%s\r\n",CAN_TxDate);
printf("Receive message:%s\r\n",CAN_RxDate);
}
}
错误的位置
while (1)
{
if(CAN_RxFlag)
{
CAN_RxFlag = 0;
printf("Transmit message:%s\r\n",CAN_TxDate);
printf("Receive message:%s\r\n",CAN_RxDate);
}
}
其实看注释名字就知道了。 在main.c里增加了按键中断的回调函数。
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
static uint16_t KeyTime = 0;
if(GPIO_Pin == KEY1_Pin)
{
KeyTime++;
sprintf((char*)CAN_TxDate,"%d",KeyTime);
HAL_CAN_AddTxMessage(&hcan1, &TxMes, CAN_TxDate, &TxMailbox);
}
}
删除了main函数里的CAN数据发送HAL_CAN_AddTxMessage(&hcan1, &TxMes, CAN_TxDate, &TxMailbox); 这样,数据只在按键按下时发送,其他不变。 结果
|