历史上的今天
返回首页

历史上的今天

今天是:2024年09月07日(星期六)

2019年09月07日 | ​​​​​​​简述STM32 CAN总线的设置

2019-09-07 来源:eefocus

简述CAN总线

最近公司开发CAN总线项目,以前也学习了,没有实际的用于项目制作,现在具体的总结一下,也是借鉴了很多大神的资料,站在巨人的肩膀之上写下来这篇文章


CAN 是 Controller Area Network 的缩写(以下称为 CAN),是 ISO 国际标准化的串行通信


协议。在当前的汽车产业中,出于对安全性、舒适性、方便性、低公害、低成本的要求,各种


各样的电子控制系统被开发了出来。由于这些系统之间通信所用的数据类型及对可靠性的要求


不尽相同,由多条总线构成的情况很多,线束的数量也随之增加。为适应“减少线束的数量”、


“通过多个 LAN,进行大量数据的高速通信”的需要,1986 年德国电气商博世公司开发出面


向汽车的 CAN 通信协议。此后,CAN 通过 ISO11898 及 ISO11519 进行了标准化,现在在欧


洲已是汽车网络的标准协议。


现在,CAN 的高性能和可靠性已被认同,并被广泛地应用于工业自动化、船舶、医疗设


备、工业设备等方面。现场总线是当今自动化领域技术发展的热点之一,被誉为自动化领域的


计算机局域网。它的出现为分布式控制系统实现各节点之间实时、可靠的数据通信提供了强有


力的技术支持。


CAN 控制器根据两根线上的电位差来判断总线电平。总线电平分为显性电平和隐性电平,


二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方。


CAN 协议具有一下特点:


1) 多主控制。在总线空闲时,所有单元都可以发送消息(多主控制),而两个以上的单元


同时开始发送消息时,根据标识符(Identifier 以下称为 ID)决定优先级。ID 并不是


表示发送的目的地址,而是表示访问总线的消息的优先级。两个以上的单元同时开始


发送消息时,对各消息 ID 的每个位进行逐个仲裁比较。仲裁获胜(被判定为优先级


最高)的单元可继续发送消息,仲裁失利的单元则立刻停止发送而进行接收工作。


2) 系统的柔软性。与总线相连的单元没有类似于“地址”的信息。因此在总线上增加单


元时,连接在总线上的其它单元的软硬件及应用层都不需要改变。


3) 通信速度较快,通信距离远。最高 1Mbps(距离小于 40M),最远可达 10KM(速率低


于 5Kbps)。


4) 具有错误检测、错误通知和错误恢复功能。所有单元都可以检测错误(错误检测功能),


检测出错误的单元会立即同时通知其他所有单元(错误通知功能),正在发送消息的单


元一旦检测出错误,会强制结束当前的发送。强制结束发送的单元会不断反复地重新


发送此消息直到成功发送为止(错误恢复功能)。


5) 故障封闭功能。CAN 可以判断出错误的类型是总线上暂时的数据错误(如外部噪声等)


还是持续的数据错误(如单元内部故障、驱动器故障、断线等)。由此功能,当总线上


发生持续数据错误时,可将引起此故障的单元从总线上隔离出去。


6) 连接节点多。CAN 总线是可同时连接多个单元的总线。可连接的单元总数理论上是没


有限制的。但实际上可连接的单元数受总线上的时间延迟及电气负载的限制。降低通


信速度,可连接的单元数增加;提高通信速度,则可连接的单元数减少。


正是因为 CAN 协议的这些特点,使得 CAN 特别适合工业过程监控设备的互连,因此,越


来越受到工业界的重视,并已公认为最有前途的现场总线之一。


CAN 协议经过 ISO 标准化后有两个标准:ISO11898标准和 ISO11519-2 标准。其中 ISO11898


是针对通信速率为 125Kbps~1Mbps 的高速通信标准,而 ISO11519-2 是针对通信速率为 125Kbps


以下的低速通信标准。


本章,我们使用的是 450Kbps 的通信速率,使用的是 ISO11898 标准,该标准的物理层特


