历史上的今天
返回首页

历史上的今天

今天是:2025年02月11日(星期二)

正在发生

2020年02月11日 | 详解stm32的CAN控制器(程序分享)

2020-02-11 来源:eefocus

首先简单介绍一下CAN总线,关于CAN总线是谁发明的,CAN总线的历史,CAN总线的发展,CAN总线的应用场合,这些,通通不说。这里只是以我个人理解,简单说说CAN通信。CAN总线的端点没有地址(除非自己在帧里定义地址),CAN总线通信不用地址,用标识符,不是单独的谁发给谁,而是,你总是发送给整个网络。然后每个节点都有过滤器,对网络上的传播的帧的标识符进行过滤,自己想要什么样的帧,可以设置自己的过滤器,接收相关的帧信息。如果两个节点同时发送怎么办?这个不用我们担心,CAN控制器会自己仲裁,让高优先级的帧先发。


然后我们可以了解一下stm32的CAN控制器。

详解stm32的CAN控制器(程序分享)

如上图所示,stm32有两个can控制器,can1(主),和can2(从),其中过滤器的设置是通过can1来设置,其他工作模式,波特率等,可以各自设置。每个控制器有三个发送邮箱,两个fifo,每个fifo有三个接收邮箱。


发送:选择一个空的发送邮箱,把帧信息写到该发送邮箱的寄存器里,请求发送,控制器就会根据标识符的优先级把帧先后发送出去。


接收:如果接收到的帧的标识符能过过滤表的一系列过滤,该帧信息就会保存在fifo接收邮箱的寄存器里。


过滤器:stm32f407共有28组过滤器,每组过滤器可以设置关联到fifo0或者fifo1,每组都包括两个32位存储器,可以配置成一个32位有位屏蔽功能的标识符过滤器,或者两个32位完全匹配的标识符过滤器,或者两个16位有位屏蔽功能的标识符过滤器,或者四个16位完全匹配的标识符过滤器。如下图所示:

详解stm32的CAN控制器(程序分享)

我所说的完全匹配的意思是,接收到的帧的标识符每一位都要跟过滤器对应的位一样,才能过得了这个过滤器。有位屏蔽功能的意思是一个寄存器放标识符,一个放屏蔽掩码,屏蔽掩码为1的位对应的接收到的帧的标识符的位与对应的放标识符的寄存器的位一致,就能通过。


传输一位的时间和波特率的计算:

详解stm32的CAN控制器(程序分享)

CAN控制器的波特率是由APB时钟线和CAN位时序寄存器CAN_BTR的TS2[3:0]、TS1[2:0]和BRP[9:0]确定的,其中,TS1[2:0]定义了时间段1占用多少个时间单元,TS2[3:0]定义了时间段2占用多少个时间单元,BRP[9:0]定义对APB1时钟的分频。

PS:设置波特率为1M

详解stm32的CAN控制器(程序分享)

其中Tpclk为APB1的时钟周期,假设为

Tpclk = 1/42M

0≦TS1≦7

0≦TS2≦15

0≦BRP≦1021

根据以上数据,有

(TS2+TS1+3)(BRP+1)=42

令BRP=2,有

TS2+TS1=11

令TS1=8,TS2=3


设置步骤:

1. 设置中断优先级分组(如果之前没有设置),这个最好一个程序里只在开头设置一次。

2. 使能相关GPIO时钟。

3. 选择相关GPIO引脚的复用功能。

4. 设置相关GPIO引脚为复用模式。

5. 设置相关GPIO引脚的速度,方式。

6. 设置主控制寄存器MCR,进入初始化模式

7. 等待进入初始化模式

8. 设置波特率。

9. 其他设置。

10. 如果要用到中断,在中断使能寄存器IER中使能相关中断响应。

11. 如果要用到中断,设置相关中断优先级(NVIC_IP)。

12. 如果要用到中断,使能相关中断(NVIC_ISER)。

13. 设置主控制寄存器MCR,进入正常工作模式。

14. 设置FMR,使过滤器组工作在初始化模式。

15. 设置FMR的CAN2SB,确定CAN2的过滤器组从哪一组开始。

16. 设置用到的过滤器组的工作方式。

