0

0

0

修罗

站点介绍

只有了解事实才能获得真正的自由

USART

修罗 2025-08-21 781 5条评论 stm32

首页 / 正文

USART

​ STM32提供了USART(Universal Synchronous Asynchronous Receiver and Transmitter)通用同步异步收发器。是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。UART相比USART去掉了同步通讯功能。

​ 一共提供5个串口供开发者选择。

image-20250821084803338.png

​ STM32的USART功能框图如下:

image-20250821084714279.png

1.1 功能引脚说明

  1. TX:发送数据输出引脚;
  2. RX:接收数据输入引脚;
  3. SW_RX:数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体外部引脚;
  4. nRTS:请求以发送(Request To Send),n 表示低电平有效。如果使能RTS流控制,当 USART接收器准备好接收新数据时就会将nRTS变成低电平;当接收寄存器已满时,nRTS 将被设置为高电平。该引脚只适用于硬件流控制; 可以把 nRTS 看作 接收端的“手”,举手(拉高)表示“先别发”
  5. nCTS:清除以发送(Clear To Send),n 表示低电平有效。如果使能CTS流控制,发送器在发送下一帧数据之前会检测nCTS引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制;可以把 nCTS 看作 接收端给发送端的“绿灯”,低电平=绿灯,允许发;高电平=红灯,暂停;
  6. SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式;

1.2 STM32 串口寄存器配置开发流程(USART1)

1.2.1 使能时钟

需要使能两个时钟:GPIOA 时钟(因为 USART1 默认用 PA9=TX, PA10=RX)、 USART1 外设时钟

RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;   // 使能 GPIOA 时钟
RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // 使能 USART1 时钟

1.2.2 配置STM32的RXTX串口引脚

配置PA9 (TX) → 复用推挽输出、PA10 (RX) → 浮空输入

// PA9 = USART1_TX
GPIOA->CRH &= ~(GPIO_CRH_MODE9 | GPIO_CRH_CNF9);
GPIOA->CRH |=  (GPIO_CRH_MODE9_1 | GPIO_CRH_MODE9_0); // 输出模式 50MHz
GPIOA->CRH |=  GPIO_CRH_CNF9_1; // 复用推挽输出

// PA10 = USART1_RX
GPIOA->CRH &= ~(GPIO_CRH_MODE10 | GPIO_CRH_CNF10);
GPIOA->CRH |=  GPIO_CRH_CNF10_0; // 浮空输入

1.2.3 配置波特率

​ 发送器和接收器的波特率是一致的,都是通过设置BRR寄存器来得到

​ 波特率计算公式:

image-20250821091520508.png

​ 这里的image-20250821091608340.png是给外设的时钟(usart1在APB2上一般是72MHz,usart2,3,4,5在APB1上一般为36MHz)。

​ 如下图BRR寄存器,往这个寄存器写USARTDIV即可完成设置波特率:

image-20250821091809023.png

​ 假设我们需要的波特率是115200,通过公式反推USARTDIV:72*10^6 / 16 / 115200,(72*10^6为72Mhz)则对应的分频值应该是:39.0625,把这个值写入到BRR寄存器中。BRR寄存器DIV_Fraction内容为小数部分 * 16

image-20250821091938167.png

​ 39.0625的小数部分:0.0625 * 16 = 1, 整数部分是:39(0x27 / 100111),所以写入到BRR寄存器的值是:0x0271 (1001110001);注: 16进制添加一位即二级制添加四位。

USART1->BRR = 0x0271; // 设置波特率 115200

1.2.4 配置 USART 控制寄存器

  • UE (USART Enable) = 1 → 使能 USART
  • TE (Transmitter Enable) = 1 → 使能发送器
  • RE (Receiver Enable) = 1 → 使能接收器
  • 8 数据位,1 停止位,无校验(默认)
USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
  • 寄存器

image-20250821094753977.png

image-20250821100451973.png

1.2.5 发送数据(轮询方式)

  • 检查 TXE (发送数据寄存器为空时) 位 → USART1->SR & USART_SR_TXE
  • 写入 USART1->DR
/**
 * @description: 发送一个字节
 * @param {char} byte 要发送的字节
 */
void USART1_SendChar(char byte)
{
    while(!(USART1->SR & USART_SR_TXE)); // 等待 SR寄存器下的TXE=1
    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]);
    }
}
  • 寄存器

image-20250821094828230.png

image-20250821094838069.png

1.2.6 接收数据(轮询方式)

  • 检查 RXNE (接收数据寄存器非空) 位 → USART1->SR & USART_SR_RXNE
  • 读取 USART1->DR
char USART1_ReceiveChar(void)
{
    while(!(USART1->SR & USART_SR_RXNE)); // 等待 RXNE=1
    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++;
    }
}
  • 寄存器

image-20250821094905541.png

