STM32F407IGHX與Ubuntu20.04虛擬串口通信
為了讓RobomasterC板(這塊板用的是STM32F407IGHX的芯片)能與上位機進行通訊,我最近翻了不少博客和CSDN文章,看到了很多文章存在一些問題,經過了一下午試錯,我成功實作了STM32F407IGHX利用STM32CubeIDE進行配置并然后用HAL庫進行編程,與安裝有ROS的Ubuntu進行虛擬串口通信,
在翻看博客的時候我發現,RM以及上下位機通信資料并不多,而且很多已有資料都只講述了實作原理,卻沒有講如何具體一步步實作某個功能,這就導致初學者可能在翻看程序中,越看越懵,反而寫不出一份能用的代碼,
所以這篇文章會盡可能詳細的講怎么實作串口通信,而盡量少講其原理,由于很多文章都已經詳盡的寫出了串口通信的原理了,所以我就不在贅述原理而著重于實作程序,
此外,我也會把一些小問題和建議寫出來,以便一篇文章就解決所有可能存在的問題,
一、概述
1、STM32端(所謂的下位機):這邊采用的是通過有圖形化的STM32CubeIDE配置工程,配置好USB-CDC創建一個虛擬串口,與上位機通信,
2、Ubuntu端(所謂的上位機):上位機是版本20.04的ubuntu,安裝有版本為noetic的ROS,通過建立一個ROS節點來打開串口并建立通信,
二、STM32端具體實作程序
思路:利用STM32CubeIDE配置好USB-CDC,接著修改對應的頭檔案,自定義所需的函式,
1、配置程序
1)先配置時鐘RCC,設定高速時鐘High Speed Clock為內部時鐘(Crystal/Ceramic Resonator),另一個暫時用不到所以不設定,

2)配置下載與除錯(必須設定,否則會鎖芯片,到時候還需要通過BOOT重啟,比較麻煩)
設定為Serial Wire,時鐘為SysTick(當然看你到底有什么,如果你擁有的是ST-LINK,那么可以這樣設定)

3)設定USB模式,打開Connectivity,選擇USB-OYG-FS(快速),選擇Mode的Device_only(從機模式),然后點開左下方的NVIC Settings,勾選Enabled,從而能夠開啟中斷,



備注:還要回傳到NVIC中,設定USB中斷的優先級,這里設定個4就行(畢竟沒有啟動其他外設,所以中斷就不需要太嚴謹)、
4)打開MiddleWare,設定USB的具體作業方式,選擇Class For FS IP的Communication Device Class,即VCP(虛擬串口),其余設定保持默認即可,不需要額外修改,

5)時鐘樹設定(時鐘樹的設定,需要查閱所使用開發板的具體原理圖)
例如,RobomasterC板原理圖里是如此說明的,所以Input frequency要設定成12MHz,此外,下方畫紅線部分是USB的時鐘,USB的時鐘需要設定成48MHz才能作業,其余部分看自己的需求,


6)堆疊設定,堆疊的大小需要足夠大,才能滿足USB初始化的需求,此處設定Heap Size為0X600即可解決初始化失敗的問題,另一個不用改,

7)到此,所有的初始化已經結束了,只需要Ctrl+s,保存并生成代碼即可,下方兩個選項均選擇Yes,即可生成STM32CubeIDE工程


2、代碼的修改
這里要先打開工程里的USB_DEVICE中的App的usbd_cdc_if.c,重構官方給出的代碼,具體內容如下

