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);
    }
    

    参考

  • 相关阅读:
    Leetcode 121. Best Time to Buy and Sell Stock
    Leetcode 120. Triangle
    Leetcode 26. Remove Duplicates from Sorted Array
    Leetcode 767. Reorganize String
    Leetcode 6. ZigZag Conversion
    KMP HDU 1686 Oulipo
    多重背包 HDU 2844 Coins
    Line belt 三分嵌套
    三分板子 zoj 3203
    二分板子 poj 3122 pie
  • 原文地址:https://www.cnblogs.com/milton/p/14711577.html
Copyright © 2011-2022 走看看