STM32CubeMX | STM32 HAL库CAN总线收发、中断方式接收示例教程

STM32CubeMX | STM32 HAL库CAN总线收发、中断方式接收示例教程

平台:战舰mini板,STM32F103RB
STM32CUBEMX V5.3
TrueSTUDIO V9.3


宣传一下 ^_^

博主做的脱机烧录器:重磅来袭!CMSIS-DAP 脱机烧录器 EasyFlasher 发布~

某宝店铺:觉皇工作室
点此链接:https://item.taobao.com/item.htm?abbucket=18&id=841061310319

实物图,MINI版:

实物图,COOL版:


配置CAN

先了解几个关键词简称:

  • 最小时间单位(Tq,Time Quantum)
  • 同步段(SS,Synchronization Segment)1tq
  • 传播时间段(PTS,Propagation Time Segment)1~8tq
  • 相位缓冲段1(PBS1,Phase Buffer Segment1)1~8tq
  • 相位缓冲段2(PBS2,Phase Buffer Segment2)2~8tq
  • 再同步补偿宽度(SJW,reSynchronization Jump Width)1~4tq
  • 波特率分频器(BRP,Baud Rate Prescaler)

STM32把传播时间段(PTS)和相位缓冲段1(PBS1)合并了,形成了时间段1(TS1)。

CAN位时序寄存器(CAN_BTR)用于设置TS1、TS2、BRP、SJW等参数,这些参数直接决定CAN的波特率。

  • SJW[1:0]再同步补偿宽度

  • TS1[3:0]时间段1

  • TS2[2:0]时间段2

  • BRP[9:0]波特率分频器

可以看到没有同步段(SS段),这是因为STM32已经将SS段固化为1。

除去这几个参数,还需要设置分频,STM32F1系列一般是跑到72M主频,CAN是挂在36M的APB1时钟上,设置CAN的分频就是分的这个36M。

下面这张图是波特率计算公式:

整合一下波特率计算公式就是这样的:

波特率 = APB1 / [(1 + (TS1+1) + (TS2+1)) * (BRP+1)]

在简化就是:波特率 = 时钟主频 / 分频 / (tq1 + tq2 + ss)

其中SS就是同步段,已经恒为1,所以:波特率 = 时钟主频 / 分频 / (tq1 + tq2 + 1)

下面我们开始实际设置波特率,这里要注意,CAN的波特率最大为1Mbps。

stm32f103的CAN的时钟主频是36M,分9频就是4M,设置tq1=5,tq2=2,ss恒等于1,那么:波特率 = 36MHz / 9分频 /(5 + 2 + 1) = 500KHz

另外还有一向是:再同步补偿宽度(reSynchronization Jump Width) 这个参数,其实就是一个由数个Tq组成的一个段,用来对同步误差进行补偿,可以简单理解为为了提高精准度的,例如两个CAN进行通讯时由于两个板子的晶振可能存在误差从而导致CAN的波特率没有500K那么精准,所以就需要设置一个补偿参数去修正,这个参数就需要根据你实际的板子情况去调整了。

注意:stm32cubemx生成的CAN代码是不带过滤器的,需要自己手动添加
在这里插入图片描述
在这里插入图片描述

代码修改

STM32CUBEMX生成的CAN配置代码是没有过滤器设置的,需要手动添加。

typedef struct
{
	uint32_t mailbox;
	CAN_TxHeaderTypeDef hdr;
	uint8_t payload[8];
}CAN_TxPacketTypeDef;

typedef struct
{
	CAN_RxHeaderTypeDef hdr;
	uint8_t payload[8];
}CAN_RxPacketTypeDef;


/// CAN过滤器寄存器位宽类型定义
typedef union
{
    __IO uint32_t value;
    struct
    {
        uint8_t REV : 1;			///< [0]    :未使用
        uint8_t RTR : 1;			///< [1]    : RTR(数据帧或远程帧标志位)
        uint8_t IDE : 1;			///< [2]    : IDE(标准帧或扩展帧标志位)
        uint32_t EXID : 18;			///< [21:3] : 存放扩展帧ID
        uint16_t STID : 11;			///< [31:22]: 存放标准帧ID
    } Sub;
} CAN_FilterRegTypeDef;


#define CAN_BASE_ID 0						///< CAN标准ID,最大11位,也就是0x7FF

#define CAN_FILTER_MODE_MASK_ENABLE 1		///< CAN过滤器模式选择:=0:列表模式  =1:屏蔽模式

