zoukankan      html  css  js  c++  java
  • STM32 printf 方法重定向到串口UART

    在嵌入式系统中调试代码是很麻烦的一件事, 如果能方便地输出调试信息(与调试者交互), 能使极大加快问题排查的过程. 串口在嵌入式领域是一个比较重要的通讯接口. 因为没有显示设备, 在单片机的程序里调用printf()打印内容是不可见的,但我们可以利用它的外设来实现printf(),比如串口, 串口基本上大多数单片机都有, 通常用串口来打印内容. 通过重写fputc()函数来实现. fputc()是printf()的底层函数, 通过它把要打印的数据发送到串口上去.

    不使用 MicroLib的普通方式

    1. 禁用半主机模式, 禁用了半主机模式才能使用标准库函数printf()打印信息到串口
      说明: 半主机模式是ARM单片机的一种调试机制,跟串口调试不一样,它需要通过仿真器来连接电脑,并调用相应的指令来实现单片机向电脑显示器打印信息(或者从电脑键盘读取输入)。这种方法比串口调试更复杂, 需要用仿真器实现.
    2. include头文件 #include "stdio.h"
    3. 重写 fputc方法
    4. 重新定义 __FILE, __stdout, __stdin这三个变量, 以及重写_sys_exit()
    #pragma import(__use_no_semihosting_swi)
    
    // Change it if you use different USART port
    #define USARTx USART1
    
    struct __FILE { int handle; };
    FILE __stdout;
    FILE __stdin;
    
    int fputc(int ch, FILE *f) {
      while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET){}
      USART_SendData(USARTx, ch);
      return(ch);
    }
    
    int fgetc(FILE *f) {
      char ch;
      while(USART_GetFlagStatus(USARTx, USART_FLAG_RXNE) == RESET){}
      ch = USART_ReceiveData(USARTx);
      return((int)ch);
    }
    
    int ferror(FILE *f) {
      return EOF;
    }
    
    void _ttywrch(int ch) {
      while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){}
      USART_SendData(USARTx, ch);
    }
    
    void _sys_exit(int return_code) {
      while (1); /* endless loop */
    }
    

    使用 MicroLib (micro-library)

    如果使用了keil uvsion开发环境, 可以用microlib简化这一过程. MicroLib是一个定制(精简)的stdio替代库, 提供无缓冲的stdin, stdout 和 stderr,当使用微库时,就默认关闭了半主机模式, 不需要#pragma注释. 使用MicroLib之后, 只需要修改fputc()使其重定向

    1. 在KEIL-MDK中开启 Use MicroLIB 选项

    打开配置面板, 定位到Target标签页, 勾选Use MicroLIB.

    2. 将 fputc 方法的输出重定向

    在 MicroLib 的 stdio.h 头文件中, fputc() 方法的prototype为

    int fputc(int ch, FILE* stream)
    

    这个方法原本是将ch输出到strem这个文件类型指针指向的文件, 现在将其替换为串口1

    #include <stdio.h>
    int fputc(int ch, FILE* stream)
    {
        USART_SendChar(USART1, (uint8_t)ch);
        return ch;
    }
    

    3. 重写fgetc方法

    同样的

    /*
    ** Rewrite fgetc function and make scanf function work
    **/
    int fgetc(FILE* file)
    {
        while((USART1->ISR & UART_IT_RXNE) == RESET);
        return USART1->RDR;
    }
    

    注意要include stdio.h, 否则会报FILE类型未定义.

    在代码中启用UART

    根据可用的pin脚, USART1可以使用PA9, PA10组合, 或者PB6, PB7组合.

    stm32f103

    void UARTmain_Init(void)
    {
      GPIO_InitTypeDef GPIO_InitStructure;
      USART_InitTypeDef USART_InitStructure;
    
      // 打开GPIO和USART时钟
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
      
      // 将USART1 Tx@PA9的GPIO配置为推挽复用模式
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(GPIOA, &GPIO_InitStructure);
    
      // 将USART1 Rx@PA10的GPIO配置为浮空输入模式
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
      GPIO_Init(GPIOA, &GPIO_InitStructure);
    
      /* 配置USART1参数
          波特率   = 115200
          数据长度 = 8
          停止位   = 1
          校验位   = No
          禁止硬件流控(即禁止RTS和CTS)
          使能接收和发送
      */
      USART_InitStructure.USART_BaudRate = 115200;
      USART_InitStructure.USART_WordLength = USART_WordLength_8b;
      USART_InitStructure.USART_StopBits = USART_StopBits_1;
      USART_InitStructure.USART_Parity = USART_Parity_No;
      USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
      USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
      USART_Init(USART1, &USART_InitStructure);
    
      // 使能 USART1
      USART_Cmd(USART1, ENABLE);
    }
    

    stm32f401

    void UARTmain_Init(void)
    {
      GPIO_InitTypeDef GPIO_InitStruct;
      USART_InitTypeDef USART_InitStruct;
      
      /**
       * Enable clock for GPIOB
       * Enable clock for USART1 peripheral
       */
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    
      /**
       * 串口1对应引脚复用映射
       * STM32F4xx USART1 为PA9/PB6对应USART1的TX, PA10/PB7对应USART1的RX
       * Tell pins PB6 and PB7 which alternating function you will use
       * @important Make sure, these lines are before pins configuration!
       */
      GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1);
      GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1);
      // Initialize pins as alternating function
      GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
      GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;
      GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;
      GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_UP;
      GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
      GPIO_Init(GPIOB, &GPIO_InitStruct);
    
      /**
       * Set Baudrate to value you pass to function
       * Disable Hardware Flow control
       * Set Mode To TX and RX, so USART will work in full-duplex mode
       * Disable parity bit
       * Set 1 stop bit
       * Set Data bits to 8
       *
       * Initialize USART2
       * Activate USART2
       */  
      USART_InitStruct.USART_BaudRate=115200;
      USART_InitStruct.USART_WordLength=USART_WordLength_8b;
      USART_InitStruct.USART_StopBits=USART_StopBits_1;
      USART_InitStruct.USART_Parity=USART_Parity_No;
      USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
      USART_InitStruct.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;
      USART_Init(USART1, &USART_InitStruct);
      USART_Cmd(USART1, ENABLE);
    }
    

    参考

  • 相关阅读:
    解决import tensorflow时发生DLL错误
    解决Docker Container in WSL2 发生DNS错误无法访问网络
    使用过vmware 再开启wsl2闪退处理
    70. Climbing Stairs. Leetcode
    miredo on mac
    解决Runtime Error on LeetCode
    HttpClient Get与Post请求数据
    Ubuntu16.04.1 安装MyCat
    CenterOS中安装Redis及开机启动设置
    ASP.ENT Core Linux 下 为 donet创建守护进程(转载)
  • 原文地址:https://www.cnblogs.com/milton/p/14711577.html
Copyright © 2011-2022 走看看