(一) 工業現場總線 CAN 的基本介紹以及 STM32 的 CAN 模塊簡介
首先通讀手冊中關於CAN的文檔,必須精讀。
STM32F10xxx 參考手冊Rev7V3.pdf
http://www.mystm32.com/bbs/redirect.php?tid=255&goto=lastpost#lastpost
需要精讀的部分爲 RCC 和 CAN 兩個章節。
爲什麼需要精讀 RCC 呢?因爲我們將學習 CAN 的波特率的設置,將要使用到 RCC 部分的設置,因此推薦大家先複習下這部分中的幾個時鐘。
關於 STM32 的 can 總線簡單介紹
bxCAN 是基本擴展 CAN (Basic Extended CAN) 的縮寫,它支持 CAN 協議 2.0A 和 2.0B 。它的設計目標是,以最小的 CPU 負荷來高效處理大量收到的報文。它也支持報文發送的優先級要求(優先級特性可軟件配置)。
對於安全緊要的應用,bxCAN 提供所有支持時間觸發通信模式所需的硬件功能。
主要特點
· 支持 CAN 協議 2.0A 和 2.0B 主動模式
· 波特率最高可達 1 兆位 / 秒
· 支持時間觸發通信功能
發送
· 3 個發送郵箱
· 發送報文的優先級特性可軟件配置
· 記錄發送 SOF 時刻的時間戳
接收
· 3 級深度的2個接收 FIFO
· 14 個位寬可變的過濾器組 - 由整個 CAN 共享
· 標識符列表
· FIFO 溢出處理方式可配置
· 記錄接收 SOF 時刻的時間戳
可支持時間觸發通信模式
· 禁止自動重傳模式
· 16 位自由運行定時器
· 定時器分辨率可配置
· 可在最後 2 個數據字節發送時間戳
管理
· 中斷可屏蔽
· 郵箱佔用單獨 1 塊地址空間,便於提高軟件效率
(二) STM32 CAN 模塊工作模式
STM32 的 can 的工作模式分爲:
/* CAN operating mode */
#define CAN_Mode_Normal ((u8)0x00) /* normal mode */
#define CAN_Mode_LoopBack ((u8)0x01) /* loopback mode */
#define CAN_Mode_Silent ((u8)0x02) /* silent mode */
#define CAN_Mode_Silent_LoopBack ((u8)0x03) /* loopback combined with silent mode */
在此章我們的 Mini-STM32 教程中我們將使用到 CAN_Mode_LoopBack 和 CAN_Mode_Normal 兩種模式。
我們第一步做的就是使用運行在 CAN_Mode_LoopBack 下進行自測試。
在參考手冊中 CAN_Mode_LoopBack (環回模式) 的定義如下:
環回模式可用於自測試。爲了避免外部的影響,在環回模式下 CAN 內核忽略確認錯誤 (在數據 / 遠程幀的確認位時刻,不檢測是否有顯性位) 。在環回模式下,bxCAN 在內部把 Tx 輸出回饋到 Rx 輸入上,而完全忽略 CANRX 引腳的實際狀態。發送的報文可以在 CANTX 引腳上檢測到。
因此這種模式也特別適合大家做好硬件後自測程序。
(三) CAN 接口端口映射
STM32 中的 CAN 物理引腳腳位可以設置成三種:
默認模式,重定義地址1模式,重定義地址2模式
。
在我們的 Mini-STM32 上面沒有接出 CAN 的接口芯片, 所以我們可以利用
RealView MDK
的 CAN 軟件
模擬
模塊來做實驗.
-------------------------------------------------------------------------
默認模式
/* Configure CAN pin: RX */
GPIO
_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Configure CAN pin: TX */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
------------------------------------------------------------------------
重定義地址1模式
/* Configure CAN pin: RX */
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
//GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Configure CAN pin: TX */
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
//GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Configure CAN Remap 重影射 */
//GPIO_PinRemapConfig(GPIO_Remap1_CAN, ENABLE);
-------------------------------------------------------------------------
重定義地址2模式
/* Configure CAN pin: RX */
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
//GPIO_Init(GPIOD, &GPIO_InitStructure);
/* Configure CAN pin: TX */
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
//GPIO_Init(GPIOD, &GPIO_InitStructure);
/* Configure CAN Remap 重影射 */
//GPIO_PinRemapConfig(GPIO_Remap2_CAN, ENABLE);
-------------------------------------------------------------------------
設置完 CAN 的引腳之後還需要打開 CAN 的時鐘:
/* CAN Periph clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN, ENABLE);
(四) CAN 波特率設置
4、我們需要搞明白CAN波特率的設置,這個章節也是使用CAN的最重要的部分之一,因爲這實際應用中我們需要根據我們實際的場合來選擇 CAN 的波特率。
一般情況下面1M bps 的速率下可以最高可靠傳輸 40 米以內的距離。
在 50K 以下的波特率中一般可以可靠傳輸數公里遠。
對於波特率的設置需要詳細學習參考手冊對應部分的解釋。我們在調試軟件的時候可以使用示波器來測試 CANTX 引腳上的波形的波特率,這樣可以得到事半功倍的效果,大大的縮短調試學習的時間。
// ***************************************************************
// BaudRate = 1 / NominalBitTime
// NominalBitTime = 1tq + tBS1 + tBS2
// tq = (BRP[9:0] + 1) x tPCLK
// tPCLK = CAN's clock = APB1's clock
// ****************************************************************
也就是BaudRate = APB1 / ((BS1 + BS2 + 1) * Prescaler)
這裏注意的是採用點的位置,也就時BS1,BS2的設置問題,這裏我也找了一些資料,抄錄下來給大家,是 CANopen 協議中推薦的設置。
1Mbps 速率下,採用點的位置在6tq位置處,BS1=5, BS2=2
500kbps 速率下,採用點的位置在8tq位置處,BS1=7, BS2=3
250kbps 速率下,採用點的位置在14tq位置處,BS1=13, BS2=2
125k, 100k, 50k, 20k, 10k 的採用點位置與 250K 相同。
因此我們需要重視的有軟件中的這麼幾個部分:
// 設置 AHB 時鐘(HCLK)
// RCC_SYSCLK_Div1 AHB 時鐘 = 系統時鐘
RCC_HCLKConfig(RCC_SYSCLK_Div8);
// 設置低速 AHB 時鐘(PCLK1)
// RCC_HCLK_Div2 APB1 時鐘 = HCLK / 2
RCC_PCLK1Config(RCC_HCLK_Div2);
// PLLCLK = 8MHz * 8 = 64 MHz
// 設置 PLL 時鐘源及倍頻係數
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_8);
CAN 波特率設置中需要的就是PCLK1 的時鐘。
CAN_InitStructure.CAN_Mode=CAN_Mode_LoopBack;
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;
CAN_InitStructure.CAN_BS1=CAN_BS1_8tq;
CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;
CAN_InitStructure.CAN_Prescaler=5;
通過上面部分的時鐘設置我們已經可以算出我們的波特率了
CAN_bps = PCLK1 / ((1 + 7 + 8) * 5) = 25K bps
大家也可以實際測試中修改時鐘值來通過示波器測試我們需要的波特率是否正確例如將PLLCLK 設置降低一半:
// PLLCLK = 8MHz * 4 = 32 MHz
// 設置 PLL 時鐘源及倍頻係數
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_4);
那麼我們得到的CAN_bps也會降低一半。
接下來還可以修改 HCLK 和 PCLK1 ,其實最終這幾個分頻和倍頻值最終影響的都是 PCLK1。
通過幾次試驗,相信大家應該很容易掌握波特率的設置了。
設置完波特率我們直接測試函數:
/* CAN transmit at 100Kb/s and receive by polling in loopback mode*/
TestRx = CAN_Polling();
if (TestRx == FAILED)
{
/* Turn on led connected to PA.00 pin (LD1) */
GPIO_SetBits(GPIOA, GPIO_Pin_0);
}
else
{
/* Turn off led connected to PA.00 pin (LD1) */
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
}
/* CAN transmit at 500Kb/s and receive by interrupt in loopback mode*/
TestRx = CAN_Interrupt();
if (TestRx == FAILED)
{
/* Turn on led connected to PA.01 pin (LD2) */
GPIO_SetBits(GPIOA, GPIO_Pin_1);
}
else
{
/* Turn off led connected to PA.01 pin (LD2) */
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
將
CAN 軟件仿真模擬器
調用出來.
大家可以仿真程序,當程序中 Test 等於 Passed 那麼說明 Loopback 模式測試通過了。
並且在 CAN 通訊框中我們可以看到發送和接收到的數據:
回循模式下的源代碼, 基於 MDK3.5:
Example7.1-CAN LoopBack Mode.rar
(493.79 KB)
閱讀權限: 10
到此時說明如果大家只有一塊CAN模塊的時候學習可以告一個段落了,不過這個並不代表大家就已經掌握了 CAN 了,正真要掌握它,大家還是需要看大量的 CAN 部分的資料,參考手冊部分的也是不夠的,市面上有幾本專門介紹現場總線和CAN總線的書,推薦大家買來經常翻翻看看,這樣到需要實際應用的時候纔可以做到 如魚得水。
(五) 正常模式
完成了 loopback 模式的測試之後接下來我們需要學習的就是多機通訊了,當然由於我們的 Mini-STM32 沒有將 CAN 接口引出來, 所以我們沒有辦法在板子上面做這部分的試驗了,只能在 RealView MDK 的軟件中進行模擬。
如果您擁有兩塊帶 CAN 硬件的 STM32 的板子,您需要自己構建硬件的物理層的連接, 使用三根線將 CANH,CANL,GND 三根線直連,當然你要接好終端電阻才能保證通訊的正常通訊,當兩塊板子都跳好後我們使用萬用表測量下 CANH和CANL之間的電阻是否爲 60 歐姆。多塊板子多機通訊的是否你只需要在總線的主機端和最後一端接上終端電阻就可以了.
在初始化完成後,軟件應該讓硬件進入正常模式,以便正常接收和發送報文。軟件可以通過對 CAN_MCR 寄存器的INRQ位清 '0',來請求從初始化模式進入正常模式,然後要等待硬件對 CAN_MSR 寄存器的 INAK 位置 '1' 的確認。在跟 CAN 總線取得同步,即在 CANRX 引腳上監測到 11 個連續的隱性位 (等效於總線空閒) 後,bxCAN 才能正常接收和發送報文。
不需要在初始化模式下進行過濾器初值的設置,但必須在它處在非激活狀態下完成 (相應的 FACT 位爲 '0' ) 。而過濾器的位寬和模式的設置,則必須在初始化模式中進入正常模式前完成。
準備工作做完我們需要設置 CAN 通訊部份軟件。
我們把 TestStatus CAN_Polling(void) 函數和 TestStatus CAN_Interrupt(void) 函數中的 LoopBack 模式修改爲 Normal 模式.
//CAN_InitStructure.CAN_Mode=CAN_Mode_LoopBack;
CAN_InitStructure.CAN_Mode=CAN_Mode_Normal;
接下來我們就可以做實驗了. 但是由於 RealView MDK 的 CAN 模塊沒有辦法接收, 所以我們只做發送的例子.
我們的例子中分別發送兩幀數據:
(1) TestStatus CAN_Polling(void) 查詢發送
第一幀數據爲: ID 爲 0x11, 數據爲 8 個字節的一個數據包.
TxMessage.StdId=0x11;
TxMessage.RTR=CAN_RTR_DATA;
TxMessage.IDE=CAN_ID_STD;
TxMessage.DLC=8;
TxMessage.Data[0]=0x01;
TxMessage.Data[1]=0x02;
TxMessage.Data[2]=0x03;
TxMessage.Data[3]=0x04;
TxMessage.Data[4]=0x05;
TxMessage.Data[5]=0x06;
TxMessage.Data[6]=0x07;
TxMessage.Data[7]=0x08;
(2) TestStatus CAN_Interrupt(void) 中斷髮送
第二幀數據爲:ID 爲 0x1234, 數據爲 8 個字節的一個數據包.
TxMessage.StdId=0x12;
TxMessage.ExtId=0x34;
TxMessage.IDE=CAN_ID_EXT;
TxMessage.RTR=CAN_RTR_DATA;
TxMessage.DLC=8;
TxMessage.Data[0]=0x11;
TxMessage.Data[1]=0x22;
TxMessage.Data[2]=0x33;
TxMessage.Data[3]=0x44;
TxMessage.Data[4]=0x55;
TxMessage.Data[5]=0x66;
TxMessage.Data[6]=0x77;
TxMessage.Data[7]=0x88;
CAN_Transmit(&TxMessage);
在主函數中初始化之後加上這兩句發送函數:
/* CAN transmit at 100Kb/s and receive by polling in Normal mode*/
CAN_Polling();
while(i++ < 1000);
/* CAN transmit at 500Kb/s and receive by interrupt in Normal mode*/
CAN_Interrupt();
程序改完了, 我們需要編譯通過後, 點軟件仿真.
將
CAN 軟件仿真模擬器
調用出來.
接下來我們全速運行到 while(1) 就可以看到結果了.