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.mzph.cn/bicheng/61219.shtml

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

相关文章

Kotlin return与return@forEachIndexed

Kotlin return与returnforEachIndexed fun main() {val data arrayOf(0, 1, 2, 3, 4)println("a")data.forEachIndexed { index, v ->if (v 2) {//类似while循环中的continue//跳过&#xff0c;继续下一个forEachIndexed迭代returnforEachIndexed}println("…

Cherno OpenGL(28 ~ 33)

批量渲染-介绍 在这里我们将在一个drawcall打包多个几何体。即 batch geometry。 我们在这里将聚焦于2d渲染&#xff0c;我们如何渲染一堆2d的quads或者说rectangles呢&#xff1f; 一种情况是比如一个2d游戏有很多个tile组成&#xff0c;要去渲染这些tile&#xff1b;另一种…

缺失值异常值的处理导入数据插值拟合工具箱

文章目录 1.构造数据2.缺失值的处理3.异常值的处理4.导入数据的注意事项5.插值拟合工具箱使用 1.构造数据 下面的这个就是生成这个正态分布的数据&#xff0c;这个时候我们的这个数据里面是没有这个异常的数据的&#xff0c;因此这个时候我们可以自己创造这个异常的数据&#…

FPGA开发流程

注&#xff1a;开发板&#xff1a;小梅哥的ACX720。本实验可直接运行在小梅哥的ACX720开发板上&#xff0c;后续的实验都可直接运行在小梅哥的ACX720上。 一、打开VIVADO并创建工程 1、双击VIVADO图标&#xff0c;打开vivado。 2、打开vivado界面打&#xff0c;点击有 Create …

知识库管理系统:企业数字化转型的加速器

在数字化转型的大潮中&#xff0c;知识库管理系统&#xff08;KBMS&#xff09;已成为企业提升效率和创新能力的关键工具。本文将探讨知识库管理系统的定义、企业建立知识库的必要性&#xff0c;以及如何快速搭建企业知识库。 知识库管理系统是什么&#xff1f; 知识库管理系统…

.NET桌面应用架构Demo与实战|WPF+MVVM+EFCore+IOC+DI+Code First+AutoMapper

目录 .NET桌面应用架构Demo与实战|WPFMVVMEFCoreIOCDICode FirstAutoPapper技术栈简述项目地址&#xff1a;功能展示项目结构项目引用1. 新建模型2. Data层&#xff0c;依赖EF Core&#xff0c;实现数据库增删改查3. Bussiness层&#xff0c;实现具体的业务逻辑4. Service层&am…

ReactPress vs VuePress vs RectPress

ReactPress&#xff1a;重塑内容管理的未来 在当今数字化时代&#xff0c;内容管理系统&#xff08;CMS&#xff09;已成为各类网站和应用的核心组成部分。ReactPress作为一款融合了现代Web开发多项先进技术的开源发布平台&#xff0c;正以其卓越的性能、灵活性和可扩展性&…

为以人工智能为中心的工作负载重新设计的全局控制台

MinIO 控制台多年来一直是一个不断发展的产品。每次学习时&#xff0c;我们都会思考如何改进交互框架中这个非常重要的部分。首先是控制台&#xff0c;它在推出后的一年内就被广泛采用。更具体地说&#xff0c;超过 10K 个组织。接下来是企业控制台。这从对象存储与其 GUI 之间…

Django5 2024全栈开发指南(三):数据库模型与ORM操作

目录 一、模型的定义二、数据迁移三、数据表关系四、数据表操作4.1 Shell工具4.2 数据新增4.3 数据修改4.4 数据删除4.5 数据查询4.6 多表查询4.7 执行SQL语句4.8 数据库事务 Django 对各种数据库提供了很好的支持&#xff0c;包括 PostgreSQL、MySQL、SQLite 和 Oracle&#x…

Java通过calcite实时读取kafka中的数据

引入maven依赖 <dependency> <groupId>org.apache.calcite</groupId> <artifactId>calcite-kafka</artifactId> <version>1.28.0</version> </dependency> 测试代码 import java.sql.Connection; import java.sql.DriverMan…

RDIFramework.NET CS敏捷开发框架 V6.1发布(.NET6+、Framework双引擎、全网唯一)

RDIFramework.NET C/S敏捷开发框架V6.1版本迎来重大更新与调整&#xff0c;全面重新设计业务逻辑代码&#xff0c;代码量减少一半以上&#xff0c;开发更加高效。全系统引入全新字体图标&#xff0c;整个界面焕然一新。底层引入最易上手的ORM框架SqlSugar&#xff0c;让开发更加…

TypeORM在Node.js中的高级应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 TypeORM在Node.js中的高级应用 TypeORM在Node.js中的高级应用 TypeORM在Node.js中的高级应用 引言 TypeORM 基本概念 1. 实体&am…

11.15组会汇报

概述 不经意传输协议,也叫茫然传输协议,是一种保护隐私的两方通信协议,消息发送者持有两条待发送的消息,接收者选择一条进行接收,事后发送者对接收者获取哪一条消息毫不知情,接收者对于未选择的消息也无法获取任何信息。即1-out-of-2 OT。在OT协议中,发送方拥有全部的数据权限,…

通过华为鲲鹏认证发行上市的集成平台产品推荐

华为鲲鹏认证是技术实力与品质的权威象征&#xff0c;代表着产品达到了高标准的要求。从技术层面看&#xff0c;认证确保产品与华为鲲鹏架构深度融合&#xff0c;能充分释放鲲鹏芯片的高性能、低功耗优势&#xff0c;为集成平台的高效运行提供强大动力。在安全方面&#xff0c;…

基于ARM+FPGA的电力通信管理机IEC61850规约通信机的实现

本章通过对比传统的通信管理机方案对需要支持多RS485端口的不足之处&#xff0c; 以及在进行海量数据处理时的性能瓶颈&#xff0c;本文使用Intel全新的Cyclone V SoC FPGA芯片&#xff0c;充分发挥FPGA的高速并行运算特性以及现场可配置优势&#xff0c;并且结合 ARM处理器的…

ASP.NET Core Webapi 返回数据的三种方式

ASP.NET Core为Web API控制器方法返回类型提供了如下几个选择&#xff1a; Specific type IActionResult ActionResult<T> 1. 返回指定类型&#xff08;Specific type&#xff09; 最简单的API会返回原生的或者复杂的数据类型&#xff08;比如&#xff0c;string 或者…

ROS机器视觉入门:从基础到人脸识别与目标检测

前言 从本文开始&#xff0c;我们将开始学习ROS机器视觉处理&#xff0c;刚开始先学习一部分外围的知识&#xff0c;为后续的人脸识别、目标跟踪和YOLOV5目标检测做准备工作。我采用的笔记本是联想拯救者游戏本&#xff0c;系统采用Ubuntu20.04&#xff0c;ROS采用noetic。 颜…

主机型入侵检测系统(HIDS)——Elkeid在Centos7的保姆级安装部署教程

一、HIDS简介 主机型入侵检测系统(Host-based Intrusion Detection System 简称:HIDS);HIDS作为主机的监视器和分析器,主要是专注于主机系统内部(监视系统全部或部分的动态的行为以及整个系统的状态)。 HIDS使用传统的C/S架构,只需要在监测端安装agent即可,且使用用户…

Django启用国际化支持(2)—实现界面内切换语言:activate()

文章目录 ⭐注意⭐1. 配置项目全局设置&#xff1a;启用国际化2. 编写视图函数3. 配置路由4. 界面演示5、扩展自动识别并切换到当前语言设置语言并保存到Session设置语言并保存到 Cookie ⭐注意⭐ 以下操作依赖于 Django 项目的国际化支持。如果你不清楚如何启用国际化功能&am…

Springboot之登录模块探索(含Token,验证码,网络安全等知识)

简介 登录模块很简单&#xff0c;前端发送账号密码的表单&#xff0c;后端接收验证后即可~ 淦&#xff01;可是我想多了&#xff0c;于是有了以下几个问题&#xff08;里面还包含网络安全问题&#xff09;&#xff1a; 1.登录时的验证码 2.自动登录的实现 3.怎么维护前后端…