STM32 USART串口发送+接收

单片机学习!


目录

 前言

一、串口发送配置步骤

二、详细步骤

2.1 RCC开启USART和GPIO时钟

2.2 GPIO初始化

2.3 配置USART

2.4 开启USART

2.5 总初始化代码

三、接收数据

3.1 查询方法

3.2 中断方法

3.2.1 中断配置

3.2.2 接收函数

总结


 前言

        上篇博文介绍了串口发送的代码设计。本文主要介绍USART初始化配置、USART串口发送和接收的基础内容。


一、串口发送配置步骤

初始化流程,可以从基本结构图来梳理:

第一步,开启时钟,把需要用的USART和GPIO的时钟打开。

第二步,GPIO初始化,把TX配置成复用输出,RX配置成输入。

第三步,配置USART,直接使用一个结构体就可以把所有需要的参数都配置好。

第四步,如果只需要发送的功能,就直接开启USART,初始化就结束了。如果需要接收的功能,可能还需要配置中断,那就在开启USART之前,再加上ITCongfig和NVIC的代码就行了。

得益于库函数的封装,内部各种细节问题就不需要再关心了。

初始化完成之后

  • 如果要发送数据,调用一个发送的函数就行了;
  • 如果要接收数据,就调用接收的函数;
  • 如果要获取发送和接收的状态,就调用获取标志位的函数。

以上就是USART外设的使用思路。

二、详细步骤

2.1 RCC开启USART和GPIO时钟

        第一步开启时钟USART和GPIO的时钟。

代码示例:

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启USART1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIOA时钟

        开启USART1的时钟,这里USART1是APB2的外设,其他都是APB1的外设。然后还需要开启GPIO的时钟,看引脚定义表,USART1的TX是PA9,RX是PA10.

2.2 GPIO初始化

        第二步初始化GPIO引脚。在引脚定义表里可以找到USART1的TX复用在了PA9;USART1的RX复用在了PA10,所以这里初始化GPIOA的Pin_9和Pin_10

代码示例:

	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF_PP;//引脚模式选复用推挽输出
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_9;//引脚选择Pin_9
	GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);//初始化GPIOA

    GPIO_InitStruct.GPIO_Mode= GPIO_Mode_IPU;//引脚模式,上拉输入
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_10;//引脚选择Pin_10
	GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);//初始化GPIOA

        引脚模式的配置选择:

  • TX引脚是USART外设控制的输出引脚,所以要选复用推挽输出。
  • RX引脚是USART外设数据的输入引脚,所以要选择输入模式。

        输入模式并不分什么普通输入,复用输入。一根线只能有一个输出,但可以有多个输入。所以输入脚、外设和GPIO都可以同时用。一般RX配置是浮空输入或者上拉输入,因为串口波形空闲状态是高电平,所以不使用下拉输入。这里引脚模式的配置可以参考手册GPIO那一节推荐的配置表。

        本章程序需要实现数据发送和数据接收,所以

  • TX引脚初始化模式选择GPIO_Mode_AF_PP复用推挽输出模式;
  • RX引脚初始化模式选择GPIO_Mode_IPU上拉输入模式。

2.3 配置USART

        第三步初始化USART

代码示例:

	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;;//串口模式
	USART_InitStructure.USART_Parity = USART_Parity_No;//校验位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位
	USART_InitStructure.USART_WordLength =USART_WordLength_8b; //字长

	USART_Init(USART1,&USART_InitStructure);

        USART_BaudRate 波特率,可以直接写一个波特率的数值。这里给9600,写完数值之后USART_Init函数内部会自动算好9600对应的分频系数,然后写到BRR寄存器。


        USART_HardwareFlowControl 硬件流控制,这个参数的取值可以是

  • USART_HardwareFlowControl_None不使用流控;
  • USART_HardwareFlowControl_CTS只用CTS;
  • USART_HardwareFlowControl_RTS只用RTS;
  • USART_HardwareFlowControl_RTS_CTS是CTS和RTS都使用。

这里不使用流控,所以选择USART_HardwareFlowControl_None这个参数。


        USART_Mode串口模式,参数有

  • USART_Mode_Tx是Tx发送模式;
  • USART_Mode_Rx是Rx接收模式。
  • 如果既需要接收又需要发送,那就用或符号把Tx和Rx或起来。

代码示例:

USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;

这里程序需要发送和接收功能,所以选择USART_Mode_Tx | USART_Mode_Rx,就是同时开启发送和接收的部分。


        USART_Parity 校验位,参数有

  • USART_Parity_No无校验;
  • USART_Parity_Odd奇校验;
  • USART_Parity_Even偶校验。

