【STM32学习笔记】6、USART

发布于 2021-01-30  609 次阅读


在之前我们用到过串口去传输一些数据,比如利用串口返回按下的按钮的键值等。但是当时都只是直接调用写好的USART库函数,并没有深入的了解其中的原理。这次的任务就是自行编写串口的初始化函数,并利用串口控制开发板上的LED开关和在OLED屏幕上显示输入的x*50+100的结果。

首先删掉模板中的已经写好的usart.h、usart.c,然后开始自己编写。首先我们自己定义一个RX和TX,这里就要用到已经很熟悉的GPIO的设置,代码如下:

GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//使能GPIOA时钟

//PA9  
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//设置PA9为推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);

//PA10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//设置PA10为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA,&GPIO_InitStructure);

将PA9接上串口模块的RXD,将PA10接上模块的TXD,然后就来到我们的USART串口的配置部分:

USART_InitTypeDef USART_InitStructure;//定义USART结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//使能时钟

USART_InitStructure.USART_BaudRate =9600;//定义波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//定义工作的发送的数据字节数为8bit
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);//初始化串口

USART_Cmd(USART1,ENABLE);//使能串口1

到这一步我们就已经配置好我们的串口了,但是工作还没完成,我们还需要配置NVIC开启中断。

NVIC_InitTypeDef NVIC_InitStructure;//定义一个NVIC结构体

NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//定义NVIC频道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;//指定抢占优先级为3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//子优先级为3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//启用IRQ通道
NVIC_Init(&NVIC_InitStructure);

//打开串口1的接收中断,当串口1接收到数据时会触发此中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

//单独编写中断服务函数
void USART1_IRQHandler(void){
	u8 res;
	if(USART_GetFlagStatus(USART1,USART_IT_RXNE)){//如果串口1接收数据中断
		res = USART_ReceiveData(USART1);//读取串口1接收到的数据
		USART_SendData(USART1,res);//通过串口1回发接收到的数据
	}
}

最后我们把上面这些代码稍微整理一下:[zhedie]

void myUSART1_Init(){/自定义串口初始化函数
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	
	//PA9  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	//PA10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
	//shutdown
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	//USART???
	
	USART_InitStructure.USART_BaudRate =9600;
	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(USART1,&USART_InitStructure);
	
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	USART_Cmd(USART1,ENABLE);
}

void USART1_IRQHandler(void){
	u8 res;
	if(USART_GetFlagStatus(USART1,USART_IT_RXNE)){
		res = USART_ReceiveData(USART1);
		USART_SendData(USART1,res);
	}

}

int main(void)
{		

delay_init();
myUSART1_Init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIc中断分组2:2位抢占优先级,2位响应优先级
LED_Init();
while(1){
	const char temp =USART_ReceiveData(USART1);
		if(strcmp(temp,"led") == 0)
			{
			PEout(1) = !PEout(1);
			}
        }
}

[/zhedie]

以上的代码实现了通过串口输入并返回接收到的文本信息,以及通过输入“led”来控制开发板上LED的开关。

接着我们要实现的功能是在OLED屏幕上显示输入的x*50+100的结果。现在我们重新调用以及编写好的库函数,我们可以在usart.c中找到相关的USART_Init的函数定义,与我们上面的程序并没有太多的不同,只是在定义波特率的时候,应用了形参,让用户可以调用函数初始化USART的时候直接定义波特率,不需要修改代码。

由于数据类型转换起来很麻烦,我打算直接用OLED_ShowNum函数去显示结果,具体代码如下。[zhedie]

#include "led.h"
#include "oled.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "string.h"

void SN(u8 num){
	OLED_ShowNum(0,0,num,3,16,1);
	delay_ms(100);
	OLED_Refresh();
}

 int main(void)
 {		

	delay_init();	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
	uart_init(115200);
 	LED_Init();
	OLED_Init();
	PEout(0) = !PEout(0);
 	while(1)
	{
		while(1){
		if(USART_RX_STA&0x8000)
		{
			if(strcmp(USART_RX_BUF,"1") == 0){
				OLED_ShowNum(0,0,150,3,16,1);
				delay_ms(100);
				OLED_Refresh();
			}else if(strcmp(USART_RX_BUF,"2") == 0){
				OLED_ShowNum(0,0,200,3,16,1);
				delay_ms(100);
				OLED_Refresh();
			}else if(strcmp(USART_RX_BUF,"3") == 0){
				OLED_ShowNum(0,0,250,3,16,1);
				delay_ms(100);
				OLED_Refresh();
			}else if(strcmp(USART_RX_BUF,"4") == 0){
				OLED_ShowNum(0,0,300,3,16,1);
				delay_ms(100);
				OLED_Refresh();
			}else if(strcmp(USART_RX_BUF,"5") == 0){
				OLED_ShowNum(0,0,350,3,16,1);
				delay_ms(100);
				OLED_Refresh();
			}else if(strcmp(USART_RX_BUF,"6") == 0){
				OLED_ShowNum(0,0,400,3,16,1);
				delay_ms(100);
				OLED_Refresh();
			}else if(strcmp(USART_RX_BUF,"7") == 0){
				OLED_ShowNum(0,0,450,3,16,1);
				delay_ms(100);
				OLED_Refresh();
			}else if(strcmp(USART_RX_BUF,"8") == 0){
				OLED_ShowNum(0,0,500,3,16,1);
				delay_ms(100);
				OLED_Refresh();
			}else if(strcmp(USART_RX_BUF,"9") == 0){
				OLED_ShowNum(0,0,550,3,16,1);
				delay_ms(100);
				OLED_Refresh();
			}else if(strcmp(USART_RX_BUF,"led") == 0)
				{
				PBout(9) = !PBout(9);
				OLED_Refresh();
				}
			printf("Display success!\r\n");
			USART_RX_STA = 0;
		}
	
	}
	}	 
 }

[/zhedie]

效果如下:


一沙一世界,一花一天堂。君掌盛无边,刹那成永恒。