自学内容网 自学内容网

4.STM32之通信接口《精讲》之USART通信---实验串口发送程序

本节将进行实战,基础了解请查看第1,2,3节(Whappy)
开始背!! USART ---》全双工   异步/同步  点对点
C语言基础printf用法,这节将用到printf的重定向,来打印到串口助手上。

先给大家看一下整个过程,然后我再给大家做一个单一的串口发送的程序(不包括接收,将在下一节讲)

串口通信的完整过程概述

在嵌入式开发中,串口通信(UART)是最常用的通信方式之一,常用于调试和数据传输。今天我们先带大家梳理一下整个串口通信的过程和原理。

  1. 通信原理 串口通信是一种异步的串行数据传输方式,数据通过起始位、数据位、校验位和停止位逐位传输。STM32 的串口模块(USART/UART)将这些数据串行发送出去,而接收端则按相同协议解析接收到的数据。

  2. 硬件连接

    • STM32 的 TX 引脚:负责发送数据。
    • 电脑的 RX 引脚:负责接收数据。
    • 共地:STM32 和电脑需要连接 GND 确保信号参考一致。
  3. 软件流程

    • STM32 端: 配置 USART 外设,完成数据发送。
    • 电脑端: 通过串口助手(如 SScom、SecureCRT 等)接收 STM32 发送的数据,并显示在屏幕上。
  4. 配置同步 STM32 和电脑串口助手需配置相同的通信参数(如波特率、数据位、停止位等),才能实现正确的通信。

在 STM32 开发中,串口通信(UART)是常用的通信方式之一。以下是详细的步骤和注意事项,包括如何实现数据发送、接收,以及配置相关的硬件和软件。


一、STM32 串口通信原理

  1. 发送端:STM32 STM32 的串口模块(USART)通过 UART 协议将数据位(包括起始位、数据位、校验位和停止位)串行输出到 TX 引脚。

  2. 接收端:电脑助手 电脑通过串口助手软件(如 SScom、SecureCRT 等)接收 STM32 发送的数据,解码并显示。

  3. 硬件连接

    • TXD(STM32 发送端)连接到电脑串口模块的 RXD(接收端)。
    • GND 需连接在一起以保证信号的公共参考。

二、串口通信基本配置

1. STM32 串口配置

以 STM32CubeMX 和 HAL 库为例:

1.1 硬件配置
  • 时钟源: 启用 USART 的时钟(比如:APB1 或 APB2)。
  • 引脚配置: 将 USART 的 TX/RX 引脚设置为 Alternate Function 模式。
1.2 CubeMX 配置

在 CubeMX 中:

  1. 打开 USARTx 外设,启用 Asynchronous 模式。
  2. 设置波特率(通常 9600、115200 等),并配置:
    • 数据位:8 位
    • 停止位:1 位
    • 校验:无校验
    • 硬件流控:无

2. 串口助手(电脑端)的配置

使用串口助手软件(如 SScom),按以下步骤操作:

  1. 选择正确的 COM 端口: 在设备管理器中查看 STM32 的虚拟串口号(如 COM3)。
  2. 配置波特率等参数: 波特率、数据位、停止位和校验位需与 STM32 的设置一致。
  3. 打开串口后即可接收 STM32 发送的数据。

