一、基本流程
基础配置分为三大块:
1.CAN外设的初始化
- RCC时钟初始化(GPIO时钟和CAN1的时钟)
- GPIO初始化(CAN_TX复用推挽输出模式,CAN_RX上拉输入模式)
- 整个CAN外设的初始化(结构体配置,模式,波特率,各种其余小功能......)
- 单独对过滤器初始化(结构体配置,过滤器位宽、模式、R1 R2的值、FIFO关联、使能的参数)
- 最后若是使用中断再加上ITConfig函数使能中断输出,再加上NVIC和中断函数
2.发送报文
库函数专门封装了一个发送结构体和发送函数,将报文数据写入发送结构体,再调用发送函数就行了
3.接收报文
库函数也有检查接收FIFO状态的函数,若是FIFO里面有报文,就调用接收函数,这样报文数据就会存入到接收的结构体当中。读取结构体就能获取接收到的数据了
二、函数介绍
/* 恢复缺省配置,把CAN配置到默认的复位状态 *****/
void CAN_DeInit(CAN_TypeDef* CANx);
/* 初始化和配置函数 *********************************/
uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct);//CAN初始化
void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct);//过滤器初始化
void CAN_StructInit(CAN_InitTypeDef* CAN_InitStruct);//赋一个默认值
void CAN_SlaveStartBank(uint8_t CAN_BankNumber); //配置CAN2的起始滤波器号,互联型设备用的
void CAN_DBGFreeze(CAN_TypeDef* CANx, FunctionalState NewState);//调试时的冻结模式
void CAN_TTComModeCmd(CAN_TypeDef* CANx, FunctionalState NewState);//用于使能TTCM模式中的TGT位
/* 发送相关函数 *********************************************************/
uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage);//发送一个CAN报文,返回值表示报文存入了哪个邮箱
uint8_t CAN_TransmitStatus(CAN_TypeDef* CANx, uint8_t TransmitMailbox);//获取发送邮箱的状态
void CAN_CancelTransmit(CAN_TypeDef* CANx, uint8_t Mailbox);//取消发送
/* 接收相关函数 **********************************************************/
void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage);//读取接收FIFO数据
void CAN_FIFORelease(CAN_TypeDef* CANx, uint8_t FIFONumber);//单独释放FIFO
uint8_t CAN_MessagePending(CAN_TypeDef* CANx, uint8_t FIFONumber);//获取指定FIFO队列里排队的报文数目
/* 工作模式相关函数(了解) **************************************************/
uint8_t CAN_OperatingModeRequest(CAN_TypeDef* CANx, uint8_t CAN_OperatingMode);//工作模式请求
uint8_t CAN_Sleep(CAN_TypeDef* CANx);//直接指定CAN进入睡眠模式
uint8_t CAN_WakeUp(CAN_TypeDef* CANx);//直接指定CAN退出睡眠模式
/* 错误管理相关函数(了解) *************************************************/
uint8_t CAN_GetLastErrorCode(CAN_TypeDef* CANx);//获取最近一次的错误码
uint8_t CAN_GetReceiveErrorCounter(CAN_TypeDef* CANx);//获取接收错误计数器REC
uint8_t CAN_GetLSBTransmitErrorCounter(CAN_TypeDef* CANx);//获取发送错误计时器的低8位TEC
/* 中断和标志位管理函数 **********************************/
void CAN_ITConfig(CAN_TypeDef* CANx, uint32_t CAN_IT, FunctionalState NewState);//用于中断输出使能
FlagStatus CAN_GetFlagStatus(CAN_TypeDef* CANx, uint32_t CAN_FLAG);//获取标志位状态
void CAN_ClearFlag(CAN_TypeDef* CANx, uint32_t CAN_FLAG);//清除标志位
ITStatus CAN_GetITStatus(CAN_TypeDef* CANx, uint32_t CAN_IT);//获取中断状态
void CAN_ClearITPendingBit(CAN_TypeDef* CANx, uint32_t CAN_IT);//清除中断挂起位
三、整体代码(主要在于备注)
MyCAN.c:
#include "stm32f10x.h" // Device header
void MyCAN_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
CAN_InitTypeDef CAN_InitStructure;
CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack;//设置CAN外设的测试模式
/*
#define CAN_Mode_Normal ((uint8_t)0x00) 正常模式
#define CAN_Mode_LoopBack ((uint8_t)0x01) 环回模式
#define CAN_Mode_Silent ((uint8_t)0x02) 静默模式
#define CAN_Mode_Silent_LoopBack ((uint8_t)0x03) 环回静默模式
*/
//这里若是改成normal,则就是3个设备互相通信代码
CAN_InitStructure.CAN_Prescaler = 48; //分频系数
//波特率 = 36M / 48(分频系数) / (1 + 2(BS1tq数) + 3(BS2tq数)) = 125K
//高速CAN波特率范围是125k~1M
CAN_InitStructure.CAN_BS1 = CAN_BS1_2tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;
CAN_InitStructure.CAN_SJW = CAN_SJW_2tq;
CAN_InitStructure.CAN_NART = DISABLE;//置0,自动重传
CAN_InitStructure.CAN_TXFP = DISABLE;//置0,优先级由报文标识符来决定
CAN_InitStructure.CAN_RFLM = DISABLE;//置0,禁用FIFO锁定,FIFO溢出时,FIFO中最后收到的报文被新报文覆盖
CAN_InitStructure.CAN_AWUM = DISABLE;//置0,手动唤醒,软件清零SLEEP,唤醒CAN外设
CAN_InitStructure.CAN_TTCM = DISABLE;//置0,关闭时间触发通信功能
//时间触发(每个节点只在一个固定的时间段内发送报文,可以避免优先级仲裁。)
CAN_InitStructure.CAN_ABOM = DISABLE;//离线自动关闭,1:自动恢复 0:手动恢复
CAN_Init(CAN1, &CAN_InitStructure);
CAN_FilterInitTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.CAN_FilterNumber = 0;//指定第几个过滤器被初始化(范围0~13)
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
//16位列表模式,4个参数分别存入一组参数即可
//16位屏蔽模式,CAN_FilterIdHigh存入第一组ID,
// CAN_FilterMaskIdHigh存入对应的屏蔽位
// CAN_FilterIdHigh存入第二组ID,
// CAN_FilterMaskIdLow存入对应的屏蔽位
//32位列表模式,IdHign和IdLow组合存到一起,存入第一组32位ID
// CAN_FilterMaskIdHigh和CAN_FilterMaskIdLow组合存到一起,存入第一组32位ID
//32位屏蔽模式,IdHign和IdLow组合存到一起,存入32位ID
// CAN_FilterMaskIdHigh和CAN_FilterMaskIdLow组合存到一起,存入对应的屏蔽位
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;//选择过滤器位宽
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;//选择过滤器模式(屏蔽模式和列表模式)
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//配置过滤器关联(进FIFO_0还是FIFO_1)
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;//激活过滤器,也就是让过滤器工作
CAN_FilterInit(&CAN_FilterInitStructure);
}
/*
函数作用:发送一个报文
参数:ID号,数据长度,传入数据内容
返回值:无
*/
void MyCAN_Transmit(uint32_t ID, uint8_t Length, uint8_t *Data)
{
CanTxMsg TxMessage;
TxMessage.StdId = ID;//标准ID
TxMessage.ExtId = ID;//扩展ID
TxMessage.IDE = CAN_Id_Standard;//CAN_ID_STD,扩展标志位,
//标准模式(StdId有效,ExtId无效)
//扩展模式(StdId无效,ExtId有效)
TxMessage.RTR = CAN_RTR_Data;//遥控标志位
TxMessage.DLC = Length;//数据长度
for (uint8_t i = 0; i < Length; i ++)
{
TxMessage.Data[i] = Data[i];//数据段内容
}
uint8_t TransmitMailbox = CAN_Transmit(CAN1, &TxMessage);//检查邮箱状态
uint32_t Timeout = 0;
while (CAN_TransmitStatus(CAN1, TransmitMailbox) != CAN_TxStatus_Ok)
{
Timeout ++;
if (Timeout > 100000)
{
break;
}
}
}
/*
函数作用:判断接收FIFO里是否有报文
参数:无
返回值:有报文返回1,没有返回0
*/
uint8_t MyCAN_ReceiveFlag(void)
{
if (CAN_MessagePending(CAN1, CAN_FIFO0) > 0)//返回FIFO里排队报文的数目
{
return 1;
}
return 0;
}
/*
函数作用:接收报文
参数:ID号,数据长度,数据内容
返回值:无
*/
void MyCAN_Receive(uint32_t *ID, uint8_t *Length, uint8_t *Data)
{
CanRxMsg RxMessage;//接收报文的结构体,包含接收报文的ID、DLC、datas和FMI
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);
if (RxMessage.IDE == CAN_Id_Standard)//如果读到的报文是标准格式
{
*ID = RxMessage.StdId;//把标准ID传出去
}
else
{
*ID = RxMessage.ExtId;//否则把扩展格式ID传出去
}
if (RxMessage.RTR == CAN_RTR_Data)//判断是否收到的是数据帧
{
*Length = RxMessage.DLC;//将长度数据传出去
for (uint8_t i = 0; i < *Length; i ++)
{
Data[i] = RxMessage.Data[i];//传出内容
}
}
else
{
//...如果收到遥控帧
}
}
main.c:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Key.h"
#include "MyCAN.h"
uint8_t KeyNum;
uint32_t TxID = 0x555;
uint8_t TxLength = 4;
uint8_t TxData[8] = {0x11, 0x22, 0x33, 0x44};
uint32_t RxID;
uint8_t RxLength;
uint8_t RxData[8];
int main(void)
{
OLED_Init();
Key_Init();
MyCAN_Init();
OLED_ShowString(1, 1, "TxID:");
OLED_ShowHexNum(1, 6, TxID, 3);
OLED_ShowString(2, 1, "RxID:");
OLED_ShowString(3, 1, "Leng:");
OLED_ShowString(4, 1, "Data:");
while (1)
{
KeyNum = Key_GetNum();
if (KeyNum == 1)
{
TxData[0] ++;
TxData[1] ++;
TxData[2] ++;
TxData[3] ++;
MyCAN_Transmit(TxID, TxLength, TxData);
}
if (MyCAN_ReceiveFlag())
{
MyCAN_Receive(&RxID, &RxLength, RxData);
OLED_ShowHexNum(2, 6, RxID, 3);
OLED_ShowHexNum(3, 6, RxLength, 1);
OLED_ShowHexNum(4, 6, RxData[0], 2);
OLED_ShowHexNum(4, 9, RxData[1], 2);
OLED_ShowHexNum(4, 12, RxData[2], 2);
OLED_ShowHexNum(4, 15, RxData[3], 2);
}
}
}