UART

UART

1. 理论知识

1.1 通讯常见概念

  1. 串行通信和并行通信分别指什么?
    串行通信是按位传输,一次传输一位。并行同学是多位同时传输。

    串行通信 并行通信
    通信距离
    抗干扰能力
    传输速率
    成本
  2. 单工、半双工和全双工的区别。
    三者主要是描述通信方向的不同

    名称 通信方向
    单工 数据只能单向传输
    半双工 数据可以从A到B,也可以从B到A,但同一时间只能一个方向
    全双工 同一时间,数据可以A到B,也可以B到A
  3. 同步通信和异步通信的区别。
    同步通信在数据传输过程中要与时钟信号一一对应,而异步通信两边各自根据约定好的频率发送和接收数据。同步通信会比异步通信多一根信号线。

1.2 串口通信协议参数

串口通信的定义等见博客:https://www.cnblogs.com/yangyang13/p/18664776
此篇主要介绍STM32的串口控制与应用。
USART工作流程如下图:

image-20250118210745806

  1. 功能引脚说明
    TX:发送数据的输出引脚;
    RX:接收数据的输入引脚;
    SW_RX:数据接收引脚,只用于单线和智能卡模式,属于内部引脚,无外部引脚;
    nRTS:请求发送(Request to Send),n表示低电平有效。使能RTS流控制,如果USART做好接收数据准备,nRTS就会变成低电平;接收寄存器装满时,就把nTRS置为高电平。仅适用硬件流控制。
    nCTS:清除发送(Clear to Send),n低电平有效。使能RTS流控制,发送器在发送下一帧数据前检测nCTS。如果为低电平,则可以发送数据;高电平则在发送完当前数据帧后停止发送。

  2. 波特率产生
    发送器和接收器波特率相同,通过设置BRR寄存器来实现
    image-20250118212143584

    APB2一般是72MHz,APB1是36MHz

    image-20250118212403441

    需要的波特率是115200,则对应的分频值应该是:39.0625,把这个值写入到BRR寄存器中。39.0625的小数部分:0.0625 * 16 = 1, 整数部分是:39(0x27)。

image-20250118212705219

所以写入到BRR寄存器的值是:0x0271。

3.串口相关寄存器的配置。主要寄存器如下图:

串口控制寄存器
image-20250204220952534
image-20250204220957226
image-20250204221002219

数据寄存器
image-20250204221020780
image-20250204221025886

状态寄存器
image-20250204221054660
image-20250204221059598
image-20250204221104227

实现中断控制串口相关寄存器
image-20250204220905406
image-20250204220806196

2. 实践验证

2.1 寄存器方式

验证的硬件环境:stm32最小系统板+串口板

验证一: 轮询的方式收发

实现串口接收数据后原样返回,效果如下图:

image-20250204210617495

实现逻辑(关键):

  1. 串口的初始化。
    开启相关时钟-》配置IO工作模式-》配置串口参数(BRR波特率寄存器,CR控制寄存器)
  2. 串口的接收和发送。
    发送等待 USART_SR_TXE 寄存器为空,接收等待 USART_SR_RXNE 寄存器非空。接收字符串时还要通过 USART_SR_IDLE 判断总线是否空闲。
USART.h
#ifndef USART_H
#define __USART_H__

#include "stm32f10x.h"

void Driver_USART1_Init(void);

void Driver_USART1_SendChar(uint8_t byte);

void Driver_USART1_SendString(uint8_t *str, uint16_t len);

uint8_t Driver_USART1_ReceiveChar(void);

void Driver_USART1_ReceiveString(uint8_t buff[], uint8_t *len);

#endif
USART.c
#include "USART.h"

/**
 * @description: 初始化串口1
 */
