STM32F407IGHX与Ubuntu20.04串口通信

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

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)

//先更新这点,等我有时间继续更

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Ubuntu