打造一个通用性MCU架构,支持CX32/AT32/NRF51/NRF52等。 OS支持RTX4/RTX5/FreeRtos。 采用VsCode+GCC组合,VsCode+KEIL5,超强开发方式。 QQ群:524408033

LiSun

打造一个通用性MCU架构,支持CX32/AT32/NRF51/NRF52等。 OS支持RTX4/RTX5/FreeRtos。 采用VsCode+GCC组合,VsCode+KEIL5,超强开发方式。 QQ群:524408033

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一、简介

CAN网络由CAN节点和CAN总线组成。

数据传输过程:CAN控制器将数据(二进制编码0和1)发送到CAN收发器。然后收发器把这个数据转化为差分信号,通过高速CAN和低速CAN传输到CAN网络。

CAN网络的4个边界条件:

  1. 最大数据速率为1 Mbit / s。
  2. 允许的最大网络扩展范围约为40米。
  3. CAN节点的最大数量为32。
  4. 在CAN网络的末端,总线端接电阻有助于避免补偿过程(反射)。

二、特性

CAN节点是指参与CAN通信的设备,包括CAN控制器,CAN收发器(CAN-Transceiver)和微控制器(Microcontroller,主机)。

  • CAN控制器提供了CAN协议规定的通信功能,从而尽可能减轻了主机的负担。在CAN网络中,CAN节点要发送和接收的CAN消息频率不同。这导致了两种基本的CAN控制器架构:具有和不具有对象存储的CAN控制器。
  • CAN收发器将CAN控制器连接到物理传输介质(CAN总线)。

CAN总线是指信号的物理传输介质。由于出于电磁兼容性的原因,CAN网络中的物理信号传输是对称的,CAN网络中的物理信号传输基于电压差的传输(差分信号传输)。因此,可以有效地消除由电动机,点火系统和开关触点引起的干扰电压。因此,传输介质(CAN总线)由两条线路组成:高速CAN和低速CAN。

CAN总线遵循如下逻辑:

  • CAN网络中的物理信号传输基于电压差的传输(差分信号传输)。高速CAN和低速CAN的电压差大于0.9V时为显性电平,对应逻辑“0”,小于0.5V为隐性电平,对应逻辑“1”。
  • CAN网络基于“线与”逻辑。显性总线电平会覆盖隐性总线电平。如果不同的CAN节点同时发送显性电平和隐性电平,那么CAN总线会处于显性电平的逻辑“0”。隐性电平的逻辑“1”仅在所有CAN节点隐性传输时才发生。

三、笔记

  1. 波特率计算:baud=(systime/(pre*(bs1+bs2+sjw)))
    例如: 500khz=(36/(12*(3+2+1)))
  2. CudeMX配置CAN:最重要的就是通讯配置要一致,且波特率也一致。
    Bit Timings Parameters(位计时参数)
    ----Prescaler(for Time Quantum) 预分频器(用于时间量)
    ----Time Quantum (时间量)
    ----Time Quanta in Bit Segment 1 (位段1中的时间量)
    ----Time Quanta in Bit Segment 2 (位段2中的时间量)
    ----Time for one Bit (can总线BT时间确定-用于确定波特率)
    ----ReSynchronization Jump Width (重同步跳转宽度)

四、示例代码


***********************************/
#include "MyCAN.h"
#include "can.h"
#include "FreeRTOS.h"
#include "cmsis_os.h"