void Driver_USART1_Init(void)
{
    /* 1. 开启时钟 */
    /* 1.1 串口1外设的时钟 */
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    /* 1.2 GPIO时钟 */
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;

    /* 2. 配置GPIO引脚的工作模式  PA9=Tx(复用推挽 CNF=10 MODE=11)  PA10=Rx(浮空输入 CNF=01 MODE=00)*/
    GPIOA->CRH |= GPIO_CRH_CNF9_1;
    GPIOA->CRH &= ~GPIO_CRH_CNF9_0;
    GPIOA->CRH |= GPIO_CRH_MODE9;

    GPIOA->CRH &= ~GPIO_CRH_CNF10_1;
    GPIOA->CRH |= GPIO_CRH_CNF10_0;
    GPIOA->CRH &= ~GPIO_CRH_MODE10;

    /* 3. 串口的参数配置 */
    /* 3.1 配置波特率 115200 */
    USART1->BRR = 0x271;
/* 3.2 串口使能,并使能接收和发送 */
    USART1->CR1 |= USART_CR1_UE;
    USART1->CR1 |= USART_CR1_TE;
    USART1->CR1 |= USART_CR1_RE;    
    /* 3.3 配置一个字的长度 8位 */
    USART1->CR1 &= ~USART_CR1_M;
    /* 3.4 配置不需要校验位 */
    USART1->CR1 &= ~USART_CR1_PCE;
    /* 3.5 配置停止位的长度为1个停止位 */
    USART1->CR2 &= ~USART_CR2_STOP;
}
/**
 * @description: 发送一个字节
 * @param {uint8_t} byte 要发送的字节
 */
void Driver_USART1_SendChar(uint8_t byte)
{
    /* 1. 等待发送寄存器为空 */
    while ((USART1->SR & USART_SR_TXE) == 0)
        ;
    /* 2. 数据写出到数据寄存器 */
    USART1->DR = byte;
}

/**
 * @description: 发送一个字符串
 * @param {uint8_t} *str 要发送的字符串
 * @param {uint16_t} len 字符串中字节的长度
 * @return {*}
 */
void Driver_USART1_SendString(uint8_t *str, uint16_t len)
{
    for (uint16_t i = 0; i < len; i++)
    {
        Driver_USART1_SendChar(str[i]);
    }
}

/**
 * @description: 接收一个字节的数据
 * @return {*} 接收到的字节
 */
uint8_t Driver_USART1_ReceiveChar(void)
{
    /* 等待数据寄存器非空 */
    while ((USART1->SR & USART_SR_RXNE) == 0)
        ;
    return USART1->DR;
}

/**
 * @description: 接收变长数据.接收到的数据存入到buff中
 * @param {uint8_t} buff 存放接收到的数据
 * @param {uint8_t} *len 存放收到的数据的字节的长度
 */
void Driver_USART1_ReceiveString(uint8_t buff[], uint8_t *len)
{
    uint8_t i = 0;
    while (1)
    {
        // 等待接收非空
        while ((USART1->SR & USART_SR_RXNE) == 0)
        {
            //在等待期间, 判断是否收到空闲帧
            if (USART1->SR & USART_SR_IDLE)
            {
                *len = i;
                return;
            }
        }
        buff[i] = USART1->DR;
        i++;
    }
}
main.c
#include "USART.h"
#include "Delay.h"
#include "string.h"

uint8_t buff[100] = {0};
uint8_t len = 0;

int main()
{
    Driver_USART1_Init();
    // Driver_USART1_SendChar('a');
    while (1)
    {
        // uint8_t *str = "Hello yangyang!\r\n";
        // Driver_USART1_SendString(str, strlen((char *)str));

        /* uint8_t *str = "yangyang\r\n";
        Driver_USART1_SendString(str, strlen((char *)str));
        Delay_s(1); */

        // uint8_t c =  Driver_USART1_ReceiveChar();
        // Driver_USART1_SendChar(c);

        Driver_USART1_ReceiveString(buff, &len);
        Driver_USART1_SendString(buff, len);
    }
}

验证二:中断方式收发

实现串口接收数据后原样返回,效果如下图:
image-20250204215912279

实现逻辑(关键):

  1. 在USART初始化时使能中断,设置中断模式和优先级
  2. 在中断响应函数函数中判断中断触发类型,完成对应的反应,利用标志位避免在中断中进行耗时类操作。
UASRT.h
#ifndef USART_H
#define __USART_H__

#include "stm32f10x.h"

void Driver_USART1_Init(void);

void Driver_USART1_SendChar(uint8_t byte);

void Driver_USART1_SendString(uint8_t *str, uint16_t len);

uint8_t Driver_USART1_ReceiveChar(void);

