基于STM32F429HAL库的CAN通信代码

基于STM32F429HAL库的CAN通信代码*

最近在学正点原子阿波罗STM32F429开发板的CAN通信章节,例程里提供了查询方式实现的CAN通信实验。昨天又复习了一遍UART串口实验的中断例程,原子哥手把手地讲了串口接收中断的实现。基于此,在正点原子代码基础上,编写了CAN通信接收中断处理方式的部分代码。

首先是CAN发送接收初始化部分。定义全局句柄,设置发送波特率,接收FIFO。用return返回值,来检测初始化是否成功(一般用在主函数里判断)。

`

```c
``
CAN_HandleTypeDef   CAN1_Handler;   //CAN1句柄
CanTxMsgTypeDef     TxMessage;//发送
CanRxMsgTypeDef     RxMessage;//接收
uint32_t std_id=0x00;
CAN初始化
//则波特率为:45M/((6+8+1)*6)=500Kbps
//返回值:0,初始化OK;
//    其他,初始化失败; 
u8 CAN1_Mode_Init(u32 tsjw,u32 tbs2,u32 tbs1,u16 brp,u32 mode)
{
    CAN_FilterConfTypeDef  CAN1_FilerConf;
    CAN1_Handler.Instance=CAN1; 
    CAN1_Handler.pTxMsg=&TxMessage;     //发送消息
	  CAN1_Handler.pRxMsg=&RxMessage;     //接收消息
    CAN1_Handler.Init.Prescaler=brp;    //分频系数(Fdiv)为brp+1
    CAN1_Handler.Init.Mode=mode;        //模式设置 
    CAN1_Handler.Init.SJW=tsjw;         //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1TQ~CAN_SJW_4TQ
    CAN1_Handler.Init.BS1=tbs1;         //tbs1范围CAN_BS1_1TQ~CAN_BS1_16TQ
    CAN1_Handler.Init.BS2=tbs2;         //tbs2范围CAN_BS2_1TQ~CAN_BS2_8TQ
    CAN1_Handler.Init.TTCM=DISABLE;     //非时间触发通信模式 
    CAN1_Handler.Init.ABOM=DISABLE;     //软件自动离线管理
    CAN1_Handler.Init.AWUM=DISABLE;     //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
    CAN1_Handler.Init.NART=ENABLE;      //禁止报文自动传送 
    CAN1_Handler.Init.RFLM=DISABLE;     //报文不锁定,新的覆盖旧的 
    CAN1_Handler.Init.TXFP=DISABLE;     //优先级由报文标识符决定 
	
    if(HAL_CAN_Init(&CAN1_Handler)!=HAL_OK) return 1;   //初始化
	  
	  //接收FIFO设置
	  CAN1_FilerConf.FilterIdHigh=0X0000;     //32位ID
    CAN1_FilerConf.FilterIdLow=0X0000;
    CAN1_FilerConf.FilterMaskIdHigh=0X0000; //32位MASK
    CAN1_FilerConf.FilterMaskIdLow=0X0000;  
    CAN1_FilerConf.FilterFIFOAssignment=CAN_FILTER_FIFO0;//过滤器0关联到FIFO0
    CAN1_FilerConf.FilterNumber=0;          //过滤器0
    CAN1_FilerConf.FilterMode=CAN_FILTERMODE_IDMASK;
    CAN1_FilerConf.FilterScale=CAN_FILTERSCALE_32BIT;
    CAN1_FilerConf.FilterActivation=ENABLE; //激活滤波器0
    CAN1_FilerConf.BankNumber=14;
	
    if(HAL_CAN_ConfigFilter(&CAN1_Handler,&CAN1_FilerConf)!=HAL_OK) return 2;//滤波器初始化
		
    else return 0;
}

接下来是编写HAL_CAN_Init()函数的回调函数,主要是GPIO引脚复用,时钟使能,设置中断优先级。
``c


```c

```c

```c

```c
```c
//CAN底层驱动,引脚配置,时钟配置,中断配置
//此函数会被HAL_CAN_Init()调用
//hcan:CAN句柄
``
void HAL_CAN_MspInit(CAN_HandleTypeDef* hcan)
{
    GPIO_InitTypeDef GPIO_Initure;`
    __HAL_RCC_CAN1_CLK_ENABLE();                //使能CAN1时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();			    //开启GPIOA时钟
	
    GPIO_Initure.Pin=GPIO_PIN_11|GPIO_PIN_12;   //PA11,12
    GPIO_Initure.Mode=GPIO_MODE_AF_PP;          //推挽复用
    GPIO_Initure.Pull=GPIO_PULLUP;              //上拉
    GPIO_Initure.Speed=GPIO_SPEED_FAST;         //快速
    GPIO_Initure.Alternate=GPIO_AF9_CAN1;       //复用为CAN1
    HAL_GPIO_Init(GPIOA,&GPIO_Initure);         //初始化
	
	  __HAL_CAN_ENABLE_IT(&CAN1_Handler,CAN_IT_FMP0);//FIFO0消息挂起中断允许
	  HAL_NVIC_SetPriority(CAN1_RX0_IRQn,1,2);    //抢占优先级1,子优先级2
    HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);          //使能中断
}