/*********************************************
函数名:CAN_Config
功  能:过滤器配置
形  参:
        hcan      --CAN信息结构体         (hcan/hcan1/hcan2)
        FIFO_Num  --通道选择              (0=RX_FIFO0/1=RX_FIFO1)
返回值:
备  注:本函数需要放到main.c中进行初始化。 放在MX_CAN_Init();后面的用户区
        CAN_Config(&hcan,0);//过滤器配置,启动通道0

笔  记:使用STM32F103CVET6芯片时发现,[PB8/PB9]只能使用FIFO0  [PA11/PA12]两个通道正常使用
**********************************************/
void CAN_Config(CAN_HandleTypeDef *phcan, uint8_t FIFO_Num)
{
    /*配置过滤器,用于接收指定范围的ID帧*/
    CAN_FilterTypeDef CAN_FilterType;
    CAN_FilterType.FilterBank = 0;                        //筛选器组[0,13]
    CAN_FilterType.SlaveStartFilterBank = 14;             //启动从过滤器组[0,27]
    CAN_FilterType.FilterIdHigh = 0x0000;                 //要过滤的ID高位[0x0000,0xFFFF]
    CAN_FilterType.FilterIdLow = 0x0000;                  //要过滤的ID低位[0x0000,0xFFFF]
    CAN_FilterType.FilterMaskIdHigh = 0x0000;             //过滤器高16位每位无必须匹配
    CAN_FilterType.FilterMaskIdLow = 0x0000;              //过滤器低16位每位无必须匹配
    CAN_FilterType.FilterFIFOAssignment = FIFO_Num;       //过滤器被关联到(0=RX_FIFO0/1=RX_FIFO1)
    CAN_FilterType.FilterMode = CAN_FILTERMODE_IDMASK;    //工作在标识符屏蔽位模式
    CAN_FilterType.FilterScale = CAN_FILTERSCALE_32BIT;   //过滤器位宽为单个32位
    CAN_FilterType.FilterActivation = ENABLE;             //使能过滤器
    if(HAL_CAN_ConfigFilter(phcan, &CAN_FilterType) != HAL_OK)
    {
        Error_Handler();
    }
    /*开启对应CAN通道的中断服务*/
    if(FIFO_Num == 0)
    {
        if(HAL_CAN_ActivateNotification(phcan, CAN_IT_RX_FIFO0_MSG_PENDING ) != HAL_OK)
        {
            Error_Handler();
        }
    }
    else
    {
        if(HAL_CAN_ActivateNotification(phcan, CAN_IT_RX_FIFO1_MSG_PENDING ) != HAL_OK)
        {
            Error_Handler();
        }
    }

    /*启动CAN通讯*/
    if(HAL_CAN_Start(phcan) != HAL_OK)
    {
        Error_Handler();
    }
}

/*********************************************
函数名:Can_TxMessage
功  能:利用CAN发送一帧数据
形  参:
        hcan      --CAN信息结构体         (hcan/hcan1/hcan2)
        ide       --帧类型                (0,标准帧  1,扩展帧)
        id        --帧ID号                (标准帧[0,0x7FF]  扩展帧[0,0x1FFFFFFF])
        len       --数据长度              (指数组长度byte) 范围:0-8
        pdata     --数据内容              例如:"12345678"或数组地址   长度由上面参数决定
返回值:0:成功。1:失败
备  注:
**********************************************/
uint8_t Can_TxMessage(CAN_HandleTypeDef *phcan, uint8_t ide, uint32_t id, uint8_t len, uint8_t *pdata)
{
    uint32_t  TxMailbox;           //得到发送成功的邮箱号
    CAN_TxHeaderTypeDef TxHeader;  //发送-头协议 信息结构体,用于填充参数
    HAL_StatusTypeDef   HAL_RetVal; //CAN返回值
    uint16_t i = 0;
    /*填充 发送 头协议*/
    if(ide == 0)
    {
        TxHeader.IDE = CAN_ID_STD;
        TxHeader.StdId = id;
    }
    else
    {
        TxHeader.IDE = CAN_ID_EXT;
        TxHeader.ExtId = id;
    }

    TxHeader.RTR = CAN_RTR_DATA,          //消息的帧类型      数据帧
    TxHeader.DLC = len,                   //帧的长度            8
    TxHeader.TransmitGlobalTime = DISABLE; //不捕获时间

    /*询问CAN是否有空闲邮箱*/
    while(HAL_CAN_GetTxMailboxesFreeLevel(phcan) == 0)
    {
        i++;
        if(i > 0xfffe)//超时,发送失败
            return 1;
    }
    osDelay(1);

    /*发送帧*/
    HAL_RetVal = HAL_CAN_AddTxMessage(phcan, &TxHeader, pdata, &TxMailbox); //发送一帧数据
    //printf("TxMailbox %d\r\n",TxMailbox);
    if(HAL_RetVal != HAL_OK)
        return 1;
    return 0;
}

