/* 初始化配置类 */
uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct);
void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct);
void CAN_StructInit(CAN_InitTypeDef* CAN_InitStruct);
/* 发送接收类 */
uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage);
void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage);
/* 模式与中断 */
uint8_t CAN_OperatingModeRequest(CAN_TypeDef* CANx, uint8_t CAN_OperatingMode);
void CAN_ITConfig(CAN_TypeDef* CANx, uint32_t CAN_IT, FunctionalState NewState);
/* 错误诊断 */
uint8_t CAN_GetLastErrorCode(CAN_TypeDef* CANx);
CAN_InitTypeDef CAN_InitStruct;
CAN_InitStruct.CAN_TTCM=DISABLE;
CAN_InitStruct.CAN_ABOM =DISABLE;
CAN_InitStruct.CAN_AWUM=DISABLE;
CAN_InitStruct.CAN_NART=DISABLE;
CAN_InitStruct.CAN_RFLM=DISABLE;
CAN_InitStruct.CAN_TXFP=DISABLE;
CAN_InitStruct.CAN_Mode=CAN_Mode_LoopBack; //回环测试模式
CAN_InitStruct.CAN_SJW=CAN_SJW_1tq;
CAN_InitStruct.CAN_BS1=CAN_BS1_9tq;
CAN_InitStruct.CAN_BS2=CAN_BS2_8tq;
CAN_InitStruct.CAN_Prescaler=4;
CAN_Init (CAN1,&CAN_InitStruct);
CAN_FilterInitTypeDef CAN_FilterInitStruct;
CAN_FilterInitStruct.CAN_FilterNumber=0;
CAN_FilterInitStruct.CAN_FilterActivation=ENABLE;
CAN_FilterInitStruct.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;
CAN_FilterInitStruct.CAN_FilterIdHigh=0x0000;
CAN_FilterInitStruct.CAN_FilterIdLow=0x0000;
CAN_FilterInitStruct.CAN_FilterMaskIdHigh=0x0000;
CAN_FilterInitStruct.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStruct.CAN_FilterMode= CAN_FilterMode_IdMask;
CAN_FilterInitStruct.CAN_FilterScale=CAN_FilterScale_32bit;
CAN_FilterInit(&CAN_FilterInitStruct);
这些电平是需要CAN的收发器实现的,目前CAN的收发器芯片有TAJ1050、TJA1042、SIST1050T。CAN收发器芯片能够将微控制器 的逻辑电平转换为CAN总线所使用的物理层电平,并负责将数据发送到总线上,同时接收总线上的数据并将其转换为适合微控制器处理的逻辑电平。收发器芯片还包含其他功能,如滤波、保护电路等,以确保可靠的通信。有了CAN的收发器实现差分信号到逻辑信号的转换之后,我们就可以基于CAN的控制器实现CAN的帧的通信。CAN的协议定义了五种类型的帧。
对于CAN的帧有数据帧、遥控帧、错误帧、过载帧、间隔帧。我们今天主要是针对标准的数据帧进行实现。
数据帧是由七段来组成的,当然数据帧又分为标准帧CAN2.0A、扩展帧CAN2.0B
帧起始:表示数据帧开始的段,是以显性信号表示,SOF发出后会产生一个跳变沿用于整个CAN网络的时间同步。
仲裁段:表示该帧优先级的段,该部分和CAN的过滤器相关,标准格式ID有11个位,从ID28到ID18被依次发,ID禁止设置高7位为隐性
控制段:表示数据的字节数和保留位的段,控制段是由6个位构成的,四个位为DLC,称数据长度码,表示后面要表示的数据的字节数,还有两个保留位
数据段:数据的内容,一帧可以发送0-8字节的数据
CRC段:检查帧的传输错误的段,由15个位的CRC顺序和一个位的CRC界定符构成
ACK段:表示确认正确接收的段,该段由两位组成,其中一位是界定符,对于发送单元而言在该段发两个隐性位,接收段在ACK Slot段发送显性位,通知发送单元是正常接收结束
帧结束:是以七个隐性电平表示数据帧的结束
对CAN的位时序介绍:在CAN总线上我们是读取电平来实现表征数据0、1。对于没有时钟线的CAN总线里,是以“位时序”的机制,实现对电平的正确采样,位数据是由四段组成的:
同步段SS:1Tq
传播时间段PTS:1~8Tq
相位缓冲段PBS1:1~8 Tq
相位缓冲段PBS2:2~8Tq
此外因时钟的频率偏差,传送延迟等,各单元有同步午餐,SJW为再补偿宽度,其实我们对位同步而言不需要太深入的了解其实现,该部分主要是由硬件自动完成,我们在程序设计中该部分主要是对我们的CAN波特率进行了设置,以STM32F103C8T6为例子
//CAN初始化
//tsjw:重新同步跳跃时间单元.范围:CAN_SJW_1tq~ CAN_SJW_4tq
//tbs2:时间段2的时间单元. 范围:CAN_BS2_1tq~CAN_BS2_8tq;
//tbs1:时间段1的时间单元. 范围:CAN_BS1_1tq ~CAN_BS1_16tq
//brp :波特率分频器.范围:1~1024; tq=(brp)*tpclk1
//波特率=Fpclk1/((tbs1+tbs2+1)*brp);
//mode:CAN_Mode_Normal,普通模式;CAN_Mode_LoopBack,回环模式;
//Fpclk1的时钟在初始化的时候设置为36M,如果设置CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack);
//则波特率为:36M/((8+9+1)*4)=500Kbps
AI写代码
在STM32 中的位时序和CAN的标准位时序有一点区别,在STM32中 包含三段,分别是同步段SYNC-SEG、位段BS1、位段BS2.采样的点是在BS1、BS2的交界处,同步段的固定长度是1Tq,对BS1、BS2可以在位时序寄存器上CAN——BTR设置他们的时间长度,它们可以在重新同步期间增加或者缩短。SJW也可以在位时序寄存器中配置。
BS1段时间:
TS1=Tq x (TS1[3:0] + 1),
BS2段时间:
TS2= Tq x (TS2[2:0] + 1),
一个数据位的时间:
T1bit =1Tq+TS1+TS2 =1+ (TS1[3:0] + 1)+ (TS2[2:0] + 1)= N Tq
其中单个时间片的长度Tq与CAN外设的所挂载的时钟总线及分频器配置有关,CAN1和CAN2外设都是挂载在APB1总线上的,而位时序寄存器CAN_BTR中的BRP[9:0]寄存器位可以设置CAN外设时钟的分频值,所以:
Tq = (BRP[9:0]+1) x TPCLK
其中的PCLK指APB1时钟,默认值 为45MHz。
最终可以计算出CAN通讯的波特率:
BaudRate = 1/N Tq
通过设置这些参数确定我们CAN总线的整体波特率。该部分也是我们在CAN的初始化过程中需要进行设置的。
关于报文寻址的问题
CAN网络中的通信基于与内容相关的寻址,因为CAN节点本身是没有ID的,但是可以通过ID区分CAN报文,所有CAN节点都会收到总线广播发送的所有CAN报文,每个接收方都根据自己的需求自行的选择CAN/报文。
STM32F103的CAN回环通信
stm32f103C8T6的最小系统板只有一个CAN的控制器,没有收发器,因此我们在实验中只能选择其回环模式进行测试。
CAN2.0A:只能处理标准数据帧且扩展帧的内容会识别错误
CAN2.0B Active可以处理标准数据帧和扩展数据帧,
CAN2.0B Passive 只能出来标准数据帧且扩展帧的内容会忽略
STM32的CAN控制器功能如下:
bxCAN模块可以完全自动的接收和发送CAN报文
我们可以通过控制、状态、配置寄存器实现
1.配置CAN参数,如波特率
2.请求发送报文
3.处理报文接收
4.管理中断
5.获取诊断信息
有三个发送邮箱来发送报文,发送调度器是根据优先级来决定那个邮箱的报文被发送。通过接收过滤器对广播的报文进行过滤,过滤器是和接收的邮箱进行连接的,在STM32F103中有14个位宽可变/可配置的标识符过滤器组,有两个接收的FIFO,每个FIFO都可以存放3个完整的报文。
对于CAN的控制器有三种工作模式:
初始化模式 该模式下对寄存器进行配置
正常模式 CAN总线同步,开始数据的接收和发送
睡眠模式 当我们复位后是进入睡眠模式,可降低功耗
同样的CAN的控制器有以下测试模式
静默模式:只向总线发送1不能发送0,可以从总线接收数据,可以实现对总线的统计。
环回模式:发送的数据直接到输入(总线可以监测数据),不能从总线接收数据,可用于自检
环回静默模式:发送的数据直接到输入(总线不可监测到数据),也不能从总线接收到数据 可用于自检并且不影响总线
发送处理的流程如下:
接收处理的流程如下:
CAN的初始化结构体
CAN的外设功能非常的多,涉及的寄存器也是十分的丰富,我们可以通过标准库 里面的各种结构体函数及库函数对这些控制过程实现简化。
/* Initialization and Configuration functions *********************************/
uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct);
void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct);//初始化CAN的过滤器
void CAN_StructInit(CAN_InitTypeDef* CAN_InitStruct);//初始化CAN的控制器
void CAN_SlaveStartBank(uint8_t CAN_BankNumber); //启动CAN从机过滤器
void CAN_DBGFreeze(CAN_TypeDef* CANx, FunctionalState NewState);//控制CAN控制器的调试模式
void CAN_TTComModeCmd(CAN_TypeDef* CANx, FunctionalState NewState);//控制CAN控制器的TT-COM模式
/* Transmit functions *********************************************************/
uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage);//用于CAN消息的发送
uint8_t CAN_TransmitStatus(CAN_TypeDef* CANx, uint8_t TransmitMailbox);//用于获取CAN消息发送状态
void CAN_CancelTransmit(CAN_TypeDef* CANx, uint8_t Mailbox);//取消发送
/* Receive functions **********************************************************/
void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage);//CAN消息接收
void CAN_FIFORelease(CAN_TypeDef* CANx, uint8_t FIFONumber);//清空缓冲区
uint8_t CAN_MessagePending(CAN_TypeDef* CANx, uint8_t FIFONumber);//获取CAN接收FIFO中待处理的消息数量
/* Operation modes functions **************************************************/
uint8_t CAN_OperatingModeRequest(CAN_TypeDef* CANx, uint8_t CAN_OperatingMode);
uint8_t CAN_Sleep(CAN_TypeDef* CANx);
uint8_t CAN_WakeUp(CAN_TypeDef* CANx);
/* Error management functions *************************************************/
uint8_t CAN_GetLastErrorCode(CAN_TypeDef* CANx);
uint8_t CAN_GetReceiveErrorCounter(CAN_TypeDef* CANx);
uint8_t CAN_GetLSBTransmitErrorCounter(CAN_TypeDef* CANx);
/* Interrupts and flags management functions **********************************/
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);
AI写代码
cpp
运行
CAN_InitTypeDef CAN_InitStruct;
CAN_InitStruct.CAN_TTCM=DISABLE;//非时间触发通信模式
CAN_InitStruct.CAN_ABOM =DISABLE;//软件自动离线管理
CAN_InitStruct.CAN_AWUM=DISABLE;//睡眠模式通过软件自动唤醒
CAN_InitStruct.CAN_NART=DISABLE;//使用报文自动传送
CAN_InitStruct.CAN_RFLM=DISABLE;//报文不锁定,新的报文可以对旧报文覆盖
CAN_InitStruct.CAN_TXFP=DISABLE;//报文的优先级可以由标识符决定
CAN_InitStruct.CAN_Mode=mode;//回环模式
CAN_InitStruct.CAN_SJW=tsjw;
CAN_InitStruct.CAN_BS1=tbs1;
CAN_InitStruct.CAN_BS2=tbs2;
CAN_InitStruct.CAN_Prescaler=brp;
CAN_Init (CAN1,&CAN_InitStruct);
AI写代码
cpp
运行
CAN_FilterInitTypeDef CAN_FilterInitStruct;
CAN_FilterInitStruct.CAN_FilterNumber=0;//选择过滤器0
CAN_FilterInitStruct.CAN_FilterActivation=ENABLE;//激活过滤器0
CAN_FilterInitStruct.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器0关联到FIFO0
CAN_FilterInitStruct.CAN_FilterIdHigh=0x0000;
CAN_FilterInitStruct.CAN_FilterIdLow=0x0000;
CAN_FilterInitStruct.CAN_FilterMaskIdHigh=0x0000;
CAN_FilterInitStruct.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStruct.CAN_FilterMode= CAN_FilterMode_IdMask;
CAN_FilterInitStruct.CAN_FilterScale=CAN_FilterScale_32bit;
CAN_FilterInit(&CAN_FilterInitStruct);
AI写代码
cpp
运行