打印
[其他ST产品]

STM32串口(USART)详解

[复制链接]
220|19
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
概述:
串口的重点难点要点就是几个标志位

===========================================================================================

重点重点重点:下面这看明白了我相信你就是真的明白32的串口工作原理了,最后面也有,这个实在很重要,所以头尾都放一份,提到的框图和和函数在下文,往下拉就有

USART_FLAG_TXE:
注意左上角紫色的框,以及发送数据的流程,当我们去调用函数**USART_SendData(pUSARTx,ch);的时候,一个字节的数据就会被写入发送数据寄存器(TDR),注意USART_FLAG_TXE这个标志位,不发送数据的时候,闲着的时候都是0,当检测到数据写入发送数据寄存器TDR的时候,就会立刻将USART_FLAG_TXE置1,然后数据移位寄存器(TSR)就开始从TDR寄存器中取数据,一位一位的取走发送出去,当被取完的时候,发送数据寄存器TDR也就为空了,所以此时USART_FLAG_TXE**标志位就会被置0 ,所以也是我们while检测这个标志的原因

USART_FLAG_TC:
我们来假设一个过程,当我们调用**USART_SendData(pUSARTx,ch)发送一个字节的数据的时候,此时数据移位寄存器(TSR)就会立刻从数据发送寄存器(TDR)中开始搬运数据并一位一位发送出去,但是受限于波特率,搬运的速度会远大于发送的速率,而只要搬运完了,USART_FLAG_TXE这个标志位就会立刻被置0,我们while循环检测这个标志,此时就会被唤醒,看我上面发送字符串的函数就能看出来,下一个数据立刻又被写入发送数据寄存器(TDR),然后移位数据寄存器又开始搬运了,注意注意:此时移位数据寄存器中,上一个字节的数据还不一定发送完成呢,所以会为空吗?不会,所以所以:USART_FLAG_TC**这个标志位的作用是:检测数据移位寄存器是否为空,当开始发送数据,数据移位寄存器从数据发送寄存器中搬运数据,这个标志位就会被置1,当所有数据发送完成,移位寄存器为空时,就会被置0,所以通过这个标志位,可以实现在字节流的传输方式中实现帧传输

使用特权

评论回复
评论
回复就哭哭 2023-7-25 12:24 回复TA
———————————————— 版权声明:本文为CSDN博主「__void」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/Newmamahaha/article/details/125431488 
沙发
回复就哭哭|  楼主 | 2023-7-25 12:24 | 只看该作者
我们常常容易混淆通讯方式和通讯协议,很多初学者分不清这些东西,USART(串口)可以理解为一种通讯的方式或者说通讯的桥梁,那通信协议呢,打个比方,两个人事先约定好,你说1,我就说2,听到你说2之后,我就接3……像这样是通讯协议,那具体你们是打电话说,还是见面说,都行,在这个例子中,使用电话或者线下见面直接说就是通讯方式,可以理解为串口/wifi/以太网,而你们之间的约定就是通讯协议,串口比较常用,通常用来调试输入信息,以及数据接收和发送,有很多的嵌入式设备都是依靠串口发送和接收的。

串口又分为TTL、RS232、RS485,那具体这些有什么区别呢?其实都一样,都是接3条线,只是TTL串口就是单片机上的串口,5V代表1,0V代表0,这样传输的时候,抗干扰能力很弱,远距离的时候,传输的波形会失真,所以后来发明了RS232和RS485,使用的是差分信号,RS232大概就是15V表示1,-15V表示0,这样传输的时候波形抗干扰就会很强,传输距离就远,差不多是这意思,下面就详细了解一下STM32的USART的使用

为什么这一节不详细分析USART的源码呢,因为我们平时使用的时候,很少见给串口搞同步的,还有什么流控之类的,也没具体去了解,到目前为止,串口的功能都裁剪的就剩下使用3条线,配置串口参数的环节,我们只关心:波特率、停止位、校验位,但是标准库的源码中,对串口这一章的配置是比较复杂的,我只是挑主要的看了个大概,并没有每一行都理清楚,所以本节不分析串口初始化源码

使用特权

评论回复
板凳
回复就哭哭|  楼主 | 2023-7-25 12:25 | 只看该作者
下面梳理一下大概的流程:

使能 RX 和 TX 引脚 GPIO 时钟和 USART 时钟
初始化 GPIO,并将 GPIO 复用到 USART 上
配置 USART 参数
配置中断控制器并使能 USART 接收中断
使能 USART
在 USART 接收中断服务函数实现数据接收和发送
第一和第二基本没啥难度了,对引脚的初始化,第三就是配置波特率等等,串口最重要的是,下面这个,只要掌握了这个,就算玩明白了,那我们就来详细的看一下几个标志位

使用特权

评论回复
地板
回复就哭哭|  楼主 | 2023-7-25 12:25 | 只看该作者
标志位!对串口最重要的就是几个发送和接收的标志位,以及发送和接收函数