void Driver_USART1_ReceiveString(uint8_t buff[], uint8_t *len);

#endif
USART.c
#include "USART.h"

/**
 * @description: 初始化串口1
 */
void Driver_USART1_Init(void)
{
    /* 1. 开启时钟 */
    /* 1.1 串口1外设的时钟 */
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    /* 1.2 GPIO时钟 */
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;

    /* 2. 配置GPIO引脚的工作模式  PA9=Tx(复用推挽 CNF=10 MODE=11)  PA10=Rx(浮空输入 CNF=01 MODE=00)*/
    GPIOA->CRH |= GPIO_CRH_CNF9_1;
    GPIOA->CRH &= ~GPIO_CRH_CNF9_0;
    GPIOA->CRH |= GPIO_CRH_MODE9;

    GPIOA->CRH &= ~GPIO_CRH_CNF10_1;
    GPIOA->CRH |= GPIO_CRH_CNF10_0;
    GPIOA->CRH &= ~GPIO_CRH_MODE10;

    /* 3. 串口的参数配置 */
    /* 3.1 配置波特率 115200 */
    USART1->BRR = 0x271;
	/* 3.2 串口使能,并使能接收和发送 */
    //USART1->CR1 |= USART_CR1_UE;
    USART1->CR1 |= USART_CR1_TE;
    USART1->CR1 |= USART_CR1_RE;    
    /* 3.3 配置一个字的长度 8位 */
    USART1->CR1 &= ~USART_CR1_M;
    /* 3.4 配置不需要校验位 */
    USART1->CR1 &= ~USART_CR1_PCE;
    /* 3.5 配置停止位的长度为1个停止位 */
    USART1->CR2 &= ~USART_CR2_STOP;
	
	/* 3.6 使能串口的各种中断 */
    USART1->CR1 |= USART_CR1_RXNEIE; /* 接收非空中断 */
    USART1->CR1 |= USART_CR1_IDLEIE; /* 空闲中断 */

	/* 4. 配置NVIC */
    /* 4.1 配置优先级组 */
    NVIC_SetPriorityGrouping(3);
    /* 4.2 设置优先级 */
    NVIC_SetPriority(USART1_IRQn, 2);
    /* 4.3 使能串口1的中断 */
    NVIC_EnableIRQ(USART1_IRQn);

    /* 4. 使能串口 */
    USART1->CR1 |= USART_CR1_UE;
}


/**
 * @description: 发送一个字节
 * @param {uint8_t} byte 要发送的字节
 */
void Driver_USART1_SendChar(uint8_t byte)
{
    /* 1. 等待发送寄存器为空 */
    while ((USART1->SR & USART_SR_TXE) == 0)
        ;
    /* 2. 数据写出到数据寄存器 */
    USART1->DR = byte;
}

/**
 * @description: 发送一个字符串
 * @param {uint8_t} *str 要发送的字符串
 * @param {uint16_t} len 字符串中字节的长度
 * @return {*}
 */
void Driver_USART1_SendString(uint8_t *str, uint16_t len)
{
    for (uint16_t i = 0; i < len; i++)
    {
        Driver_USART1_SendChar(str[i]);
    }
}

/**
 * @description: 接收一个字节的数据
 * @return {*} 接收到的字节
 */
uint8_t Driver_USART1_ReceiveChar(void)
{
    /* 等待数据寄存器非空 */
    while ((USART1->SR & USART_SR_RXNE) == 0)
        ;
    return USART1->DR;
}

/**
 * @description: 接收变长数据.接收到的数据存入到buff中
 * @param {uint8_t} buff 存放接收到的数据
 * @param {uint8_t} *len 存放收到的数据的字节的长度
 */
void Driver_USART1_ReceiveString(uint8_t buff[], uint8_t *len)
{
    uint8_t i = 0;
    while (1)
    {
        // 等待接收非空
        while ((USART1->SR & USART_SR_RXNE) == 0)
        {
            //在等待期间, 判断是否收到空闲帧
            if (USART1->SR & USART_SR_IDLE)
            {
                *len = i;
                return;
            }
        }
        buff[i] = USART1->DR;
        i++;
    }
}

