CAN 是Controller Area Network 的縮寫(以下稱為CAN),該通信使用的是ISO11898標準,該標準的物理層特征如下圖所示。

CAN協(xié)議是通過以下5種類型的幀進行的:
-
數(shù)據(jù)幀
-
搖控幀
-
錯誤幀
-
過載幀
-
幀間隔
另外,數(shù)據(jù)幀和遙控幀有標準格式和擴展格式兩種格式。標準格式有11 個位的標識符(ID),擴展格式有29 個位的ID。
大部分系統(tǒng)使用的都是數(shù)據(jù)幀 ,我這里使用的也是數(shù)據(jù)幀。
數(shù)據(jù)幀一般由7個段構(gòu)成,即:
(1) 幀起始。表示數(shù)據(jù)幀開始的段。
(2) 仲裁段。表示該幀優(yōu)先級的段。
(3) 控制段。表示數(shù)據(jù)的字節(jié)數(shù)及保留位的段。
(4) 數(shù)據(jù)段。數(shù)據(jù)的內(nèi)容,一幀可發(fā)送0~8個字節(jié)的數(shù)據(jù)。
(5) CRC段。檢查幀的傳輸錯誤的段。
(6) ACK段。表示確認正常接收的段。
(7) 幀結(jié)束。表示數(shù)據(jù)幀結(jié)束的段。
明確了數(shù)據(jù)幀概念,還需要理解一下過濾器的作用。
STM32的標識符屏蔽濾波目的是減少了CPU處理CAN通信的開銷。STM32的過濾器組最多有28個(互聯(lián)型),但是STM32F103ZET6只有14個(增強型),每個濾波器組x由2個32為寄存器,CAN_FxR1和CAN_FxR2組成。
STM32每個過濾器組的位寬都可以獨立配置,以滿足應(yīng)用程序的不同需求。根據(jù)位寬的不同,每個過濾器組可提供:
-
1個32位過濾器,包括:STDID[10:0]、EXTID[17:0]、IDE和RTR位
-
2個16位過濾器,包括:STDID[10:0]、IDE、RTR和EXTID[17:15]位
此外過濾器可配置為,屏蔽位模式和標識符列表模式。
在屏蔽位模式下,標識符寄存器和屏蔽寄存器一起,指定報文標識符的任何一位,應(yīng)該按照“必須匹配”或“不用關(guān)心”處理。
而在標識符列表模式下,屏蔽寄存器也被當作標識符寄存器用。因此,不是采用一個標識符加一個屏蔽位的方式,而是使用2個標識符寄存器。接收報文標識符的每一位都必須跟過濾器標識符相同。相關(guān)文章:CAN總線詳解。
一般也都是使用標識符列表模式,這里使用的也是標識符列表模式。濾波過程舉例如下:
在程序中就是:
//要過濾的ID高位CAN_FilterInitStructure.CAN_FilterIdHigh=0X00;//要過濾的ID低位CAN_FilterInitStructure.CAN_FilterIdLow= (((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF;//過濾器屏蔽標識符的高16位值CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0xFFFF;//過濾器屏蔽標識符的低16位值CAN_FilterInitStructure.CAN_FilterMaskIdLow=0xFFFF;
這里的CAN_FilterId和CAN_FilterMaskId是配合使用的,意思是CAN_FilterId指出需要屏蔽ID的什么內(nèi)容,什么格式;CAN_FilterMaskId是指CAN_FilterId的每一位是否需要過濾,若CAN_FilterMaskId在某位上是1的話,ID對應(yīng)位上的數(shù)值就必須和CAN_FilterId該位上的一樣,保持一致,反之則是“不關(guān)心”。
上述程序的設(shè)置的含義就是:只接收來自0x1314的數(shù)據(jù),屏蔽其他ID的數(shù)據(jù)。
程序思路
這里準備做一個主機與從機的通信,主要用擴展標識符ExtId來區(qū)分,分配的標識符是: 主機:0x1314 從機:0x1311
主機負責接收所有從機的數(shù)據(jù),不需要過濾,用擴展標識符ExtId來區(qū)分不同從機的數(shù)據(jù);主機還可以向不同從機發(fā)送信息。而從機則只接收來自主機的數(shù)據(jù),同樣用擴展標識符ExtId來區(qū)分是否是發(fā)向自己的數(shù)據(jù);同時,也能夠向主機發(fā)送信息。
相關(guān)代碼
代碼也是非常簡單的,這里貼出了主機和從機的can.c和can.h兩個文件。
從機相關(guān)代碼
can.c文件:
/* 在中斷處理函數(shù)中返回 *///__IO uint32_t ret = 0;//接收數(shù)據(jù)緩沖器u8 RxBuf[5];u8 Rx_flag=0;void CAN1_Init(void){GPIO_InitTypeDef GPIO_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;CAN_InitTypeDef CAN_InitStructure;CAN_FilterInitTypeDef CAN_FilterInitStructure;/* 復(fù)用功能和GPIOB端口時鐘使能*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB, ENABLE);/* CAN1 模塊時鐘使能 */RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);/* Configure CAN pin: RX */ // PB8GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉輸入GPIO_Init(GPIOB, &GPIO_InitStructure);/* Configure CAN pin: TX */ // PB9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復(fù)用推挽輸出GPIO_Init(GPIOB, &GPIO_InitStructure);//#define GPIO_Remap_CAN GPIO_Remap1_CAN1 本實驗沒有用到重映射I/OGPIO_PinRemapConfig(GPIO_Remap1_CAN1, ENABLE);//CAN_NVIC_Configuration(); //CAN中斷初始化/* Configure the NVIC Preemption Priority Bits */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);/* Set the Vector Table base location at 0x20000000 */NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);/* Set the Vector Table base location at 0x08000000 */NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);/* enabling interrupt */NVIC_InitStructure.NVIC_IRQChannel=USB_LP_CAN1_RX0_IRQn;;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);//CAN_INIT();//CA初始化N模塊/* CAN register init */CAN_DeInit(CAN1); //將外設(shè)CAN的全部寄存器重設(shè)為缺省值CAN_StructInit(&CAN_InitStructure); //把CAN_InitStruct中的每一個參數(shù)按缺省值填入/* CAN cell init */CAN_InitStructure.CAN_TTCM=DISABLE; //沒有使能時間觸發(fā)模式CAN_InitStructure.CAN_ABOM=DISABLE; //沒有使能自動離線管理CAN_InitStructure.CAN_AWUM=DISABLE; //沒有使能自動喚醒模式CAN_InitStructure.CAN_NART=DISABLE; //沒有使能非自動重傳模式CAN_InitStructure.CAN_RFLM=DISABLE; //沒有使能接收FIFO鎖定模式CAN_InitStructure.CAN_TXFP=DISABLE; //沒有使能發(fā)送FIFO優(yōu)先級CAN_InitStructure.CAN_Mode=CAN_Mode_Normal; //CAN設(shè)置為正常模式CAN_InitStructure.CAN_SJW=CAN_SJW_1tq; //重新同步跳躍寬度1個時間單位CAN_InitStructure.CAN_BS1=CAN_BS1_3tq; //時間段1為3個時間單位CAN_InitStructure.CAN_BS2=CAN_BS2_2tq; //時間段2為2個時間單位CAN_InitStructure.CAN_Prescaler=60; //時間單位長度為60CAN_Init(CAN1,&CAN_InitStructure); //波特率為:72M/2/60(1+3+2)=0.1 即波特率為100KBPs// CAN filter init 過濾器,注意,只接收主機發(fā)過來的數(shù)據(jù),屏蔽其他數(shù)據(jù)CAN_FilterInitStructure.CAN_FilterNumber=1; //指定過濾器為1CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //指定過濾器為標識符屏蔽位模式CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //過濾器位寬為32位//CAN_FilterInitStructure.CAN_FilterIdHigh= (((u32)0x1314<<3)&0xFFFF0000)>>16;CAN_FilterInitStructure.CAN_FilterIdHigh=0X00; //要過濾的ID高位CAN_FilterInitStructure.CAN_FilterIdLow= (((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF; //要過濾的ID低位CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0xFFFF; //過濾器屏蔽標識符的高16位值CAN_FilterInitStructure.CAN_FilterMaskIdLow=0xFFFF; //過濾器屏蔽標識符的低16位值CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0; //設(shè)定了指向過濾器的FIFO為0CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //使能過濾器CAN_FilterInit(&CAN_FilterInitStructure); //按上面的參數(shù)初始化過濾器/* CAN FIFO0 message pending interrupt enable */CAN_ITConfig(CAN1,CAN_IT_FMP0, ENABLE); //使能FIFO0消息掛號中斷}/* 發(fā)送兩個字節(jié)的數(shù)據(jù)*/u8 CAN_SetMsg(u8 Data1,u8 Data2){u8 mbox;u16 i=0;CanTxMsg TxMessage;TxMessage.StdId=0x0000; //標準標識符為0x00TxMessage.ExtId=0x1311; //擴展標識符0x1311,可以更改該標識符以示區(qū)分不同從機TxMessage.IDE=CAN_ID_EXT; //使用擴展標識符TxMessage.RTR=CAN_RTR_DATA; //為數(shù)據(jù)幀TxMessage.DLC=2; //消息的數(shù)據(jù)長度為2個字節(jié)TxMessage.Data[0]=Data1; //第一個字節(jié)數(shù)據(jù)TxMessage.Data[1]=Data2; //第二個字節(jié)數(shù)據(jù)//發(fā)送數(shù)據(jù)mbox= CAN_Transmit(CAN1, &TxMessage);while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++; //等待發(fā)送結(jié)束if(i>=0XFFF)return 0;return 1;}u8 CAN_GetMsg(u8 *msg1,u8 *msg2){if(Rx_flag == 1)//發(fā)現(xiàn)數(shù)據(jù){*msg1=RxBuf[0];*msg2=RxBuf[1];Rx_flag=0;//數(shù)據(jù)已經(jīng)取走,可以更新數(shù)據(jù)return 1;}elsereturn 0;}/* USB中斷和CAN接收中斷服務(wù)程序,USB跟CAN公用I/O,這里只用到CAN的中斷。*/void USB_LP_CAN1_RX0_IRQHandler(void){CanRxMsg RxMessage;RxMessage.StdId=0x00;RxMessage.ExtId=0x00;RxMessage.IDE=0;RxMessage.DLC=0;RxMessage.FMI=0;RxMessage.Data[0]=0x00;RxMessage.Data[1]=0x00;CAN_Receive(CAN1,CAN_FIFO0, &RxMessage); //接收FIFO0中的數(shù)據(jù)if(Rx_flag == 0)//數(shù)據(jù)已取走或者緩沖器為空{RxBuf[0]=RxMessage.Data[0];RxBuf[1]=RxMessage.Data[1];Rx_flag=1;//數(shù)據(jù)已經(jīng)備好,等待取走}}
can.h文件