征如图 30.1.1 所示:


们所要设置的寄存器CAN1->sFilterRegister[ x ].FR1便是如上图所说的标识符屏蔽模式中的 ID,


而CAN1->sFilterRegister[ x ].FR2, 就是屏蔽寄存器; 在标识符列表模式中,两者都是ID。


我们按上面的格式去设置寄存器:


标准ID / 拓展ID + IDE + RTR + x 。


已知 IDE = 0 代表标准ID,  IDE =1 代表拓展ID;  RTR = 0代表数据帧,RTR = 1代表远程帧; 


那么,我们给个例子:


  标识符列表 ID = 0x09  拓展帧 :


 CAN1->sFilterRegister[0].FR1=0x09<<3|0x04;     //id=0x01,拓展帧数据

 CAN1->sFilterRegister[0].FR2=0x09<<3|0x04;      //标识符列表,相同

过滤寄存器如此设置就能过滤拓展帧的id为0x09的数据了

         标识符屏蔽  ID = 0x11  标准帧 :


CAN1->sFilterRegister[1].FR1=0x11<<21|0x04;  //标准id, 0x04为屏蔽模式,

CAN1->sFilterRegister[1].FR2=0xffc00004;    //id全部屏蔽,IDE屏蔽,RTR屏蔽    

过滤器如此设置就能过滤标准帧id为0x11的数据。


为了使工程更加有条理,我们把CAN控制器相关的代码独立分开存储,方便以后移植。在"串口实验"之上新建"bsp_can.c"及"bsp_can.h"文件,这些文件也可根据您的喜好命名,它们不属于STM32标准库的内容,是由我们自己根据应用需要编写的。

 

1.    编程要点

(1)    初始化CAN通讯使用的目标引脚及端口时钟;

 

(2)    使能CAN外设的时钟;

 

(3)    配置CAN外设的工作模式、位时序以及波特率;

 

(4)    配置筛选器的工作方式;

 

(5)    编写测试程序,收发报文并校验。

 

2.    代码分析

CAN硬件相关宏定义

我们把CAN硬件相关的配置都以宏的形式定义到"bsp_can.h"文件中,见代码清单 242。

 

代码清单 404 CAN硬件配置相关的宏(bsp_can.h文件)

 

1

 

2 /*CAN硬件相关的定义*/

 

3 #define CANx CAN1

 

4 #define CAN_CLK RCC_APB1Periph_CAN1

 

5 /*接收中断号*/

 

6 #define CAN_RX_IRQ CAN1_RX0_IRQn

 

7 /*接收中断服务函数*/

 

8 #define CAN_RX_IRQHandler CAN1_RX0_IRQHandler

 

9

 

10 /*引脚*/

 

11 #define CAN_RX_PIN GPIO_Pin_8

 

12 #define CAN_TX_PIN GPIO_Pin_9

 

13 #define CAN_TX_GPIO_PORT GPIOB

 

14 #define CAN_RX_GPIO_PORT GPIOB

 

15 #define CAN_TX_GPIO_CLK RCC_AHB1Periph_GPIOB

 

16 #define CAN_RX_GPIO_CLK RCC_AHB1Periph_GPIOB

 

17 #define CAN_AF_PORT GPIO_AF_CAN1

 

18 #define CAN_RX_SOURCE GPIO_PinSource8

 

19 #define CAN_TX_SOURCE GPIO_PinSource9

 

以上代码根据硬件连接,把与CAN通讯使用的CAN号、引脚号、引脚源以及复用功能映射都以宏封装起来,并且定义了接收中断的中断向量和中断服务函数,我们通过中断来获知接收FIFO的信息。

 

初始化CAN的 GPIO

利用上面的宏,编写CAN的初始化函数,见代码清单 243。

 

代码清单 405 CAN的GPIO初始化函数(bsp_can.c文件)

 

  1 /*

2 * 函数名:CAN_GPIO_Config

3 * 描述:CAN的GPIO 配置

4 * 输入:无

5 * 输出 : 无

6 * 调用:内部调用

7 */

 

