您的位置:首页 > 健康 > 养生 > STM的CAN通信学习

STM的CAN通信学习

2024/12/23 4:20:07 来源:https://blog.csdn.net/doudou2weiwei/article/details/142298507  浏览:    关键词:STM的CAN通信学习

 显性电平:0    隐性电平:1

一、帧结构

1.帧类型

1)数据帧:发送设备主动发送数据(广播式)

2)请求帧:接收设备主动请求数据(请求式)

2.帧结构

1)标准帧

1位SOF+11位ID+1位RTR+1位IDE+1位r0+4位DLC+0~64位Data+15位CRC效验码+1位CRC界定符+1位ACK槽+1位ACK界定符+7位结束符

  • 1位  SOF:帧起始,表示后面一段波形为传输的数据位,显性0
  • 11位  ID:标识符,区分功能,同时决定优先级
  • 1位  RTR:远程请求位,区分数据帧(显性0)和遥控帧(隐性1)
  • 1位  IDE:扩展标志位,区分标准格式(0)和扩展格式(1)
  • 1位  r0/r1:保留位,为后续协议升级留下空间(显性0)
  • 4位  DLC:数据长度,指示数据段Data有几个字节
  • 0~64位  Data:数据段的1~8个字节有效数据
  • 15位 CRC:循环冗余校验,校验数据是否正确
  • 1位 CRC界定符:隐性1
  • 1位 ACK:应答位,判断数据有没有被接收方接收(显性0收到数据,隐形1没收到数据)
  • 1位 ACK界定符:为应答位前后发送方和接收方释放总线留下时间(隐性1)
  • 7位 EOF:帧结束,表示数据位已经传输完毕(隐性电平1)

2)扩展帧

11位ID结束后,加1位SRR和1位IDE。其中SRR是代替RTR,协议升级时留下的无意义位。IDE是隐性电平1,表示这是扩展帧。其余与标准帧设定一致。

4.关于位填充

位填充规则:发送方每发送5个相同电平后,自动追加一个相反电平的填充位,接收方检测到填充位时,会自动移除填充位,恢复原始数据

位填充例子:

二、仲裁

1.先占先得

2.非破坏性仲裁

仲裁段就是ID号+RTR位,ID号越小,仲裁的优先级越高,填充位不会影响仲裁,如果ID号一致,数据帧的优先级高于遥控桢

如下图所示,单元一和单元二同时开始波形, 根据线与特性到标红的位置,总线呈现显性0,单元二回读为0,与发出0一致,单元一回读为0,与发出的1不一致。此时,单元一仲裁失败,转为接收状态。

三、STM32的CAN外设

1.STM32的默认CAN复用端口:

PA11——CAN_RX

PA12——CAN_TX

2.CAN收发报文

  • CAN_RX写入指令到控制器,接收过滤器过滤所接收到的指令,再放入指定的FIFO队列(队列遵循先进先出),接收的队列一共有两个,可以指定两个队列的优先级。每一个队列共有三个邮箱,邮箱如果满了,接收的数据就会溢出。接收流程:接收到一个报文→匹配过滤器后进入FIFO 0或FIFO 1→CPU读取

  • CAN_TX发送指令时,也有三个发送邮箱,三个发送邮箱可以按照指定顺序进行发送,如先进先出或者ID优先级发送。ID优先级一般是ID号小的先发送。发送流程:选择一个空置邮箱——写入报文——请求发送
  • NART:置1,关闭自动重传,CAN报文只被发送1次;置0,自动重传,直到发送成功
  • TXFP:置1,优先级由发送请求的顺序来决定,先请求的先发送;置0,优先级由报文标识符(报文ID)来决定,标识符值小的先发送(标识符值相等时,邮箱号小的报文先发送)
  • RFLM:置1,接收FIFO锁定,FIFO溢出时,新收到的报文会被丢弃;置0,禁用FIFO锁定,FIFO溢出时,FIFO中最后收到的报文被新报文覆盖

3.标识符过滤器

  • FSCx:位宽设置,置0,16位;置1,32位

  • FBMx:模式设置,置0,屏蔽模式;置1,列表模式

  • FFAx:关联设置,置0,FIFO 0;置1,FIFO 1

  • FACTx:激活设置,置0,禁用;置1,启用

4.测试模式:(CAN_MODE)

静默模式、环回模式、静默环回模式

	/* CAN_Mode_Normal             ((uint8_t)0x00)  正常模式CAN_Mode_LoopBack           ((uint8_t)0x01)  环回模式CAN_Mode_Silent             ((uint8_t)0x02)  静默模式CAN_Mode_Silent_LoopBack    ((uint8_t)0x03)  环回静默模式*/

5.工作模式:

初始化模式、正常模式、睡眠模式、AWUM(置1自动唤醒,置0手动唤醒)

