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)) = 125K
    	CAN_InitStructure.CAN_Prescaler = 48;	//预分频器的值为48	
    	CAN_InitStructure.CAN_BS1 = CAN_BS1_2tq;//BS1的时间长度,1-16tq
    	CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;//BS2的时间长度
    	CAN_InitStructure.CAN_SJW = CAN_SJW_2tq;//SJW的时间长度,1-4tq
    	CAN_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位ID
    	32位频闭模式: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_FIFO1
    	CAN_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;//标准ID
	TxMessage.ExtId = ID;//扩展ID
	TxMessage.IDE = CAN_Id_Standard;		//扩展标志位,CAN_Id_Standard 标准ID ,CAN_Id_Extended扩展ID
	TxMessage.RTR = CAN_RTR_Data; //遥控标志位,CAN_RTR_Remote 遥控帧,	CAN_RTR_Data数据帧
	TxMessage.DLC = Length;//数据段长度,传入的参数
	//把形参DATA传过来的数组赋值给TxMessage.Data
	for (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还是扩展ID
	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
	{
		//遥控帧,暂时不做处理
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值