/* 缓冲接收到的数据 */
uint8_t buff[100] = {0};
/* 存储接收到的字节的长度 */
uint8_t len = 0;
uint8_t isToSend = 0;
void USART1_IRQHandler(void)
{
    /* 数据接收寄存器非空 */
    if (USART1->SR & USART_SR_RXNE)
    {
        // 对USART_DR的读操作可以将接收非空的中断位清零。 所以不用单独清除了.
        //USART1->SR &= ~USART_SR_RXNE;
        buff[len] = USART1->DR;
        len++;
    }
    else if (USART1->SR & USART_SR_IDLE)
    {
        /* 清除空闲中断标志位: 先读sr,再读dr.就可以实现清除了 */
        USART1->SR;
        USART1->DR;
        /* 变长数据接收完毕 */
        //Driver_USART1_SendString(buff, len);
        isToSend = 1;
        /* 把接收字节的长度清0 */
        // len = 0;
    }
}
main.c
#include "USART.h"


/* 缓冲接收到的数据 */
extern uint8_t buff[100];
/* 存储接收到的字节的长度 */
extern uint8_t len;
extern uint8_t isToSend;

int main()
{
    Driver_USART1_Init();
    while (1)
    {
        if(isToSend){
            Driver_USART1_SendString(buff, len);
            isToSend = 0;
            len = 0;
        }
    }
}

验证三: 重定向printf

image-20250205194550008

实现逻辑(关键):

​ printf实现的底层时调用fputc函数,所以要重写fputc函数。
在usart.h中引入stdio.h, usart.c添加fputc定义,同时在keil设置包含c语言库
image-20250205194723960

USART.h
#ifndef USART_H
#define USART_H

#include "stm32f10x.h"
#include "stdio.h"

void Driver_USART1_Init(void);

void Driver_USART1_SendChar(uint8_t byte);

void Driver_USART1_SendString(uint8_t *str, uint16_t len);

uint8_t Driver_USART1_ReceiveChar(void);

void Driver_USART1_ReceiveString(uint8_t buff[], uint8_t *len);

#endif

USART.c
#include "USART.h"

/**
 * @description: 初始化串口1
 */
void Driver_USART1_Init(void)
{
    /* 1. 开启时钟 */
    /* 1.1 串口1外设的时钟 */
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
    /* 1.2 GPIO时钟 */
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;

    /* 2. 配置GPIO引脚的工作模式  PA9=Tx(复用推挽 CNF=10 MODE=11)  PA10=Rx(浮空输入 CNF=01 MODE=00)*/
    GPIOA->CRH |= GPIO_CRH_CNF9_1;
    GPIOA->CRH &= ~GPIO_CRH_CNF9_0;
    GPIOA->CRH |= GPIO_CRH_MODE9;

    GPIOA->CRH &= ~GPIO_CRH_CNF10_1;
    GPIOA->CRH |= GPIO_CRH_CNF10_0;
    GPIOA->CRH &= ~GPIO_CRH_MODE10;

    /* 3. 串口的参数配置 */
    /* 3.1 配置波特率 115200 */
    USART1->BRR = 0x271;
	/* 3.2 串口使能,并使能接收和发送 */
    //USART1->CR1 |= USART_CR1_UE;
    USART1->CR1 |= USART_CR1_TE;
    USART1->CR1 |= USART_CR1_RE;    
    /* 3.3 配置一个字的长度 8位 */
    USART1->CR1 &= ~USART_CR1_M;
    /* 3.4 配置不需要校验位 */
    USART1->CR1 &= ~USART_CR1_PCE;
    /* 3.5 配置停止位的长度为1个停止位 */
    USART1->CR2 &= ~USART_CR2_STOP;
	
	/* 3.6 使能串口的各种中断 */
    USART1->CR1 |= USART_CR1_RXNEIE; /* 接收非空中断 */
    USART1->CR1 |= USART_CR1_IDLEIE; /* 空闲中断 */

	/* 4. 配置NVIC */
    /* 4.1 配置优先级组 */
    NVIC_SetPriorityGrouping(3);
    /* 4.2 设置优先级 */
    NVIC_SetPriority(USART1_IRQn, 2);
    /* 4.3 使能串口1的中断 */
    NVIC_EnableIRQ(USART1_IRQn);

    /* 4. 使能串口 */
    USART1->CR1 |= USART_CR1_UE;
}