#define CAN_ID_TYPE_STD_ENABLE      1       ///< CAN过滤ID类型选择:=1:标准ID,=0:扩展ID

void CAN_Filter_Config(void)
{
    CAN_FilterTypeDef sFilterConfig;
    CAN_FilterRegTypeDef IDH = {0};
    CAN_FilterRegTypeDef IDL = {0};

#if CAN_ID_TYPE_STD_ENABLE
    IDH.Sub.STID = (CAN_BASE_ID >> 16) & 0xFFFF;		// 标准ID高16位
    IDL.Sub.STID = (CAN_BASE_ID & 0xFFFF);				// 标准ID低16位
#else
    IDH.Sub.EXID = (CAN_BASE_ID >> 16) & 0xFFFF;		// 扩展ID高16位
    IDL.Sub.EXID = (CAN_BASE_ID & 0xFFFF);				// 扩展ID低16位
    IDL.Sub.IDE  = 1;									// 扩展帧标志位置位
#endif
    sFilterConfig.FilterBank           = 0;												// 设置过滤器组编号
#if CAN_FILTER_MODE_MASK_ENABLE
    sFilterConfig.FilterMode           = CAN_FILTERMODE_IDMASK;							// 屏蔽位模式
#else
    sFilterConfig.FilterMode           = CAN_FILTERMODE_IDLIST;							// 列表模式
#endif
    sFilterConfig.FilterScale          = CAN_FILTERSCALE_32BIT;							// 32位宽
    sFilterConfig.FilterIdHigh         = IDH.value;										// 标识符寄存器一ID高十六位,放入扩展帧位
    sFilterConfig.FilterIdLow          = IDL.value;										// 标识符寄存器一ID低十六位,放入扩展帧位
    sFilterConfig.FilterMaskIdHigh     = IDH.value;										// 标识符寄存器二ID高十六位,放入扩展帧位
    sFilterConfig.FilterMaskIdLow      = IDL.value;										// 标识符寄存器二ID低十六位,放入扩展帧位
    sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;									// 过滤器组关联到FIFO0
    sFilterConfig.FilterActivation     = ENABLE;										// 激活过滤器
    sFilterConfig.SlaveStartFilterBank = 14;											// 设置从CAN的起始过滤器编号,本单片机只有一个CAN,顾此参数无效
    if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
    {
        Error_Handler();
    }
}

uint8_t CAN_Transmit(CAN_TxPacketTypeDef* packet)
{
	if(HAL_CAN_AddTxMessage(&hcan, &packet->hdr, packet->payload, &packet->mailbox) != HAL_OK)
		return 1;
	return 0;
}

void CAN_Init(void)
{
    MX_CAN_Init();
    CAN_Filter_Config();
    HAL_CAN_Start(&hcan);
    HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING);					// 使能CAN接收中断
}

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *canHandle)
{
	static CAN_RxPacketTypeDef packet;
	
    // CAN数据接收
    if (canHandle->Instance == hcan.Instance)
    {
        if (HAL_CAN_GetRxMessage(canHandle, CAN_RX_FIFO0, &packet.hdr, packet.payload) == HAL_OK)		// 获得接收到的数据头和数据
        {
			printf("\r\n\r\n\r\n################### CAN RECV ###################\r\n");
			printf("STID:0x%X\r\n",packet.hdr.StdId);
			printf("EXID:0x%X\r\n",packet.hdr.ExtId);
			printf("DLC :%d\r\n", packet.hdr.DLC);
			printf("DATA:");
			for(int i = 0; i < packet.hdr.DLC; i++)
			{
				printf("0x%02X ", packet.payload[i]);
			}
           HAL_CAN_ActivateNotification(canHandle, CAN_IT_RX_FIFO0_MSG_PENDING);						// 再次使能FIFO0接收中断
        }
    }
}


CAN_TxPacketTypeDef g_CanTxPacket;

void CAN_SetTxPacket(void)
{
	g_CanTxPacket.hdr.StdId = 0x321;			// 标准ID
//	g_CanTxPacket.hdr.ExtId = 0x10F01234;		// 扩展ID
	g_CanTxPacket.hdr.IDE = CAN_ID_STD;			// 标准ID类型
//	g_CanTxPacket.hdr.IDE = CAN_ID_EXT;			// 扩展ID类型
	g_CanTxPacket.hdr.DLC = 8;					// 数据长度
	g_CanTxPacket.hdr.RTR = CAN_RTR_DATA;		// 数据帧
//	g_CanTxPacket.hdr.RTR = CAN_RTR_REMOTE;		// 远程帧
	g_CanTxPacket.hdr.TransmitGlobalTime = DISABLE;
	
	for(int i = 0; i < 8; i++)
	{
		g_CanTxPacket.payload[i] = i;
	}
}