17. 设置用到的过滤器组的位宽。

18. 给fifo0和fifo2划分(关联)过滤组。

19. 禁用用到的过滤器组。

20. 设置过滤器组的标识符,帧类型等。

21. 使能相关过滤器组。

22. 设置FMR,使过滤器组工作在正常模式。

23. 如果要用中断,编写中断服务函数(函数名是固定的)。

24. 中断服务函数里检查是哪个中断。

25. 编写相应服务程序。


电路请参见本博客:小工具之——CAN收发器

程序:

[plain] view plaincopy/************************************

标题:操作CAN的练习

软件平台:IAR for ARM6.21

硬件平台:stm32f4-discovery

主频:168M

描述:通过硬件收发器连接CAN1,CAN2

组成一个两个端点的网络

CAN1循环发出数据帧

CAN2接收过滤数据帧

用uart把CAN2接收到

的数据帧发到超级终端

author:小船

data:2012-08-14

*************************************/

#include 《stm32f4xx.h》

#include “MyDebugger.h”

#define RECEIVE_BUFFER_SIZE 20

u32 CAN2_receive_buffer[RECEIVE_BUFFER_SIZE][4];

u8 UART_send_buffer[1800];

u8 Consumer = 0;

u8 Producer = 0;

u32 Gb_TImingDelay;

void Delay(uint32_t nTIme);

void TIM7_init();//定时1s

u32 get_rece_data();

void CAN_GPIO_config();

void main ()

