目录
概述
1 认识bxCAN Loopback
1.1 环回模式
1.2 环回模式特点
2 使用STM32CubeMX 生成工程
2.1 参数配置
2.2 生成工程代码
2.4 了解can.c
3 认识Hal库中的bxCAN
3.1 认识3个重要数据结构
3.2 函数组
3.2.1 初始化函数组
3.2.2 控制函数组
3.2.3 中断管理函数组
3.2.4 中断回调函数组
4 实现bxCAN Loopback功能
4.1 初始化筛选器结构
4.2 发送数据函数
4.3 中断回调函数
4.5 完整代码
5 测试
5.1 编写测试代码
5.2 测试
源代码下载地址: stm32-bxCAN-Loopback测试程序,使用MCU:STM32F407IGX资源-CSDN文库
概述
本文主要介绍STM32F4XX的外设bxCAN的一个重要测试功能: Loopback。包括其实现原理,寄存器配置,代码实现,且还介绍了实现该功能的一些函数接口。并编写测试程序验证其功能。
1 认识bxCAN Loopback
1.1 环回模式
配置环回模式方法可以通过将 CAN_BTR 寄存器的 LBKM 位置 1,将 bxCAN 置于环回模式。
CAN 位时序寄存器 (CAN_BTR)
1.2 环回模式特点
1)在环回模式下,bxCAN 将其自身发送的消息作为接收的消息来处理并存储(如果这些消息通过了验收筛选)在接收邮箱中。
2)该模式为自检功能提供。为了不受外部事件的影响, CAN 内核在环回模式下将忽略确认错误(在数据/远程帧的确认时隙不对显性位采样)。
3)在此模式下, bxCAN 将执行从发送输出到接收输入的内部反馈。 bxCAN 将忽略 CANRX 输入引脚的实际值。从 CANTX 引脚可以监视发送的消息
2 使用STM32CubeMX 生成工程
2.1 参数配置
1) 配置RCC,使能外部晶振
2) GPIO Setting
本测试使用CAN1,与之相关的IO为PH13和PI9
3)CAN Parameter Settings
4) NVIC Setting
2.2 生成工程代码
在Project Manger选项卡中作如下配置,点击GENERATE CODE 生成工程代码。
打开工程文件如下:
2.4 了解can.c
使用STM32CubeMX 生成的工程文件,已经将需要的外设已经做了配置。本文重点介绍和CAN相关的接口,打开can.c可以看到:
在MX_CAN1_Init()中,主要实现can相关参数的配置
在HAL_CAN_MspInit()中配置IO,使其复用到CAN功能。
3 认识Hal库中的bxCAN
3.1 认识3个重要数据结构
1) CAN筛选器结构体
typedef struct {
uint16_t CAN_FilterIdHigh; /*CAN_FxR1寄存器的高16位 */
uint16_t CAN_FilterIdLow; /*CAN_FxR1寄存器的低16位*/
uint16_t CAN_FilterMaskIdHigh; /*CAN_FxR2寄存器的高16位*/
uint16_t CAN_FilterMaskIdLow; /*CAN_FxR2寄存器的低16位 */
uint16_t CAN_FilterFIFOAssignment; /*设置经过筛选后数据存储到哪个接收FIFO */
uint8_t CAN_FilterNumber; /*筛选器编号,范围0-27*/
uint8_t CAN_FilterMode; /*筛选器模式 */
uint8_t CAN_FilterScale; /*设置筛选器的尺度 */
FunctionalState CAN_FilterActivation; /*是否使能本筛选器*/
} CAN_FilterInitTypeDef;
2) 发送/接收结构体
/**
* 发送结构体
*/
typedef struct {
uint32_t StdId; /*存储报文的标准标识符11位,0-0x7FF. */
uint32_t ExtId; /*存储报文的扩展标识符29位,0-0x1FFFFFFF. */
uint8_t IDE; /*存储IDE扩展标志 */
uint8_t RTR; /*存储RTR远程帧标志*/
uint8_t DLC; /*存储报文数据段的长度,0-8 */
uint8_t Data[8]; /*存储报文数据段的内容 */
} CanTxMsg;
/**
* 接收结构体
*/
typedef struct {
uint32_t StdId; /*存储了报文的标准标识符11位,0-0x7FF. */
uint32_t ExtId; /*存储了报文的扩展标识符29位,0-0x1FFFFFFF. */
uint8_t IDE; /*存储了IDE扩展标志 */
uint8_t RTR; /*存储了RTR远程帧标志*/
uint8_t DLC; /*存储了报文数据段的长度,0-8 */
uint8_t Data[8]; /*存储了报文数据段的内容 */
uint8_t FMI; /*存储了 本报文是由经过筛选器存储进FIFO的,0-0xFF */
} CanRxMsg;
3.2 函数组
3.2.1 初始化函数组
使用STM32CubeMX 生成工程中,这部分接口STM32CubeMX已经将其在合适的位置,无需程序员在重新调用。
3.2.2 控制函数组
3.2.3 中断管理函数组
3.2.4 中断回调函数组
4 实现bxCAN Loopback功能
创建stm32f4_drv_can.c 文件,编写代码
4.1 初始化筛选器结构
static void CAN_Drv_fitter( CAN_HandleTypeDef *hcan )
{
CAN_FilterTypeDef filterStru;
filterStru.FilterActivation = ENABLE;
filterStru.FilterBank = 0;
filterStru.FilterFIFOAssignment = CAN_FILTER_FIFO0; //帅选后的数据存储在FIFO0
filterStru.FilterIdHigh = 0;
filterStru.FilterIdLow = 0;
filterStru.FilterMaskIdHigh = 0;
filterStru.FilterMaskIdLow = 0;
filterStru.FilterMode = CAN_FILTERMODE_IDMASK; //配置过滤模式:IDMASK
filterStru.FilterScale = CAN_FILTERSCALE_32BIT; //设置筛选器的尺度: 32 bit
filterStru.SlaveStartFilterBank = 0;
HAL_CAN_ConfigFilter( hcan, &filterStru);
}
4.2 发送数据函数
void CAN_Drv_sendMessage( CAN_HandleTypeDef *hcan,
uint8 *databuff, uint8 datalen,
uint32 stdId , uint32 extId, uint32 ide)
{
uint32 txMailbox;
CAN_TxHeaderTypeDef txHdrStru;
txHdrStru.DLC = datalen; /*发送的数据长度*/
txHdrStru.ExtId = extId; /*报文的扩展标识符 ID */
txHdrStru.StdId = stdId; /*报文的标准标识符 ID */
/*
ide = CAN_ID_STD 标准帧
ide = CAN_ID_EXT 扩展帧
*/
txHdrStru.IDE = ide;
txHdrStru.RTR = CAN_RTR_DATA; /*RTR远程帧标志 */
txHdrStru.TransmitGlobalTime = DISABLE;
HAL_CAN_AddTxMessage( hcan, &txHdrStru, databuff, &txMailbox);
}
4.3 中断回调函数
CAN0的中断函数void CAN1_RX0_IRQHandler(void)会自动调用该函数。主要实现数据接收和解析
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
uint8_t recv_data[8];
CAN_RxHeaderTypeDef header;
// 从FIFO读取数据
HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &header, recv_data);
if(header.IDE == CAN_ID_STD)
{
printf("StdId ID: 0x%x\n",header.StdId);
}
else
{
printf("ExtId ID:%d\n",header.ExtId);
}
printf("CAN IDE:0x%x\n",header.IDE);
printf("CAN RTR:0x%x\n",header.RTR);
printf("CAN DLC:0x%x\n",header.DLC);
printf("RECV DATA:");
printf("%s \n",recv_data);
printf("\n");
}
4.5 完整代码
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : stm32f4_drv_can.c
* Description : Code for board can driver
******************************************************************************
* @attention
*
* Copyright (c) mingfei.tang
* All rights reserved.
*
******************************************************************************
*/
/* USER CODE END Header */
#include "can.h"
#include "stm32f4_drv_can.h"
static void CAN_Drv_fitter( CAN_HandleTypeDef *hcan );
void CAN_Drv_Init( CAN_HandleTypeDef *hcan )
{
CAN_Drv_fitter( hcan ); //初始化过滤器
HAL_CAN_Start( hcan ); //启动CAN
HAL_CAN_ActivateNotification(hcan,CAN_IT_RX_FIFO0_MSG_PENDING); //接收触发方式
}
void CAN_Drv_sendMessage( CAN_HandleTypeDef *hcan,
uint8 *databuff, uint8 datalen,
uint32 stdId , uint32 extId, uint32 ide)
{
uint32 txMailbox;
CAN_TxHeaderTypeDef txHdrStru;
txHdrStru.DLC = datalen;
txHdrStru.ExtId = extId;
txHdrStru.StdId = stdId;
txHdrStru.IDE = ide;
txHdrStru.RTR = CAN_RTR_DATA;
txHdrStru.TransmitGlobalTime = DISABLE;
HAL_CAN_AddTxMessage( hcan, &txHdrStru, databuff, &txMailbox);
}
static void CAN_Drv_fitter( CAN_HandleTypeDef *hcan )
{
CAN_FilterTypeDef filterStru;
filterStru.FilterActivation = ENABLE;
filterStru.FilterBank = 0;
filterStru.FilterFIFOAssignment = CAN_FILTER_FIFO0;
filterStru.FilterIdHigh = 0;
filterStru.FilterIdLow = 0;
filterStru.FilterMaskIdHigh = 0;
filterStru.FilterMaskIdLow = 0;
filterStru.FilterMode = CAN_FILTERMODE_IDMASK;
filterStru.FilterScale = CAN_FILTERSCALE_32BIT;
filterStru.SlaveStartFilterBank = 0;
HAL_CAN_ConfigFilter( hcan, &filterStru);
}
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
uint8_t recv_data[8];
CAN_RxHeaderTypeDef header;
HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &header, recv_data);
if(header.IDE == CAN_ID_STD)
{
printf("StdId ID: 0x%x \n",header.StdId);
}
else
{
printf("ExtId ID:0x%x \n",header.ExtId);
}
printf("CAN IDE:0x%x\n",header.IDE);
printf("CAN RTR:0x%x\n",header.RTR);
printf("CAN DLC:0x%x\n",header.DLC);
printf("RECV DATA:");
printf("%s \n",recv_data);
printf("\n");
}
/* End of this file */
5 测试
5.1 编写测试代码
在main.c文件中实现如下函数:
1) 发送接口
void test_can1_send( void )
{
static uint8 i = 0;
uint32 stdId , extId, ide;
uint8 sendBuff[8] ="can-1:";
sendBuff[6] = 0X30 + i;
sendBuff[7] = 0X30 + i +1;
stdId = 0x321;
extId = 2;
ide = CAN_ID_STD;
CAN_Drv_sendMessage( &hcan1, sendBuff, sizeof(sendBuff), stdId, extId, ide);
i++;
}
2)执行函数中,主要做键盘扫描,当按键按下时,发送一次数据
void tick_action( void )
{
static bool flag_1s = 0;
static unsigned int tick_cnt = 0;
static unsigned int beforTick = 0;
unsigned int currentTick;
unsigned char val;
currentTick = HAL_GetTick();
if(beforTick != currentTick )
{
beforTick = currentTick;
tick_cnt++;
// 1s action
if( (tick_cnt % 1000) == 0)
{
flag_1s = true;;
}
//1ms action
bsp_KeyMonitor();
}
if( flag_1s )
{
flag_1s = false;
HAL_GPIO_TogglePin(SYS_RUN_LED_GPIO_Port, SYS_RUN_LED_Pin);
}
// send CAN packet
val = bsp_KeyGetValue( KEY_1 );
if( val )
{
test_can1_send();
}
}
3) main()函数中初始化CAN配置,在while(1)下执行tick_action()
5.2 测试
编译代码,将代码下载到板卡中测试。每按下一次按键,发送数据的值都会加1,接收中断函数也能正确的接受到数据。