三、调试与查看数据

  1. 通过串口助手查看数据 数据通过串口助手的软件窗口实时显示,确保编码格式正确(通常选择 ASCII 或 HEX)。

  2. 硬件问题排查

    • 确认硬件连线正确。
    • 使用示波器或逻辑分析仪查看串口波形,检查电平是否正常。


       

      在 STM32 开发中,串口通信(UART)是常用的通信方式之一。以下是详细的步骤和注意事项,包括如何实现数据发送、接收,以及配置相关的硬件和软件。


      一、STM32 串口通信原理

    • 发送端:STM32 STM32 的串口模块(USART)通过 UART 协议将数据位(包括起始位、数据位、校验位和停止位)串行输出到 TX 引脚。

    • 接收端:电脑助手 电脑通过串口助手软件(如 SScom、SecureCRT 等)接收 STM32 发送的数据,解码并显示。

    • 硬件连接

      • TXD(STM32 发送端)连接到电脑串口模块的 RXD(接收端)。
      • GND 需连接在一起以保证信号的公共参考。

    • 二、串口通信基本配置

      1. STM32 串口配置

      以 STM32CubeMX 和 HAL 库为例:

      1.1 硬件配置
    • 时钟源: 启用 USART 的时钟(比如:APB1 或 APB2)。
    • 引脚配置: 将 USART 的 TX/RX 引脚设置为 Alternate Function 模式。
    • 1.2 CubeMX 配置

      在 CubeMX 中:

    • 打开 USARTx 外设,启用 Asynchronous 模式。
    • 设置波特率(通常 9600、115200 等),并配置:
      • 数据位:8 位
      • 停止位:1 位
      • 校验:无校验
      • 硬件流控:无
    • 生成代码。
    • 1.3 初始化代码

      生成的代码中包含初始化函数,例如:

       

      1.4 数据发送代码

      使用 HAL_UART_Transmit 函数发送数据:

       

      1.5 数据接收代码(轮询模式)

      使用 HAL_UART_Receive 函数接收数据:

       

      2. 串口助手(电脑端)的配置

      使用串口助手软件(如 SScom),按以下步骤操作:

    • 选择正确的 COM 端口: 在设备管理器中查看 STM32 的虚拟串口号(如 COM3)。
    • 配置波特率等参数: 波特率、数据位、停止位和校验位需与 STM32 的设置一致。
    • 打开串口后即可接收 STM32 发送的数据。

    • 三、调试与查看数据

    • 通过串口助手查看数据 数据通过串口助手的软件窗口实时显示,确保编码格式正确(通常选择 ASCII 或 HEX)。

    • 硬件问题排查

      • 确认硬件连线正确。
      • 使用示波器或逻辑分析仪查看串口波形,检查电平是否正常。

    • 四、完整的示例程序

      发送“Hello, UART!”到串口助手

      4.1 主程序
       
      4.2 必备的 STM32 外设初始化代码

      包含 GPIO、时钟等初始化函数(CubeMX 自动生成)。


      五、注意事项

    • 波特率的匹配: STM32 和电脑助手的波特率必须一致。
    • 电平匹配: STM32 的串口为 TTL 电平,若使用 RS232 接口需添加电平转换芯片(如 MAX232)。
    • 调试工具:
      • 可用串口助手接收数据,调试发送内容。
      • 使用 STM32 的 SWD 接口进行代码调试和断点跟踪。

    • 总结

      通过这个简单的串口发送程序,我们可以实现 STM32 向电脑串口助手发送数据的基本功能。下一节我们将详细讲解串口数据的接收方法,包括如何解析收到的数据并进行处理。

接下一来就是,我学习的重点了   用标准库写一个串口发送程序

连接方式

标准库介绍
 

TM32 USART 库函数详解

STM32 的 USART 库函数是通过标准外设库(StdPeriph Library)提供的接口,用于初始化、配置、控制 USART 外设,以及实现串口通信功能。以下对这些函数的作用、使用方法和关键点进行详细说明,并提供示例代码。


1. 函数详细说明

(1)USART 初始化和配置函数
函数作用
void USART_DeInit(USART_TypeDef* USARTx)将指定的 USART 外设寄存器恢复为默认值。用于复位配置。
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)初始化 USART 外设,包括波特率、数据位、停止位等参数配置。
void USART_StructInit(USART_InitTypeDef* USART_InitStruct)USART_InitTypeDef 结构体初始化为默认值(一般用于快速配置前设置默认参数)。
(2)时钟相关函数
函数作用
void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct)配置 USART 的同步时钟模式(如 SPI 模式需要)。
void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct)初始化 USART_ClockInitTypeDef 结构体为默认值。
(3)使能和中断相关函数
函数作用
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)使能或禁用指定的 USART 外设。
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)使能或禁用 USART 的中断(如接收、发送完成中断)。
(4)DMA 功能
函数作用
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState)使能或禁用 USART 的 DMA 传输请求(支持发送或接收)。
(5)通信模式和高级配置
函数作用
void USART_SetAddress(USART_TypeDef* USARTx, uint8_t USART_Address)配置 USART 的地址(多处理器通信模式下使用)。
void USART_WakeUpConfig(USART_TypeDef* USARTx, uint16_t USART_WakeUp)配置 USART 唤醒模式。
void USART_ReceiverWakeUpCmd(USART_TypeDef* USARTx, FunctionalState NewState)使能或禁用接收器的唤醒功能。
(6)数据收发函数
函数作用
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)向发送数据寄存器写入数据,发送数据。
uint16_t USART_ReceiveData(USART_TypeDef* USARTx)从接收数据寄存器读取数据。
(7)标志位与中断状态管理
函数作用
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG)获取指定的 USART 标志位状态(如发送完成、接收完成)。
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG)清除指定的 USART 标志位。
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)获取指定的 USART 中断状态。
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT)清除指定的 USART 中断挂起标志。