{

u32 empty_box;

SysTIck_Config(SystemCoreClock / 1000); //设置systemtick一毫秒中断

SCB-》AIRCR = 0x05FA0000 | 0x400; //中断优先级分组 抢占:响应=3:1

MyDebugger_Init();

TIM7_init();

MyDebugger_Message( “nrtesting.。。.。。nr” ,

sizeof(“nrtesting.。。.。。nr”)/sizeof(char) );

CAN_GPIO_config();

RCC-》APB1ENR |= ((1《《25)|(1《《26));//使能CAN1、CAN2时钟

CAN1-》MCR = 0x00000000;

/*

请求进入初始化模式

禁止报文自动重传

自动唤醒模式

*/

CAN1-》MCR |= ((1《《0)|(1《《4)|(1《《5));

CAN1-》MCR &= ~(1《《16);//在调试时,CAN照常工作

while(!(CAN1-》MSR & 0xfffffffe)) //等待进入初始化模式

{

MyDebugger_LEDs(orange, on);

}

MyDebugger_LEDs(orange, off);

/*

正常模式

重新同步跳跃宽度(1+1)tq

TS2[2:0]=3

TS1[3:0]=8

BRP[9:0]=2

ps:

tq = (BRP[9:0] + 1) x tPCLK,

tBS2 = tq x (TS2[2:0] + 1),

tBS1 = tq x (TS1[3:0] + 1),

NominalBitTime = 1 × tq+tBS1+tBS2,

BaudRate = 1 / NominalBitTime

波特率设为1M

*/

CAN1-》BTR = ((0《《30)|(0x01《《24)|(3《《20)|(8《《16)|(2《《0));

CAN1-》MCR &= ~(0x00000001);//正常工作模式

CAN2-》MCR = 0x00000000;

/*

请求进入初始化模式

禁止报文自动重传

自动唤醒模式

*/

CAN2-》MCR |= ((1《《0)|(1《《4)|(1《《5));

CAN2-》MCR &= ~(1《《16);//在调试时,CAN照常工作

while(!(CAN2-》MSR & 0xfffffffe)) //等待进入初始化模式

{

MyDebugger_LEDs(orange, on);

}

MyDebugger_LEDs(orange, off);

/*

正常模式

重新同步跳跃宽度(1+1)tq

TS2[2:0]=3

TS1[3:0]=8

BRP[9:0]=2

ps:

tq = (BRP[9:0] + 1) x tPCLK,

tBS2 = tq x (TS2[2:0] + 1),

tBS1 = tq x (TS1[3:0] + 1),

NominalBitTime = 1 × tq+tBS1+tBS2,

BaudRate = 1 / NominalBitTime

波特率设为1M

*/

CAN2-》BTR = ((0《《30)|(0x01《《24)|(3《《20)|(8《《16)|(2《《0));

CAN2-》IER &= 0x00000000;

/*

FIFO1消息挂号中断使能

FIFO1满中断使能

FIFO1溢出中断使能

*/

CAN2-》IER |= ((1《《4)|(1《《5)|(1《《6));

NVIC-》IP[65] = 0xa0; //抢占优先级101,响应优先级0

NVIC-》ISER[2] |= (1《《1); //使能中断线65,也就是can2_rx1中断

CAN2-》MCR &= ~(0x00000001);//正常工作模式

//总共有28组过滤器

CAN1-》FMR |= 1; //过滤器组工作在初始化模式

CAN1-》FMR &= 0xffffc0ff;//CAN2的过滤器组从14开始

CAN1-》FMR |= (14《《8);

CAN1-》FM1R |= (1《《14);//过滤器组14的寄存器工作在标识符列表模式

//位宽为16位,2个32位分为四个16位寄存器,过滤四个标识符

//CAN1-》FS1R |= (1《《15);//过滤器组15为单个32位寄存器,用于扩展标识符

CAN1-》FFA1R = 0x0fffc000;//0~13号过滤器组关联到fifo0,14~27号过滤器组关联到fifo1

CAN1-》FA1R &= ~(1《《14);//禁用过滤器组14

/*

过滤器组0寄存器分为4个十六位过滤器:

标识符列表:

过滤器编号 匹配标准标识符 RTR IDE EXID[17:15]

0 0x7cb(111 1100 1011b) 数据帧 标准标识符 000b

1 0x4ab(100 1010 1011b) 数据帧 标准标识符 000b

2 0x7ab(111 1010 1011b) 数据帧 标准标识符 000b

3 0x40b(100 0000 1011b) 数据帧 标准标识符 000b

*/

CAN1-》sFilterRegister[14].FR1 &= 0x00000000;

CAN1-》sFilterRegister[14].FR2 &= 0x00000000;

CAN1-》sFilterRegister[14].FR1 |= ((0x7cb《《5)|(0《《4)|(0《《3));

CAN1-》sFilterRegister[14].FR1 |= ((0x4ab《《21)|(0《《20)|(0《《19));

CAN1-》sFilterRegister[14].FR2 |= ((0x7ab《《5)|(0《《4)|(0《《3));

CAN1-》sFilterRegister[14].FR2 |= ((0x40b《《21)|(0《《20)|(0《《19));

CAN1-》FA1R |= (1《《14);//使能过滤器组14

CAN1-》FMR &= ~1; //过滤器组正常工作

while(1)

{

/*

选择空的发送邮箱:

标准标识符0x7ab(111 1010 1011b)

数据帧

不使用扩展标识符

*/

if( CAN1-》TSR & ((1《《26)|(1《《27)|(1《《28)) )

{

empty_box = ((CAN1-》TSR》》24) & 0x00000003);

CAN1-》sTxMailBox[empty_box].TIR = (0x7ab《《21);

CAN1-》sTxMailBox[empty_box].TDTR &= 0xfffffff0;

CAN1-》sTxMailBox[empty_box].TDTR |= 0x00000008;//发送数据长度为8

CAN1-》sTxMailBox[empty_box].TDLR = 0x12345678;

CAN1-》sTxMailBox[empty_box].TDHR = 0x9abcdef0;

CAN1-》sTxMailBox[empty_box].TIR |= (1《《0);//请求发送

}

else

{

MyDebugger_LEDs(orange, on);

}

Delay(100);

/*

选择空的发送邮箱:

标准标识符0x4ab(100 1010 1011b)

数据帧

不使用扩展标识符

*/

if( CAN1-》TSR & ((1《《26)|(1《《27)|(1《《28)) )

{

empty_box = ((CAN1-》TSR》》24) & 0x00000003);

CAN1-》sTxMailBox[empty_box].TIR = (0x4ab《《21);

CAN1-》sTxMailBox[empty_box].TDTR &= 0xfffffff0;

CAN1-》sTxMailBox[empty_box].TDTR |= 0x00000008;//发送数据长度为8

CAN1-》sTxMailBox[empty_box].TDLR = 0x56781234;

CAN1-》sTxMailBox[empty_box].TDHR = 0x9abcdef0;

CAN1-》sTxMailBox[empty_box].TIR |= (1《《0);//请求发送

}

else

{

MyDebugger_LEDs(orange, on);

}

Delay(100);

/*

选择空的发送邮箱:

标准标识符0x7cb(100 1010 1011b)

数据帧

不使用扩展标识符

*/

if( CAN1-》TSR & ((1《《26)|(1《《27)|(1《《28)) )

{

empty_box = ((CAN1-》TSR》》24) & 0x00000003);

CAN1-》sTxMailBox[empty_box].TIR = (0x7cb《《21);

CAN1-》sTxMailBox[empty_box].TDTR &= 0xfffffff0;

CAN1-》sTxMailBox[empty_box].TDTR |= 0x00000006;//发送数据长度为6

CAN1-》sTxMailBox[empty_box].TDLR = 0x56781234;

CAN1-》sTxMailBox[empty_box].TDHR = 0x00009abc;

CAN1-》sTxMailBox[empty_box].TIR |= (1《《0);//请求发送

}

else

{

MyDebugger_LEDs(orange, on);

}

Delay(100);

/*

选择空的发送邮箱:

标准标识符0x40b(100 0000 1011b)

数据帧

不使用扩展标识符

*/

if( CAN1-》TSR & ((1《《26)|(1《《27)|(1《《28)) )

{

empty_box = ((CAN1-》TSR》》24) & 0x00000003);

CAN1-》sTxMailBox[empty_box].TIR = (0x40b《《21);

CAN1-》sTxMailBox[empty_box].TDTR &= 0xfffffff0;

CAN1-》sTxMailBox[empty_box].TDTR |= 0x00000004;//发送数据长度为4

CAN1-》sTxMailBox[empty_box].TDLR = 0x56781234;

CAN1-》sTxMailBox[empty_box].TDHR = 0x00000000;

CAN1-》sTxMailBox[empty_box].TIR |= (1《《0);//请求发送

}

else

{

MyDebugger_LEDs(orange, on);

}

Delay(100);

}

}

