STM32之USART串口通信

Good Good Study,Day Day Up!!!


今天写一下关于串口通信的内容,实现的是串口助手向单片机发送信息,并显示到LCD上。


明确几点:

  • USART通信中我们大多采用异步通信;
  • 异步通信的通信内容是数据帧,以波特率为媒介进行通信,所以通信时一定要保证通信双方的波特率一致!!!
  • 波特率表示每秒发送一位二进制数的速率,常用的通信波特率有:2400、4800、9600、115200;
  • 数据帧的格式是:起始位(0表示开始发送)+数据位(8位)+奇偶校验位+停止位(1表示数据发送完毕);

来看原理图:
在这里插入图片描述
可以看到有两个串口,那么我们再来分别看一下这两个串口在电路图中是怎么连接的:
在这里插入图片描述
我们可以看到串口1的通信引脚是接在RS232的接口上的,这种接口一般不是很常用,我们经常使用的接口应该都是USB接口,所以我们不用串口1来进行通信。再来看一下串口2:
在这里插入图片描述
串口2连接USB接口,所以我们利用串口2(USART2)来进行串口通信,那么我们就用到PA2(数据发送引脚)和PA3(数据接收引脚)这两个引脚。

下面我们来看一下USART的配置流程:使能时钟+配置相关引脚->配置时钟相关参数->时钟中断配置->时钟使能+中断使能
我们来按照步骤一步一步配置:

  • 使能时钟(GPIOA和USART2)+配置相关引脚(PA2和PA3
    这里要注意的是:
  1. TXD(PA2)的引脚模式要配置为复用推挽输出模式,RXD(PA3)要配置为浮空输入模式;
  2. 只有输出引脚(PA2)需要配置输出速率;
  3. GPIOA是挂接在APB2的总线上的,USART2是挂接在APB1通信总线上的。
//使能GPIOA和USART2时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);

//RXD-PA3 设置为浮空模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);

//TXD-PA2 设置为推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
  • 配置时钟相关参数
    时钟参数包括:波特率、数据长度、停止位数、校验位、模式、硬件控制流;
    这些参数我们同样可以在库函数(stm32f10x_usart.h)中找到相关定义:
    在这里插入图片描述
    代码:
//USART2的配置
USART_InitStructure.USART_BaudRate = 9600;//设置波特率为9600
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//设置数据长度为8位
USART_InitStructure.USART_StopBits = USART_StopBits_1;//设置停止位数为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(USART2, &USART_InitStructure);//初始化USART2

做几点说明:

  1. 关于数据长度:可设置为8位数据模式也可设置为9位数据模式;
    在这里插入图片描述
  2. 停止位数:可设置为1位-2位;
    在这里插入图片描述
  3. 奇偶校验位:
    在这里插入图片描述
  4. 硬件流控制:
    依次是:硬件控制流失能、发送请求RTS使能、清除发送CTS使能、PTS和CTS使能
    在这里插入图片描述
  5. 串口模式:
    在这里插入图片描述
  • 时钟中断配置(中断优先级配置)
    与定时器中断优先级配置类似,这里不做详细说明:
//USART2的中断优先级配置
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;//选择USART2通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级为0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//响应优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能通道
NVIC_Init(&NVIC_InitStructure);//调用初始化函数
  • 时钟使能+中断使能
    也就是开启外设和中断,与定时器配置部分相类似:
USART_Cmd(USART2, ENABLE);//开启USART2
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启USART2接收中断

这里要注意一点:这里只能使能串口接收中断!!!发送中断在数据发送前使能,数据发送完毕后就要关闭!!!
总的串口初始化程序:

void USART2_Init(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	
	//开启GPIOA和USART2时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);
	
	//USART2的中断优先级配置
	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;//选择USART2通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级为0
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//响应优先级为0
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能通道
	NVIC_Init(&NVIC_InitStructure);//调用初始化函数
	
	//RXD-PA3 设置为浮空模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//TXD-PA2 设置为推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//USART2的配置
	USART_InitStructure.USART_BaudRate = 9600;//设置波特率为9600
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//设置数据长度为8位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//设置停止位数为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(USART2, &USART_InitStructure);//初始化USART2
	
	USART_Cmd(USART2, ENABLE);//开启USART2
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启USART2接收中断
	
}

初始化串口之后,我们还要写一个串口发送数据的函数:

void USART2_SendString(u8 *str)
{
	u8 index = 0;
	
	while(str[index++] != 0)
	{
		USART_SendData(USART2, str[index-1]);//发送一个字节的数据,注意这里是index-1!,如果是index则不能显示第一个字符!!!
		while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == 0);//数据发送结束(TXE为1)则跳出循环
		index++;
	}
}