接下来我们就要根据上述框图来配置USART的的发送程序,根据程序上述蓝色框图,我们要让单片机发送程序,仅需将我们要发送的程序放到TX管脚即可!(接收部分下节再讲)

具体配置如下

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>


void Serial_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

GPIO_InitTypeDef GPIO_InitStructrue;
GPIO_InitStructrue.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructrue.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructrue);


USART_InitTypeDef USART1_InitStructure;
USART1_InitStructure.USART_BaudRate = 9600;
USART1_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART1_InitStructure.USART_Mode = USART_Mode_Tx;
USART1_InitStructure.USART_Parity = USART_Parity_No ;
USART1_InitStructure.USART_StopBits = USART_StopBits_1;
USART1_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&USART1_InitStructure);
USART_Cmd(USART1,ENABLE);
}

包含头文件

 

c

复制代码

#include "stm32f10x.h" // STM32F10x 系列的设备头文件 #include <stdio.h> #include <stdarg.h>

  • stm32f10x.h

    • 包含 STM32F10x 系列微控制器的外设寄存器定义和标准外设库支持。
    • 提供对 GPIO、USART 等外设的配置和操作功能。
  • stdio.hstdarg.h

    • 提供标准输入输出函数,如 printf
    • 如果需要处理可变参数,这两个头文件是必要的。

2. 函数 Serial_Init

该函数的作用是初始化串口1(USART1)以及相关的 GPIO 引脚。

2.1 使能时钟
 

c

复制代码

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

  • RCC_APB2PeriphClockCmd
    • 用于使能外设的时钟。
    • RCC_APB2Periph_USART1:使能 USART1 的时钟。
    • RCC_APB2Periph_GPIOA:使能 GPIOA 的时钟(USART1 的 TX 引脚位于 GPIOA9)。
2.2 配置 GPIO
 

c

复制代码

GPIO_InitTypeDef GPIO_InitStructrue; GPIO_InitStructrue.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出模式(用于串口 TX) GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_9; // 配置 GPIOA 的第 9 引脚(USART1 TX) GPIO_InitStructrue.GPIO_Speed = GPIO_Speed_50MHz; // 输出速率为 50MHz GPIO_Init(GPIOA, &GPIO_InitStructrue); // 初始化 GPIOA9

  • 配置 GPIOA 引脚 9 为串口 TX(传输引脚)。
    • GPIO_Mode_AF_PP:复用推挽输出模式,适用于串口功能。
    • GPIO_Pin_9:USART1 的 TX 引脚。
    • GPIO_Speed_50MHz:输出速度设为 50 MHz(用于快速信号传输)。
2.3 配置 USART1
 

c

复制代码

USART_InitTypeDef USART1_InitStructure; USART1_InitStructure.USART_BaudRate = 9600; // 波特率为 9600 USART1_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控 USART1_InitStructure.USART_Mode = USART_Mode_Tx; // 工作模式:仅发送(Tx) USART1_InitStructure.USART_Parity = USART_Parity_No; // 无校验位 USART1_InitStructure.USART_StopBits = USART_StopBits_1; // 停止位:1 USART1_InitStructure.USART_WordLength = USART_WordLength_8b; // 数据位:8 位 USART_Init(USART1, &USART1_InitStructure); // 初始化 USART1

  • 配置串口的通信参数:
    • 波特率:9600 波特。
    • 硬件流控:禁用(常用于硬件握手信号)。
    • 工作模式:仅发送模式(可以扩展为接收和发送)。
    • 校验位:无校验位。
    • 停止位:1 个停止位。
    • 数据位:8 位。