/****************************************

函数名:CAN_GPIO_config

参数:无

返回值:无

功能:设置CAN1,2控制器用到IO口

CAN1_TX---------PD1

CAN1_RX---------PB8

CAN2_TX---------PB13

CAN2_RX---------PB5

****************************************/

void CAN_GPIO_config()

{

RCC-》AHB1ENR |= ((1《《1) | (1《《3));//使能GPIOB、D时钟

GPIOB-》AFR[0] |= 0x00900000; //AF9

GPIOB-》AFR[1] |= 0x00900009;

GPIOD-》AFR[0] |= 0x00000090;

GPIOB-》MODER &= 0xF3FCF3FF; //第二功能

GPIOB-》MODER |= 0x08020800;

GPIOD-》MODER &= 0xFFFFFFF3;

GPIOD-》MODER |= 0x00000008;

GPIOB-》OSPEEDR &= 0xF3FCF3FF; //50M

GPIOB-》OSPEEDR |= 0x08020800;

GPIOD-》OSPEEDR &= 0xFFFFFFF3;

GPIOD-》OSPEEDR |= 0x00000008;

GPIOB-》PUPDR &= 0xF3FCF3FF; //上拉

GPIOB-》PUPDR |= 0x04010400;

GPIOD-》PUPDR &= 0xFFFFFFF3;

GPIOD-》PUPDR |= 0x00000004;

}

/****************************************

函数名:CAN2_RX1_IRQHandler

参数:无

返回值:无

功能:CAN2fifo1接收中断处理

把信息存进循环队列

****************************************/

void CAN2_RX1_IRQHandler()

