首先開啟STM32CubeMX 選擇晶片型號建立工程,我這裡選擇STM32F03ZET6(正點原子戰艦V3) 具體新建工程的過程不再贅述,太基礎。
首先設定System Core目錄下的SYS和RCC索引標籤如下圖:
(1)設定SYS下的Debug為Serial Wire
(2)設定RCC下的HSE(高速時鐘)為外部晶振Crystal/Ceramic Resonator,具體根據板子的硬體設定設定
(3)使能CAN匯流排並設定相關引數
在此說明CAN匯流排相關引數的設定方法,在F103ZET6中CAN是掛載在APB1下的低速外設,時鐘36MHz,引數中的Prescaler(for Time Quantum)是指預分頻係數,跟CAN匯流排的波特率相關,我這裡設定為36分頻,如此一來時鐘就是1MHz,每一個週期為1us(即後面引數的1個Time是1us)方便計算,Time Quanta in Bit Segment 1和Time Quanta in Bit Segment 2 也是影響波特率的兩個引數,具體計算如下:
Bound=1/(1*Tq+Tqbs1+Tqbs2)
其中Tqbs1代表的就是Time Quanta in Bit Segment 1所對應的時間,Tqbs2代表的就是Time Quanta in Bit Segment 2所對應的時間,Tq代表的是1個Time的時間。
所以以我的設定舉例:1/(1us+4us+5us)=100K,這就是我的波特率。
另外順帶提一下因為我只有一塊戰艦V3,MIni板上的CAN沒有引出,所以我設定Advenced Parameter下的Operating Mode 為Loopback,即迴環模式(自發自收)。
(4)串列埠設定(用於上位機顯示收發資料方便看到效果)
設定串列埠模式為Asynchronous,硬體流控制Disable就可以,波特率字寬等引數預設即可,另外開啟NVIC Setting下面的 USART1 global interruput 方便接收來自上位機的資料:
既然講到串列埠就順便提一下DMA吧,開啟DMA可以大大節約CPU資源,減輕CPU負擔。在DMA Setting索引標籤下Add兩個DMA通道(USART1_RX和USART1_TX),Mode暫都設定為Normal模式,當然也可以設定為Circle模式,那樣DMA就會不停的持續瘋狂往外發,在本測試中是不需要的。Data Width預設Byte.如下圖:
至此串列埠和CAN 匯流排的設定已經完畢。
(5)時鐘樹設定
上圖就行了
(6)設定專案名稱和檔案路徑和IDE
IDE根據自己使用的開發工具選擇,我這裡使用Keil V5,勾選Copy only the necessry library files,這樣生成的工程內只包含設定用到的相關庫檔案沒有冗餘。勾選Generate peripheral initialization as a pair of '.c/.h’file per peripheral,此選項的作用是會把每一個外設的相關設定程式碼分別生成不同的原始檔和標頭檔案,工程更加清潔。
(7)生成程式碼
直接GENERATE CODE 即可
生成完成後可以開啟資料夾看到生成的檔案目錄,或者直接開啟工程都可以。
(1)增加部分CAN匯流排的收發函數和過濾函數
開啟工程目錄下的Application/User下的can.c檔案, 在最後的
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
之間增加自己的程式碼, 否則如果STM32CubeMX 設定出錯需要修改設定重新生成程式碼的時候,就會覆蓋掉這部分程式碼,相當於白寫,所以一定要寫在這兩個註釋中間切記 !!
這裡我把我的程式碼貼出來供大家使用, 發現bug請告訴我謝謝 !
void CAN_FilterInit(CAN_HandleTypeDef *hcan)
{
CAN_FilterTypeDef CAN_FilterInitStructure;
CAN_FilterInitStructure.FilterMode = CAN_FILTERMODE_IDMASK;
CAN_FilterInitStructure.FilterScale = CAN_FILTERSCALE_32BIT;
CAN_FilterInitStructure.FilterIdHigh = 0x2460;
CAN_FilterInitStructure.FilterIdLow = 0x0000;
CAN_FilterInitStructure.FilterMaskIdHigh = 0xF0E0;
CAN_FilterInitStructure.FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.FilterBank = 0;
CAN_FilterInitStructure.FilterFIFOAssignment = CAN_RX_FIFO0;
CAN_FilterInitStructure.FilterActivation = ENABLE;
CAN_FilterInitStructure.SlaveStartFilterBank = 14;
if(HAL_CAN_ConfigFilter(hcan,&CAN_FilterInitStructure) != HAL_OK)
{
Error_Handler();
}
if(HAL_CAN_ActivateNotification(hcan,CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
{
Error_Handler();
}
if(HAL_CAN_Start(hcan) != HAL_OK)
{
Error_Handler();
}
}
並且記得在can.h中申明一下.
(2) CAN 匯流排的收發函數串列埠的接收傳送完成回撥函數我直接放在main.c裡面了,這裡也貼出來給大家參考(也要記得在main.c靠前的部分申明一下):
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)//CAN Receive
{
CAN_RxHeaderTypeDef RX_Header;
HAL_CAN_GetRxMessage(hcan,CAN_RX_FIFO0,&RX_Header,RX_Data);
HAL_CAN_ActivateNotification(hcan,CAN_IT_RX_FIFO0_MSG_PENDING);
}
void CAN_Send_Msg(CAN_HandleTypeDef *hcan,uint8_t *msg,uint8_t len,uint32_t id)//CAN Transmit
{
CAN_TxHeaderTypeDef Tx_Header;
uint32_t TxMailBox;
Tx_Header.StdId = id;
Tx_Header.IDE = CAN_ID_STD;
Tx_Header.RTR = CAN_RTR_DATA;
Tx_Header.DLC = len;
HAL_CAN_AddTxMessage(hcan,&Tx_Header,msg,&TxMailBox);
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart == &huart1)
{
HAL_UART_Receive_DMA(&huart1,(uint8_t*)TX_Data,sizeof(TX_Data));
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart == &huart1)
{
CAN_Send_Msg(&hcan,TX_Data,sizeof(TX_Data),0x123);
HAL_UART_Transmit_DMA(&huart1,(uint8_t*)RX_Data,sizeof(RX_Data));
}
}
(3)此外記得在Main函數的開頭處開啟串列埠DMA接收和傳送,並定義相關變數
uint8_t RX_Data[8];
uint8_t TX_Data[8];
CAN_TxHeaderTypeDef TxHeader;
CAN_FilterInit(&hcan);
HAL_UART_Transmit_DMA(&huart1,(uint8_t*)RX_Data,sizeof(RX_Data));
HAL_UART_Receive_DMA(&huart1,(uint8_t*)TX_Data,sizeof(TX_Data));
注意:此外在程式中用到的sprintf等函數要包含stdio標頭檔案.
此測試實現的功能是:接收來自串列埠的資料,然後通過CAN匯流排傳送出去,因為設定了迴環模式,會收到一模一樣的CAN資料輸入,接收到資料後再通過串列埠DMA方式發出去上位機顯示,類似串列埠的迴音實驗,只不過中間又串了一個CAN線,哈哈哈有點繞我也是夠了…
編譯下載後效果如圖
文章到這裡就結束了,謝謝大家