使用特权

评论回复
5
回复就哭哭|  楼主 | 2023-7-25 12:25 | 只看该作者
//USART初始化结构体
typedef struct {
    uint32_t USART_BaudRate;            // 波特率
    uint16_t USART_WordLength;          // 字长
    uint16_t USART_StopBits;            // 停止位
    uint16_t USART_Parity;              // 校验位
    uint16_t USART_Mode;                // USART模式
    uint16_t USART_HardwareFlowControl; // 硬件流控制
} USART_InitTypeDef;

使用特权

评论回复
6
回复就哭哭|  楼主 | 2023-7-25 12:25 | 只看该作者
USART_BaudRate: 波特率设置。一般设置为2400、9600、19200、115200。标准库函数会根据设定值计算得到USARTDIV值,从而设置USART_BRR寄存器值。

USART_WordLength: 数据帧字长,可选8位或9位。它设定USART_CR1寄存器的M位的值。如果没有使能奇偶校验控制,一般使用8数据位;如果使能了奇偶校验则一般设置为9数据位。

USART_StopBits: 停止位设置,可选0.5个、1个、1.5个和2个停止位,它设定USART_CR2寄存器的STOP[1:0]位的值,一般我们选择1个停止位。

USART_Parity: 奇偶校验控制选择,可选USART_Parity_No(无校验)、USART_Parity_Even(偶校验)以及USART_Parity_Odd(奇校验),它设定USART_CR1寄存器的PCE位和PS位的值。

USART_Mode: USART模式选择,有USART_Mode_Rx和USART_Mode_Tx,允许使用逻辑或运算选择两个,它设定USART_CR1寄存器的RE位和TE位。

USART_HardwareFlowControl: 硬件流控制选择,只有在硬件流控制模式才有效,可选有⑴使能RTS、⑵使能CTS、⑶同时使能RTS和CTS、⑷不使能硬件流。

当使用同步模式时需要配置SCLK引脚输出脉冲的属性,标准库使用一个时钟初始化结构体USART_ClockInitTypeDef来设置,该结构体内容也只有在同步模式才需要设置。

使用特权

评论回复
7
回复就哭哭|  楼主 | 2023-7-25 12:26 | 只看该作者
下面是从野火例程白**过来的初始化函数,写的很好很精髓,很香只能说,可以参考一下
/**
* 串口宏定义,不同的串口挂载的总线和IO不一样,移植时需要修改这几个宏
*/

// 串口1-USART1
#define  DEBUG_USARTx                   USART1
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd

#define  DEBUG_USART_TX_GPIO_PORT       GPIOA
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

#define  DEBUG_USART_IRQ                USART1_IRQn
#define  DEBUG_USART_IRQHandler         USART1_IRQHandler

static void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    /* 嵌套向量中断控制器组选择 */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    /* 配置USART为中断源 */
    NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
    /* 抢断优先级为1 */
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    /* 子优先级为1 */
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    /* 使能中断 */
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    /* 初始化配置NVIC */
    NVIC_Init(&NVIC_InitStructure);
}

void USART_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 打开串口GPIO的时钟
    DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);

    // 打开串口外设的时钟
    DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

    // 将USART Tx的GPIO配置为推挽复用模式
    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

    // 将USART Rx的GPIO配置为浮空输入模式
    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);

    // 配置串口的工作参数
    // 配置波特率
    USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
    // 配置 针数据字长
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    // 配置停止位
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    // 配置校验位
    USART_InitStructure.USART_Parity = USART_Parity_No ;
    // 配置硬件流控制
    USART_InitStructure.USART_HardwareFlowControl =
        USART_HardwareFlowControl_None;
    // 配置工作模式,收发一起
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    // 完成串口的初始化配置
    USART_Init(DEBUG_USARTx, &USART_InitStructure);

    // 串口中断优先级配置
    NVIC_Configuration();

    // 使能串口接收中断
    USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);

    // 使能串口
    USART_Cmd(DEBUG_USARTx, ENABLE);
}

使用特权

评论回复
8
回复就哭哭|  楼主 | 2023-7-25 12:26 | 只看该作者
梳理一下流程:

引脚配置,引脚使能
因为串口接收是可以触发中断的,轮询的方式太low了,所以配置串口接收中断
USART参数配置
使能串口,使能接收中断
提供中断服务函数
然后下面就可以愉快的接收和发送了

使用特权

评论回复
9
回复就哭哭|  楼主 | 2023-7-25 12:26 | 只看该作者
我们先来分析5,中断服务函数,以例程中的串口为例,

申明:很多代码呢,我本人学的时候就是用野火的学的,写的时候也参考他们的,所以现在回顾,他们的代码非常好啊,而且流程就是这样,你说我原创博客就该全部自己写,没必要的其实,那我怎么写也是这么写,很多代码是野火例程中抄过来的,不喜勿看