8 static void CAN_GPIO_Config(void)

 

9 {

 

10 GPIO_InitTypeDef GPIO_InitStructure;

 

11

 

12 /* 使能GPIO时钟*/

 

13 RCC_AHB1PeriphClockCmd(CAN_TX_GPIO_CLK|CAN_RX_GPIO_CLK, ENABLE);

 

14

 

15 /* 引脚源*/

 

16 GPIO_PinAFConfig(CAN_TX_GPIO_PORT, CAN_RX_SOURCE, CAN_AF_PORT);

 

17 GPIO_PinAFConfig(CAN_RX_GPIO_PORT, CAN_TX_SOURCE, CAN_AF_PORT);

 

18

 

19 /* 配置 CAN TX 引脚 */

 

20 GPIO_InitStructure.GPIO_Pin = CAN_TX_PIN;

 

21 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

 

22 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

 

23 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

 

24 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;

 

25 GPIO_Init(CAN_TX_GPIO_PORT, &GPIO_InitStructure);

 

26

 

27 /* 配置 CAN RX 引脚 */

 

28 GPIO_InitStructure.GPIO_Pin = CAN_RX_PIN ;

 

29 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

 

30 GPIO_Init(CAN_RX_GPIO_PORT, &GPIO_InitStructure);

 

31 }

 

与所有使用到GPIO的外设一样,都要先把使用到的GPIO引脚模式初始化,配置好复用功能。CAN的两个引脚都配置成通用推挽输出模式即可。

 

配置CAN的工作模式

接下来我们配置CAN的工作模式,由于我们是自己用的两个板子之间进行通讯,波特率之类的配置只要两个板子一致即可。如果您要使实验板与某个CAN总线网络的通讯的节点通讯,那么实验板的CAN配置必须要与该总线一致。我们实验中使用的配置见代码清单 244。

 

代码清单 406 配置CAN的工作模式(bsp_can.c文件)

 

1 /*

2 * 函数名:CAN_Mode_Config

3 * 描述:CAN的模式配置

4 * 输入:无

5 * 输出 : 无

6 * 调用:内部调用

7 */

 

8 static void CAN_Mode_Config(void)

 

9 {

 

10 CAN_InitTypeDef CAN_InitStructure;

 

11 /************************CAN通信参数设置************************/

 

12 /* Enable CAN clock */

 

13 RCC_APB1PeriphClockCmd(CAN_CLK, ENABLE);

 

14

 

15 /*CAN寄存器初始化*/

 

16 CAN_DeInit(CAN1);

 

17 CAN_StructInit(&CAN_InitStructure);

 

18

 

19 /*CAN单元初始化*/

 

20 CAN_InitStructure.CAN_TTCM=DISABLE; //MCR-TTCM 关闭时间触发通信模式使能

 

21 CAN_InitStructure.CAN_ABOM=ENABLE; //MCR-ABOM 使能自动离线管理

 

22 CAN_InitStructure.CAN_AWUM=ENABLE; //MCR-AWUM 使用自动唤醒模式

 

23 CAN_InitStructure.CAN_NART=DISABLE; //MCR-NART 禁止报文自动重传

 

24 CAN_InitStructure.CAN_RFLM=DISABLE; //MCR-RFLM 接收FIFO 不锁定

 

25                         // 溢出时新报文会覆盖原有报文

 

26 CAN_InitStructure.CAN_TXFP=DISABLE; //MCR-TXFP 发送FIFO优先级取决于报文标示符

 

27 CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; //正常工作模式

 

28 CAN_InitStructure.CAN_SJW=CAN_SJW_2tq; //BTR-SJW 重新同步跳跃宽度 2个时间单元

 

29

 

30 /* ss=1 bs1=5 bs2=3 位时间宽度为(1+5+3) 波特率即为时钟周期tq*(1+3+5) */

 

31 CAN_InitStructure.CAN_BS1=CAN_BS1_5tq; //BTR-TS1 时间段1 占用了5个时间单元

 

32 CAN_InitStructure.CAN_BS2=CAN_BS2_3tq; //BTR-TS1 时间段2 占用了3个时间单元

 

33

 

34 /* CAN Baudrate = 1 MBps (1MBps已为stm32的CAN最高速率) (CAN 时钟频率为 APB 1 = 45 MHz) */

 

35 ////BTR-BRP 波特率分频器定义了时间单元的时间长度 45/(1+5+3)/5=1 Mbps

 

36 CAN_InitStructure.CAN_Prescaler =5;

 

37 CAN_Init(CANx, &CAN_InitStructure);

 

38 }

 