{

if(CAN2-》RF1R & (0x00000003))//接收到新的消息,fifo1非空

{

Producer++;

if(Producer == RECEIVE_BUFFER_SIZE)Producer = 0;

if(Producer != Consumer)

{

CAN2_receive_buffer[Producer][0] = CAN2-》sFIFOMailBox[1].RIR;

CAN2_receive_buffer[Producer][1] = CAN2-》sFIFOMailBox[1].RDTR;

CAN2_receive_buffer[Producer][2] = CAN2-》sFIFOMailBox[1].RDLR;

CAN2_receive_buffer[Producer][3] = CAN2-》sFIFOMailBox[1].RDHR;

}

else

{

if(Producer == 0)Producer = RECEIVE_BUFFER_SIZE;

Producer--;

MyDebugger_LEDs(blue, on);

}

CAN2-》RF1R |= (1《《5);//释放邮箱

}

if(CAN2-》RF1R & (1《《3))//fifo0满

{

MyDebugger_LEDs(red, on);

CAN2-》RF1R &= ~(1《《3);

}

if(CAN2-》RF1R & (1《《4))//fifo0溢出

{

MyDebugger_LEDs(red, on);

CAN2-》RF1R &= ~(1《《4);

}

}

/****************************************

函数名:TIM7_init

参数:无

返回值:无

功能:初始化定时器7

作1s定时用

****************************************/

void TIM7_init()

{

RCC-》APB1ENR |= (1《《5); //打开TIM7时钟

TIM7-》PSC = 8399; //对时钟84M进行8400分频,使得计数频率为10k

TIM7-》ARR = 10000; //定时一秒

TIM7-》CNT = 0; //清空计数器

TIM7-》CR1 |= (1《《7); //自动重装载预装载使能

TIM7-》DIER |= 1; //使能中断

NVIC-》IP[55] = 0xe0;

NVIC-》ISER[1] |= (1《《(55-32));

TIM7-》CR1 |= 1; //开始计时

}

/****************************************

函数名:TIM7_IRQHandler

参数:无

返回值:无

功能:定时器7中断处理

1s定时到

把can2收到的信息转换格式

用usrt发送到超级终端显示

****************************************/

void TIM7_IRQHandler(void)

{

u32 length;

if(TIM7-》SR)

{

length = get_rece_data();

MyDebugger_Message( UART_send_buffer, length );

TIM7-》SR &= ~(0x0001);

}

}

/****************************************

函数名:get_rece_data

参数:无

返回值:length 整理后要发送数据的长度

功能:把循环队列的信息取出

进行格式转换

把信息存到uart发送缓冲区

****************************************/

u32 get_rece_data()