这里不需要校验,所以选择USART_Parity_No无校验。


        USART_StopBits停止位,参数可以选择

  • USART_StopBits_0_5  是0.5位停止位;
  • USART_StopBits_1      是1位停止位;
  • USART_StopBits_1_5  是1.5位停止位;
  • USART_StopBits_2      是2位停止位。

这里选择USART_StopBits_1参数,就是1位停止位。


        USART_WordLength 字长,参数可以选择

  • USART_WordLength_8b八位字长;
  • USART_WordLength_9b九位字长。

因为不需要校验,前面设置了无校验参数,这里就选择USART_WordLength_8b字长为8位。

        以上结构体参数的初始化就完成了,对串口的配置是9600波特率、无流控、发送+接收模式、无校验位、1位停止位、八位字长。

2.4 开启USART

        第四步,开启USART,调用USART_Cmd函数,给指定的通道USART1使能。

代码示例:

    USART_Cmd(USART1,ENABLE);

2.5 总初始化代码

代码示例:

void Serial_Init(void)
{
	//第一步开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启USART1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIO的时钟

	
	//第二步初始化GPIO引脚
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF_PP;//引脚模式
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_9;//引脚选择Pin_9
	GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);//初始化GPIOA

	GPIO_InitStruct.GPIO_Mode= GPIO_Mode_IPU;//引脚模式
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_10;//引脚选择Pin_10
	GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);//初始化GPIOA
	
	
	//第三步初始化USART
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//串口模式
	USART_InitStructure.USART_Parity = USART_Parity_No;//校验位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位
	USART_InitStructure.USART_WordLength =USART_WordLength_8b; //字长

	USART_Init(USART1,&USART_InitStructure);
	
	USART_Cmd(USART1,ENABLE);
	
}

三、接收数据

        以上串口接收的代码已经配置差不多了。对于串口接收来说,可以使用查询和中断两种方法,如果使用查询,那初始化就结束了。如果使用中断,那还需要开启中断,配置NVIC。

下文将对查询和中断的方法分别举例。

3.1 查询方法

        查询的流程是在主函数里不断判断RXNE标志位,如果RXNE标志位置1了,就说明收到数据了,那再调用ReceiveData函数读取DR寄存器就可以了。

代码示例:

if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == SET)
{
    RxData = USART_ReceiveData(USART1);
}	

        目前接收到的一个字节就已经在RxData里了。还需要分析一下清除标志位的问题,查看手册:

        当RDR移位寄存器中的数据被转移到USART_DR寄存器中,该位被硬件置位。如果 USART_CR1寄存器中的RXNEIE1,则产生中断。对USART_DR的读操作可以将该位清零。RXNE位也可以通过写入0来清除,只有在多缓存通讯中才推荐这种清除程序。

        手册中表示读USART_DR可以自动清零标志位,所以上述代码读完USART_DR就不需要再清除标志位了。

        

3.2 中断方法

3.2.1 中断配置

        使用中断方法首先需要在初始化里加上开启中断的代码。

代码示例:

    USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//中断通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1; 
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =1;
	NVIC_Init(&NVIC_InitStructure);

        第一步,开启RXNE标志位到NVIC的输出,也就是配置USART1的接收中断使能,之后就可以配置NVIC了。

USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);

        配置中断分组:

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

        初始化NVIC的USART1通道:

    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//中断通道
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1; 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =1;
    NVIC_Init(&NVIC_InitStructure);

代码配置:使能中断通道为USART1全局中断,先占优先级为1,从优先级也为1,最后将结构体地址放入NVIC_Init函数。


        以上配置使RXNE标志位一但置1了,就会向NVIC申请中断,之后就可以在中断函数里接收数据。

中断接收初始化配置总代码:

void Serial_Init(void)
{
	//第一步开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启USART1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIO的时钟

	
	//第二步初始化GPIO引脚
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF_PP;//引脚模式
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_9;//引脚选择Pin_9
	GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);//初始化GPIOA

	GPIO_InitStruct.GPIO_Mode= GPIO_Mode_IPU;//引脚模式
	GPIO_InitStruct.GPIO_Pin= GPIO_Pin_10;//引脚选择Pin_10
	GPIO_InitStruct.GPIO_Speed= GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);//初始化GPIOA
	
	
	//第三步初始化USART
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件流控制
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//串口模式
	USART_InitStructure.USART_Parity = USART_Parity_No;//校验位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位
	USART_InitStructure.USART_WordLength =USART_WordLength_8b; //字长
	USART_Init(USART1,&USART_InitStructure);
	
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//配置USART中断
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组

	NVIC_InitTypeDef NVIC_InitStructure;//初始化NVIC的USART1通道
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//中断通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1; 
	NVIC_InitStructure.NVIC_IRQChannelSubPriority =1;
	NVIC_Init(&NVIC_InitStructure);

	USART_Cmd(USART1,ENABLE);
	
}