6.位时间特性

  • 波特率 = APB1时钟频率 / 分频系数 / 一位的Tq数量

                   = 36MHz / (BRP[9:0]+1) / (1 + (TS1[3:0]+1) + (TS2[2:0]+1))

  • 7.四个中断

    • 发送中断:发送邮箱空时产生
    • FIFO 0中断:收到一个报文/FIFO 0满/FIFO 0溢出时产生
    • FIFO 1中断:收到一个报文/FIFO 1满/FIFO 1溢出时产生
    • 状态改变错误中断:出错/唤醒/进入睡眠时产生
    • 8.时间触发通信

    • TTCM:置1,开启时间触发通信功能;置0,关闭时间触发通信功能(CAN外设内部有一个16位计数器,用于记录时间戳)

    • 9.离线恢复

    • ABOM:置1,开启离线自动恢复,进入离线状态后,就自动开启恢复过程;置0,关闭离线自动恢复,软件必须先请求进入然后再退出初始化模式,随后恢复过程才被开启

    • 四、具体代码使用

  • 1.CAN初始化函数调用

  • void MyCAN_Init(void)
    {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//启用GPIOA时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//启用CAN1时钟//初始化GPIOA的12号引脚GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//模式为复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//12号引脚GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//引脚速度GPIO_Init(GPIOA, &GPIO_InitStructure);//引脚初始化//初始化GPIO的11号引脚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);//初始化CAN1控制器CAN_InitTypeDef CAN_InitStructure;//定义一个CAN的结构体/* CAN_Mode_Normal             ((uint8_t)0x00)  正常模式CAN_Mode_LoopBack           ((uint8_t)0x01)  环回模式CAN_Mode_Silent             ((uint8_t)0x02)  静默模式CAN_Mode_Silent_LoopBack    ((uint8_t)0x03)  环回静默模式*/CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack;//CAN工作模式为环回模式,用于自收自发测试用//波特率 = 36M(时钟频率) / 48 (预分频器的值)/ (1 + 2(BS1)+ 3(BS2)) = 125KCAN_InitStructure.CAN_Prescaler = 48;	//预分频器的值为48	CAN_InitStructure.CAN_BS1 = CAN_BS1_2tq;//BS1的时间长度,1-16tqCAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;//BS2的时间长度CAN_InitStructure.CAN_SJW = CAN_SJW_2tq;//SJW的时间长度,1-4tqCAN_InitStructure.CAN_NART = DISABLE;//DISABLE,表示寄存器置0,自动重传CAN_InitStructure.CAN_TXFP = DISABLE;//发送邮箱优先级,DISABLE置零,ID小的先发送,如果是ENABLE置一,先进先出CAN_InitStructure.CAN_RFLM = DISABLE;//禁用FIOF锁定,溢出后,新报文覆盖最后一个报文CAN_InitStructure.CAN_AWUM = DISABLE;//DISABLE 手动唤醒,ENABLE 自动唤醒CAN_InitStructure.CAN_TTCM = DISABLE;//关闭时间触发通信CAN_InitStructure.CAN_ABOM = DISABLE;//DISABLE 手动恢复,ENABLE 自动恢复CAN_Init(CAN1, &CAN_InitStructure);//初始化CAN//初始化CAN过滤器CAN_FilterInitTypeDef CAN_FilterInitStructure;//定义CAN过滤器结构体CAN_FilterInitStructure.CAN_FilterNumber = 0;//过滤器编号,0-13/*16位列表模式,四个参数分别存入一组ID即可,共四个16位ID列表屏蔽模式:IDHIGH存入第一组ID,MaskIDHIGH存入对应的屏蔽位,共两组16位ID和两组屏蔽位32位列表模式:IDHIGH和IDLOW组合成一个32位ID,MaskIDHIGH和MaskIDLOW组成第二组32位ID32位频闭模式:IDHIGH和IDLOW组合成一个32位ID,MaskIDHIGH和MaskIDLOW组成第二组32位屏蔽位*/CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;//高16位CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;//低16位CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;//选则相应模式为32位屏蔽模式CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;//过滤器位宽,32位或者16位,这里是32位CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;//过滤器模式,CAN_FilterMode_IdMask 屏蔽模式 CAN_FilterMode_IdList  列表模式CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0;//配置过滤器关联,这里有两个CAN_Filter_FIFO0,CAN_Filter_FIFO1CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;//激活过滤器CAN_FilterInit(&CAN_FilterInitStructure);//初始化过滤器
    }
    

2.发送报文

/*
Name:MyCAN_Transmit CAN 发送报文
Param: ID Length   数据长度*Data    数据指针
*/
void MyCAN_Transmit(uint32_t ID, uint8_t Length, uint8_t *Data)
{CanTxMsg TxMessage; //定义CanTxMsg结构体变量TxMessage.StdId = ID;//标准IDTxMessage.ExtId = ID;//扩展IDTxMessage.IDE = CAN_Id_Standard;		//扩展标志位,CAN_Id_Standard 标准ID ,CAN_Id_Extended扩展IDTxMessage.RTR = CAN_RTR_Data; //遥控标志位,CAN_RTR_Remote 遥控帧,	CAN_RTR_Data数据帧TxMessage.DLC = Length;//数据段长度,传入的参数//把形参DATA传过来的数组赋值给TxMessage.Datafor (uint8_t i = 0; i < Length; i ++){TxMessage.Data[i] = Data[i];//Data为8字节的数组}/*CAN_Transmit的原理:选择空发送邮箱——如果邮箱有空位,则将报文写入指定寄存器——TXRQ置1,请求发送*/uint8_t TransmitMailbox = CAN_Transmit(CAN1, &TxMessage);//请求发送结构体指向的报文,返回值是邮箱编号//uint32_t Timeout = 0;//等待函数返回OK,当CAN1的邮箱状态为CAN_TxStatus_Ok表示发送成功,如果不成功则进入循环while (CAN_TransmitStatus(CAN1, TransmitMailbox) != CAN_TxStatus_Ok){Timeout ++;//如果大于超时时间,则跳出循环if (Timeout > 100000){break;}}
}

3.接收报文 

/*
@brief: recieve CAN message
@param: *ID        the ID*Length    the length of DATA*DATA      the Data of CAN massage
*/
void MyCAN_Receive(uint32_t *ID, uint8_t *Length, uint8_t *Data)
{CanRxMsg RxMessage;//定义一个CanRxMsg结构体CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//判断接收的报文是标准ID还是扩展IDif (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{//遥控帧,暂时不做处理}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com