void DEBUG_USART_IRQHandler(void)
{
    uint8_t ucTemp;
    if (USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET) {
        ucTemp = USART_ReceiveData( DEBUG_USARTx );
        USART_SendData(DEBUG_USARTx,ucTemp);
    }
}

使用特权

评论回复
10
回复就哭哭|  楼主 | 2023-7-25 12:26 | 只看该作者
USART_IT_RXNE:接收数据寄存器满标志,再简单分析,首先,当接收到数据的时候,就会立刻进入到这个函数,但是这里为什么要一直检测这个标志位呢?USART_IT_RXNE的取值只有SET和RESET两种,当接收数据完成,也就说8个bit都接收了,代表一个字节的数据接收完成,就能跳出while循环,调用了**USART_ReceiveData()**这个函数读取数据,实际上这个函数是读取DR寄存器,读DR寄存器会将这个标志位清0,所以此处没有手动清0,当然手动清0也可以,我真的服了去百度上搜这个标志位,我就想搜这玩意的作用,真的是很难搜到,搜索结果真是花里胡哨牛头不对马嘴

使用特权

评论回复
11
回复就哭哭|  楼主 | 2023-7-25 12:27 | 只看该作者
接收的比较简单,还不明白的看下面,发送函数的分析

使用特权

评论回复
12
回复就哭哭|  楼主 | 2023-7-25 12:27 | 只看该作者
/*****************  发送一个字符 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
    /* 发送一个字节数据到USART */
    USART_SendData(pUSARTx,ch);

    /* 等待发送数据寄存器为空 */
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

/*****************  发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
    unsigned int k=0;
    do {
        Usart_SendByte( pUSARTx, *(str + k) );
        k++;
    } while (*(str + k)!='\0');

    /* 等待发送完成 */
    while (USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET) {
    }
}

使用特权

评论回复
13
回复就哭哭|  楼主 | 2023-7-25 12:27 | 只看该作者
重点重点重点:下面这看明白了我相信你就是真的明白32的串口工作原理了

USART_FLAG_TXE:
注意左上角紫色的框,以及发送数据的流程,当我们去调用函数**USART_SendData(pUSARTx,ch);的时候,一个字节的数据就会被写入发送数据寄存器(TDR),注意USART_FLAG_TXE这个标志位,不发送数据的时候,闲着的时候都是0,当检测到数据写入发送数据寄存器TDR的时候,就会立刻将USART_FLAG_TXE置1,然后数据移位寄存器(TSR)就开始从TDR寄存器中取数据,一位一位的取走发送出去,当被取完的时候,发送数据寄存器TDR也就为空了,所以此时USART_FLAG_TXE**标志位就会被置0 ,所以也是我们while检测这个标志的原因

使用特权

评论回复
14
回复就哭哭|  楼主 | 2023-7-25 12:27 | 只看该作者
USART_FLAG_TC:
我们来假设一个过程,当我们调用**USART_SendData(pUSARTx,ch)发送一个字节的数据的时候,此时数据移位寄存器(TSR)就会立刻从数据发送寄存器(TDR)中开始搬运数据并一位一位发送出去,但是受限于波特率,搬运的速度会远大于发送的速率,而只要搬运完了,USART_FLAG_TXE这个标志位就会立刻被置0,我们while循环检测这个标志,此时就会被唤醒,看我上面发送字符串的函数就能看出来,下一个数据立刻又被写入发送数据寄存器(TDR),然后移位数据寄存器又开始搬运了,注意注意:此时移位数据寄存器中,上一个字节的数据还不一定发送完成呢,所以会为空吗?不会,所以所以:USART_FLAG_TC**这个标志位的作用是:检测数据移位寄存器是否为空,当开始发送数据,数据移位寄存器从数据发送寄存器中搬运数据,这个标志位就会被置1,当所有数据发送完成,移位寄存器为空时,就会被置0,所以通过这个标志位,可以实现在字节流的传输方式中实现帧传输

使用特权

评论回复
15
zhuomuniao110| | 2023-7-25 21:46 | 只看该作者
看了一下都是以前的老库,新的都是LL或者HAL。

使用特权

评论回复
16
Clyde011| | 2023-12-29 07:16 | 只看该作者

要把纹波电流平均分布

使用特权

评论回复
17
公羊子丹| | 2023-12-29 08:09 | 只看该作者

发射出过量的电磁干扰(EMI)

使用特权

评论回复
18
万图| | 2023-12-29 09:12 | 只看该作者

典型的转换时间大约是50ns

使用特权

评论回复
19
Uriah| | 2023-12-29 10:15 | 只看该作者

这种方法可以使散热体积和表面面积增大两倍多

使用特权

评论回复
20
帛灿灿| | 2023-12-29 12:11 | 只看该作者

负载环能以直流方式汲取能量

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

19

主题

268

帖子

0

粉丝