你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

cubemx STM32 CAN通讯,多包数据收发送的处理!

[复制链接]
STMCU 发布时间:2020-9-28 13:43
一、遇到的问题

1、发送单包数据,我想都非常简答,例子一大堆包括,demo。但是发送多包数据怎么处理?
2、发送多包数据,网上基本上就两种说发
*两个数据之间加延时
*查询邮箱是否满了
HAL_CAN_GetTxMailboxesFreeLevel()
对比上面两种方法,为了效率我们选择第二种



二、配置与代码

直接上cubemx配置



1_meitu_39.jpg

2_meitu_40.jpg



直接生成后,cubemx没有设置过滤器,需要自己添加,结合网上各资料总结代码如下


  1. int main(void)
  2. {
  3.   /* USER CODE BEGIN 1 */

  4.   /* USER CODE END 1 */

  5.   /* MCU Configuration--------------------------------------------------------*/

  6.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  7.   HAL_Init();

  8.   /* USER CODE BEGIN Init */

  9.   /* USER CODE END Init */

  10.   /* Configure the system clock */
  11.   SystemClock_Config();

  12.   /* USER CODE BEGIN SysInit */

  13.   /* USER CODE END SysInit */

  14.   /* Initialize all configured peripherals */
  15.   MX_GPIO_Init();
  16.   MX_CAN1_Init();
  17.   /* USER CODE BEGIN 2 */
  18.         CAN_Init();
  19.   /* USER CODE END 2 */

  20.   /* Infinite loop */
  21.   /* USER CODE BEGIN WHILE */
  22.   while (1)
  23.   {
  24.     /* USER CODE END WHILE */

  25.     /* USER CODE BEGIN 3 */
  26.                 uint8_t TxData[100] ;
  27.                 for(uint8_t i=0;i<100;i++)
  28.                         TxData[i]=i;
  29.                 CAN_SendStdMsg(&hcan1,TxData,sizeof(TxData));
  30.                 HAL_Delay(100);
  31.   }
  32.   /* USER CODE END 3 */
  33. }
复制代码

