记录自己的CAN学习过程(念念不忘,必有回响)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

几乎所有的嵌入式岗位的面试要求中都会含有熟悉CAN的这个要求,特此重新学习一遍,采用的是正点原子的STM32F4和STM32F1进行双机通信测试,以及各自的回环测试。

一、涉及到CAN的基础知识介绍

1.CAN中出现的名词解释

CANController Area Network(控制器局域网),

2.四种工作模式

正常模式
静默模式:在STM32的手册中可以找到具体的描述,官方解释是此时CAN可以正常的接收遥控帧和数据帧,但是不能发送数据。也就是说的可以发送隐形电平逻辑1,但不能发送显性电平逻辑0,不会干扰到总线(发送的隐形位不会影响到总线),一般应用都是用来检测和分析CAN总线的活动。
在这里插入图片描述

环回模式:看下面的图也可以看出来,这时候的CAN是自发自收,为了测试CAN通信是否正确。
在这里插入图片描述

静默环回模式

二、CAN在STM32F4的应用

CAN的物理层是,两根差分线,对于STM32来说,有一个控制器和收发器。控制器是STM32本身带有的,而收发器是外部的,可以将自己的信号转化成差分信号。

1.进行GPIO的初始化配置

这方面就比较简单了,STM32F4标准库代码如下(示例):

void Can_Gpio_Config(void)
{
	GPIO_InitTypeDef GPIO_InitTypeStruct;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟

	GPIO_InitTypeStruct.GPIO_Pin = GPIO_Pin_11| GPIO_Pin_12;
	GPIO_InitTypeStruct.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitTypeStruct.GPIO_OType = GPIO_OType_PP;//推挽输出
	GPIO_InitTypeStruct.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
	GPIO_InitTypeStruct.GPIO_PuPd = GPIO_PuPd_UP;//上拉   这里可以浮空输入或者上拉
	GPIO_Init(GPIOA, &GPIO_InitTypeStruct);//初始化PA11,PA12
	//引脚复用映射配置
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_CAN1);  //GPIOA11复用为CAN1
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_CAN1); //GPIOA12复用为CAN1
}

在参考手册找到的GPIO说明,发送引脚是推挽复用,接收引脚二者其一就行
在这里插入图片描述


2.对CAN的结构体进行配置

在这里插入图片描述
关于各个模式的介绍可以在参考手册的439页找到详细的说明,强烈建议大家去读一下对应的手册,豁然开朗(英文和我一样的就去读中文的,效果一样很好)
在这里插入图片描述
接下来介绍的就是我们在STM32中比较重要的了,MODE配置,一共四种模式

#define IS_CAN_MODE(MODE) (((MODE) == CAN_Mode_Normal) || \
                           ((MODE) == CAN_Mode_LoopBack)|| \
                           ((MODE) == CAN_Mode_Silent) || \
                           ((MODE) == CAN_Mode_Silent_LoopBack))

当我们只有一个开发板时候,就可以选择环回模式,这时候不经过收发器,而直接通过内部的控制器。
接下来就是波特率的配置 STM32F1,CAN搭载在APB1低速时钟下,最高频率为36M,STM32F4的CAN搭载在APB1下,最高频率为42M。

以代码为例进行波特率的运算(示例):

void Can_Config(void)
{
	CAN_InitTypeDef  CAN_InitTypeStruct;

	CAN_InitTypeStruct.CAN_TTCM=DISABLE;
	CAN_InitTypeStruct.CAN_ABOM=DISABLE;//手动置1然后清零进入退出离线模式
	CAN_InitTypeStruct.CAN_AWUM=DISABLE;//软件唤醒
	CAN_InitTypeStruct.CAN_NART=ENABLE;//只发送一次
	CAN_InitTypeStruct.CAN_RFLM=ENABLE;//锁定模式,新的会被丢弃
	CAN_InitTypeStruct.CAN_TXFP=DISABLE;//由标识符决定发送的优先级
	              
	CAN_InitTypeStruct.CAN_Mode= CAN_Mode_LoopBack;//回环模式
	CAN_InitTypeStruct.CAN_SJW=CAN_SJW_1tq;
	CAN_InitTypeStruct.CAN_BS1=CAN_BS1_7tq;
	CAN_InitTypeStruct.CAN_BS2=CAN_BS2_6tq;
	CAN_InitTypeStruct.CAN_Prescaler=6;
	CAN_Init(CAN1, &CAN_InitTypeStruct);
}

先算1TQ的运行时间,我使用的是F4,CAN搭载在APB1上,时钟频率为42M,这里的CAN分频系数CAN_Prescaler为6,所以1TQ的时间为(1/42M)*6;
标准的CAN的一个数据(0或1),是有四段组成,STM32这里由三段构成,出了BS1和BS2还有个SS(固定为1TQ),所以这里一位数据就有7+6+1=14TQ(这里的1不是SJW而是SS),所以一个数据的时间为14乘以((1/42M)*6)=2us,一秒内的数据就是1S除以一个数据的时间即就是500Kbit。


3.筛选器配置

