历史上的今天
今天是: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控制器,can1(主),和can2(从),其中过滤器的设置是通过can1来设置,其他工作模式,波特率等,可以各自设置。每个控制器有三个发送邮箱,两个fifo,每个fifo有三个接收邮箱。
发送:选择一个空的发送邮箱,把帧信息写到该发送邮箱的寄存器里,请求发送,控制器就会根据标识符的优先级把帧先后发送出去。
接收:如果接收到的帧的标识符能过过滤表的一系列过滤,该帧信息就会保存在fifo接收邮箱的寄存器里。
过滤器:stm32f407共有28组过滤器,每组过滤器可以设置关联到fifo0或者fifo1,每组都包括两个32位存储器,可以配置成一个32位有位屏蔽功能的标识符过滤器,或者两个32位完全匹配的标识符过滤器,或者两个16位有位屏蔽功能的标识符过滤器,或者四个16位完全匹配的标识符过滤器。如下图所示:

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

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

其中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’;
史海拾趣
|
一.填空题 1. 在Linux系统中,以 方式访问设备 。 2. Linux内核引导时,从文件 中读取要加载的文件系统。 3. Linux文件系统中每个文件用 来标识。 4. 全部磁盘块由四个部分组成,分别为 。 5. 链接分为: 和 。 6. 超级块包含了 和 等重要的 ...… 查看全部问答> |
|
根据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编程!有的大大请加我QQ 41368886 还有哪个大大有Pocket PC 2002一Win32 (WINCE X 86) Debug 的开发环境也发给我! 求~~跪求这两个 … 查看全部问答> |
|
我写了一个sdram的测试小程序,compile通过,没有错误,但是build的 时候给出 \\"c:\\\\ti\\\\c6000\\\\cgtools\\\\bin\\\\cl6x\\" -@\\"Debug.lkf\\" undefined &nbs ...… 查看全部问答> |