2.4 使能 USART1
 

c

复制代码

USART_Cmd(USART1, ENABLE);

  • 打开 USART1,使能其工作。

总结

这段代码完成了以下工作:

  1. 使能时钟:

    • 为 GPIOA 和 USART1 启用外设时钟。
  2. 配置 GPIO:

    • 将 GPIOA 的第 9 引脚配置为 USART1 的 TX 引脚(推挽输出模式)。
  3. 配置串口参数:

    • 波特率:9600。
    • 数据位:8 位。
    • 停止位:1 位。
    • 无校验位。
    • 无硬件流控。
  4. 使能 USART1:

    • 启用 USART1,使其能够发送数据。

注意事项

  • 接收功能:目前只启用了发送模式。如果需要接收数据,还需配置 RX 引脚(GPIOA10)并将模式改为 USART_Mode_Tx | USART_Mode_Rx
  • 波特率匹配:确保外部接收设备的波特率与 USART1 的设置一致。
  • 中断或 DMA:如果需要更高效的串口通信,可以启用 USART 的中断或 DMA 功能。


     

接下来就是写几个库函数

这段代码的详细解释

函数 Serial_Printf 的作用是通过串口发送一个格式化的字符串。它模仿了标准库中的 printf 函数,用于将格式化后的数据通过串口输出。


函数功能

  • 输入参数:

    • char* format:格式化字符串,用于定义输出格式(如 %d 表示整数,%s 表示字符串)。
    • ...:可变参数,用于提供格式化字符串中占位符对应的实际值。
  • 实现流程:

    1. 将格式化字符串和对应的可变参数组合成一个完整的字符串。
    2. 将生成的字符串通过串口发送。

代码逐步解析

1. 定义缓冲区
 

c

复制代码

char String[100];

  • 定义一个字符数组 String,大小为 100,用于存储格式化后的字符串。
  • 注意:String 的大小应该足够容纳格式化后的字符串。如果格式化结果超出 100 个字符,会导致缓冲区溢出。
  • 可用 snprintf 替代 vsprintf 来限制格式化结果的长度。

2. 定义可变参数列表
 

c

复制代码

va_list arg;

  • va_list 是一个类型,用于存储和访问可变参数。
  • 它由头文件 <stdarg.h> 定义,是处理可变参数的核心工具。

3. 初始化可变参数列表
 

c

复制代码

va_start(arg, format);

  • 功能:
    • 初始化 arg,使其指向第一个可变参数。
    • 参数 format 是固定参数,它告诉 va_start 可变参数从哪里开始。
  • 原理:
    • 在函数调用时,固定参数和可变参数在内存中的位置是连续的。
    • va_start 通过 format 确定固定参数的位置,从而找到第一个可变参数的位置。

4. 格式化字符串
 

c

复制代码

vsprintf(String, format, arg);

  • 功能:
    • 根据 format 和可变参数 arg,将格式化后的结果存入 String
    • vsprintf 是标准库中的函数,作用类似于 sprintf,但支持可变参数。
  • 参数解释:
    • String:用于存储格式化结果的缓冲区。
    • format:格式化字符串,定义输出格式。
    • arg:可变参数列表,包含需要插入到格式化字符串中的值。
  • 示例:
     

    c

    复制代码

    char buffer[100]; va_list args; va_start(args, "Value: %d\n"); vsprintf(buffer, "Value: %d\n", args); // 如果传入的可变参数是 42,则 buffer 的内容是 "Value: 42\n"。


5. 清理可变参数列表
 

c

复制代码

va_end(arg);

  • 功能:
    • 清理 va_list,释放相关资源。
    • va_startva_end 之间,arg 的内容是有效的。调用 va_end 后,它不能再被访问。

6. 发送字符串
 

c

复制代码

Serial_SendString(String);

  • 功能:
    • 调用 Serial_SendString 函数,将格式化后的字符串通过串口发送出去。
    • Serial_SendString 会逐字符发送缓冲区 String 的内容,直到遇到字符串的结束符 \0