这段代码主要是把CAN的模式设置成了正常工作模式,如果您阅读的是"CAN—回环测试"的工程,这里是被配置成回环模式的,除此之外,两个工程就没有其它差别了。

 

代码中还把位时序中的BS1和BS2段分别设置成了5Tq和3Tq,再加上SYNC_SEG段,一个CAN数据位就是9Tq了,加上CAN外设的分频配置为5分频,CAN所使用的总线时钟fAPB1 = 45MHz,于是我们可计算出它的波特率:

 

1Tq = 1/(45M/5)=1/9 us

 

T1bit=(5+3+1) x Tq =1us

 

波特率=1/T1bit =1Mbps

 

配置筛选器

以上是配置CAN的工作模式,为了方便管理接收报文,我们还要把筛选器用起来,见代码清单 245。

 

代码清单 407 配置CAN的筛选器(bsp_can.c文件)

 

1

 

2 /*IDE位的标志*/

 

3 #define CAN_ID_STD ((uint32_t)0x00000000) /*标准ID */

 

4 #define CAN_ID_EXT ((uint32_t)0x00000004) /*扩展ID */

 

5

 

6 /*RTR位的标志*/

 

7 #define CAN_RTR_Data ((uint32_t)0x00000000) /*数据帧 */

 

8 #define CAN_RTR_Remote ((uint32_t)0x00000002) /*远程帧*/

 

9

 

10 /************************************************************************/

 

11 /*

12 * 函数名:CAN_Filter_Config

13 * 描述:CAN的筛选器配置

14 * 输入:无

15 * 输出 : 无

16 * 调用:内部调用

17 */

 

18 static void CAN_Filter_Config(void)

 