1.2.7 完整代码和测试

// Driver_USART

/**
 * @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 配置停止位的长度 */
    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 "string.h"

uint8_t buff[100] = {0};
uint8_t len = 0;
int main()
{
    Driver_USART1_Init();
    while (1)
    {
        Driver_USART1_ReceiveString(buff, &len);
        Driver_USART1_SendString(buff, len);
    }
}

1.2.8 中断方式接收数据

​ USART提供了多个中断事件

image-20250821151713648.png

  • 使能串口、使能接收中断 RXNEIE
  • 配置 NVIC
// 上节初始化代码Driver_USART1_Init加上下面代码:


USART1->CR1 |= USART_CR1_IDLEIE; /* 空闲中断 */
USART1->CR1 |= USART_CR1_RXNEIE; // 使能接收中断

NVIC_SetPriorityGrouping(3); /*  配置优先级组 */
NVIC_SetPriority(USART1_IRQn, 2);/* 设置优先级 */
NVIC_EnableIRQ(USART1_IRQn);     // 开启 NVIC 中断

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

// 这里只是声明,不分配存储空间,分配在main.c中,最终编译时,这几个变量只会有一份存储空间
extern uint8_t buffer[100];
extern uint8_t size;
extern uint8_t isOver;
// 中断服务函数
void USART1_IRQHandler(void)
{
    if(USART1->SR & USART_SR_RXNE) { // 收到数据,SR寄存器的USART_SR_RXNE这位为高电平
         buffer[size] = USART1->DR;// 读数据放到缓冲区
         size++;  
    }else if (USART1->SR & USART_SR_IDLE)
    {
        // 字符串整体接收完成
        // 清除IDLE标志位
        USART1->DR;

        isOver = 1;
    }
}

main.c

#include "stm32f10x.h"
#include "Driver_USART.h"
#include "string.h"

// 定义全局变量,接收缓冲区和size
uint8_t buffer[100] = {0};
uint8_t size = 0;

// 定义标志位
uint8_t isOver = 0;
int main(void)
{

    Driver_USART1_Init();
    // 发送字符串
    uint8_t * str = "Hello, World!\n";
    Driver_USART1_SendString(str, strlen((char *)str));
    while(1)
    {
        // 中断接收到数据,回复相同数据
        if (isOver)
        {
            Driver_USART1_SendString(buffer, size);

            // 清除标志
            isOver = 0;
            size = 0;
        }
    }
}

评论(5)

  1. 1 游客 2025-09-01 11:26 回复

    1

  2. 1 游客 2025-09-01 12:02 回复

    1

  3. @@ZZAh2 游客 2025-09-01 12:03 回复

    1

  4. 1 游客 2025-09-01 12:06 回复

    1

  5. 1 游客 2025-09-01 12:07 回复

    1


最新评论

  • https://www.studyglobus.com/author/carroll34a5552/

    Ценники на вывоз мусора сейчас космос, любые лайфхаки по экономии в тему.

  • Richardgrold

    1xbet onlayn kazino platforması ilə vaxtınızı daha səmərəli və maraqlı keçirin. Saytın canlı kazino bölməsi istifadəçilərə fərqli bir dünya təklif edir. Burada canlı dilerli kazino oyunlarının hər bir növünü sınaqdan keçirə bilərsiniz. Krupyelərin peşəkarlığı oyunun dinamikasını və marağını artırır. Müntəzəm olaraq yenilənən oyun siyahısı cansıxıcılığa yol vermir. Hesabınızı artırmaq üçün onlarla fərqli və etibarlı üsul mövcuddur. Qazanılan pullar cəmi bir neçə dəqiqə ərzində şəxsi hesabınıza köçürülür. Aktiv fəaliyyətinizə görə fərdi bonuslar və hədiyyələr qazana bilərsiniz. Dəstək xidməti həm çat, həm də e-poçt vasitəsilə kömək göstərir. 1xbet ailəsinə qoşulun və böyük uduşlar üçün ilk addımı atın. onlayn kazino

  • 砂岩白丘

    我一直梦想, 看到你们相册那样的地方。真棒。

  • 城市地標

    我喜欢, 这里分享真实经验。你的项目 就是 属于这里的。请继续。

  • 宗教地標

    我热爱这样的想法, 去那么多国家。真的很鼓舞。

  • 觀景高地

    能感受到热爱。继续保持 感受。

  • 最佳季節

    我关注这样的资源, 写得很实在。你的网站 就是 属于这里的。加油。

  • 1

    1

  • 1

    1

  • -1' OR 2+158-158-1=0+0+0+1 or 'TKCTZnRa'='

    1

日历

2026年07月

   1234
567891011
12131415161718
19202122232425
262728293031 

文章目录

推荐关键字: Linux webpack js 算法 MongoDB laravel JAVA jquery javase redis