示例运行过程

假设调用如下代码:

 

c

复制代码

Serial_Printf("Temperature: %d°C, Humidity: %.2f%%\n", 25, 65.23);

运行过程如下:

  1. format 的内容是 "Temperature: %d°C, Humidity: %.2f%%\n"
  2. 可变参数列表 arg 包含两个值:2565.23
  3. vsprintfformatarg 合并,生成的字符串为:
     

    swift

    复制代码

    "Temperature: 25°C, Humidity: 65.23%\n"

  4. Serial_SendString 将上述字符串逐字节发送到串口。

注意事项

  1. 缓冲区大小限制:

    • 如果格式化后的字符串超过了 String[100] 的容量,可能会导致缓冲区溢出。
    • 可以使用 vsnprintf 替代 vsprintf,限制格式化结果的长度。例如:
       

      c

      复制代码

      vsnprintf(String, sizeof(String), format, arg);

  2. 性能:

    • 该实现直接调用 Serial_SendString 将整个字符串发送,性能较高。
    • 如果字符串过长,发送时间可能会较久。
  3. 线程安全性:

    • 该实现没有考虑多线程环境。如果多个线程同时调用 Serial_Printf,可能会出现数据混乱。
    • 可以通过互斥锁或其他同步机制解决。

总结

  • 作用:Serial_Printf 是一个自定义的格式化输出函数,支持通过串口发送格式化字符串。
  • 实现原理:
    • 使用可变参数列表(va_list)。
    • 利用标准库函数 vsprintf 格式化字符串。
    • 调用 Serial_SendString 通过串口发送。
  • 优点:
    • 灵活性强,支持多种数据格式的输出。
    • 易于扩展,类似于标准库中的 printf
  • 改进建议:
    • 使用 vsnprintf 提升安全性,防止缓冲区溢出。
    • 添加线程同步以支持多线程环境。

这种可变函数特别重要,在以后自己想要打印别的函数

可变参数函数(Variadic Functions)是一种函数设计模式,它允许函数接收数量可变的参数。C语言中,通过 <stdarg.h> 提供的工具(如 va_listva_startva_argva_end),可以实现这种功能。


作用

可变参数函数的主要作用是为函数调用提供灵活性,适应输入参数数量和类型不固定的场景。以下是一些典型应用:

  1. 格式化输出:

    • 函数如 printf 可以接收不同数量和类型的参数,灵活地处理格式化字符串和对应的数据。
       

      c

      复制代码

      printf("Hello, %s! You have %d messages.\n", "User", 5);

  2. 日志记录:

    • 日志函数 log(const char* format, ...) 可以根据不同的输入生成对应的日志消息。
       

      c

      复制代码

      log("Error code: %d, Message: %s\n", 404, "Not Found");

  3. 多参数计算:

    • 例如实现一个简单的求和函数 sum(int count, ...)
       

      c

      复制代码

      int sum(int count, ...) { int total = 0; va_list args; va_start(args, count); for (int i = 0; i < count; i++) { total += va_arg(args, int); } va_end(args); return total; } sum(3, 1, 2, 3); // 返回 6

  4. 通用处理接口:

    • 允许定义一个函数处理多种类型的数据,减少代码重复。例如在嵌入式系统中,可变参数函数用于调试信息的格式化输出。

好处

1. 提高灵活性
  • 可变参数函数适用于输入参数数量或类型不确定的情况,可以根据需求动态调整输入参数,避免定义多个类似的函数。
  • 例如,printf 函数通过解析格式化字符串自动匹配参数,无需为不同的输出需求定义多个函数。
2. 简化代码
  • 可以用一个通用函数代替多个特定函数,减少代码重复,提高代码可维护性。
  • 示例:
     

    c

    复制代码

    void print_hello(int times) { for (int i = 0; i < times; i++) { printf("Hello\n"); } }

3. 扩展性强
  • 新的需求只需修改格式化字符串或参数传递方式,而不需要修改函数本身。
  • 示例:
    • 增加新参数的日志记录需求,只需调整调用方式,而无需重写函数逻辑。