/*********************************************
函数名:HAL_CAN_RxFifo0MsgPendingCallback
功  能:CAN 通道0 接收回调函数,当有完整帧到达,就会触发。
形  参:hcan      --CAN信息结构体         (hcan/hcan1/hcan2)
返回值:
备  注:
**********************************************/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
    uint8_t Rxbuff[8] = "";         //存放帧数据
    CAN_RxHeaderTypeDef RxHeader;   //存放帧头协议
    HAL_StatusTypeDef   HAL_RetVal; //CAN返回值

    if(hcan->Instance == CAN1)
    {

        HAL_RetVal = HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, Rxbuff); //从通道0缓存区读取一帧
        if(HAL_RetVal == HAL_OK)
        {
            /*用户自定义区*/
            if(RxHeader.IDE == 0)
            {
                printf("FIFO0,ID:%d -- Rxbuff:%s\r\n", RxHeader.StdId, Rxbuff);
            }
            else
            {
                printf("FIFO0,ID:%d -- Rxbuff:%s\r\n", RxHeader.ExtId, Rxbuff);
            }
        }
    }
    //if(hcan->Instance == CAN2)//针对有两个CAN时,共享通道0
}
/*********************************************
函数名:HAL_CAN_RxFifo1MsgPendingCallback
功  能:CAN 通道1 接收回调函数,当有完整帧到达,就会触发。
形  参:hcan      --CAN信息结构体         (hcan/hcan1/hcan2)
返回值:
备  注:
**********************************************/
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
    uint8_t Rxbuff[8] = "";         //存放帧数据
    CAN_RxHeaderTypeDef RxHeader;   //存放帧头协议
    HAL_StatusTypeDef   HAL_RetVal; //CAN返回值

    if(hcan->Instance == CAN1)
    {
        HAL_RetVal = HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO1, &RxHeader, Rxbuff); //从通道1缓存区读取一帧
        if(HAL_RetVal == HAL_OK)
        {
            /*用户自定义区*/
            if(RxHeader.IDE == 0)
            {
                printf("FIFO1,ID:%d -- Rxbuff:%s\r\n", RxHeader.StdId, Rxbuff);
            }
            else
            {
                printf("FIFO1,ID:%d -- Rxbuff:%s\r\n", RxHeader.ExtId, Rxbuff);
            }
        }
    }
    //if(hcan->Instance == CAN2)//针对有两个CAN时,共享通道1
}


/*用于回环测试*/
void Can_Dome(CAN_HandleTypeDef *hcan1)
{
    uint8_t sta;
    sta = Can_TxMessage(hcan1, 0, 8, 8, "789456");
    if(sta != 0)
    {
        printf("err:%d\r\n", sta);
    }
}

#ifndef _MYCAN_H
#define _MYCAN_H
#include "main.h"
void CAN_Config(CAN_HandleTypeDef *phcan, uint8_t FIFO_Num);

uint8_t Can_TxMessage(CAN_HandleTypeDef *phcan, uint8_t ide, uint32_t id, uint8_t len, uint8_t *pdata);

void Can_Dome(CAN_HandleTypeDef *hcan1);
#endif

posted on 2020-04-11 10:59  xuejianqiang  阅读(83)  评论(0编辑  收藏  举报  来源
打造一个通用性MCU架构,支持CX32/AT32/NRF51/NRF52等。 OS支持RTX4/RTX5/FreeRtos。 采用VsCode+GCC组合,VsCode+KEIL5,超强开发方式。 QQ群:524408033