3.2.2 接收函数

代码示例:

uint8_t Serial_RxData;//接收到的数据
uint8_t Serial_RxFlag;//接收到数据的标志位

uint8_t Serial_GetRxFlag(void)
{
	if(Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}

uint8_t Serial_GetRxData(void)
{
	return Serial_RxData;
}	


void USART1_IRQHandler(void)
{
	if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)
	{
		Serial_RxData = USART_ReceiveData(USART1);
		Serial_RxFlag = 1;
		USART_ClearITPendingBit(USART1,USART_IT_RXNE); 
	}
}

    if(Serial_GetRxFlag()==1)
    {
        RxData = Serial_GetRxData();
        Serial_SendByte(RxData);
	}				


        Serial_GetRxFlag函数实现功能为变量Serial_RxData读后自动清除标志位Serial_RxFlag。

uint8_t Serial_GetRxFlag(void)
{
    if(Serial_RxFlag == 1)
    {
        Serial_RxFlag = 0;
        return 1;
    }
    return 0;
}


        Serial_GetRxData函数的功能为返回接收到的数据。

uint8_t Serial_GetRxData(void)
{
	return Serial_RxData;
}	


        USART1_IRQHandler函数是把数据进行了一次转存,最终还是要扫描查询Serial_RxFlag来接收数据。

void USART1_IRQHandler(void)
{
	if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)
	{
		Serial_RxData = USART_ReceiveData(USART1);
		Serial_RxFlag = 1;/
		USART_ClearITPendingBit(USART1,USART_IT_RXNE); 
	}
}

        先判断标志位:如果RXNE确实置1了,就进入if。

    if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)

        将接收到的数据读取给自定义变量Serial_RxData,读完后置一个自定义的标志位Serial_RxFlag。

        Serial_RxData = USART_ReceiveData(USART1);
        Serial_RxFlag = 1;

        在if里可以直接读取DR,执行一些操作。if里是否需要清除标志位可以看有没有读取DR:

  • 如果读取了DR,就可以自动清除标志位;
  • 如果没读取DR,就需要手动清除标志位。

示例里直接清除一下标志位

        USART_ClearITPendingBit(USART1,USART_IT_RXNE); 

        以上中断接收和变量的封装就完成了!想中断接收数据就可以直接调用函数来使用:

if(Serial_GetRxFlag()==1)
{
	RxData = Serial_GetRxData();//目前接收到的一个字节就已经在RxData里了
	Serial_SendByte(RxData);//把接收到的数据回传到电脑
}	

目前这个程序只支持一个字节的接收。


总结

         以上就是今天要讲的内容,本文仅仅简单介绍了USART初始化配置以及一些配置代码的细节。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络安全事件管理

一、背景 信息化技术的迅速发展已经极大地改变了人们的生活,网络安全威胁也日益多元化和复杂化。传统的网络安全防护手段难以应对当前繁杂的网络安全问题,构建主动防御的安全整体解决方案将更有利于防范未知的网络安全威胁。 国内外的安全事件在不断增…

AIGC--AIGC与人机协作:新的创作模式

AIGC与人机协作:新的创作模式 引言 人工智能生成内容(AIGC)正在以惊人的速度渗透到创作的各个领域。从生成文本、音乐、到图像和视频,AIGC使得创作过程变得更加快捷和高效。然而,AIGC并非完全取代了人类的创作角色&am…

【数据结构实战篇】用C语言实现你的私有队列

🏝️专栏:【数据结构实战篇】 🌅主页:f狐o狸x 在前面的文章中我们用C语言实现了栈的数据结构,本期内容我们将实现队列的数据结构 一、队列的概念 队列:只允许在一端进行插入数据操作,在另一端…

RHCSA作业

课后练习 将整个 /etc 目录下的文件全部打包并用 gzip 压缩成/back/etcback.tar.gz [rootlocalhost ~]# tar -czvf /back/etcback.tar.gz -C / etc 使当前用户永久生效的命令别名:写一个命令命为hello,实现的功能为每输入一次hello命令,就有hello&#…

java:拆箱和装箱,缓存池概念简单介绍

1.基本数据类型及其包装类: 举例子: Integer i 10; //装箱int n i; //拆箱 概念: 装箱就是自动将基本数据类型转换为包装器类型; 拆箱就是自动将包装器类型转换为基本数据类型; public class Main {public s…

如何选择最适合企业的ETL解决方案?