1 /* USER CODE BEGIN Header */ 2 /** 3 ****************************************************************************** 4 * @file : usbd_cdc_if.c 5 * @version : v1.0_Cube 6 * @brief : Usb device for Virtual Com Port. 7 ****************************************************************************** 8 * @attention 9 * 10 * Copyright (c) 2023 STMicroelectronics. 11 * All rights reserved. 12 * 13 * This software is licensed under terms that can be found in the LICENSE file 14 * in the root directory of this software component. 15 * If no LICENSE file comes with this software, it is provided AS-IS. 16 * 17 ****************************************************************************** 18 */ 19 /* USER CODE END Header */ 20 21 /* Includes ------------------------------------------------------------------*/ 22 #include "usbd_cdc_if.h" 23 24 /* USER CODE BEGIN INCLUDE */ 25 26 /* USER CODE END INCLUDE */ 27 28 /* Private typedef -----------------------------------------------------------*/ 29 /* Private define ------------------------------------------------------------*/ 30 /* Private macro -------------------------------------------------------------*/ 31 32 /* USER CODE BEGIN PV */ 33 /* Private variables ---------------------------------------------------------*/ 34 35 /* USER CODE END PV */ 36 37 /** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY 38 * @brief Usb device library. 39 * @{ 40 */ 41 42 /** @addtogroup USBD_CDC_IF 43 * @{ 44 */ 45 46 /** @defgroup USBD_CDC_IF_Private_TypesDefinitions USBD_CDC_IF_Private_TypesDefinitions 47 * @brief Private types. 48 * @{ 49 */ 50 51 /* USER CODE BEGIN PRIVATE_TYPES */ 52 53 /* USER CODE END PRIVATE_TYPES */ 54 55 /** 56 * @} 57 */ 58 59 /** @defgroup USBD_CDC_IF_Private_Defines USBD_CDC_IF_Private_Defines 60 * @brief Private defines. 61 * @{ 62 */ 63 64 /* USER CODE BEGIN PRIVATE_DEFINES */ 65 /* USER CODE END PRIVATE_DEFINES */ 66 67 /** 68 * @} 69 */ 70 71 /** @defgroup USBD_CDC_IF_Private_Macros USBD_CDC_IF_Private_Macros 72 * @brief Private macros. 73 * @{ 74 */ 75 76 /* USER CODE BEGIN PRIVATE_MACRO */ 77 78 /* USER CODE END PRIVATE_MACRO */ 79 80 /** 81 * @} 82 */ 83 84 /** @defgroup USBD_CDC_IF_Private_Variables USBD_CDC_IF_Private_Variables 85 * @brief Private variables. 86 * @{ 87 */ 88 /* Create buffer for reception and transmission */ 89 /* It's up to user to redefine and/or remove those define */ 90 /** Received data over USB are stored in this buffer */ 91 uint8_t UserRxBufferFS[APP_RX_DATA_SIZE]; 92 93 /** Data to send over USB CDC are stored in this buffer */ 94 uint8_t UserTxBufferFS[APP_TX_DATA_SIZE]; 95 96 /* USER CODE BEGIN PRIVATE_VARIABLES */ 97 98 /* USER CODE END PRIVATE_VARIABLES */ 99 100 /** 101 * @} 102 */ 103 104 /** @defgroup USBD_CDC_IF_Exported_Variables USBD_CDC_IF_Exported_Variables 105 * @brief Public variables. 106 * @{ 107 */ 108 109 extern USBD_HandleTypeDef hUsbDeviceFS; 110 111 /* USER CODE BEGIN EXPORTED_VARIABLES */ 112 113 /* USER CODE END EXPORTED_VARIABLES */ 114 115 /** 116 * @} 117 */ 118 119 /** @defgroup USBD_CDC_IF_Private_FunctionPrototypes USBD_CDC_IF_Private_FunctionPrototypes 120 * @brief Private functions declaration. 121 * @{ 122 */ 123 124 static int8_t CDC_Init_FS(void); 125 static int8_t CDC_DeInit_FS(void); 126 static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length); 127 static int8_t CDC_Receive_FS(uint8_t* pbuf, uint32_t *Len); 128 static int8_t CDC_TransmitCplt_FS(uint8_t *pbuf, uint32_t *Len, uint8_t epnum); 129 130 /* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */ 131 132 /* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */ 133 134 /** 135 * @} 136 */ 137 138 USBD_CDC_ItfTypeDef USBD_Interface_fops_FS = 139 { 140 CDC_Init_FS, 141 CDC_DeInit_FS, 142 CDC_Control_FS, 143 CDC_Receive_FS, 144 CDC_TransmitCplt_FS 145 }; 146 147 /* Private functions ---------------------------------------------------------*/ 148 /** 149 * @brief Initializes the CDC media low layer over the FS USB IP 150 * @retval USBD_OK if all operations are OK else USBD_FAIL 151 */ 152 static int8_t CDC_Init_FS(void) 153 { 154 /* USER CODE BEGIN 3 */ 155 /* Set Application Buffers */ 156 USBD_CDC_SetTxBuffer(&hUsbDeviceFS, UserTxBufferFS, 0); 157 USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS); 158 return (USBD_OK); 159 /* USER CODE END 3 */ 160 } 161 162 /** 163 * @brief DeInitializes the CDC media low layer 164 * @retval USBD_OK if all operations are OK else USBD_FAIL 165 */ 166 static int8_t CDC_DeInit_FS(void) 167 { 168 /* USER CODE BEGIN 4 */ 169 return (USBD_OK); 170 /* USER CODE END 4 */ 171 } 172 173 /** 174 * @brief Manage the CDC class requests 175 * @param cmd: Command code 176 * @param pbuf: Buffer containing command data (request parameters) 177 * @param length: Number of data to be sent (in bytes) 178 * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL 179 */ 180 static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length) 181 { 182 /* USER CODE BEGIN 5 */ 183 switch(cmd) 184 { 185 case CDC_SEND_ENCAPSULATED_COMMAND: 186 187 break; 188 189 case CDC_GET_ENCAPSULATED_RESPONSE: 190 191 break; 192 193 case CDC_SET_COMM_FEATURE: 194 195 break; 196 197 case CDC_GET_COMM_FEATURE: 198 199 break; 200 201 case CDC_CLEAR_COMM_FEATURE: 202 203 break; 204 205 /*******************************************************************************/ 206 /* Line Coding Structure */ 207 /*-----------------------------------------------------------------------------*/ 208 /* Offset | Field | Size | Value | Description */ 209 /* 0 | dwDTERate | 4 | Number |Data terminal rate, in bits per second*/ 210 /* 4 | bCharFormat | 1 | Number | Stop bits */ 211 /* 0 - 1 Stop bit */ 212 /* 1 - 1.5 Stop bits */ 213 /* 2 - 2 Stop bits */ 214 /* 5 | bParityType | 1 | Number | Parity */ 215 /* 0 - None */ 216 /* 1 - Odd */ 217 /* 2 - Even */ 218 /* 3 - Mark */ 219 /* 4 - Space */ 220 /* 6 | bDataBits | 1 | Number Data bits (5, 6, 7, 8 or 16). */ 221 /*******************************************************************************/ 222 case CDC_SET_LINE_CODING: 223 224 break; 225 226 case CDC_GET_LINE_CODING: 227 228 break; 229 230 case CDC_SET_CONTROL_LINE_STATE: 231 232 break; 233 234 case CDC_SEND_BREAK: 235 236 break; 237 238 default: 239 break; 240 } 241 242 return (USBD_OK); 243 /* USER CODE END 5 */ 244 } 245 246 /** 247 * @brief Data received over USB OUT endpoint are sent over CDC interface 248 * through this function. 249 * 250 * @note 251 * This function will issue a NAK packet on any OUT packet received on 252 * USB endpoint until exiting this function. If you exit this function 253 * before transfer is complete on CDC interface (ie. using DMA controller) 254 * it will result in receiving more data while previous ones are still 255 * not sent. 256 * 257 * @param Buf: Buffer of data to be received 258 * @param Len: Number of data received (in bytes) 259 * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL 260 */ 261 static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len) 262 { 263 /* USER CODE BEGIN 6 */ 264 USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]); 265 USBD_CDC_ReceivePacket(&hUsbDeviceFS); 266 return (USBD_OK); 267 /* USER CODE END 6 */ 268 } 269 270 /** 271 * @brief CDC_Transmit_FS 272 * Data to send over USB IN endpoint are sent over CDC interface 273 * through this function. 274 * @note 275 * 276 * 277 * @param Buf: Buffer of data to be sent 278 * @param Len: Number of data to be sent (in bytes) 279 * @retval USBD_OK if all operations are OK else USBD_FAIL or USBD_BUSY 280 */ 281 uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len) 282 { 283 uint8_t result = USBD_OK; 284 /* USER CODE BEGIN 7 */ 285 USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData; 286 if (hcdc->TxState != 0){ 287 return USBD_BUSY; 288 } 289 USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len); 290 result = USBD_CDC_TransmitPacket(&hUsbDeviceFS); 291 /* USER CODE END 7 */ 292 return result; 293 } 294 295 /** 296 * @brief CDC_TransmitCplt_FS 297 * Data transmitted callback 298 * 299 * @note 300 * This function is IN transfer complete callback used to inform user that 301 * the submitted Data is successfully sent over USB. 302 * 303 * @param Buf: Buffer of data to be received 304 * @param Len: Number of data received (in bytes) 305 * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL 306 */ 307 static int8_t CDC_TransmitCplt_FS(uint8_t *Buf, uint32_t *Len, uint8_t epnum) 308 { 309 uint8_t result = USBD_OK; 310 /* USER CODE BEGIN 13 */ 311 if(flag) 312 { 313 CDC_Transmit_FS(UserTxBufferFS, APP_TX_DATA_SIZE); 314 } 315 316 UNUSED(Buf); 317 UNUSED(Len); 318 UNUSED(epnum); 319 /* USER CODE END 13 */ 320 return result; 321 } 322 323 /* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */ 324 325 /* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */ 326 327 /** 328 * @} 329 */ 330 331 /** 332 * @} 333 */usbd_cdc_if.c
注意,不要輕易重新初始化代碼,否則這些對官方代碼的修改會被重新覆寫,導致又要再改一遍,最好一次就初始化好,
3、自定義結構體
在這里我不會給出具體的代碼,但我會舉個例子來說明如何定義所需結構體,
1 typedef struct ControlData_Chassis _Controldata_Chassis;//這里是定義該結構體的別名 2 typedef struct ControlData_Chassis{ 3 uint8_t Y_Speed; //縱軸方向速度 4 uint8_t X_Speed; //橫軸方向速度 5 uint8_t Rotational_Speed; //小車旋轉速度 6 uint8_t Chassis_State; //底盤狀態 7 }*_Controldata_ChassisInfo;//這里定義了該結構體的結構體指標,C語言允許這樣的操作!
在實際操作的時候,可以把這種結構體變數的數值放入到指定的陣列中(這也就是所謂的打包,而把接收到的陣列中的資料按結構體成員形式放入到指定結構體的程序,就稱之為解包,),從而實作打包,
此外,可以把結構體定義在頭檔案中,便于在.c檔案里函式的具體實作,
4、自定義解包/打包函式
這里我也只會給出一個例子,
向該打包函式傳入一個結構體指標和陣列指標,從而便于在函式內對陣列進行操作,
此外,可以通過左移右移,來將uint16_t資料拆分,
1 void Pack_Data(_FeedBack* feedback,uint8_t* feedArray) 2 { //把陣列中資訊封入資料包中 3 feedArray[0] = 0XFF;//這是幀頭 4 feedArray[1] = feedback->Shoot_Mode; 5 feedArray[2] = feedback->Shoot_Speed; 6 feedArray[3] = feedback->Armor_Id; 7 feedArray[4] = (uint8_t)(feedback->HP_Remain); 8 feedArray[5] = (uint8_t)(feedback->HP_Remain >> 8); 9 feedArray[6] = 0XAA;//暫時無意義 10 feedArray[7] = 0XFE;//芝士幀尾 11 }
實際上,解包函式也是類似上文的操作,只不過是反了過來,
注:1.可以利用與 “ | ” 來將兩個資料拼成一個,將拆分的資料合成一個,
2.幀頭和幀尾起到了驗證的作用,可以用來驗證資料完整性,
5、自定義發送/接收函式
1 int CDC_SendFeed(uint8_t* Fed, uint16_t Len) 2 { 3 CDC_Transmit_FS(Fed, Len); 4 return 0; 5 }
上文呼叫了之前修改過的官方代碼,這樣模塊化的代碼更容易理解與閱讀,
6、備注
1)如果你要定義一個結構體指標并想給它賦值,那么你需要在賦值前給它分配空間,否則這個指標無法進行賦值,
例子:
_FeedBack* ft,fd;
ft=(_FeedBack*)malloc(sizeof(_FeedBack));//這里是結構體的空間分配以及具體賦值
//先更新這點,等我有時間繼續更
三、Ubuntu端具體實作程序
思路:利用ROS的serial包來實作串口通信,
//先更新這點,等我有時間繼續更
四、產生的結果以及可能存在的報錯
//先更新這點,等我有時間繼續更
五、備注
1、如果PC無法連接到虛擬串口,并顯示“無法獲取設備描述符”,
我的解決辦法:
1)線路連接不良或者線路有問題,建議重新連接或者換一根線(有一定可能)
2)工程配置錯誤,時鐘樹有誤(需要根據你的開發板,重新觀察時鐘樹的配置,是否引入了正確的時鐘,以及是否配置好了USB時鐘(48MHz))
3)
2、Ubuntu無法打開串口
1)連接有問題或者根本沒有連接
2)沒有權限打開串口(進入管理員模式(終端輸入sudo -i),接著編輯/etc/udev/rules.d/70-ttyusb.rules,加上一行KERNEL=="ttyUSB[0-9]*",MODE="0666" 保存退出即可,注意,要看具體需要給什么串口權限,虛擬串口一般叫做/dev/ttyACM0,所以可以寫入KERNEL=="ttyACM[0-9]*",MODE="0666" ,而真實串口一般叫/dev/ttyUSB0,可以用前面所說的內容,)
3)STM32CubeIDE報錯GDB服務端無法打開,
我在博客里已經給出了詳盡的解釋
關于STM32CubeIDE無法正常啟動GDB服務端的解決辦法 - 墨髯 - 博客園 (cnblogs.com)
//先更新這點,等我有時間繼續更
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/542990.html
標籤:嵌入式
上一篇:Keycloak部署及與Jenkins集成SSO配置
下一篇:01 LED點燈