19 {

 

20 CAN_FilterInitTypeDef CAN_FilterInitStructure;

 

21

 

22 /*CAN筛选器初始化*/

 

23 CAN_FilterInitStructure.CAN_FilterNumber=0; //筛选器组0

 

24 //工作在掩码模式

 

25 CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;

 

26 //筛选器位宽为单个32位。

 

27 CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;

 

28

 

29 /* 使能筛选器,按照标志符的内容进行比对筛选,

30 扩展ID不是如下的就抛弃掉,是的话,会存入FIFO0。 */

 

31//要筛选的ID高位,第0位保留,第1位为RTR标志,第2位为IDE标志,从第3位开始是EXID

 

32CAN_FilterInitStructure.CAN_FilterIdHigh= ((((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF0000)>>16;

 

33 //要筛选的ID低位

 

34 CAN_FilterInitStructure.CAN_FilterIdLow= (((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF;

 

35 //筛选器高16位每位必须匹配

 

36 CAN_FilterInitStructure.CAN_FilterMaskIdHigh= 0xFFFF;

 

37 //筛选器低16位每位必须匹配

 

38 CAN_FilterInitStructure.CAN_FilterMaskIdLow= 0xFFFF;

 

39 //筛选器被关联到FIFO0

 

40 CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ;

 

41 //使能筛选器

 

42 CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;

 

43

 

44 CAN_FilterInit(&CAN_FilterInitStructure);

 

45 /*CAN通信中断使能*/

 

46 CAN_ITConfig(CANx, CAN_IT_FMP0, ENABLE);

 

47 }

 

这段代码把筛选器第0组配置成了32位的掩码模式,并且把它的输出连接到接收FIFO0,若通过了筛选器的匹配,报文会被存储到接收FIFO0。

 

筛选器配置的重点是配置ID和掩码,根据我们的配置,这个筛选器工作在图 4017中的模式。

 

 

 

图 4017 一个32位的掩码模式筛选器

 

在该配置中,结构体成员CAN_FilterIdHigh和CAN_FilterIdLow存储的是要筛选的ID,而CAN_FilterMaskIdHigh和CAN_FilterMaskIdLow存储的是相应的掩码。在赋值时,要注意寄存器位的映射,在32位的ID中,第0位是保留位,第1位是RTR标志,第2位是IDE标志,从第3位起才是报文的ID(扩展ID)。

 

因此在上述代码中我们先把扩展ID"0x1314"、IDE位标志"宏CAN_ID_EXT"以及RTR位标志"宏CAN_RTR_DATA"根据寄存器位映射组成一个32位的数据,然后再把它的高16位和低16位分别赋值给结构体成员CAN_FilterIdHigh和CAN_FilterIdLow。

 

而在掩码部分,为简单起见我们直接对所有位赋值为1,表示上述所有标志都完全一样的报文才能经过筛选,所以我们这个配置相当于单个ID列表的模式,只筛选了一个ID号,而不是筛选一组ID号。这里只是为了演示方便,实际使用中一般会对不要求相等的数据位赋值为0,从而过滤一组ID,如果有需要,还可以继续配置多个筛选器组,最多可以配置28个,代码中只是配置了筛选器组0。

 

对结构体赋值完毕后调用库函数CAN_FilterInit把个筛选器组的参数写入到寄存器中。

 

配置接收中断

在配置筛选器代码的最后部分我们还调用库函数CAN_ITConfig使能了CAN的中断,该函数使用的输入参数宏CAN_IT_FMP0表示当FIFO0接收到数据时会引起中断,该接收中断的优先级配置如下,见代码清单 246。

 

代码清单 408 配置CAN接收中断的优先级(bsp_can.c文件)

 

1 /*接收中断号*/

 

2 #define CAN_RX_IRQ CAN1_RX0_IRQn

 

3 /*

4 * 函数名:CAN_NVIC_Config

5 * 描述:CAN的NVIC 配置,第1优先级组,0,0优先级

6 * 输入:无

7 * 输出 : 无

8 * 调用:内部调用

9 */

 

10 static void CAN_NVIC_Config(void)

 

11 {

 

12 NVIC_InitTypeDef NVIC_InitStructure;

 

13 /* Configure one bit for preemption priority */

 

14 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

 

15 /*中断设置*/

 

16 NVIC_InitStructure.NVIC_IRQChannel = CAN_RX_IRQ; //CAN RX中断

 

17 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

推荐阅读

史海拾趣

安路科技(Anlogic)公司的发展小趣事

随着技术的不断积累和市场的逐步开拓,安路科技在XXXX年成功推出了其首款高性能FPGA产品。这款产品以其优异的性能、稳定的品质和良好的性价比,迅速赢得了市场的青睐。此后,安路科技的产品线不断丰富,逐渐覆盖了更多应用领域,公司的市场份额也逐年攀升。

Emerson公司的发展小趣事

进入21世纪后,Emerson继续坚持创新战略,通过不断研发新技术和产品,保持其在电气行业的领先地位。公司注重环保和可持续发展,推出了一系列高效节能的电气产品和解决方案。同时,Emerson还积极拥抱数字化和智能化趋势,通过引入人工智能、物联网等先进技术,推动公司业务的数字化转型。此外,Emerson还积极参与全球竞争和合作,通过跨国并购和战略合作,不断拓展其全球市场份额和影响力。

赛微(Cellwise)公司的发展小趣事

在技术创新和产品研发的过程中,赛微高度重视知识产权的保护和管理。公司积极申请各类专利和软件著作权,加强知识产权保护力度。截至目前,赛微已经拥有国际/国内软件著作权98项,国际/国内专利166项,正在申请的国际/国内专利64项。这些知识产权的积累不仅提升了公司的核心竞争力,也为公司的长期发展奠定了坚实基础。

EnerSys公司的发展小趣事

作为一家具有社会责任感的企业,EnerSys积极参与社区回馈活动,并致力于可持续发展。公司不仅在业务上取得了显著成就,还通过参与各种公益活动,回馈社会。同时,EnerSys还关注环境保护和可持续发展问题,努力减少生产过程中的能源消耗和废物排放。这些举措不仅提升了EnerSys的品牌形象,也为公司的长远发展奠定了基础。

请注意,以上故事框架为概述性质,并未详细展开每个故事的细节。在实际撰写时,您可以根据这些框架进一步补充和完善具体内容。

Dynex公司的发展小趣事

EnerSys公司及其前身在工业电池制造领域拥有超过100年的历史。自20世纪初起,公司就开始专注于为各种工业应用提供可靠的电池解决方案。随着时间的推移,EnerSys不断吸收新技术和制造工艺,逐渐发展成为工业电池市场的领导者。这一长期的历史积淀,为EnerSys在电子行业中的发展奠定了坚实的基础。

Circuit Assembly公司的发展小趣事

EnerSys一直致力于技术创新和研发投入。公司拥有一支专业的研发团队,不断推出具有竞争力的新产品和解决方案。例如,EnerSys在锂电池领域取得了重要突破,成功开发出高能量密度、长寿命的锂电池产品。这些创新产品不仅满足了客户的多样化需求,还推动了公司在电子行业中的持续发展。

问答坊 | AI 解惑

从51到ARM. 32位嵌入式系统入门

从51到ARM. 32位嵌入式系统入门…

查看全部问答>

求助!FPGA输入语音

各位大侠, 我最近在做一个报告,要求要用FPGA连接耳麦采取声音信号然后处理 储存。 真是万事开头难啊,刚开始就卡住了 无从下手。 希望大侠们给点建议。多谢了! 有相关研究的同仁们可以加我izhangdan@gmail.com  希望在这方面有所长 ...…

查看全部问答>

关于RegQueryValueEx操作问题

关于RegQueryValueEx操作问题 LPBYTE dwValue;                                 DWORD dwLength = sizeof(DWORD);         i ...…

查看全部问答>

请教Windriver中WD_MultiTransfer使用方法(内有写好的程序)

以下是自己编写的Windriver开发PCI驱动的程序,目前已经调通,但是dwResult结果不对,跟踪程序结果是00xc3f000e8,但与Windriver的自带程序读同一地址所得到的结果086300A2不同。请问问题出现在哪里? 我想认识开发Windriver的朋友们,我QQ号码:819 ...…

查看全部问答>

EVC下窗口切换

小弟我最近在测个功能.我们在GPS应用程序.由我们的程序创建了一个新的进程也就是地图导航软件.这时有个硬件开关.我想在按的时候能够切换回我们的应用程序.再按下又返回到地图软件.可是老是实现不了. 注:这个硬件开关在GPS应用程序里可以返回到主窗 ...…

查看全部问答>

开发板资料大搜集(即日起到2月28日),精美礼物等你拿!

对于想学习和正在学习单片机和嵌入式的你来说,最想拥有的是什么?我想可能是一块资源丰富、功能强大、资料齐全的开发板!!!   目前市场上各种各样的单片机和嵌入式系统开发板可谓是琳琅满目,价格也是高低不等,每一种芯片的开发板都有各 ...…

查看全部问答>

重映射后,SPI3与SPI1共用了PA4,这两个SPI能正常使用么?

                                 在stm32F107里,重映射后,SPI3与SPI1共用了PA4,这两个SPI能正常使用么?有没有什么问题?…

查看全部问答>

单片机 关于EA引脚的功能,求大神指导

如果我让程序在内部的ROM运行一会,比如说从0000H运行到0016H,在这个时候我拉低EA(我把P3.4口的引脚接到了EA引脚上,拉低P3.4相当于拉低EA),这样的话程序会怎么运行?假设拉低EA是单字节指令。运行拉低EA命令后,地址变成0017H,然后程序是不是会 ...…

查看全部问答>

新手学ZigBee,求大神引导

新手学ZigBee,求大神引导…

查看全部问答>