int main()
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    CAN_Init();
	printf("----------------------------------------\r\n");

	CAN_SetTxPacket();
	
    while(1)
    {
		if(CAN_Transmit(&g_CanTxPacket) != 0)
			printf("failed\r\n");
		HAL_Delay(1000);
    }
}

CAN总线是一种高速、可靠的通信协议,可用于连接多个节点,例如汽车电子和工业控制系统。在STM32CubeMXHAL库中,使用CAN总线需要以下步骤: 1. 配置CAN硬件 在STM32CubeMX中,选择正确的芯片型号并打开CAN总线选项卡。在该选项卡中,您可以配置CAN的时钟、波特率和过滤器等参数。根据您的具体应用需求进行配置。 2. 初始化CAN总线 使用HAL库的CAN初始化函数`HAL_CAN_Init()`初始化CAN总线。该函数需要传入CAN_HandleTypeDef结构体,该结构体包含了CAN的基本参数。 ```c CAN_HandleTypeDef hcan; hcan.Instance = CANx; hcan.Init.Prescaler = 10; hcan.Init.Mode = CAN_MODE_NORMAL; hcan.Init.SyncJumpWidth = CAN_SJW_1TQ; hcan.Init.TimeSeg1 = CAN_BS1_13TQ; hcan.Init.TimeSeg2 = CAN_BS2_2TQ; hcan.Init.TimeTriggeredMode = DISABLE; hcan.Init.AutoBusOff = ENABLE; hcan.Init.AutoWakeUp = DISABLE; hcan.Init.AutoRetransmission = ENABLE; hcan.Init.ReceiveFifoLocked = DISABLE; hcan.Init.TransmitFifoPriority = DISABLE; if (HAL_CAN_Init(&hcan) != HAL_OK) { Error_Handler(); } ``` 3. 配置CAN过滤器 使用HAL库的CAN过滤器配置函数`HAL_CAN_ConfigFilter()`配置CAN过滤器,以确保只有特定的消息可以通过。该函数需要传入CAN_HandleTypeDef结构体和CAN_FilterTypeDef结构体,后者包括过滤器的掩码和ID等参数。 ```c CAN_FilterTypeDef sFilterConfig; sFilterConfig.FilterBank = 0; sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; sFilterConfig.FilterIdHigh = 0x0000; sFilterConfig.FilterIdLow = 0x0000; sFilterConfig.FilterMaskIdHigh = 0x0000; sFilterConfig.FilterMaskIdLow = 0x0000; sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; sFilterConfig.FilterActivation = ENABLE; sFilterConfig.SlaveStartFilterBank = 14; if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK) { Error_Handler(); } ``` 4. 发送CAN消息 使用HAL库的CAN消息发送函数`HAL_CAN_AddTxMessage()`发送CAN消息。该函数需要传入CAN_HandleTypeDef结构体和CAN_TxHeaderTypeDef结构体,后者包括消息的ID和数据等参数。 ```c CAN_TxHeaderTypeDef TxHeader; uint8_t aData[8]; TxHeader.StdId = 0x123; TxHeader.ExtId = 0x00; TxHeader.IDE = CAN_ID_STD; TxHeader.RTR = CAN_RTR_DATA; TxHeader.DLC = 8; TxHeader.TransmitGlobalTime = DISABLE; aData[0] = 0x01; aData[1] = 0x02; aData[2] = 0x03; aData[3] = 0x04; aData[4] = 0x05; aData[5] = 0x06; aData[6] = 0x07; aData[7] = 0x08; if (HAL_CAN_AddTxMessage(&hcan, &TxHeader, aData, &TxMailbox) != HAL_OK) { Error_Handler(); } ``` 5. 接收CAN消息 使用HAL库的CAN消息接收函数`HAL_CAN_GetRxMessage()`接收CAN消息。该函数需要传入CAN_HandleTypeDef结构体和CAN_RxHeaderTypeDef结构体,后者包括消息的ID和数据等参数。 ```c CAN_RxHeaderTypeDef RxHeader; uint8_t aData[8]; if (HAL_CAN_GetRxMessage(&hcan, CAN_RX_FIFO0, &RxHeader, aData) != HAL_OK) { Error_Handler(); } ``` 以上就是使用STM32CubeMXHAL库CAN总线应用教程。根据您的应用需求,您可以进一步优化CAN总线的配置和使用。
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

觉皇嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值