单片机学习小组
直播中

张宇

7年用户 1651经验值
私信 关注

如何使用STM32Cube MX快速上手STM32 CAN协议配置?

如何使用STM32Cube MX快速上手STM32 CAN协议配置?

回帖(1)

李英

2022-2-7 15:51:41
前言

博主也是刚接触CAN协议的新手,如有不对的地方欢迎交流
本文旨在使用STM32Cube MX快速上手STM32 CAN协议配置教程,目的在于 “会用” CAN总线,对CAN 协议原理并未深入讲解。
CAN协议基本特点

基本特点如下:


  • 可多主控制: 当CAN总线空闲时,所有在总线上的终端都可以发送报文,根据标识符(CAN ID)决定优先级,当总线上有两个以上的终端发送消息时,对各消息CAN ID的每个位进行逐个仲裁比较。CAN ID值越低,报文优先级越高
  • 速度快,距离远:CAN 协议最快可达1Mbps(距离小于40m),最远可达10KM(速率小于 5Kbps)
  • CAN 帧种类:CAN 通信中包含五种帧种类,数据帧、遥控帧、错误帧、过载帧、间隔帧。其中最重要的是数据帧,用于通讯节点向外传送数据。数据帧中有数据段,用于承载数据的内容,一帧可发送0~8个字节的数据,MSB先行。

简单了解上述基本特点即可快速上手CAN总线的配置。
STM32Cube MX中的配置

CAN为了实现位同步,CAN协议把每一个数据位的时序分解成SS段,PTS段,PBS1段,PBS2段,这四段的长度加起来即为一个CAN数据位的长度。但在STM32中,将上述四个时序段整合成BS1和BS2两个时序段,对这两个时序段进行配置即可得到所需的通信波特率。
这里以F103C8T6为例,APB1时钟为36Mhz
计算波特率的方法:36M/分频系数/(BS1 + BS2 + 1)
如图设置,波特率是500Kbps,36M / 18 /(1 + 2 + 1) = 0.5M = 500K

配置选项说明:



  • Prescaler:分频系数
  • Time Quanta in Bit Segment 1:时间段1(BS1):定义采样点的位置。其值可以编程为1到16个时间单元,但也可以被自动延长,以补偿因为网络中不同节点的频率差异所造成的相位的正向漂移。
  • Time Quanta in Bit Segment 1::时间段2(BS2):定义发送点的位置。其值可以编程为1到8个时间单元,但也可以被自动缩短以补偿相位的负向漂移。

以上三个参数是决定CAN通信波特率的重要参数
Automatic Bus-Off Management:自动离线管理,节点检测到它发送错误或接收错误超过一定值时,会自动进入离线状态, 在离线状态中, CAN 不能接收或发送报文。
Automatic Retransmission:自动重发机制,当CAN总线发送失败时,会自动重发。
Operating Mode:CAN模式选择,有正常、回环、静默和静默回环四种模式可选,本篇文章只讲正常模式(双机通信)。
分享一个CAN波特率计算小工具:https://download.csdn.net/download/theLeMon/13138490

开启CAN接收中断,以便进行中断接收


配置好CubeMX后,点击GENERATE CODE 生成模板代码


CAN 收发配置
做完CAN的基本配置后,为了收发CAN数据,还需要进行CAN滤波器配置和发送消息句柄、接收消息句柄设置。


CAN滤波器配置
CAN过滤器是用于处理CAN接收过滤问题的,
/**
* @brief           CAN过滤器配置  
* @param[in]         
* @return         
*/
void CAN_Filter_Config(void)
{
    CAN_FilterTypeDef  sFilterConfig;
   
    /*配置CAN过滤器*/
    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)
    {
        while(1){}
    }


    //启动CAN外围设备
    if (HAL_CAN_Start(&hcan) != HAL_OK)
    {
        while(1){}
    }
      
    //激活可以RX通知
    if (HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
    {
        while(1){}
    }
}


CAN接收中断
/**
* @brief           CAN接收回调函数
*/

// can接收buffer,这里用全局变量保存,以便其他函数处理
uint8_t can_rec_dat[8];       


void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
    CAN_RxHeaderTypeDef        can_rx_header;      //接收
    if(hcan->Instance == CAN1)
    {
        HAL_CAN_GetRxMessage(hcan,CAN_FILTER_FIFO0,&can_rx_header,can_rec_dat);
    }
}


3、CAN 发送


/**
* @brief           CAN发送一组数据  
* @param[in]       msg:待发送的数据(最大为8个字节)
* @param[in]       len:数据帧的长度(最大为8)
* @param[in]       std_id:CAN_ID
* @return          0:发送成功   1:发送失败
*/
uint8_t CAN_Send_Msg(uint8_t* msg, uint8_t len, uint8_t std_id)
{       
    CAN_TxHeaderTypeDef        can_tx_header;      
        uint32_t TxMailbox;
    can_tx_header.StdId = std_id;        
    can_tx_header.IDE = CAN_ID_STD;   
    can_tx_header.RTR = CAN_RTR_DATA;  
    can_tx_header.TransmitGlobalTime = ENABLE;
    can_tx_header.DLC = len;            
    //MailboxesFreeLevel = HAL_CAN_GetTxMailboxesFreeLevel(&hcan);
    while(HAL_CAN_GetTxMailboxesFreeLevel(&hcan)==0);
    if(HAL_CAN_AddTxMessage(&hcan, &can_tx_header, msg, &TxMailbox) != HAL_OK)//发送
        {
                return 1;
        }
    return 0;
}
举报

更多回帖

发帖
×
20
完善资料,
赚取积分