{

u8 filter_No;

u8 Data_length;

char i;

u32 length = 0;

const char ascii[16] = {‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’,

‘8’, ‘9’, ‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’};

while(1)

{

if(Producer != Consumer)

{

Consumer++;

if(Consumer == RECEIVE_BUFFER_SIZE)Consumer=0;

UART_send_buffer[length++] = ‘n’;

UART_send_buffer[length++] = ‘r’;

//Filter No.xx

UART_send_buffer[length++] = ‘F’;

UART_send_buffer[length++] = ‘i’;

UART_send_buffer[length++] = ‘l’;

UART_send_buffer[length++] = ‘t’;

UART_send_buffer[length++] = ‘e’;

UART_send_buffer[length++] = ‘r’;

UART_send_buffer[length++] = ‘ ’;

UART_send_buffer[length++] = ‘N’;

UART_send_buffer[length++] = ‘o’;

UART_send_buffer[length++] = ‘。’;

filter_No = (CAN2_receive_buffer[Consumer][1]》》8) & 0x000000ff;

UART_send_buffer[length++] = filter_No%100/10 + ‘0’;

UART_send_buffer[length++] = filter_No%10 + ‘0’;

UART_send_buffer[length++] = ‘n’;

UART_send_buffer[length++] = ‘r’;

推荐阅读

史海拾趣

Enable Semiconductor Corp公司的发展小趣事

Enable Semiconductor Corp公司自创立之初,便专注于半导体技术的研发与创新。在早期阶段,公司研发团队经过无数次实验,成功研发出一种新型的半导体材料,这种材料具有更高的导电性能和更低的能耗。这一技术突破不仅提升了公司产品的性能,也为公司赢得了市场的初步认可。随着技术的不断迭代和优化,Enable Semiconductor Corp的产品逐渐在电子行业崭露头角,其市场份额也稳步上升。

EDO Corp公司的发展小趣事

在制药工业中,Eclipse Magnetics的磁选机技术得到了广泛应用。通过与制药企业的合作,Eclipse Magnetics成功解决了制药过程中可能存在的金属污染问题。这种合作不仅提升了制药企业的产品质量,也进一步巩固了Eclipse Magnetics在电子行业的地位。

FEMA Electronics Corporation公司的发展小趣事

FEMA在追求经济效益的同时,也积极履行社会责任。公司积极参与公益事业和慈善活动,为社会做出了积极贡献。同时,FEMA还注重环保和可持续发展,努力降低生产过程中的能耗和排放。这些举措不仅提升了公司的社会形象,也为公司的长期发展奠定了坚实的基础。在履行社会责任的过程中,FEMA实现了经济效益和社会效益的双赢。

GSME Electronics公司的发展小趣事

GSME Electronics的起点可以追溯到2001年,当时公司在广西桂林市成立,作为桂林国家高新区的高新技术企业。初期,公司面临着技术设备落后的挑战,但管理层高瞻远瞩,决定从日本、韩国、欧美等国家引进具有国际先进水平的自动化生产设备。这一决策为公司后续的发展奠定了坚实的基础,使得公司能够快速进入半导体器件的生产领域,并不断提升产品质量和生产效率。

AMOTECH(阿莫泰克)公司的发展小趣事

1994年,AMOTECH在韩国创立,凭借创始人的远见卓识和技术团队的扎实能力,公司迅速在电子行业中崭露头角。经过两年的努力,1996年,AMOTECH被政府通讯部评为“光明前途企业”,这是对其技术实力和市场潜力的肯定。随后,公司不断加大研发投入,终于在2000年获得ISO9000认证,这标志着AMOTECH在产品质量管理上达到了国际标准。

Chemi-Con公司的发展小趣事

品质是Chemi-Con公司的生命线。公司始终坚持严格的质量管理体系,从原材料采购到生产制造,再到产品出厂,每一个环节都经过严格的把控。这种对品质的执着追求,使得Chemi-Con的产品在市场上获得了良好的口碑。同时,公司还积极拓展市场,与全球多家知名企业建立了稳定的合作关系,产品远销海外市场。

问答坊 | AI 解惑

单片机工具自制系列2

自制K149BC PIC专用USB接口编程器的完整资料…

查看全部问答>

PLD设计技巧——消除组合逻辑产生的毛刺

PLD设计技巧——消除组合逻辑产生的毛刺…

查看全部问答>

Linux面试题,看你能得多少分?

一.填空题 1. 在Linux系统中,以 方式访问设备 。 2. Linux内核引导时,从文件 中读取要加载的文件系统。 3. Linux文件系统中每个文件用 来标识。 4. 全部磁盘块由四个部分组成,分别为 。 5. 链接分为: 和 。 6. 超级块包含了 和 等重要的 ...…

查看全部问答>

stepldr引导eboot不成功

     根据datasheet (nand flash controller)The S3C2416 is equipped with an internal SRAM buffer called ‘Steppingstone’. This supports NAND flash boot loader. When you use IROM boot and select nand flash as boo ...…

查看全部问答>

为什么我在evc中输出只有emulator可选,没有设备可选?

为什么我在evc中输出只有emulator可选,没有设备可选? 这样我就没法把程序download到wince设备上了?为什么? 是不是少装了什么?请各位帮帮忙.…

查看全部问答>

求一完整程序~关于EVC4.0下的UDP编程

求一完整程序~关于EVC4.0下的UDP编程!有的大大请加我QQ 41368886 还有哪个大大有Pocket PC 2002一Win32 (WINCE X 86) Debug 的开发环境也发给我!  求~~跪求这两个 …

查看全部问答>

寻迹小车

各位大哥,跪求:寻迹小车原理图和程序,要详细的谢谢了!!…

查看全部问答>

SRAM的A0与FSMC_A1相连,这样怎么操作呢?

                                 求大侠指导一下…

查看全部问答>

symbol referencing errors 怎么办

我写了一个sdram的测试小程序,compile通过,没有错误,但是build的 时候给出 \\"c:\\\\ti\\\\c6000\\\\cgtools\\\\bin\\\\cl6x\\" -@\\"Debug.lkf\\" undefined               &nbs ...…

查看全部问答>