在今天的大数据时代,企业的数据管理和处理变得愈发重要。企业也越来越依赖于数据仓库和数据湖来提取、转换和加载(ETL)关键业务信息。一个高效、灵活的ETL解决方案不仅能提升数据处理能力,还能为企业决策提供有力支持。然而&#…

[网鼎杯 2020 朱雀组]phpweb 详细题解(反序列化绕过命令执行)

知识点: call_user_func() 函数 反序列化魔术方法 find命令查找flag 代码审计 打开题目,弹出上面的提示,是一个警告warning,而且页面每隔几秒就会刷新一次,根据warning中的信息以及信息中的时间一直在变,可以猜测是date()函数一直在被调用 查看源代码发现一些信息,但是作用…

数字图像处理(2):Verilog基础语法

(1)Verilog常见数据类型: reg型、wire型、integer型、parameter型 (2)Verilog 常见进制:二进制(b或B)、十进制(d或D)、八进制(o或O)、…

c++:面向对象三大特性--继承

面向对象三大特性--继承 一、继承的概念及定义(一)概念(二)继承格式1、继承方式2、格式写法3、派生类继承后访问方式的变化 (三)普通类继承(四)类模板继承 二、基类和派生类的转换&a…

数据结构 (12)串的存储实现

一、顺序存储结构 顺序存储结构是用一组连续的存储单元来存储串中的字符序列。这种存储方式类似于线性表的顺序存储结构,但串的存储对象仅限于字符。顺序存储结构又可以分为定长顺序存储和堆分配存储两种方式。 定长顺序存储: 使用静态数组存储&#xff…

在线绘制Nature Communication同款双色、四色火山图,突出感兴趣的基因

导读:火山图通常使用三种颜色分别表示显著上调,显著下调和不显著。通过为特定的数据点添加另一种颜色,可以创建双色或四色火山图,从而更直观地突出感兴趣的数据点。 《Nature Communication》文章“Molecular and functional land…

2024赣ctf-web -wp

1.你到底多想要flag??? 首先来解决第一关: 先了解一下stripos(); 并且此函数处理数组返回false。而且pre_match同样遇见数组是返回false(解释一下正则 i:这是正则表达式的修饰符,代表“不区…

计算机毕业设计Python+大模型美食推荐系统 美食可视化 美食数据分析大屏 美食爬虫 美团爬虫 机器学习 大数据毕业设计 Django Vue.js

温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…

Linux 查看内核日志的方法

文章目录 1. dmesg 命令一. 介绍内核环形缓冲区的特点 二. 主要功能三. dmesg 使用 2. 查看kmsg文件/dev/kmsg 的用途使用 /dev/kmsg与 dmesg 的关系 3. 内核日志消息的打印行为 1. dmesg 命令 一. 介绍 dmesg(display message 或 display driver message 的缩写&…

Perforce SAST专家详解:自动驾驶汽车的安全与技术挑战,Klocwork、Helix QAC等静态代码分析成必备合规性工具

自动驾驶汽车安全吗?现代汽车的软件包含1亿多行代码,支持许多不同的功能,如巡航控制、速度辅助和泊车摄像头。而且,这些嵌入式系统中的代码只会越来越复杂。 随着未来汽车的互联程度越来越高,这一趋势还将继续。汽车越…

从Full-Text Search全文检索到RAG检索增强

从Full-Text Search全文检索到RAG检索增强 时光飞逝,转眼间六年过去了,六年前铁蛋优化单表千万级数据查询性能的场景依然历历在目,铁蛋也从最开始做CRUD转行去了大数据平台开发,混迹包装开源的业务,机缘巧合下做了实时…

LLM PPT Translator

LLM PPT Translator 引言Github 地址UI PreviewTranslated Result Samples 引言 周末开发了1个PowerPoint文档翻译工具,上传PowerPoint文档,指定想翻译的目标语言,通过LLM的能力将文档翻译成目标语言的文档。 Github 地址 https://github.…

【踩坑】git中文乱码问题

转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你,欢迎[点赞、收藏、关注]哦~ 背景说明 使用git diff显示中文乱码,如: 修复方法 执行一次: export LESSCHARSETutf-8 如果需要下次登录免输入…

安装Docker报错TCP connection reset by peer或者Timeout

原因:访问的外网下载导致超时或者断连接报错 修改为国内阿里下载地址 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

Linux宝塔部署wordpress网站更换服务器IP后无法访问管理后台和打开网站页面显示错乱

一、背景: wordpress网站搬家,更换服务器IP后,如果没有域名时,使用服务器IP地址无法访问管理后台和打开网站页面显示错乱。 二、解决方法如下: 1.wordpress搬家后,在新服务器上,新建站点时&am…