主機相關(guān)代碼
這里主機代碼大部分是和從機類似的,就只貼出不同的地方了。 can.c文件:
/* 在中斷處理函數(shù)中返回 *///__IO uint32_t ret = 0;void CAN1_Init(void){......//以上與從機部分相同//CAN filter init 過濾器,已經(jīng)設(shè)置為任意,可以通過ExtId標識符區(qū)分從機代號CAN_FilterInitStructure.CAN_FilterNumber=1; //指定過濾器為1CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //指定過濾器為標識符屏蔽位模式CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //過濾器位寬為32位CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000; //過濾器標識符的高16位值CAN_FilterInitStructure.CAN_FilterIdLow=CAN_ID_EXT|CAN_RTR_DATA;//過濾器標識符的低16位值CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000; //過濾器屏蔽標識符的高16位值CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000; //過濾器屏蔽標識符的低16位值CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0; //設(shè)定了指向過濾器的FIFO為0CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //使能過濾器CAN_FilterInit(&CAN_FilterInitStructure); //按上面的參數(shù)初始化過濾器/* CAN FIFO0 message pending interrupt enable */CAN_ITConfig(CAN1,CAN_IT_FMP0, ENABLE); //使能FIFO0消息掛號中斷}//接收數(shù)據(jù)緩沖器u8 CAN_RX_BUF[CAN_RX_LEN]={0}; //接收緩沖,最大USART_REC_LEN個字節(jié).//接收標志位u8 Rx_flag=0;/* USB中斷和CAN接收中斷服務(wù)程序,USB跟CAN公用I/O,這里只用到CAN的中斷。*/void USB_LP_CAN1_RX0_IRQHandler(void){u8 i=0;CanRxMsg RxMessage;RxMessage.StdId=0x00;RxMessage.ExtId=0x00;RxMessage.IDE=0;RxMessage.DLC=0;RxMessage.FMI=0;CAN_Receive(CAN1,CAN_FIFO0, &RxMessage); //接收FIFO0中的數(shù)據(jù)if(Rx_flag == 0)//數(shù)據(jù)已取走或者緩沖器為空{if((RxMessage.DLC) == 2)//是否收到2位字節(jié)數(shù)據(jù){CAN_RX_BUF[0]=RxMessage.Data[0];CAN_RX_BUF[1]=RxMessage.Data[1];}}}/* 發(fā)送兩個字節(jié)的數(shù)據(jù)*/u8 CAN_SendMsg(u8* data1, u8* data2){u8 mbox;u16 i=0;CanTxMsg TxMessage;TxMessage.StdId=0x0000; //標準標識符為0x00TxMessage.ExtId=0x1314; //擴展標識符0x0000TxMessage.IDE=CAN_ID_EXT; //使用擴展標識符TxMessage.RTR=CAN_RTR_DATA; //為數(shù)據(jù)幀TxMessage.DLC=2; //消息的數(shù)據(jù)長度為2個字節(jié)TxMessage.Data[0]=Data1; //第一個字節(jié)數(shù)據(jù)TxMessage.Data[1]=Data2; //第二個字節(jié)數(shù)據(jù)//發(fā)送數(shù)據(jù)mbox= CAN_Transmit(CAN1, &TxMessage);while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++; //等待發(fā)送結(jié)束if(i>=0XFFF)return 0;//發(fā)送失敗return 1;//發(fā)送成功}u8 CAN_GetMsg(u8 *msg1,u8 *msg2){if(Rx_flag == 1)//發(fā)現(xiàn)數(shù)據(jù){*msg1=CAN_RX_BUF[0];*msg2=CAN_RX_BUF[1];Rx_flag=0;//數(shù)據(jù)已經(jīng)取走,可以更新數(shù)據(jù)return 1;}elsereturn 0;}void Clear_canBuffer(void){Rx_flag=0;//清楚接收標志位memset(CAN_RX_BUF, 0, sizeof(u8)*CAN_RX_LEN);//清空緩沖區(qū)}u8 Check_canRX(void){return (Rx_flag == 6);}
can.h文件:
extern u8 CAN_RX_BUF[CAN_RX_LEN]; //接收緩沖,最大USART_REC_LEN個字節(jié).末字節(jié)為換行符void CAN1_Init(void);u8 CAN_SendMsg(u8* data1, u8* data2);u8 CAN_GetMsg(u8 *msg1,u8 *msg2);
文章出處:【微信公眾號:STM32嵌入式開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
-
CAN
+關(guān)注
關(guān)注
59文章
3067瀏覽量
472754 -
STM32
+關(guān)注
關(guān)注
2309文章
11162瀏覽量
373462
原文標題:使用STM32F103做CAN的收發(fā)通信
文章出處:【微信號:c-stm32,微信公眾號:STM32嵌入式開發(fā)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
CW32L012與STM32F103的三角運算性能對比
【新品上市】華清遠見AIoT實戰(zhàn)平臺-STM32F103&amp;ESP32-S3 AI開發(fā)板套件,玩轉(zhuǎn)小智AI桌寵機器狗智能車等項目
MH32F103A系列單片機的功能特性
CherryUSB +STM32F103C8 example usb cdc device不識別是怎么回事?
STM32F103長時間運行,串口發(fā)送失敗怎么解決?
在STM32F103c8上做DLQR最優(yōu)控制算法的C編程資料
STM32F103驅(qū)動LD3320語音識別模塊
【硬核項目】STM32F103 智能小車全棧開發(fā):紅外循跡 / 避障算法 + WiFi 遠程控制,附原理圖與代碼
使用STM32F103做CAN的收發(fā)通信
評論