CAN控制器
首先簡單介紹一下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收發器
程序:
/************************************
標題:操作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 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';
//DataLength:x
UART_send_buffer[length++] = 'D';
UART_send_buffer[length++] = 'a';
UART_send_buffer[length++] = 't';
UART_send_buffer[length++] = 'a';
UART_send_buffer[length++] = 'L';
UART_send_buffer[length++] = 'e';
UART_send_buffer[length++] = 'n';
UART_send_buffer[length++] = 'g';
UART_send_buffer[length++] = 't';
UART_send_buffer[length++] = 'h';
UART_send_buffer[length++] = ':';
Data_length = CAN2_receive_buffer[Consumer][1] & 0x0000000f;
UART_send_buffer[length++] = Data_length % 10 + '0';
UART_send_buffer[length++] = '\n';
UART_send_buffer[length++] = '\r';
if(CAN2_receive_buffer[Consumer][0] & (1<<1))
{
UART_send_buffer[length++] = 'R';
UART_send_buffer[length++] = 'e';
UART_send_buffer[length++] = 'm';
UART_send_buffer[length++] = 'o';
UART_send_buffer[length++] = 't';
UART_send_buffer[length++] = 'e';
UART_send_buffer[length++] = 'F';
UART_send_buffer[length++] = 'r';
UART_send_buffer[length++] = 'a';
UART_send_buffer[length++] = 'm';
UART_send_buffer[length++] = 'e';
}
else
{
UART_send_buffer[length++] = 'D';
UART_send_buffer[length++] = 'a';
UART_send_buffer[length++] = 't';
UART_send_buffer[length++] = 'a';
UART_send_buffer[length++] = 'F';
UART_send_buffer[length++] = 'r';
UART_send_buffer[length++] = 'a';
UART_send_buffer[length++] = 'm';
UART_send_buffer[length++] = 'e';
}
UART_send_buffer[length++] = '\n';
UART_send_buffer[length++] = '\r';
if(CAN2_receive_buffer[Consumer][0] & (1<<2))
{
UART_send_buffer[length++] = 'e';
UART_send_buffer[length++] = 'x';
UART_send_buffer[length++] = 't';
UART_send_buffer[length++] = ' ';
UART_send_buffer[length++] = 'I';
UART_send_buffer[length++] = 'D';
UART_send_buffer[length++] = ':';
UART_send_buffer[length++] =
ascii[CAN2_receive_buffer[Consumer][0] >> 31];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] >> 27)& 0x0000000f];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] >> 23)& 0x0000000f];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] >> 19)& 0x0000000f];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] >> 15)& 0x0000000f];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] >> 11)& 0x0000000f];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] >> 7)& 0x0000000f];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] >> 3)& 0x0000000f];
}
else
{
UART_send_buffer[length++] = 's';
UART_send_buffer[length++] = 't';
UART_send_buffer[length++] = 'd';
UART_send_buffer[length++] = ' ';
UART_send_buffer[length++] = 'I';
UART_send_buffer[length++] = 'D';
UART_send_buffer[length++] = ':';
UART_send_buffer[length++] =
ascii[CAN2_receive_buffer[Consumer][0] >> 29];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] >> 25)& 0x0000000f];
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][0] >> 21)& 0x0000000f];
}
UART_send_buffer[length++] = '\n';
UART_send_buffer[length++] = '\r';
UART_send_buffer[length++] = 'D';
UART_send_buffer[length++] = 'a';
UART_send_buffer[length++] = 't';
UART_send_buffer[length++] = 'a';
UART_send_buffer[length++] = ':';
if(Data_length > 4)
{
for(i = 2*Data_length - 8; i > 0; i--)
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][3] >> ((i-1)*4))& 0x0000000f];
for(i = 8; i > 0; i--)
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][2] >> ((i-1)*4))& 0x0000000f];
}
else
{
for(i = 2*Data_length; i > 0; i--)
UART_send_buffer[length++] =
ascii[(CAN2_receive_buffer[Consumer][2] >> ((i-1)*4))& 0x0000000f];
}
UART_send_buffer[length++] = '\n';
UART_send_buffer[length++] = '\r';
}
else
break;
}
return length;
}
void Delay(uint32_t nTime)
{
Gb_TimingDelay = nTime;
while(Gb_TimingDelay != 0);
}
void SysTick_Handler(void)
{
if (Gb_TimingDelay != 0x00)
{
Gb_TimingDelay--;
}
}
運行結果: