STM32学习笔记--串口实验

目录:

一、实验任务

  用串口助手通过串口1向单片机发送数据,并将单片机接收到的数据通过串口返回到串口助手。主要任务如下:
(1)编写串口初始化函数;
(2)编写串口中断服务函数;
(3)编写实现功能的main函数。

二、硬件设计

  实验中用到的串口 1 与 USB 串口并没有在 PCB 上连接在一起,需要通过跳线帽来连接一下。这里我们把 P4 的 RXD 和 TXD 用跳线帽与 PA9 和 PA10 连接起来。
STM32学习笔记--串口实验

三、软件设计

1.uart_init 函数

//初始化 IO 串口 1
//bound:波特率
void uart_init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//①串口时钟使能,GPIO 时钟使能,复用时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|
RCC_APB2Periph_GPIOA, ENABLE); //使能 USART1,GPIOA 时钟
//②串口复位
USART_DeInit(USART1); //复位串口 1
//③GPIO 端口模式设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //ISART1_TX PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.9

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //USART1_RX PA.10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.10
//④串口参数初始化
USART_InitStructure.USART_BaudRate = bound; //波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为 8 位
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(USART1, &USART_InitStructure); //初始化串口
#if EN_USART1_RX //如果使能了接收
//⑤初始化 NVIC
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; //抢占优先级 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //中断优先级初始化
//⑤开启中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启中断
#endif
//⑥使能串口
USART_Cmd(USART1, ENABLE); //使能串口
}

  从该代码可以看出,其初始化串口的过程,我们用标号①~⑥标示了顺序:
① 串口时钟使能,GPIO 时钟使能
② 串口复位
③ GPIO 端口模式设置
④ 串口参数初始化
⑤ 初始化 NVIC 并且开启中断
⑥ 使能串口
  配置全双工的串口 1,那么 TX(PA9)管脚需要配置为推挽复用输出,RX(PA10)管脚配置为浮空输入或者带上拉输入。模式配置参考下面表格:
STM32学习笔记--串口实验
  使用到了串口的中断接收,必须在 usart.h 里面设置EN_USART1_RX 为 1(默认设置就是 1 ) ,该函数才会配置中断使能,以及开启串口 1 的NVIC 中断。这里我们把串口 1 中断放在组 2,优先级设置为组 2 里面的最低。从该代码可以看出,其初始化串口的过程,和我们前面介绍的一致。先计算得到USART1->BRR 的内容。然后开始初始化串口引脚,接着把 USART1 复位,然之后设置波特率和奇偶校验等。
2.中断服务函数 USART1_IRQHandler

void USART1_IRQHandler(void) //串口 1 中断服务程序
{
	u8 Res;
	#if SYSTEM_SUPPORT_OS //如果 SYSTEM_SUPPORT_OS 为真,则需要支持 OS
	OSIntEnter();
	#endif
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
	//接收中断(接收到的数据必须是 0x0d 0x0a 结尾)
		{
			Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
			if((USART_RX_STA&0x8000)==0)//接收未完成
				{
				if(USART_RX_STA&0x4000)//接收到了 0x0d
					 {
						if(Res!=0x0a)
						USART_RX_STA=0;//接收错误,重新开始
						else USART_RX_STA|=0x8000; //接收完成了
					}
			    else //还没收到 0X0D
				   {
				      if(Res==0x0d)
				      USART_RX_STA|=0x4000;
				      else
				         {
			    	        USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					        USART_RX_STA++;
					       if(USART_RX_STA>(USART_REC_LEN-1))
					       USART_RX_STA=0;
					       //接收数据错误,重新开始接收
				        }
			      }
		      } 
	     }
	#if SYSTEM_SUPPORT_OS //如果 SYSTEM_SUPPORT_OS 为真,则需要支持 OS
	OSIntExit();
	#endif
}

  void USART1_IRQHandler(void)函数是串口 1 的中断响应函数,当串口 1 发生了相应的中断后,就会跳到该函数执行。
  函数体里面通过函数:

if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)

  判断是否接受中断,如果是串口接受中断,则读取串口接受到的数据:

Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据

  读到数据后接下来就对数据进行分析。
  这 里 我 们 设 计 了 一 个接 收 协 议 : 通 过 这 个 函 数 , 配 合 一 个 数 组USART_RX_BUF[],一个接收状态寄存器 USART_RX_STA(此寄存器其实就是一个全局变量,由作者自行添加。由于它起到类似寄存器的功能,这里暂且称之为寄存器)实现对串口数据的接收管理。USART_RX_BUF 的大小由 USART_REC_LEN 定义,也就是一次接收的数据最大不能超过 USART_REC_LEN 个字节。USART_RX_STA 是一个接收状态寄存器其各的定义如下:
STM32学习笔记--串口实验
  当接收到从电脑发过来的数据,把接收到的数据保存在 USART_RX_BUF 中,同时在接收状态寄存器(USART_RX_STA)中计数接收到的有效数据个数,当收到回车(回车的表示由 2 个字节组成:0X0D 和 0X0A)的第一个字节 0X0D 时,计数器将不再增加,等待0X0A 的到来,而如果 0X0A 没有来到,则认为这次接收失败,重新开始下一次接收。如果顺利接收到 0X0A,则标记 USART_RX_STA 的第 15 位,这样完成一次接收,并等待该位被其他程序清除,从而开始下一次的接收,而如果迟迟没有收到 0X0D,那么在接收数据超过 USART_REC_LEN 的时候,则会丢弃前面的数据,重新接收。
3.main.c函数

#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
int main(void)
{
	u8 t;
	u8 len;
	u16 times=0;
	delay_init(); //延时函数初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断分组
	uart_init(9600); //串口初始化为 9600
	LED_Init(); //初始化与 LED 连接的硬件接口
	while(1)
		{
			if(USART_RX_STA&0x8000)
			{
				len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
				printf("\r\n 您发送的消息为:\r\n");
				for(t=0;t<len;t++)
				{
					USART1->DR=USART_RX_BUF[t];
					while((USART1->SR&0X40)==0);//等待发送结束
				}
				printf("\r\n\r\n");//插入换行
				USART_RX_STA=0;
			}
		   else
			{
				times++;
				if(times%5000==0)
				{
					printf("\r串口实验\r\n");
				}
				if(times%200==0)printf("请输入数据,以回车键结束\r\n");
				if(times%30==0)LED0=!LED0;//闪烁 LED,提示系统正在运行.
				delay_ms(10);
			}
		}
}

  NVIC_PriorityGroupConfig ()函数设置中断分组号为 2,也就是 2 位抢占优先级和 2 位子优先级。

USART_SendData(USART1, USART_RX_BUF[t]); //向串口 1 发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);

  第一句,其实就是发送一个字节到串口。第二句呢,就是我们在我们发送一个数据到串口之后,要检测这个数据是否已经被发送完成了。USART_FLAG_TC 是宏定义的数据发送完成标识符。
  参考资料:STM32学习笔记—USART串口通信

上一篇:leetcode-回文数判断


下一篇:STM32引脚列表中主功能,默认复用功能和重定义功能的区别&STM32F103RCT6引脚功能及使用