/**
 * @description: 发送一个字节
 * @param {uint8_t} byte 要发送的字节
 */
void Driver_USART1_SendChar(uint8_t byte)
{
    /* 1. 等待发送寄存器为空 */
    while ((USART1->SR & USART_SR_TXE) == 0)
        ;
    /* 2. 数据写出到数据寄存器 */
    USART1->DR = byte;
}

/**
 * @description: 发送一个字符串
 * @param {uint8_t} *str 要发送的字符串
 * @param {uint16_t} len 字符串中字节的长度
 * @return {*}
 */
void Driver_USART1_SendString(uint8_t *str, uint16_t len)
{
    for (uint16_t i = 0; i < len; i++)
    {
        Driver_USART1_SendChar(str[i]);
    }
}

/**
 * @description: 接收一个字节的数据
 * @return {*} 接收到的字节
 */
uint8_t Driver_USART1_ReceiveChar(void)
{
    /* 等待数据寄存器非空 */
    while ((USART1->SR & USART_SR_RXNE) == 0)
        ;
    return USART1->DR;
}

/**
 * @description: 接收变长数据.接收到的数据存入到buff中
 * @param {uint8_t} buff 存放接收到的数据
 * @param {uint8_t} *len 存放收到的数据的字节的长度
 */
void Driver_USART1_ReceiveString(uint8_t buff[], uint8_t *len)
{
    uint8_t i = 0;
    while (1)
    {
        // 等待接收非空
        while ((USART1->SR & USART_SR_RXNE) == 0)
        {
            //在等待期间, 判断是否收到空闲帧
            if (USART1->SR & USART_SR_IDLE)
            {
                *len = i;
                return;
            }
        }
        buff[i] = USART1->DR;
        i++;
    }
}

/* 缓冲接收到的数据 */
uint8_t buff[100] = {0};
/* 存储接收到的字节的长度 */
uint8_t len = 0;
uint8_t isToSend = 0;
void USART1_IRQHandler(void)
{
    /* 数据接收寄存器非空 */
    if (USART1->SR & USART_SR_RXNE)
    {
        // 对USART_DR的读操作可以将接收非空的中断位清零。 所以不用单独清除了.
        //USART1->SR &= ~USART_SR_RXNE;
        buff[len] = USART1->DR;
        len++;
    }
    else if (USART1->SR & USART_SR_IDLE)
    {
        /* 清除空闲中断标志位: 先读sr,再读dr.就可以实现清除了 */
        USART1->SR;
        USART1->DR;
        /* 变长数据接收完毕 */
        //Driver_USART1_SendString(buff, len);
        isToSend = 1;
        /* 把接收字节的长度清0 */
        // len = 0;
    }
}

int fputc(int c, FILE *file)
{
    Driver_USART1_SendChar(c);
    return c;
}
main.c
#include "USART.h"

#include "Delay.h"

int main()
{
    Driver_USART1_Init();

    while (1)
    {
        printf("hello Yangyang\r\n");
        Delay_ms(500);
    }
}

2.2 库函数方式

验证一: 轮询的方式收发

实现串口接收数据后原样返回,效果如下图:
image-20250205000213797

实现逻辑(关键):

除了配置debug和时钟等基础功能外,还要在CubeMX配置USART相关参数,然后再在生成的工程中实现想要的功能逻辑。

配置过程:
image-20250204234932378
image-20250204234941786
image-20250204234948880
image-20250204234955860

添加功能代码

uint8_t buff[10];
int main(void)
{
   
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    while (1)
    {
        /* 从串口读取数据:
               参数1 指定的串口
               参数2:存储读取到的数据
               参数3:一共接收多少条数据
               参数4:超时时间
       */
        if (HAL_UART_Receive(&huart1, buff, 10, HAL_MAX_DELAY) == HAL_OK)
        {
            // 把收到的数据原封不动的发出去
            HAL_UART_Transmit(&huart1, buff, 10, HAL_MAX_DELAY);
        }
    }
}

验证二:中断方式收发定长数据和变长数据