``
然后是编写CAN发送函数和CAN接收中断处理函数。CAN发送函数CAN1_Send_Msg()相对简单,即在内部先设置发送ID、字长等信息,然后利用数组循环发送数据。

//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)	
//len:数据长度(最大为8)				     
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
//		 其他,失败;
	
u8 CAN1_Send_Msg(u8* msg,u8 len)
{	
	 u16 i=0;
	 TxMessage.RTR=CAN_RTR_DATA;
	 TxMessage.IDE=CAN_ID_STD;
	 TxMessage.ExtId=0x00;
	 TxMessage.StdId=std_id;
	 TxMessage.DLC=len;
	 for(i=0;i<len;i++)
	 TxMessage.Data[i]=msg[i];
  // CAN1_Handler.pTxMsg->Data[i]=msg[i];
   if(HAL_CAN_Transmit(&CAN1_Handler,10)!=HAL_OK) return 1;     //发送
   else return 0;		
}

`最重要的,即是CAN接收中断函数的编写。HAL库提供了名为CAN1_RX0_IRQHandler()的CAN中断处理函数。在其内部调用 HAL_CAN_IRQHandler()函数,而该函数会调用CAN_Receive_IT()函数,CAN_Receive_IT()函数,最终调用_weak回调函数HAL_CAN_RxCpltCallback()来处理。因此,CAN接收代码写在该回调函数中。其主要实现逻辑为,CAN每发送一个数据,都传到TxMessage.Data[i]数组中,在中断回调函数里将数据传递,即 RxMessage.Data[i]=TxMessage.Data[i]语句,实现了CAN接收功能。另外,在其内部,还有LCD屏幕显示功能,可在CAN回环模式下,显示发送接收是否正常。

 //CAN中断处理过程
//此函数会被CAN_Receive_IT()调用
//hcan:CAN句柄
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan)
{   
    int i=0;
    //CAN_Receive_IT()函数会关闭FIFO0消息挂号中断,因此我们需要重新打开
    __HAL_CAN_ENABLE_IT(&CAN1_Handler,CAN_IT_FMP0);//重新开启FIF00消息挂号中断
    for(i=0;i<8;i++)
    RxMessage.Data[i]=TxMessage.Data[i];
	  LCD_Fill(30,270,160,310,WHITE);//清除之前的显示
 			for(i=0;i<RxMessage.DLC;i++)
			{									    
				if(i<4)LCD_ShowxNum(30+i*32,270,RxMessage.Data[i],3,16,0X80);	//显示数据
				else LCD_ShowxNum(30+(i-4)*32,290,RxMessage.Data[i],3,16,0X80);	//显示数据
 			}
}

``
最后贴一下,can.c内的完整代码,如下,

``

#include "can.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "lcd.h"
#include "sdram.h"

  //
CAN_HandleTypeDef   CAN1_Handler;   //CAN1句柄
CanTxMsgTypeDef     TxMessage;//发送
CanRxMsgTypeDef     RxMessage;//接收
uint32_t std_id=0x00;
CAN初始化
//则波特率为:45M/((6+8+1)*6)=500Kbps
//返回值:0,初始化OK;
//    其他,初始化失败; 

u8 CAN1_Mode_Init(u32 tsjw,u32 tbs2,u32 tbs1,u16 brp,u32 mode)
{
    CAN_FilterConfTypeDef  CAN1_FilerConf;
    
    CAN1_Handler.Instance=CAN1; 
    CAN1_Handler.pTxMsg=&TxMessage;     //发送消息
	  CAN1_Handler.pRxMsg=&RxMessage;     //接收消息
    CAN1_Handler.Init.Prescaler=brp;    //分频系数(Fdiv)为brp+1
    CAN1_Handler.Init.Mode=mode;        //模式设置 
    CAN1_Handler.Init.SJW=tsjw;         //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1TQ~CAN_SJW_4TQ
    CAN1_Handler.Init.BS1=tbs1;         //tbs1范围CAN_BS1_1TQ~CAN_BS1_16TQ
    CAN1_Handler.Init.BS2=tbs2;         //tbs2范围CAN_BS2_1TQ~CAN_BS2_8TQ
    CAN1_Handler.Init.TTCM=DISABLE;     //非时间触发通信模式 
    CAN1_Handler.Init.ABOM=DISABLE;     //软件自动离线管理
    CAN1_Handler.Init.AWUM=DISABLE;     //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
    CAN1_Handler.Init.NART=ENABLE;      //禁止报文自动传送 
    CAN1_Handler.Init.RFLM=DISABLE;     //报文不锁定,新的覆盖旧的 
    CAN1_Handler.Init.TXFP=DISABLE;     //优先级由报文标识符决定 
	
    if(HAL_CAN_Init(&CAN1_Handler)!=HAL_OK) return 1;   //初始化
	  
	  //接收FIFO设置
	  CAN1_FilerConf.FilterIdHigh=0X0000;     //32位ID
    CAN1_FilerConf.FilterIdLow=0X0000;
    CAN1_FilerConf.FilterMaskIdHigh=0X0000; //32位MASK
    CAN1_FilerConf.FilterMaskIdLow=0X0000;  
    CAN1_FilerConf.FilterFIFOAssignment=CAN_FILTER_FIFO0;//过滤器0关联到FIFO0
    CAN1_FilerConf.FilterNumber=0;          //过滤器0
    CAN1_FilerConf.FilterMode=CAN_FILTERMODE_IDMASK;
    CAN1_FilerConf.FilterScale=CAN_FILTERSCALE_32BIT;
    CAN1_FilerConf.FilterActivation=ENABLE; //激活滤波器0
    CAN1_FilerConf.BankNumber=14;
	
    if(HAL_CAN_ConfigFilter(&CAN1_Handler,&CAN1_FilerConf)!=HAL_OK) return 2;//滤波器初始化
		
    else return 0;
}

//CAN底层驱动,引脚配置,时钟配置,中断配置
//此函数会被HAL_CAN_Init()调用
//hcan:CAN句柄
void HAL_CAN_MspInit(CAN_HandleTypeDef* hcan)
{
    GPIO_InitTypeDef GPIO_Initure;
    
    __HAL_RCC_CAN1_CLK_ENABLE();                //使能CAN1时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();			    //开启GPIOA时钟
	
    GPIO_Initure.Pin=GPIO_PIN_11|GPIO_PIN_12;   //PA11,12
    GPIO_Initure.Mode=GPIO_MODE_AF_PP;          //推挽复用
    GPIO_Initure.Pull=GPIO_PULLUP;              //上拉
    GPIO_Initure.Speed=GPIO_SPEED_FAST;         //快速
    GPIO_Initure.Alternate=GPIO_AF9_CAN1;       //复用为CAN1
    HAL_GPIO_Init(GPIOA,&GPIO_Initure);         //初始化
	
	  __HAL_CAN_ENABLE_IT(&CAN1_Handler,CAN_IT_FMP0);//FIFO0消息挂起中断允许
	  HAL_NVIC_SetPriority(CAN1_RX0_IRQn,1,2);    //抢占优先级1,子优先级2
    HAL_NVIC_EnableIRQ(CAN1_RX0_IRQn);          //使能中断
}

//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)	
//len:数据长度(最大为8)				     
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
//		 其他,失败;
	
u8 CAN1_Send_Msg(u8* msg,u8 len)
{	
	 u16 i=0;
	 TxMessage.RTR=CAN_RTR_DATA;
	 TxMessage.IDE=CAN_ID_STD;
	 TxMessage.ExtId=0x00;
	 TxMessage.StdId=std_id;
	 TxMessage.DLC=len;
	 for(i=0;i<len;i++)
	 TxMessage.Data[i]=msg[i];
  // CAN1_Handler.pTxMsg->Data[i]=msg[i];
   if(HAL_CAN_Transmit(&CAN1_Handler,10)!=HAL_OK) return 1;     //发送
   else return 0;		
}

//CAN接收中断
void CAN1_RX0_IRQHandler(void)
{
    HAL_CAN_IRQHandler(&CAN1_Handler);//此函数会调用CAN_Receive_IT()接收数据
}

//CAN中断处理过程
//此函数会被CAN_Receive_IT()调用
//hcan:CAN句柄
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* hcan)
{   
    int i=0;
    //CAN_Receive_IT()函数会关闭FIFO0消息挂号中断,因此我们需要重新打开
    __HAL_CAN_ENABLE_IT(&CAN1_Handler,CAN_IT_FMP0);//重新开启FIF00消息挂号中断
    for(i=0;i<8;i++)
    RxMessage.Data[i]=TxMessage.Data[i];
	  LCD_Fill(30,270,160,310,WHITE);//清除之前的显示
 			for(i=0;i<RxMessage.DLC;i++)
			{									    
				if(i<4)LCD_ShowxNum(30+i*32,270,RxMessage.Data[i],3,16,0X80);	//显示数据
				else LCD_ShowxNum(30+(i-4)*32,290,RxMessage.Data[i],3,16,0X80);	//显示数据
 			}
}

`
其对应的头文件等,正点原子官方均有定义。此外,本例只实现了CAN通信的最基本功能(接收发送),代码逻辑、代码完成度,尚有很多不足之处,请大家能够积极讨论,互相进步。

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值