单片机 STM32 HAL CAN 总线 例子

/*************笔记**************** 一、波特率计算:baud=(systime/(pre*(bs1+bs2+sjw))) 例如: 500khz=(36/(12*(3+2+1))) 二、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