实现逻辑(关键):
启用串口中断,两种逻辑思路。
1.在中断中实现串口收发。(中断中耗时和系统资源增加)
2.在中断中改变标志位,在主函数中循环判断标志位,完成收发。(主函数需要放置判断标志位到while循环)

实现串口接收数据后原样返回,效果如下图:
定长数据:

image-20250205115928306

变长数据:

image-20250205162820980

添加功能代码
方法一:在中断中设置标志位:

 main.c
 /* USER CODE BEGIN 2 */
// HAL_UART_Receive_IT(&huart1, buff, 10);  
HAL_UARTEx_ReceiveToIdle_IT(&huart1, rxBuff, 1000);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  
    
    if (isOver)
    {
      // HAL_UART_Transmit(&huart1, buff, 10, HAL_MAX_DELAY);
      HAL_UART_Transmit(&huart1, rxBuff, size, HAL_MAX_DELAY);
      isOver = 0;
    }
    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
it.c
/* USER CODE BEGIN 1 */
extern uint8_t isOver;
extern uint16_t size;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    isOver = 1;
}
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) 
{
    isOver = 1;
    size = Size;
}
/* USER CODE END 1 */

方法二:在中断中发送和接收:

定长数据
uint8_t buff[1];        // 接收缓冲, 一次接受一个字节的数据
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart1.Instance == USART1)
    {
        HAL_UART_Transmit(&huart1, buff, 1, HAL_MAX_DELAY);
        HAL_UART_Receive_IT(&huart1, buff, 1); // 继续接收
    }
}

int main(void)
{
    HAL_Init();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    /* 用中断的方式接收一个字节的数据 */
    HAL_UART_Receive_IT(&huart1, buff, 1);

    while (1)
    {
    }
}
变长数据
/* USER CODE BEGIN 0 */
uint8_t rxBuff[1000]; // 接收缓冲区
// Size 是实际接收的数据的长度
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) 
{
    if (huart1.Instance == USART1)
    {
        HAL_UART_Transmit(&huart1, rxBuff, Size, HAL_MAX_DELAY);
        HAL_UARTEx_ReceiveToIdle_IT(&huart1, rxBuff, 1000);
    }
}
/* USER CODE END 0 */

/**
 * @brief  The application entry point.
 * @retval int
 */
int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    /* 当接收到1000个字符或者碰到空闲帧, 则接收结束 */
    HAL_UARTEx_ReceiveToIdle_IT(&huart1, rxBuff, 1000);
    while (1)
    {
    }
}

验证三: 重定向printf

实现效果:
image-20250205201248020

实现逻辑(关键):
cubmx创建完工程后,在keil界面添加c语言简洁库,然后在usart.h头文件中添加stdio.h,最后在usart.c重写fputc感受,在函数内调用串口输出函数。
关键代码:

usart.c
/* USER CODE BEGIN 1 */
int fputc(int ch, FILE *f)
{
    /* 发送一个字节数据到串口DEBUG_USART */
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);    
    
    return (ch);
}
/* USER CODE END 1 */

usart.h
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */

main.c

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  printf("Hello World!\r\n");
  int n = 373737;
  printf("n = %d\r\n", n);

补充

HAL库函数中,调用USART函数的说明,来之 stm32f1xx_hal_uart.c

image-20250205112746976

image-20250205001328603

接收与发送字符

/* 从串口读取数据:
               参数1 指定的串口
               参数2:存储读取到的数据
               参数3:一共接收多少条数据
               参数4:超时时间
  */
HAL_UART_Receive(&huart1, buff, 10, HAL_MAX_DELAY)
HAL_UART_Transmit(&huart1, buff, 10, HAL_MAX_DELAY)

接收到定长字符触发中断,buff实际数据的地址,“1”传输的数据大小
HAL_UART_Receive_IT(&huart1, buff, 1)
接收定长数据中断的响应
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

接收不定长中断的触发
HAL_UARTEx_ReceiveToIdle_IT(&huart1, rxBuff, 1000);
接收不定长中断的响应
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) 

printf重定向,关键在重写fputc函数,需要引入stdio.h,要在keil的Target界面选上Use MicroLIB
posted @ 2025-01-18 22:51  Arsun  阅读(114)  评论(0)    收藏  举报