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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/474159.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

搭建MC服务器

局域网中玩MC&#xff0c;直接自己创建房间开启局域网就可以了。如果想开一个24小时不关机的服务器呢&#xff1f;其实最开始我是想在windows云服务器&#xff0c;图形化界面运行一个开启局域网即可。可能是云服务器上没有显卡&#xff0c;还是其他什么原因&#xff0c;游戏打开…

css 使用图片作为元素边框

先看原始图片 再看效果 边框的四个角灭有拉伸变形,但是图片的中部是拉伸的 代码 border-style: solid;/* 设置边框图像的来源 */border-image-source: url(/static/images/mmwz/index/bk_hd3x.png);/* 设置如何切割图像 */border-image-slice: 66;/* 设置边框的宽度 */border…

通用定时器---输出比较功能

目录 一、概念 二、输出比较的8种模式 三、输出比较输出PWM波形的基本结构 配置步骤 四、示例代码 一、概念 OC&#xff08;OutPut Compare&#xff09;输出比较。输出比较可以通过比较CNT与CCR寄存器的关系&#xff0c;来对输出电平进行置1/置0/翻转的操作&#xff0c;可…

【网页设计】CSS3 进阶(动画篇)

1. CSS3 2D 转换 转换&#xff08;transform&#xff09;是CSS3中具有颠覆性的特征之一&#xff0c;可以实现元素的位移、旋转、缩放等效果 转换&#xff08;transform&#xff09;你可以简单理解为变形 移动&#xff1a;translate旋转&#xff1a;rotate缩放&#xf…

探索 HTML 和 CSS 实现的 3D旋转相册

效果演示 这段HTML与CSS代码创建了一个包含10张卡片的3D旋转效果&#xff0c;每张卡片都有自己的边框颜色和图片。通过CSS的3D变换和动画&#xff0c;实现了一个动态的旋转展示效果 HTML <div class"wrapper"><div class"inner" style"-…

WTV芯片在智能电子锁语音留言上的应用方案解析

一、概述 电子锁的留言功能允许用户通过语音或文字方式给其他家庭成员留下信息。这项功能可以增强家庭成员之间的沟通&#xff0c;特别是在忙碌的家庭生活中提供便利。 WTV是一款功能强大的高品质语音芯片&#xff0c;采用了高性能32位处理器、最高频率可达120MHz。具有低成本、…

Ajax的相关内容

一、Ajax的使用步骤 1.创建XML对象 const xhrnew XMLHttpRequest(); 2.监听事件&#xff0c;处理响应 3.准备发送请求 true表示异步 ajax中永远是异步&#xff0c;永远是true 4.发送请求 二、GET和POST请求 三、JSON的三种形式 四、JSON的方法 五、跨域 六、XHR的属性和方法…

群控系统服务端开发模式-应用开发-前端级别功能开发

一、添加视图 在根目录下src文件夹下views文件夹下param文件夹下grade文件夹下&#xff0c;新建index.vue&#xff0c;代码如下 <template><div class"app-container"><div class"filter-container" style"float:left;"><…

【含开题报告+文档+PPT+源码】基于springboot的教师评价系统的设计与实现

开题报告 随着信息技术的迅猛发展&#xff0c;教育信息化已成为现代教育的必然趋势。教研室作为高校教学管理的重要机构&#xff0c;肩负着提升教学质量、推动教学改革的重要使命。然而&#xff0c;传统的教学管理方式往往存在效率低下、数据分散、管理不便等问题&#xff0c;…

Nginx 使用入门介绍

大家好&#xff0c;我是G探险者&#xff01; 今天聊一聊nginx. Nginx 是一款高性能的 Web 服务器、反向代理服务器以及负载均衡器。它因其轻量级、稳定性和高并发处理能力&#xff0c;在全球范围内得到了广泛应用。许多大型网站&#xff08;如 Netflix、Dropbox 和 WordPress…

Elasticsearch 重建索引 数据迁移

Elasticsearch 重建索引 数据迁移 处理流程创建临时索引数据迁移重建索引写在最后 大家都知道&#xff0c;es的索引创建完成之后就不可以再修改了&#xff0c;包括你想更改字段属性或者是分词方式等。那么随着业务数据量的发展&#xff0c;可能会出现需要修改索引&#xff0c;或…

vue3 路由写法及传参方式 !超详细

Vue Router 是 Vue.js 官方的路由管理器。它主要用于单页面应用程序&#xff08;SPA, Single Page Application&#xff09;中&#xff0c;帮助解决页面导航、组件复用等问题。 基本的使用 1.router配置文件代码 创建一个ts文件,用来写路由器 // 创建一个路由器,并暴露出去 …

有限状态机(续)

一、添加刀光和场景 1、资源链接&#xff1a; 武器刀光&#xff1a;https://assetstore.unity.com/packages/tools/particles-effects/melee-weapon-trail-1728 场景&#xff1a;https://assetstore.unity.com/packages/3d/environments/fantasy/casual-tiny-environment-ju…

内网安全隧道搭建-ngrok-frp-nps-sapp

1.ngrok 建立内网主机与公网跳板机的连接&#xff1a; 内网主机为客户机&#xff1a; 下载客户端执行 2.frp

模电数电,融会贯通

模电与数电在传统电子工程中似乎被划分为两大领域&#xff0c;然而&#xff0c;它们实际上是对同一器件的不同应用方法。这种观念有助于我们理解元器件在各种工作状态下的多样性&#xff0c;并在复杂的电路设计中实现更高效的系统集成。 一、三极管的多重身份&#xff1a;放大…

鸿蒙动画开发07——粒子动画

1、概 述 粒子动画是在一定范围内随机生成的大量粒子产生运动而组成的动画。 动画元素是一个个粒子&#xff0c;这些粒子可以是圆点、图片。我们可以通过对粒子在颜色、透明度、大小、速度、加速度、自旋角度等维度变化做动画&#xff0c;来营造一种氛围感&#xff0c;比如下…

Java——并发工具类库线程安全问题

摘要 本文探讨了Java并发工具类库中的线程安全问题&#xff0c;特别是ThreadLocal导致的用户信息错乱异常场景。文章通过一个Spring Boot Web应用程序示例&#xff0c;展示了在Tomcat线程池环境下&#xff0c;ThreadLocal如何因线程重用而导致异常&#xff0c;并讨论了其他并发…

PostgreSQL技术内幕18:物理备份工具pg_basebackup

0.简介 PG自带备份工具有多种&#xff0c;pg_basebackup、pg_dump、pg_dumpall&#xff0c;其中pg_basebackup是文件系统级别的备份&#xff0c;其余两种是逻辑备份。本文主要介绍PG备份工具产生的背景和概念&#xff0c;以及对pg_basebackup使用方法和其实现原理进行详细说明…

基于Python实现的HDR图像处理算法

此代码会读取两张图片&#xff0c;一张用于保留高光细节&#xff0c;另一张用于保留暗部细节。两张图片按指定比例进行像素融合&#xff0c;最终生成一张合成的HDR图片。 import cv2 import numpy as npdef hdr_fusion(highlight_img_path, shadow_img_path, output_path, alp…

计算机网络-理论部分(二):应用层

网络应用体系结构 Client-Server客户-服务器体系结构&#xff1a;如Web&#xff0c;FTP&#xff0c;Telnet等Peer-Peer&#xff1a;点对点P2P结构&#xff0c;如BitTorrent 应用层协议定义了&#xff1a; 交换的报文类型&#xff0c;请求or响应报文类型的语法字段的含义如何…