这里涉及到了标志位的问题,我们怎么判断一帧数据发送完了呢?
我们先来了解一下发送数据的流程:内部总线->TDR数据寄存器->发送移位寄存器->TXD引脚输出;这里的发送移位寄存器可以理解为一个数据的中转站,一帧的数据会先由数据寄存器送达中转站,然后再输出,这时我们的TDR寄存器是空的,那么就会产生一个标志位(TXE = 1),表明我们可以发送下一帧的数据,这个TXE就是硬件设置的TXD为空的标志,我们也可以看一下这个引脚的功能:
在这里插入图片描述
读数据的流程与发送数据的流程相类似,用到的标志位是RXNE,从下面的图片中我们可以看到当RXNE为1时,表示数据接收到了,那么我们在写接收数据的函数时肯定也是在这种情况下进行操作的;
在这里插入图片描述
明白了上面的内容,我们也就能理解下面这行代码的意思了:

while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == 0);//数据发送结束(TXE为1)则跳出循环

当TXE为0时,表明这一帧数据没有发送完,那么我们就一直在while循环内执行,直到这一帧数据完全发送到移位寄存器(即TXE = 1);

发送完数据我们肯定还要接收数据啊,接收数据是利用串口中断来接收的,所以我们还要在stm32f10x_it.c文件中编写一个USART中断服务函数:
NOTICE!!!stm32的中断服务函数是有特定的名称的,我们编写的中断服务函数必须与这个名称一致才可以,否则就会报错!!!我们用到的是串口2,所以要编写串口2的中断服务函数:(void USART2_IRQHandler(void))

void USART2_IRQHandler(void)
{
	u16 tmp;
	
	if(USART_GetITStatus(USART2, USART_IT_RXNE) == 1)//检测接收中断标志位,RXNE为1表示接收到了数据
	{
		USART_ClearITPendingBit(USART2, USART_IT_RXNE);//清除中断标志位(即清除RXNE)
		tmp = USART_ReceiveData(USART2);//读取串口2的数据
		if(tmp == '\n')//‘/n’表示本次读取结束
		{
			RxdBuf[RxdCnt-1] = 0;//避免\r显示在LCD发生的乱码
			RxdCnt = 0;//读取结束清RxdCnt
			RxdOver = 1;//本次数据读取结束标志置1
			USART_ITConfig(USART2, USART_IT_RXNE, DISABLE);//接收完毕后关闭,防止处理过程发生干扰。
		}
		else
		{
			RxdBuf[RxdCnt++] = tmp;//将读取到的数据存到RXDBuf中
		}
	}
}

主函数:

#include "stm32f10x.h"
#include "lcd.h"
#include "usart2.h"

u32 TimingDelay = 0;
u8 RxdCnt = 0;//用来计数读取到的字节数
u8 RxdOver = 0;//读取数据是否结束:0-未结束,1-结束
u8 RxdBuf[20];//存储读到的数据

void Delay_Ms(u32 nTime);

//Main Body
int main(void)
{
    u8 i;
    
	STM3210B_LCD_Init();
	LCD_Clear(Blue);
	LCD_SetBackColor(Blue);
	LCD_SetTextColor(White);
	
	SysTick_Config(SystemCoreClock/1000);

    USART2_Init();//串口初始化
    
	while(1)
    {
        if(RxdOver)
		{
			RxdOver = 0;
			LCD_ClearLine(Line5);//清除LCD的对应行
			LCD_DisplayStringLine(Line5, RxdBuf);
			USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//接收的数据处理完毕后打开接收中断
			for(i=0; i<20; i++)//清空缓冲区
				RxdBuf[i] = 0;
		}
    }

}

//
void Delay_Ms(u32 nTime)
{
	TimingDelay = nTime;
	while(TimingDelay != 0);	
}

这段代码实现的功能是:利用串口助手向单片机发送数据,数据可显示到LCD上,关于LCD的部分,我是直接用蓝桥嵌入式里面的液晶显示例程来建工程的,emmm,LCD的部分我暂时还没有学到。


一些错误:
在这里插入图片描述
在这里插入图片描述
一直搜索不到串口怎么办?
右键点击:
在这里插入图片描述
选择更新设备程序:
在这里插入图片描述
选择自动搜索更新,ok

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ReRrain

觉得写的不错,不妨请我喝杯~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值