4. 支持通用接口
  • 在接口设计中,可变参数函数能够统一处理不同类型和数量的输入,提供一致的接口。
  • 例如,设计一个数据打包函数 pack(int count, ...),可以根据数据类型和数量自动生成字节流。

局限性

尽管可变参数函数提供了许多优势,但它也有一些潜在的局限性和风险:

1. 缺乏类型检查
  • 编译器无法对可变参数的类型和数量进行验证,容易引发运行时错误。
     

    c

    复制代码

    printf("%d", "string"); // 类型不匹配,可能导致未定义行为

2. 难以理解和调试
  • 可变参数函数的实现较为复杂,维护时需要仔细检查 va_list 的使用,特别是错误传参或遗漏 va_end 时,可能导致不可预测的行为。
3. 性能开销
  • 可变参数的处理需要额外的时间开销,特别是在函数被频繁调用的情况下,性能可能受到一定影响。
4. 不适用于所有场景
  • 如果参数类型和数量可以提前确定,使用普通函数往往更安全和高效。

应用场景总结

可变参数函数适合以下场景:

  1. 参数数量和类型不固定。
  2. 需要统一接口处理不同类型的数据。
  3. 提供通用的输出格式(如日志记录、调试信息、格式化输出等)。

使用建议

  1. 安全使用:
    • 使用 vsnprintf 替代 vsprintf 以避免缓冲区溢出。
    • 明确函数的用途,严格控制输入格式。
  2. 合理场景:
    • 对于参数确定的情况,优先选择普通函数。
    • 在接口设计中,尽量提供一个明确的标志(如参数数量或格式字符串)以描述参数内容。
  3. 注重文档和注释:
    • 为可变参数函数编写清晰的文档,描述参数的用法和格式。

总结

可变参数函数的主要好处是灵活、简化和通用,但它也需要小心处理,特别是在参数类型和数量动态变化的场景下。通过合理使用,可以极大提高代码的可扩展性和复用性,同时降低维护成本。


整个发送代码如下!
 

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>


void Serial_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

GPIO_InitTypeDef GPIO_InitStructrue;
GPIO_InitStructrue.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructrue.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructrue);


USART_InitTypeDef USART1_InitStructure;
USART1_InitStructure.USART_BaudRate = 9600;
USART1_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART1_InitStructure.USART_Mode = USART_Mode_Tx;
USART1_InitStructure.USART_Parity = USART_Parity_No ;
USART1_InitStructure.USART_StopBits = USART_StopBits_1;
USART1_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&USART1_InitStructure);
USART_Cmd(USART1,ENABLE);
}

void Serial_SendByte(uint8_t Byte)
{
USART_SendData(USART1,Byte);
while ((USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET));
}



void Serial_SendArray(uint8_t* Array, uint8_t Lenght)
{
uint16_t i;
for(i=0; i<Lenght; i++)
{
Serial_SendByte(Array[i]);
}
}


void Serial_SendString(char* String)
{
uint8_t i;
for(i=0; String[i] != '\0'; i++)
{
Serial_SendByte(String[i]);
}
}

uint32_t Result(uint32_t X, uint32_t Y)
{
uint8_t result = 1;
while(Y--)
{
result = result * X;
}

return result;
}

void Serial_SendNum(uint32_t Num, uint16_t Lenght)
{
uint16_t i;
uint32_t ww;
for(i=Lenght; i>0; i--)
{
ww = Result(10,i-1);
Serial_SendByte((Num/ww )% 10 + '0');

}

}


int fputc(int ch, FILE* f)
{
Serial_SendByte(ch);
return ch;
}


void Serial_Printf(char* format, ...)
{
char String[100];
va_list arg;
va_start(arg,format);
vsprintf(String, format, arg);
va_end(arg);
Serial_SendString(String);
}

重定向C库函数printf()到串口,重定向后可使用printf();
//int fputc(int ch,FILE *f)
//{
//USART_SendData(USART1,(uint8_t)ch);
//while(!(USART_GetFlagStatus(USART1,USART_FLAG_TC)));
//return ch;
//}



下一节是接收程序代码!请看下节!!!!!!!!


原文地址:https://blog.csdn.net/qq_58662017/article/details/143818878

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!