• cubeIDE开发, stm32调试信息串口通信输出显示


    目录

    一、stm32串口配置

    三、串口驱动功能调用及编译实现

    四、编译测试


            关于cubeIDE开发基本技巧及流程,本文不详细叙述,请参考:cubeIDE快速开发流程_py_free的博客-CSDN博客_cubeide汉化

    一、stm32串口配置

            本文采用的开发板是stm32L496VGT3,其有两个 USB 接口,一个为 USB ST-link 复用接口,作用为软件下载/调试/ 系统供电输入口。另一个为 USB OTG,用户可以外接 USB 设备,支持 1000mA,USB2.0 设备接入。本文就采用 USB ST-link口实现串口上下行通信及调试信息输出显示。

            USB ST-Link(USB1) 接口的串口通信及调试信息需要我们映射实现。

             打开cubeMX配置界面,配置串口映射,开启LPUART串口或其他USART*(1)的异步传输模式(2),配置串口参数(3),并将串口收发数据端口映射到PB10、PB11上(4),并开启中断功能(5),最后点击保存或生成代码按钮生成代码。

            开启外部中断:

            设置上拉输出:

      二、串口驱动设计及代码实现

            重新构建系统的printf函数,将其映射到LPUART串口。

            【1】首先禁用CubeIDE生成syscall.c文件

             【2】创建print.h和print.c两个驱动文件来取代syscall.c内的函数_read、_write等实现,本文是将这两个文件放置在ICore目录内

             print.h内容如下:

    1. /*
    2. * print.h
    3. *
    4. * Created on: 2021年11月16日
    5. * Author: Administrator
    6. */
    7. #ifndef INC_RETARGET_H_
    8. #define INC_RETARGET_H_
    9. #include "stm32l4xx_hal.h"
    10. #include "stdio.h"//用于printf函数串口重映射
    11. #include
    12. void ResetPrintInit(UART_HandleTypeDef *huart);//将printf()函数映射到指定串口上
    13. int _isatty(int fd);
    14. int _write(int fd, char* ptr, int len);
    15. int _close(int fd);
    16. int _lseek(int fd, int ptr, int dir);
    17. int _read(int fd, char* ptr, int len);
    18. int _fstat(int fd, struct stat* st);
    19. #endif /* INC_RETARGET_H_ */

            print.c内容如下,主要是将syscall.c内的空实现函数通过串口实现数据收发处理

    1. /*
    2. * print.c
    3. *
    4. * Created on: 2021年11月16日
    5. * Author: Administrator
    6. */
    7. #include <_ansi.h>
    8. #include <_syslist.h>
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include
    15. #include
    16. #include "print.h"
    17. #if !defined(OS_USE_SEMIHOSTING)
    18. #define STDIN_FILENO 0
    19. #define STDOUT_FILENO 1
    20. #define STDERR_FILENO 2
    21. UART_HandleTypeDef *gHuart;
    22. void ResetPrintInit(UART_HandleTypeDef *huart) {
    23. gHuart = huart;
    24. /* Disable I/O buffering for STDOUT stream, so that
    25. * chars are sent out as soon as they are printed. */
    26. setvbuf(stdout, NULL, _IONBF, 0);
    27. }
    28. int _isatty(int fd) {
    29. if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
    30. return 1;
    31. errno = EBADF;
    32. return 0;
    33. }
    34. int _write(int fd, char* ptr, int len) {
    35. HAL_StatusTypeDef hstatus;
    36. if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
    37. //串口发送数据实现,可以main.c等功能代码中直接调用HAL_UART_Transmit也能实现串口发送数据
    38. hstatus = HAL_UART_Transmit(gHuart, (uint8_t *) ptr, len, HAL_MAX_DELAY);
    39. if (hstatus == HAL_OK)
    40. return len;
    41. else
    42. return EIO;
    43. }
    44. errno = EBADF;
    45. return -1;
    46. }
    47. int _close(int fd) {
    48. if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
    49. return 0;
    50. errno = EBADF;
    51. return -1;
    52. }
    53. int _lseek(int fd, int ptr, int dir) {
    54. (void) fd;
    55. (void) ptr;
    56. (void) dir;
    57. errno = EBADF;
    58. return -1;
    59. }
    60. int _read(int fd, char* ptr, int len) {
    61. HAL_StatusTypeDef hstatus;
    62. if (fd == STDIN_FILENO) {
    63. hstatus = HAL_UART_Receive(gHuart, (uint8_t *) ptr, 1, HAL_MAX_DELAY);
    64. if (hstatus == HAL_OK)
    65. return 1;
    66. else
    67. return EIO;
    68. }
    69. errno = EBADF;
    70. return -1;
    71. }
    72. int _fstat(int fd, struct stat* st) {
    73. if (fd >= STDIN_FILENO && fd <= STDERR_FILENO) {
    74. st->st_mode = S_IFCHR;
    75. return 0;
    76. }
    77. errno = EBADF;
    78. return 0;
    79. }
    80. #endif //#if !defined(OS_USE_SEMIHOSTING)

            【3】创建串口驱动接口,主要就是实现串口回调函数

            前面print.h/c实现串口输出能力,但其输入能力由于lpusart开启了外部中断功能,还需我们实现其回调函数,因为cubeMX生成的HAL库(stm32l4xx_hal_uart.c,文件名因芯片不同有所区别)的串口接收回调函数是个弱声明函数,其实现源码没有做什么事。

             现在创建usart.h、usart.c两个串口驱动文件,放置ICore目录下

            usart.h内容如下,HAL_UART_RxCpltCallback会覆盖stm32l4xx_hal_uart.c的同名回调函数:

    1. /*
    2. * usart.h
    3. *
    4. * Created on: Oct 20, 2022
    5. * Author: Administrator
    6. */
    7. #ifndef INC_USART_H_
    8. #define INC_USART_H_
    9. #include "stm32l4xx_hal.h" //HAL库文件声明
    10. #include //用于字符串处理的库
    11. #include "print.h"//用于printf函数串口重映射
    12. extern UART_HandleTypeDef hlpuart1;//声明USART1的HAL库结构体
    13. #define HLPUSART_REC_LEN 256//定义USART1最大接收字节数
    14. extern uint8_t HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收缓冲,最大HLPUSART_REC_LEN个字节.末字节为换行符
    15. extern uint16_t HLPUSART_RX_STA;//接收状态标记
    16. extern uint8_t HLPUSART_NewData;//当前串口中断接收的1个字节数据的缓存
    17. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//串口中断回调函数声明
    18. #endif /* INC_USART_H_ */

            usart.c内容如下:

    1. /*
    2. * usart.c
    3. *
    4. * Created on: Oct 20, 2022
    5. * Author: Administrator
    6. */
    7. #include "usart.h"
    8. uint8_t HLPUSART_RX_BUF[HLPUSART_REC_LEN];//接收缓冲,最大HLPUSART_REC_LEN个字节.末字节为换行符
    9. /*
    10. * bit15:接收到回车(0x0d)时设置HLPUSART_RX_STA|=0x8000;
    11. * bit14:接收溢出标志,数据超出缓存长度时,设置HLPUSART_RX_STA|=0x4000;
    12. * bit13:预留
    13. * bit12:预留
    14. * bit11~0:接收到的有效字节数目(0~4095)
    15. */
    16. uint16_t HLPUSART_RX_STA=0;接收状态标记//bit15:接收完成标志,bit14:接收到回车(0x0d),bit13~0:接收到的有效字节数目
    17. uint8_t HLPUSART_NewData;//当前串口中断接收的1个字节数据的缓存
    18. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中断回调函数
    19. {
    20. if(huart ==&hlpuart1)//判断中断来源(串口1:USB转串口)
    21. {
    22. if(HLPUSART_RX_STA&0x4000){//溢出,重新开始
    23. HLPUSART_RX_STA=0;//接收错误,重新开始
    24. }
    25. if(HLPUSART_NewData==0x0d){//回车标记
    26. printf("getdata:%.*s \r\n", HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
    27. HLPUSART_RX_STA|=0x8000;//标记接到回车
    28. }else{
    29. if((HLPUSART_RX_STA&0X0FFF)
    30. HLPUSART_RX_BUF[HLPUSART_RX_STA&0X0FFF]=HLPUSART_NewData; //将收到的数据放入数组
    31. HLPUSART_RX_STA++; //数据长度计数加1
    32. }else{
    33. HLPUSART_RX_STA|=0x4000;//数据超出缓存长度,标记溢出
    34. }
    35. }
    36. HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData,1); //再开启接收中断
    37. }
    38. }

    三、串口驱动功能调用及编译实现

            在原来的key.h/key.c中添加按键按下识别函数,

            key.h:

    1. /*
    2. * key.h
    3. *
    4. * Created on: Sep 29, 2022
    5. * Author: py_hp
    6. */
    7. #ifndef KEY_H_
    8. #define KEY_H_
    9. #include "main.h"
    10. #include "gpio.h"
    11. GPIO_PinState get_key0_val();
    12. GPIO_PinState get_key1_val();
    13. GPIO_PinState get_key2_val();
    14. uint8_t KEY_0(void);
    15. uint8_t KEY_1(void);
    16. uint8_t KEY_2(void);
    17. #endif /* KEY_H_ */

            key.c内容如下:

    1. /*
    2. * key.c
    3. *
    4. * Created on: Sep 29, 2022
    5. * Author: py_hp
    6. */
    7. #include "key.h"
    8. GPIO_PinState get_key0_val()
    9. {
    10. return HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin);
    11. };
    12. GPIO_PinState get_key1_val()
    13. {
    14. return HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin);
    15. };
    16. GPIO_PinState get_key2_val()
    17. {
    18. return HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin);
    19. };
    20. uint8_t KEY_0(void)
    21. {
    22. uint8_t a;
    23. a=0;//如果未进入按键处理,则返回0
    24. if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==GPIO_PIN_RESET){//读按键接口的电平
    25. HAL_Delay(20);//延时去抖动
    26. if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==GPIO_PIN_RESET){ //读按键接口的电平
    27. a=1;//进入按键处理,返回1
    28. }
    29. }
    30. while(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)==GPIO_PIN_RESET); //等待按键松开
    31. return a;
    32. }
    33. uint8_t KEY_1(void)
    34. {
    35. uint8_t a;
    36. a=0;//如果未进入按键处理,则返回0
    37. if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET){//读按键接口的电平
    38. HAL_Delay(20);//延时去抖动
    39. if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET){ //读按键接口的电平
    40. a=1;//进入按键处理,返回1
    41. }
    42. }
    43. while(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET); //等待按键松开
    44. return a;
    45. }
    46. uint8_t KEY_2(void)
    47. {
    48. uint8_t a;
    49. a=0;//如果未进入按键处理,则返回0
    50. if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET){//读按键接口的电平
    51. HAL_Delay(20);//延时去抖动
    52. if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET){ //读按键接口的电平
    53. a=1;//进入按键处理,返回1
    54. }
    55. }
    56. while(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET); //等待按键松开
    57. return a;
    58. }

            在main.c中调用串口驱动,其部分代码如下:

    1. /* USER CODE END Header */
    2. /* Includes ------------------------------------------------------------------*/
    3. #include "main.h"
    4. #include "usart.h"
    5. #include "gpio.h"
    6. /* Private includes ----------------------------------------------------------*/
    7. /* USER CODE BEGIN Includes */
    8. //用户代码1
    9. #include "../../ICore/key.h"
    10. #include "../../ICore/led.h"
    11. #include "../../ICore/print.h"
    12. #include "../../ICore/usart.h"
    13. /* USER CODE END Includes */
    14. /* Private typedef -----------------------------------------------------------*/
    15. /* USER CODE BEGIN PTD */
    16. /* USER CODE END PTD */
    17. /* Private define ------------------------------------------------------------*/
    18. /* USER CODE BEGIN PD */
    19. /* USER CODE END PD */
    20. /* Private macro -------------------------------------------------------------*/
    21. /* USER CODE BEGIN PM */
    22. /* USER CODE END PM */
    23. /* Private variables ---------------------------------------------------------*/
    24. /* USER CODE BEGIN PV */
    25. /* USER CODE END PV */
    26. /* Private function prototypes -----------------------------------------------*/
    27. void SystemClock_Config(void);
    28. /* USER CODE BEGIN PFP */
    29. /* USER CODE END PFP */
    30. /* Private user code ---------------------------------------------------------*/
    31. /* USER CODE BEGIN 0 */
    32. /* USER CODE END 0 */
    33. /**
    34. * @brief The application entry point.
    35. * @retval int
    36. */
    37. int main(void)
    38. {
    39. /* USER CODE BEGIN 1 */
    40. /* USER CODE END 1 */
    41. /* MCU Configuration--------------------------------------------------------*/
    42. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    43. HAL_Init();
    44. /* USER CODE BEGIN Init */
    45. /* USER CODE END Init */
    46. /* Configure the system clock */
    47. SystemClock_Config();
    48. /* USER CODE BEGIN SysInit */
    49. /* USER CODE END SysInit */
    50. /* Initialize all configured peripherals */
    51. MX_GPIO_Init();
    52. MX_LPUART1_UART_Init();
    53. /* USER CODE BEGIN 2 */
    54. //用户代码2
    55. ResetPrintInit(&hlpuart1);
    56. HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
    57. HLPUSART_RX_STA = 0;
    58. set_led0_val(0);
    59. set_led1_val(get_key0_val());
    60. /* USER CODE END 2 */
    61. /* Infinite loop */
    62. /* USER CODE BEGIN WHILE */
    63. while (1)
    64. {
    65. //用户代码3
    66. if(KEY_1()) //按键KEY1判断为1时按键按下
    67. {
    68. set_led0_val(1);//LED1灯控制(1点亮,0熄灭)
    69. set_led1_val(0);//LED2灯控制(1点亮,0熄灭)
    70. printf("KEY1\r\n");//向USART1串口发送字符串
    71. }
    72. if(KEY_2()) //按键KEY2判断为1时按键按下
    73. {
    74. set_led0_val(0);//LED1灯控制(1点亮,0熄灭)
    75. set_led1_val(1);//LED2灯控制(1点亮,0熄灭)
    76. printf("KEY2\r\n");//向USART1串口发送字符串
    77. }
    78. if(HLPUSART_RX_STA&0xC000){//串口1判断中断接收标志位
    79. printf("read flag HLPUSART_RX_STA&0XC000\r\n");//向USART1串口发送字符串
    80. if(HLPUSART_RX_BUF[0]=='1'){
    81. set_led0_val(1);//LED1灯控制(1点亮,0熄灭)
    82. set_led1_val(1);//LED2灯控制(1点亮,0熄灭)
    83. }
    84. if(HLPUSART_RX_BUF[0]=='0'){
    85. set_led0_val(0);//LED1灯控制(1点亮,0熄灭)
    86. set_led1_val(0);//LED2灯控制(1点亮,0熄灭)
    87. }
    88. HLPUSART_RX_STA=0;//串口接收标志清0,即开启下一轮接收
    89. }
    90. /* USER CODE END WHILE */
    91. /* USER CODE BEGIN 3 */
    92. }
    93. /* USER CODE END 3 */
    94. }

    四、编译测试

            编译及加载程序到开发板:

             验证数据收发,按键1和按键2按下,顺利传输数据,输入“1”和“0”字段,可下行控制LED灯

  • 相关阅读:
    初识C++|类和对象(中)——类的默认成员函数
    PE文件解析(5):重定位表详解
    分页列表缓存,你真的会吗
    Could not autowire field: protected tk.mybatis.mapper.common.Mapper
    Kotlin-高阶函数
    [需求管理-1]:IT需求管理总体流程+需求管理需要的技能
    第3章 初识SqlSugarCore之ConfigureOptions注入实现
    Vue首次使用Element
    艾美捷 DetergentOUT GB-S10去垢剂的特点和多种应用
    11 个 Flutter 最佳实践
  • 原文地址:https://blog.csdn.net/py8105/article/details/127893716