bsp_can.c


  1. #include "bsp_can.h"


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


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

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

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

  18. void CAN_Filter_Config(void)
  19. {
  20.     CAN_FilterTypeDef sFilterConfig;
  21.     CAN_FilterRegTypeDef IDH = {0};
  22.     CAN_FilterRegTypeDef IDL = {0};

  23. #if CAN_ID_TYPE_STD_ENABLE
  24.     IDH.Sub.STID = (CAN_BASE_ID >> 16) & 0xFFFF;                                                                                        // 标准ID高16位
  25.     IDL.Sub.STID = (CAN_BASE_ID & 0xFFFF);                                                                                                                // 标准ID低16位
  26. #else
  27.     IDH.Sub.EXID = (CAN_BASE_ID >> 16) & 0xFFFF;                                                                                        // 扩展ID高16位
  28.     IDL.Sub.EXID = (CAN_BASE_ID & 0xFFFF);                                                                                                                // 扩展ID低16位
  29.     IDL.Sub.IDE  = 1;                                                                                                                                                                                                        // 扩展帧标志位置位
  30. #endif
  31.     sFilterConfig.FilterBank           = 0;                                                                                                    // 设置过滤器组编号
  32. #if CAN_FILTER_MODE_MASK_ENABLE
  33.     sFilterConfig.FilterMode           = CAN_FILTERMODE_IDMASK;                                // 屏蔽位模式
  34. #else
  35.     sFilterConfig.FilterMode           = CAN_FILTERMODE_IDLIST;                                // 列表模式
  36. #endif
  37.     sFilterConfig.FilterScale          = CAN_FILTERSCALE_32BIT;                                // 32位宽
  38.     sFilterConfig.FilterIdHigh         = IDH.value;                                                                                // 标识符寄存器一ID高十六位,放入扩展帧位
  39.     sFilterConfig.FilterIdLow          = IDL.value;                                                                                // 标识符寄存器一ID低十六位,放入扩展帧位
  40.     sFilterConfig.FilterMaskIdHigh     = IDH.value;                                                                                // 标识符寄存器二ID高十六位,放入扩展帧位
  41.     sFilterConfig.FilterMaskIdLow      = IDL.value;                                                                                // 标识符寄存器二ID低十六位,放入扩展帧位
  42.     sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0;                                                                // 过滤器组关联到FIFO0
  43.     sFilterConfig.FilterActivation     = ENABLE;                                                                                        // 激活过滤器
  44.     sFilterConfig.SlaveStartFilterBank = 14;                                                                                                        // 设置从CAN的起始过滤器编号,本单片机只有一个CAN,顾此参数无效
  45.     if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
  46.     {
  47.         Error_Handler();
  48.     }
  49. }



  50. void CAN_Init(void)
  51. {
  52.     CAN_Filter_Config();
  53.     HAL_CAN_Start(&hcan1);
  54.     HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);                                        // 使能CAN接收中断
  55. }

  56. void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *canHandle)
  57. {
  58.         static CAN_RxPacketTypeDef packet;
  59.        
  60.     // CAN数据接收
  61.     if (canHandle->Instance == hcan1.Instance)
  62.     {
  63.         if (HAL_CAN_GetRxMessage(canHandle, CAN_RX_FIFO0, &packet.hdr, packet.payload) == HAL_OK)                // 获得接收到的数据头和数据
  64.         {
  65. //                        printf("\r\n\r\n\r\n################### CAN RECV ###################\r\n");
  66. //                        printf("STID:0x%X\r\n",packet.hdr.StdId);
  67. //                        printf("EXID:0x%X\r\n",packet.hdr.ExtId);
  68. //                        printf("DLC :%d\r\n", packet.hdr.DLC);
  69. //                        printf("DATA:");
  70. //                        for(int i = 0; i < packet.hdr.DLC; i++)
  71. //                        {
  72. //                                printf("0x%02X ", packet.payload[i]);
  73. //                        }
  74.            HAL_CAN_ActivateNotification(canHandle, CAN_IT_RX_FIFO0_MSG_PENDING);                                                // 再次使能FIFO0接收中断
  75.         }
  76.     }
  77. }


  78. uint8_t CAN_Transmit(CAN_TxPacketTypeDef* packet)
  79. {
  80.         if(HAL_CAN_AddTxMessage(&hcan1, &packet->hdr, packet->payload, &packet->mailbox) != HAL_OK)
  81.                 return 1;
  82.         return 0;
  83. }


  84. CAN_TxHeaderTypeDef TxMeg;
  85. uint16_t TimsRepeat;
  86. uint8_t CAN_SendStdMsg(CAN_HandleTypeDef* __hcan,uint8_t *pData,uint16_t Len)
  87. {
  88.         uint16_t SendCNT=0;
  89.         uint32_t TxMailbox;
  90.         HAL_StatusTypeDef        HAL_RetVal;
  91.        
  92.         TxMeg.StdId = 0x321; //标准ID
  93. //        TxMeg.ExtId = 0x10F01234;
  94.         TxMeg.IDE = CAN_ID_STD;// 标准ID类型
  95. //TxMeg.IDE = CAN_ID_EXT;// 扩展ID类型
  96.         TxMeg.RTR = CAN_RTR_DATA;                // 数据帧
  97. //TxMeg.RTR =  CAN_RTR_REMOTE;                // 远程帧
  98.         TxMeg.TransmitGlobalTime = DISABLE;
  99.        
  100.         if(!__hcan || ! pData ||!Len) return 1;

  101.         TimsRepeat =Len/8+(Len%8?1:0);
  102.         while(TimsRepeat--)
  103.         {
  104.                 while(HAL_CAN_GetTxMailboxesFreeLevel(__hcan)==0);
  105.                 if(TimsRepeat>0)
  106.                 {       
  107.                         TxMeg.DLC=8;
  108.                 }
  109.                 else
  110.                 {
  111.                         if(Len%8)
  112.                         TxMeg.DLC=Len%8;
  113.                 }
  114.                 HAL_RetVal=HAL_CAN_AddTxMessage(__hcan,&TxMeg,(pData+SendCNT),&TxMailbox);  
  115.                 if(HAL_RetVal!=HAL_OK)
  116.                 {
  117.                         return 0;
  118.                 }
  119.                 SendCNT+=8;
  120.         }
  121.         return 1;
复制代码


bsp_can.h


  1. #ifndef _BSP_CAN_H
  2. #define _BSP_CAN_H
  3. #include "stm32f4xx_hal.h"
  4. #include "can.h"

  5. //typedef struct
  6. //{
  7. //        uint32_t mailbox;
  8. //        CAN_TxHeaderTypeDef hdr;
  9. //        uint8_t payload[8];
  10. //}CAN_TxPacketTypeDef;

  11. typedef struct
  12. {
  13.         CAN_RxHeaderTypeDef hdr;
  14.         uint8_t payload[8];
  15. }CAN_RxPacketTypeDef;


  16. void CAN_Init(void);
  17. uint8_t CAN_SendStdMsg(CAN_HandleTypeDef* __hcan,uint8_t *pData,uint16_t Len);
  18. #endif
复制代码

就可以工作了???非也
其实我发现还是有问题


3_meitu_41.jpg



为什么第2 3包数据到最后去了?


查了一些资料发现了问题:
”发送邮箱“是用于CAN总线数据发送的,总共有3个,并且存在优先级关系。优先级越高表示其里面的数据会被优先发送。数据在发送前都会被送到优先级最高且空闲的发送邮箱,然后依次发送。最后说明一点:“发送邮箱有3个,且每个邮箱只能装一个报文”

原因是,三个邮箱有优先级,我发送前会检查三个邮箱是否都满,没满就发送。导致 第 2 3包数据在两个优先级低的邮箱,导致才最后发送


查看库函数


  1. /**
  2.   * @brief  Return Tx Mailboxes free level: number of free Tx Mailboxes.
  3.   * @param  hcan pointer to a CAN_HandleTypeDef structure that contains
  4.   *         the configuration information for the specified CAN.
  5.   * @retval Number of free Tx Mailboxes.
  6.   */
  7. uint32_t HAL_CAN_GetTxMailboxesFreeLevel(CAN_HandleTypeDef *hcan)
  8. {
  9.   uint32_t freelevel = 0U;
  10.   HAL_CAN_StateTypeDef state = hcan->State;

  11.   if ((state == HAL_CAN_STATE_READY) ||
  12.       (state == HAL_CAN_STATE_LISTENING))
  13.   {
  14.     /* Check Tx Mailbox 0 status */
  15.     if ((hcan->Instance->TSR & CAN_TSR_TME0) != 0U)
  16.     {
  17.       freelevel++;
  18.     }

  19.     /* Check Tx Mailbox 1 status */
  20.     if ((hcan->Instance->TSR & CAN_TSR_TME1) != 0U)
  21.     {
  22.       freelevel++;
  23.     }

  24.     /* Check Tx Mailbox 2 status */
  25.     if ((hcan->Instance->TSR & CAN_TSR_TME2) != 0U)
  26.     {
  27.       freelevel++;
  28.     }
  29.   }

  30.   /* Return Tx Mailboxes free level */
  31.   return freelevel;
  32. }
复制代码

解决办法如下:当然你也可以自己封装一下


  1. uint32_t HAL_CAN_GetTxMailboxesFreeLevel(CAN_HandleTypeDef *hcan)
  2. {
  3.   uint32_t freelevel = 0U;
  4.   HAL_CAN_StateTypeDef state = hcan->State;

  5.   if ((state == HAL_CAN_STATE_READY) ||
  6.       (state == HAL_CAN_STATE_LISTENING))
  7.   {
  8.                
  9.                 if (((hcan->Instance->TSR & CAN_TSR_TME0) != 0U)&& \
  10.                           ((hcan->Instance->TSR & CAN_TSR_TME1) != 0U)&& \
  11.                      (hcan->Instance->TSR & CAN_TSR_TME2) != 0U)
  12.     {
  13.       freelevel++;
  14.     }

  15.     /* Check Tx Mailbox 0 status */
  16. //    if ((hcan->Instance->TSR & CAN_TSR_TME0) != 0U)
  17. //    {
  18. //      freelevel++;
  19. //    }

  20.     /* Check Tx Mailbox 1 status */
  21. //    if ((hcan->Instance->TSR & CAN_TSR_TME1) != 0U)
  22. //    {
  23. //      freelevel++;
  24. //    }

  25. //    /* Check Tx Mailbox 2 status */
  26. //    if ((hcan->Instance->TSR & CAN_TSR_TME2) != 0U)
  27. //    {
  28. //      freelevel++;
  29. //    }
  30.   }

  31.   /* Return Tx Mailboxes free level */
  32.   return freelevel;
  33. }
复制代码

4_meitu_42.jpg


三 总结

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、发送数据结构体指针

  
  1. /**
  2.   * @brief  CAN Tx message header structure definition
  3.   */
  4. typedef struct
  5. {
  6. //表示标准的ID
  7.   uint32_t StdId;    /*!< Specifies the standard identifier.
  8.                                                   This parameter must be a number between Min_Data = 0 and Max_Data = 0x7FF. */
  9. //表示扩展的ID
  10.   uint32_t ExtId;    /*!< Specifies the extended identifier.
  11.                           This parameter must be a number between Min_Data = 0 and Max_Data = 0x1FFFFFFF. */
  12. //要发送的是扩展帧还是标准帧
  13.   uint32_t IDE;      /*!< Specifies the type of identifier for the message that will be transmitted.
  14.                           This parameter can be a value of @ref CAN_identifier_type */
  15. //表示发送的帧类型。如:数据帧、远程帧等
  16.   uint32_t RTR;      /*!< Specifies the type of frame for the message that will be transmitted.
  17.                           This parameter can be a value of @ref CAN_remote_transmission_request */
  18. //表示发送的数据长度
  19.   uint32_t DLC;      /*!< Specifies the length of the frame that will be transmitted.
  20.                           This parameter must be a number between Min_Data = 0 and Max_Data = 8. */

  21.   FunctionalState TransmitGlobalTime; /*!< Specifies whether the timestamp counter value captured on start
  22.                           of frame transmission, is sent in DATA6 and DATA7 replacing pData[6] and pData[7].
  23.                           @note: Time Triggered Communication Mode must be enabled.
  24.                           @note: DLC must be programmed as 8 bytes, in order these 2 bytes are sent.
  25.                           This parameter can be set to ENABLE or DISABLE. */

  26. } CAN_TxHeaderTypeDef;
复制代码

7、过滤器:为了方便接收想要的ID,过滤掉不想要的ID。28组或14组过滤器,每组2个32为寄存器
屏蔽模式
屏蔽位模式: 标识符寄存器(设置想接收的ID)和屏蔽寄存器(设置关心的位)。又比如说,当你值接收一个固定的 ID 的时候,你 把屏蔽寄存器全部设置为 1(即必须比较),然后在标示符寄存器里面存放你
想要的接收的 ID,那么当 CAN 总线上面出现你想要的 ID 的时候,它就将它接收,其他的不接收。
屏蔽列表模式:每组过滤器 中的两个寄存器全部用来做标示符寄存器,也就是说当接收的 ID 必须跟表 示符寄存器中的 ID 相同的时候才接收。



8、过滤器结构体



  1. /**
  2.   * @brief  CAN filter configuration structure definition
  3.   */
  4. typedef struct
  5. {
  6.         //这个是标示符寄存器的高 16 位
  7.   uint32_t FilterIdHigh;          /*!< Specifies the filter identification number (MSBs for a 32-bit
  8.                                        configuration, first one for a 16-bit configuration).
  9.                                        This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
  10.         //这个是标示符寄存器的低 16 位
  11.   uint32_t FilterIdLow;           /*!< Specifies the filter identification number (LSBs for a 32-bit
  12.                                        configuration, second one for a 16-bit configuration).
  13.                                        This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
  14.         //这个是屏蔽寄存器高字节的设置
  15.   uint32_t FilterMaskIdHigh;      /*!< Specifies the filter mask number or identification number,
  16.                                        according to the mode (MSBs for a 32-bit configuration,
  17.                                        first one for a 16-bit configuration).
  18.                                        This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
  19.         //这个是屏蔽寄存器低字节的设置
  20.   uint32_t FilterMaskIdLow;       /*!< Specifies the filter mask number or identification number,
  21.                                        according to the mode (LSBs for a 32-bit configuration,
  22.                                        second one for a 16-bit configuration).
  23.                                        This parameter must be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF. */
  24.         //选择你要使用的邮箱,它有 0 和 1
  25.   uint32_t FilterFIFOAssignment;  /*!< Specifies the FIFO (0 or 1U) which will be assigned to the filter.
  26.                                        This parameter can be a value of @ref CAN_filter_FIFO */
  27.   //表示标示符寄存器的ID,)我们这里使用第一组寄存器设置为:1。
  28.   uint32_t FilterBank;            /*!< Specifies the filter bank which will be initialized.
  29.                                        For single CAN instance(14 dedicated filter banks),
  30.                                        this parameter must be a number between Min_Data = 0 and Max_Data = 13.
  31.                                        For dual CAN instances(28 filter banks shared),
  32.                                        this parameter must be a number between Min_Data = 0 and Max_Data = 27. */
  33. //表示过滤模式。我们上面讲到它有屏蔽位模式和
  34. //标示符列表模式。我们使用屏蔽位模式,所以设置为 :
  35. //CAN_FilterMode_IdMask。
  36.   uint32_t FilterMode;            /*!< Specifies the filter mode to be initialized.
  37.                                        This parameter can be a value of @ref CAN_filter_mode */
  38. //表示设置过滤器的长度。过滤器是可以设置为16位和32位两种模式的(即接收标准帧和拓展帧的区别)。
  39.   uint32_t FilterScale;           /*!< Specifies the filter scale.
  40.                                        This parameter can be a value of @ref CAN_filter_scale */
  41. //表示过滤器的使能
  42.   uint32_t FilterActivation;      /*!< Enable or disable the filter.
  43.                                        This parameter can be a value of @ref CAN_filter_activation */

  44.   uint32_t SlaveStartFilterBank;  /*!< Select the start filter bank for the slave CAN instance.
  45.                                        For single CAN instances, this parameter is meaningless.
  46.                                        For dual CAN instances, all filter banks with lower index are assigned to master
  47.                                        CAN instance, whereas all filter banks with greater index are assigned to slave
  48.                                        CAN instance.
  49.                                        This parameter must be a number between Min_Data = 0 and Max_Data = 27. */
  50. } CAN_FilterTypeDef;
复制代码


2 收藏 2 评论1 发布时间:2020-9-28 13:43

举报

1个回答
yklstudent-1794 回答时间:2020-9-29 08:29:13
不应该中断方式发送多包嘛
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版