STM32-CAN配置与库函数解析
CAN总线介绍:https://blog.csdn.net/weixin_46251230/article/details/129147612
STM32-CAN控制器介绍:https://blog.csdn.net/weixin_46251230/article/details/129150872
STM32CubeMx配置
因为bxCAN是挂载在APB1总线上的,所以设置APB1总线的时钟为36M
选择CAN接口进行配置
勾选主CAN模式,这里并不是主机的意思
配置位时间参数
根据STM32参考手册里位时间特性的介绍,来配置最小时间单位Tq
在位时间参数里可以配置分频系数,这个系数没有下拉列表,需要自己写,APB1 36MHz时钟来到这里经过分频再给后面使用,根据实际通信速度来配置,例如可以设置为4分频,那36MHz/4 = 9MHz
同步段因为固定为1个时间单元,所以不用配置
根据采样点最好在一个位的50% ~ 80%位置采样,所以时间段1可以配置长一点,其范围为1到16个时间单元,这里可根据下拉列表选择11个时间单元
时间段2的设置就要注意不要让总和超过最大Tq数,因为同步段+时间段1+时间段2的Tq数范围是8 ~ 25个,不过配置工具已经把参数规定好了,所以不用担心超出的问题,这里可以设置为6个时间单元,则会自动计算出一个位占用的时间(Time for one Bit)为2000ns
计算过程:
36MHz/4 = 9MHz,因为同步段(1个Tq)+时间段1(11个Tq)+时间段2(6个Tq)=18个Tq
所以 9MHz/18 = 0.5MHz,即每一个Tq的频率就是0.5MHz,转为时间就为 1/0.5MHz = 1/500000Hz = 0.000002s = 2us = 2000ns
而2000ns的速率就是500KHz
重新同步跳跃宽度(SJW)可设置范围是1 ~ 4个时间单元,这里可以选择2
配置基础参数
接收FIFO锁定模式:选择Enable(锁定)时,当接收FIFO满时,新接收到的报文就丢弃,软件可以读到FIFO中最早收到的3个报文。
选择Disable(不锁定)时,那么FIFO中最后收到的报文就被新报文所覆盖。这样,最新收到的报文不会被丢弃掉。
发送FIFO优先级:未使能就按邮箱序号进行发送
配置工作模式
正常模式就需要两个或更多的实验板来进行通信
环回模式就只使用一个实验板就可以测试通信
本次实验使用环回模式
NVIC中断配置
CAN发送使用轮询的方式,接收就用RX0中断方式
GPIO配置
因为CAN收发器的STB接到了单片机的PC13引脚,所以将PC13配置为推挽输出模式
上面配置完就可以生成Keil工程进行代码编写
生成的CAN初始化函数如下:
void MX_CAN_Init(void)
{
hcan.Instance = CAN1;
hcan.Init.Prescaler = 4;
hcan.Init.Mode = CAN_MODE_LOOPBACK;
hcan.Init.SyncJumpWidth = CAN_SJW_2TQ;
hcan.Init.TimeSeg1 = CAN_BS1_11TQ;
hcan.Init.TimeSeg2 = CAN_BS2_6TQ;
hcan.Init.TimeTriggeredMode = DISABLE;
hcan.Init.AutoBusOff = DISABLE;
hcan.Init.AutoWakeUp = DISABLE;
hcan.Init.AutoRetransmission = DISABLE;
hcan.Init.ReceiveFifoLocked = DISABLE;
hcan.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan) != HAL_OK)
{
Error_Handler();
}
}
CAN使用到的库函数介绍
1、配置过滤器
CAN_FilterTypeDef结构体就是过滤器的一些参数设置
HAL_StatusTypeDef HAL_CAN_ConfigFilter(CAN_HandleTypeDef *hcan, CAN_FilterTypeDef *sFilterConfig);
2、发送数据
CAN_TxHeaderTypeDef结构体是对发送报文进行组帧,aData数组存放着要发送的数据,pTxMailbox指针是返回控制器使用了哪个邮箱进行发送
HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *pHeader, uint8_t aData[], uint32_t *pTxMailbox);
3、中止发送请求
HAL_StatusTypeDef HAL_CAN_AbortTxRequest(CAN_HandleTypeDef *hcan, uint32_t TxMailboxes);
4、获取空邮箱的个数
uint32_t HAL_CAN_GetTxMailboxesFreeLevel(CAN_HandleTypeDef *hcan);
5、接收数据
RxFifo指定用于接收报文的FIFO缓存,CAN_RxHeaderTypeDef结构体定义接收报文的格式,aData数组存放接收到的报文
HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, uint32_t RxFifo, CAN_RxHeaderTypeDef *pHeader, uint8_t aData[]);
6、获取接收FIFO满的个数
uint32_t HAL_CAN_GetRxFifoFillLevel(CAN_HandleTypeDef *hcan, uint32_t RxFifo);
CAN通信实现(环回模式)
CAN.h
进行宏定义,定义结构体类型
//发送接收状态宏定义
#define CAN_SEND_OK 0
#define CAN_SEND_FAIL 1
#define CAN_REC_OK 0
#define CAN_REC_FAIL 1
//定义结构体类型
typedef struct
{
uint32_t uiOperate_Mode; //操作模式
uint8_t ucRec_Flag; //接收标志位
uint8_t ucSend_Buf[8]; //发送缓存
uint8_t ucRec_Buf[8]; //接收缓存
void (*CAN_Init)(void);
void (*CAN_Config)(void);
uint8_t (*CAN_Send_Msg)(uint8_t* pSend_Buf,uint8_t LEN);
uint8_t (*CAN_Rec_Msg)(uint8_t* pRec_Buf);
}CAN_Test_T;
/* extern variables-----------------------------------------------------------*/
extern CAN_Test_T CAN_Test;
CAN.c
配置过滤器并启动CAN
/**
* @name CAN_Config
* @brief CAN配置
* @param None
* @retval None
*/
static void CAN_Config(void)
{
//CAN过滤器参数配置
CAN_FilterTypeDef CAN_FilterTypeDefSture;
CAN_FilterTypeDefSture.FilterBank = 0; //配置过滤器0(F1共有14个,0-13)
CAN_FilterTypeDefSture.FilterScale = CAN_FILTERSCALE_16BIT; //配置为16位过滤器
CAN_FilterTypeDefSture.FilterMode = CAN_FILTERMODE_IDMASK; //屏蔽位模式
//ID号为0x00,屏蔽位为0x00,说明任何ID都接收
CAN_FilterTypeDefSture.FilterIdLow = 0x00; //FR1
CAN_FilterTypeDefSture.FilterMaskIdLow = 0x00;
CAN_FilterTypeDefSture.FilterIdHigh = 0x00; //FR2
CAN_FilterTypeDefSture.FilterMaskIdHigh = 0x00;
CAN_FilterTypeDefSture.FilterFIFOAssignment = CAN_FILTER_FIFO0; //过滤器0关联到FIFO0
CAN_FilterTypeDefSture.FilterActivation = ENABLE; //激活过滤器0
CAN_FilterTypeDefSture.SlaveStartFilterBank = 14;
//启动过滤器
if(HAL_CAN_ConfigFilter(&hcan,&CAN_FilterTypeDefSture) != HAL_OK)
{
printf("CAN配置过滤器成功!");
System.Error_Handler();
}
//使能FIFO0接收到一个新报文中断,具体为FIFO0的挂起中断
if(HAL_CAN_ActivateNotification(&hcan,CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
{
printf("CAN使能FIFO0接收到一个新报文中断");
System.Error_Handler();
}
//启动CAN
if(HAL_CAN_Start(&hcan) != HAL_OK)
{
printf("CAN启动失败!");
System.Error_Handler();
}
printf("配置成功,CAN成功启动!\r\n");
}
CAN发送报文,可在主函数中调用
/**
* @name CAN_Send_Msg
* @brief CAN发送信息
* @param pSend_Buf:发送缓存指针
* LEN:发送报文长度
* @retval CAN_SEND_OK:发送成功
* CAN_SEND_FAIL:发送失败
*/
static uint8_t CAN_Send_Msg(uint8_t* pSend_Buf,uint8_t LEN)
{
uint8_t i = 0;
static uint8_t ucTestData = 0;
uint32_t uiTxMailBox; //接收CAN发送数据成功时返回的邮箱号(0-2)
//定义CAN TX消息头参数
CAN_TxHeaderTypeDef CAN_TxHeaderTypeDefStrue =
{
0x88, //标准标识符-11位
0x00, //拓展标识符-29位
CAN_ID_STD, //设置为标准格式
CAN_RTR_DATA, //设置为数据帧
8, //发送数据的长度 0 ~ 8
DISABLE //不使用捕获时间戳计数器
};
//判断工作模式
if(CAN_Test.uiOperate_Mode == CAN_MODE_LOOPBACK)
{
printf("\r\nCAN工作在环回模式,使用一块实验板来测试\r\n");
}
else
{
printf("\r\nCAN工作在正常模式,需要两块以上的实验板才能测试\r\n");
}
//设置要发送的报文
printf("CAN要发送的报文如下:\r\n");
for(i=0;i<8;i++)
{
printf("%#.2x ",ucTestData);
CAN_Test.ucSend_Buf[i] = ucTestData++;
}
printf("\r\n");
//将消息添加到第一个空闲的Tx邮箱并激活相应的传输要求
if(HAL_CAN_AddTxMessage(&hcan,&CAN_TxHeaderTypeDefStrue,pSend_Buf,&uiTxMailBox) != HAL_OK)
{
return CAN_SEND_FAIL;
}
//通过检查空闲邮箱个数确认是否发送完成
Timer6.usDelay_Timer = 0;
do
{
//超时处理
if(Timer6.usDelay_Timer >= TIMER_1s)
{
printf("CAN发送超时\r\n");
return CAN_SEND_FAIL;
}
} while (HAL_CAN_GetTxMailboxesFreeLevel(&hcan) != 3); //如果3个发送邮箱都不是空闲的话,就说明数据还在发送
//发送成功
return CAN_SEND_OK;
}
CallBack.c
重写FIFO0挂起中断,接收CAN消息
因为是环回模式,所以主函数中发送的数据会被中断接收,通过HAL_CAN_GetRxMessage函数放到CAN_Test.ucRec_Buf缓存中
/**
* @name HAL_CAN_RxFifo0MsgPendingCallback
* @brief CAN接收FIFO0 挂起中断
* @param *_hcan:CAN结构体指针
* @retval None
*/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan_)
{
//定义CAN Rx消息头参数
CAN_RxHeaderTypeDef CAN_RxHeaderTypeDefStrue;
//CAN接收消息
if(HAL_CAN_GetRxMessage(&hcan,CAN_RX_FIFO0,&CAN_RxHeaderTypeDefStrue,CAN_Test.ucRec_Buf) == HAL_OK)
{
CAN_Test.ucRec_Flag = TRUE;
}
LED.LED_Fun(LED3,LED_Flip);
}
System.c
主函数中,判断触摸按键1是否按下,是则发送一次数据,通过接收标志位判断中断是否已经完成数据接收,是则打印出数据文章来源:https://www.toymoban.com/news/detail-783539.html
/**
* @name Run
* @brief 系统运行
* @param None
* @retval None
*/
static void Run()
{
//计数
static uint16_t usCAN_Send_OK_Cnt = 0;
static uint16_t usCAN_Send_Fail_Cnt = 0;
static uint16_t usCAN_Rec_OK_Cnt = 0;
//发送数据
if(KEY1.KEY_Flag == TRUE)
{
KEY1.KEY_Flag = FALSE;
//发送
if(CAN_Test.CAN_Send_Msg(CAN_Test.ucSend_Buf,8) == CAN_SEND_OK)
{
printf("CAN发送数据成功次数:%u\r\n",++usCAN_Send_OK_Cnt);
}
else
{
printf("CAN发送数据失败次数:%u\r\n",++usCAN_Send_Fail_Cnt);
}
}
//接收数据
if(CAN_Test.ucRec_Flag == TRUE)
{
CAN_Test.ucRec_Flag = FALSE;
printf("CAN接收数据成功次数:%u\r\n",++usCAN_Rec_OK_Cnt);
//打印接收到的数据
CAN_Test.CAN_Rec_Msg(CAN_Test.ucRec_Buf);
}
}
实验结果
每触摸一次按键,就成功发送一次数据,并成功接收一次数据
文章来源地址https://www.toymoban.com/news/detail-783539.html
到了这里,关于STM32-CAN配置与库函数解析,实现环回模式通信的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!