首先简单介绍一下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( “\n\rtesting.。。.。。\n\r” ,
sizeof(“\n\rtesting.。。.。。\n\r”)/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