一、遇到的问题 1、发送单包数据,我想都非常简答,例子一大堆包括,demo。但是发送多包数据怎么处理?
2、发送多包数据,网上基本上就两种说发
*两个数据之间加延时
*查询邮箱是否满了
HAL_CAN_GetTxMailboxesFreeLevel()
对比上面两种方法,为了效率我们选择第二种
二、配置与代码
直接上cubemx配置
直接生成后,cubemx没有设置过滤器,需要自己添加,结合网上各资料总结代码如下
- int main(void)
- {
- /* USER CODE BEGIN 1 */
- /* USER CODE END 1 */
- /* MCU Configuration--------------------------------------------------------*/
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
- HAL_Init();
- /* USER CODE BEGIN Init */
- /* USER CODE END Init */
- /* Configure the system clock */
- SystemClock_Config();
- /* USER CODE BEGIN SysInit */
- /* USER CODE END SysInit */
- /* Initialize all configured peripherals */
- MX_GPIO_Init();
- MX_CAN1_Init();
- /* USER CODE BEGIN 2 */
- CAN_Init();
- /* USER CODE END 2 */
- /* Infinite loop */
- /* USER CODE BEGIN WHILE */
- while (1)
- {
- /* USER CODE END WHILE */
- /* USER CODE BEGIN 3 */
- uint8_t TxData[100] ;
- for(uint8_t i=0;i<100;i++)
- TxData[i]=i;
- CAN_SendStdMsg(&hcan1,TxData,sizeof(TxData));
- HAL_Delay(100);
- }
- /* USER CODE END 3 */
- }
复制代码
bsp_can.c
- #include "bsp_can.h"
- /// 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(&hcan1, &sFilterConfig) != HAL_OK)
- {
- Error_Handler();
- }
- }
- void CAN_Init(void)
- {
- CAN_Filter_Config();
- HAL_CAN_Start(&hcan1);
- HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING); // 使能CAN接收中断
- }
- void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *canHandle)
- {
- static CAN_RxPacketTypeDef packet;
-
- // CAN数据接收
- if (canHandle->Instance == hcan1.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接收中断
- }
- }
- }
- uint8_t CAN_Transmit(CAN_TxPacketTypeDef* packet)
- {
- if(HAL_CAN_AddTxMessage(&hcan1, &packet->hdr, packet->payload, &packet->mailbox) != HAL_OK)
- return 1;
- return 0;
- }
- CAN_TxHeaderTypeDef TxMeg;
- uint16_t TimsRepeat;
- uint8_t CAN_SendStdMsg(CAN_HandleTypeDef* __hcan,uint8_t *pData,uint16_t Len)
- {
- uint16_t SendCNT=0;
- uint32_t TxMailbox;
- HAL_StatusTypeDef HAL_RetVal;
-
- TxMeg.StdId = 0x321; //标准ID
- // TxMeg.ExtId = 0x10F01234;
- TxMeg.IDE = CAN_ID_STD;// 标准ID类型
- //TxMeg.IDE = CAN_ID_EXT;// 扩展ID类型
- TxMeg.RTR = CAN_RTR_DATA; // 数据帧
- //TxMeg.RTR = CAN_RTR_REMOTE; // 远程帧
- TxMeg.TransmitGlobalTime = DISABLE;
-
- if(!__hcan || ! pData ||!Len) return 1;
- TimsRepeat =Len/8+(Len%8?1:0);
- while(TimsRepeat--)
- {
- while(HAL_CAN_GetTxMailboxesFreeLevel(__hcan)==0);
- if(TimsRepeat>0)
- {
- TxMeg.DLC=8;
- }
- else
- {
- if(Len%8)
- TxMeg.DLC=Len%8;
- }
- HAL_RetVal=HAL_CAN_AddTxMessage(__hcan,&TxMeg,(pData+SendCNT),&TxMailbox);
- if(HAL_RetVal!=HAL_OK)
- {
- return 0;
- }
- SendCNT+=8;
- }
- return 1;
复制代码
bsp_can.h
- #ifndef _BSP_CAN_H
- #define _BSP_CAN_H
- #include "stm32f4xx_hal.h"
- #include "can.h"
- //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;
- void CAN_Init(void);
- uint8_t CAN_SendStdMsg(CAN_HandleTypeDef* __hcan,uint8_t *pData,uint16_t Len);
- #endif
复制代码
就可以工作了???非也
其实我发现还是有问题
为什么第2 3包数据到最后去了?
查了一些资料发现了问题:
”发送邮箱“是用于CAN总线数据发送的,总共有3个,并且存在优先级关系。优先级越高表示其里面的数据会被优先发送。数据在发送前都会被送到优先级最高且空闲的发送邮箱,然后依次发送。最后说明一点:“发送邮箱有3个,且每个邮箱只能装一个报文” 原因是,三个邮箱有优先级,我发送前会检查三个邮箱是否都满,没满就发送。导致 第 2 3包数据在两个优先级低的邮箱,导致才最后发送
查看库函数
- /**
- * @brief Return Tx Mailboxes free level: number of free Tx Mailboxes.
- * @param hcan pointer to a CAN_HandleTypeDef structure that contains
- * the configuration information for the specified CAN.
- * @retval Number of free Tx Mailboxes.
- */
- uint32_t HAL_CAN_GetTxMailboxesFreeLevel(CAN_HandleTypeDef *hcan)
- {
- uint32_t freelevel = 0U;
- HAL_CAN_StateTypeDef state = hcan->State;
- if ((state == HAL_CAN_STATE_READY) ||
- (state == HAL_CAN_STATE_LISTENING))
- {
- /* Check Tx Mailbox 0 status */
- if ((hcan->Instance->TSR & CAN_TSR_TME0) != 0U)
- {
- freelevel++;
- }
- /* Check Tx Mailbox 1 status */
- if ((hcan->Instance->TSR & CAN_TSR_TME1) != 0U)
- {
- freelevel++;
- }
- /* Check Tx Mailbox 2 status */
- if ((hcan->Instance->TSR & CAN_TSR_TME2) != 0U)
- {
- freelevel++;
- }
- }
- /* Return Tx Mailboxes free level */
- return freelevel;
- }
复制代码
解决办法如下:当然你也可以自己封装一下
- uint32_t HAL_CAN_GetTxMailboxesFreeLevel(CAN_HandleTypeDef *hcan)
- {
- uint32_t freelevel = 0U;
- HAL_CAN_StateTypeDef state = hcan->State;
- if ((state == HAL_CAN_STATE_READY) ||
- (state == HAL_CAN_STATE_LISTENING))
- {
-
- if (((hcan->Instance->TSR & CAN_TSR_TME0) != 0U)&& \
- ((hcan->Instance->TSR & CAN_TSR_TME1) != 0U)&& \
- (hcan->Instance->TSR & CAN_TSR_TME2) != 0U)
- {
- freelevel++;
- }
- /* Check Tx Mailbox 0 status */
- // if ((hcan->Instance->TSR & CAN_TSR_TME0) != 0U)
- // {
- // freelevel++;
- // }
- /* Check Tx Mailbox 1 status */
- // if ((hcan->Instance->TSR & CAN_TSR_TME1) != 0U)
- // {
- // freelevel++;
- // }
- // /* Check Tx Mailbox 2 status */
- // if ((hcan->Instance->TSR & CAN_TSR_TME2) != 0U)
- // {
- // freelevel++;
- // }
- }
- /* Return Tx Mailboxes free level */
- return freelevel;
- }
复制代码
三 总结
CAN数据在发送前都会被送到优先级最高且空闲的发送邮箱,所有发送完后 我们不能检测三个邮箱是否有空邮箱,而是应该检测三个邮箱是否都是空的才对。
四、CAN以及库函数分析
CAN总线
1、根据标示符(也就是 ID)来决定优先级的。
2、在同一网络中,所有单元必须设定成统一的通信速度,最高 1Mbps(距离小于40M),最远可达 10KM(速率低于 5Kbps)
3、可通过发送“遥控帧” 请求其他单元发送数据。
4、CAN Model:
Normal 正常模式
Silent 静默模式
Loopback 环回模式 测试
Silent_loopback 静默换回模式 测试
5、波特率
波特率(Kpbs) = fpclk / ((CAN_BS1 + CAN_BS2 + 1) * CAN_Prescaler)
6、发送数据结构体指针 - /**
- * @brief CAN Tx message header structure definition
- */
- typedef struct
- {
- //表示标准的ID
- uint32_t StdId; /*!< Specifies the standard identifier.
- This parameter must be a number between Min_Data = 0 and Max_Data = 0x7FF. */
- //表示扩展的ID
- uint32_t ExtId; /*!< Specifies the extended identifier.
- This parameter must be a number between Min_Data = 0 and Max_Data = 0x1FFFFFFF. */
- //要发送的是扩展帧还是标准帧
- uint32_t IDE; /*!< Specifies the type of identifier for the message that will be transmitted.
- This parameter can be a value of @ref CAN_identifier_type */
- //表示发送的帧类型。如:数据帧、远程帧等
- uint32_t RTR; /*!< Specifies the type of frame for the message that will be transmitted.
- This parameter can be a value of @ref CAN_remote_transmission_request */
- //表示发送的数据长度
- uint32_t DLC; /*!< Specifies the length of the frame that will be transmitted.
- This parameter must be a number between Min_Data = 0 and Max_Data = 8. */
- FunctionalState TransmitGlobalTime; /*!< Specifies whether the timestamp counter value captured on start
- of frame transmission, is sent in DATA6 and DATA7 replacing pData[6] and pData[7].
- @note: Time Triggered Communication Mode must be enabled.
- @note: DLC must be programmed as 8 bytes, in order these 2 bytes are sent.
- This parameter can be set to ENABLE or DISABLE. */
- } CAN_TxHeaderTypeDef;
复制代码
7、过滤器:为了方便接收想要的ID,过滤掉不想要的ID。28组或14组过滤器,每组2个32为寄存器
屏蔽模式
屏蔽位模式: 标识符寄存器(设置想接收的ID)和屏蔽寄存器(设置关心的位)。又比如说,当你值接收一个固定的 ID 的时候,你 把屏蔽寄存器全部设置为 1(即必须比较),然后在标示符寄存器里面存放你
想要的接收的 ID,那么当 CAN 总线上面出现你想要的 ID 的时候,它就将它接收,其他的不接收。
屏蔽列表模式:每组过滤器 中的两个寄存器全部用来做标示符寄存器,也就是说当接收的 ID 必须跟表 示符寄存器中的 ID 相同的时候才接收。
8、过滤器结构体
- /**
- * @brief CAN filter configuration structure definition
- */
- typedef struct
- {
- //这个是标示符寄存器的高 16 位
- uint32_t FilterIdHigh; /*!< Specifies the filter identification number (MSBs for a 32-bit
- configuration, first one for a 16-bit configuration).
- This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
- //这个是标示符寄存器的低 16 位
- uint32_t FilterIdLow; /*!< Specifies the filter identification number (LSBs for a 32-bit
- configuration, second one for a 16-bit configuration).
- This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
- //这个是屏蔽寄存器高字节的设置
- uint32_t FilterMaskIdHigh; /*!< Specifies the filter mask number or identification number,
- according to the mode (MSBs for a 32-bit configuration,
- first one for a 16-bit configuration).
- This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
- //这个是屏蔽寄存器低字节的设置
- uint32_t FilterMaskIdLow; /*!< Specifies the filter mask number or identification number,
- according to the mode (LSBs for a 32-bit configuration,
- second one for a 16-bit configuration).
- This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
- //选择你要使用的邮箱,它有 0 和 1
- uint32_t FilterFIFOAssignment; /*!< Specifies the FIFO (0 or 1U) which will be assigned to the filter.
- This parameter can be a value of @ref CAN_filter_FIFO */
- //表示标示符寄存器的ID,)我们这里使用第一组寄存器设置为:1。
- uint32_t FilterBank; /*!< Specifies the filter bank which will be initialized.
- For single CAN instance(14 dedicated filter banks),
- this parameter must be a number between Min_Data = 0 and Max_Data = 13.
- For dual CAN instances(28 filter banks shared),
- this parameter must be a number between Min_Data = 0 and Max_Data = 27. */
- //表示过滤模式。我们上面讲到它有屏蔽位模式和
- //标示符列表模式。我们使用屏蔽位模式,所以设置为 :
- //CAN_FilterMode_IdMask。
- uint32_t FilterMode; /*!< Specifies the filter mode to be initialized.
- This parameter can be a value of @ref CAN_filter_mode */
- //表示设置过滤器的长度。过滤器是可以设置为16位和32位两种模式的(即接收标准帧和拓展帧的区别)。
- uint32_t FilterScale; /*!< Specifies the filter scale.
- This parameter can be a value of @ref CAN_filter_scale */
- //表示过滤器的使能
- uint32_t FilterActivation; /*!< Enable or disable the filter.
- This parameter can be a value of @ref CAN_filter_activation */
- uint32_t SlaveStartFilterBank; /*!< Select the start filter bank for the slave CAN instance.
- For single CAN instances, this parameter is meaningless.
- For dual CAN instances, all filter banks with lower index are assigned to master
- CAN instance, whereas all filter banks with greater index are assigned to slave
- CAN instance.
- This parameter must be a number between Min_Data = 0 and Max_Data = 27. */
- } CAN_FilterTypeDef;
复制代码
|