STM32F1有14组筛选器,每个筛选器有四种模式,主要是32位和16位,标识符列表和标识符屏蔽,所谓筛选器,就是只有你跟它配置完全一样时,才能通过。而列表和屏蔽呢,列表是相同的才能通过,屏蔽相当于多了一个条件,例如

ID   11011011
MSK  10000000

MSK是屏蔽的八位,ID也是八位,MAK对应为1表示只有首位为1的才能通过
在这里插入图片描述
代码讲解

#define PASS_ID ((uint32_t)0x1314)
void Can_Filter_Config(void)
{
	CAN_FilterInitTypeDef CAN_FilterInitTypeStruct;
	
	CAN_FilterInitTypeStruct.CAN_FilterNumber=0;  //配置哪一个过滤器
	CAN_FilterInitTypeStruct.CAN_FilterFIFOAssignment= CAN_Filter_FIFO0;//指定是哪一个FIFO接收
	CAN_FilterInitTypeStruct.CAN_FilterScale=CAN_FilterScale_32bit;//32位
	CAN_FilterInitTypeStruct.CAN_FilterMode=CAN_FilterMode_IdMask;//MASK和LIST;
	
	CAN_FilterInitTypeStruct.CAN_FilterMaskIdHigh=((PASS_ID<<3 | CAN_Id_Extended |CAN_RTR_Data)&0xffff0000)>>16;
	CAN_FilterInitTypeStruct.CAN_FilterIdLow=(PASS_ID<<3 | CAN_Id_Extended |CAN_RTR_Data)&0xffff;
	CAN_FilterInitTypeStruct.CAN_FilterMaskIdHigh=0xffff;//所有都要完全一样
	CAN_FilterInitTypeStruct.CAN_FilterMaskIdLow=0xffff;
	
	CAN_FilterInitTypeStruct.CAN_FilterActivation=ENABLE;
	CAN_FilterInit(&CAN_FilterInitTypeStruct);
}

其中的这个是配置他的模式,这里我选择的是32位掩码模式(屏蔽)

CAN_FilterInitTypeStruct.CAN_FilterScale=CAN_FilterScale_32bit;//32位
CAN_FilterInitTypeStruct.CAN_FilterMode=CAN_FilterMode_IdMask;//MASK和LIST;

在这里插入图片描述
第一行ID表示的就是他设置的ID号
而最终赋值给下面的映像寄存器
((PASS_ID<<3 | CAN_Id_Extended |CAN_RTR_Data)&0xffff0000)>>16
这个就是将你的ID配置给下面的32位映像寄存器,低三位 IDE是拓展ID还是普通ID(拓展的有29个,普通的有11个,如上图所示,第四位到32位),这个里面要好好体会。
最后再设置掩码的格式,1是必须和标识符一样,0是可以不一样,这里为了方便直接设置成0XFFFFFFFF。

4.发送和接收数据

邮箱:发送报文的地方(STM32里面有三个)
FIFO: 接收报文的地方 (两个FIFO,每个三个深度,一共六个)
我采用了中断的方式进行接收数据

void CAN1_RX0_IRQHandler(void)
{
	CAN_Receive(CAN1, 0, &RxMessage_txt);//标志位不用清空
	CAN_flag=1;//采集到数据后到主函数进行处理
}
//主函数中
```c 

if(CAN_flag)
{
	CAN_flag=0;
	LCD_Fill(30,270,160,310,WHITE);//清除之前的显示
	for(i=0;i<8;i++)
	{	
		canbuf[i]=RxMessage_txt.Data[i];//将接收到的报文进行复制
		if(i<4)LCD_ShowxNum(30+i*32,270,canbuf[i],3,16,0X80);	//显示数据
		else LCD_ShowxNum(30+(i-4)*32,290,canbuf[i],3,16,0X80);	//显示数据
	}
}

发送数据

u8 CAN1_Send_Msg(u8* msg,u8 len)
{	
  u8 mbox;
  u16 i=0;
  CanTxMsg TxMessage;
  TxMessage.StdId=0;	 // 标准标识符为0
  TxMessage.ExtId=PASS_ID;	 // 设置扩展标示符(29位)
  TxMessage.IDE=CAN_Id_Extended;		  // 使用扩展标识符
  TxMessage.RTR=CAN_RTR_Data;		  // 消息类型为数据帧,一帧8位
  TxMessage.DLC=len;							 // 发送两帧信息
  for(i=0;i<len;i++)
  TxMessage.Data[i]=msg[i];				 // 第一帧信息          
  mbox= CAN_Transmit(CAN1, &TxMessage);   
  i=0;
  while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++;	//等待发送结束
  if(i>=0XFFF)return 1;
  return 0;		

}

当配置成扩展格式时候,ID号只取决于扩展的格式

总结

CAN总线很牛,不过STM32里面的操作很方便,主要是搞懂,筛选器和ID,以及波特率的计算
百度网盘链接
链接:https://pan.baidu.com/s/1ruyHxXhybqrDNm4E5k6IiQ
提取码:1234
提取码1234.

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值