欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

stm32学习--串口中断

程序员文章站 2024-02-25 18:26:15
...

实验目的

显然不是纯粹为了钻研,有被毕设逼迫的成分,本来选择51,但是感觉51资源不太够用,而且51的逼格不够,答辩的话stm32感觉逼格高一些,但是也增加了风险,我对51的资源,寄存器,开发流程都相当熟悉,但是32的话仅仅还是入门,只能跟着比人用库开发,万一有懂行的老师,那就GG,所以无论什么原因,既然选择32,就要好好学习。闲话到此结束,上正文。

串口简介

stm32的串口资源还是比较丰富的,有三个同步异步串口,两个异步串口,这里针对的是stm32f103ZET6。分别挂接在APB1和APB2上,其中串口1挂接在APB2总线上,有更快的时钟频率,这里采用的是串口1。串口1有多种事件可以触发中断,这里采用的是接收缓冲区非空触发,即RXNE。即把接收消息放在中断里,也可以不采用中断的方式,在主函数循环里面轮询,效率相对较低,而且不能满足实时要求。实验采用的方法是由PC机向stm32发送一个字节的数据,然后判断字节内容,点亮LED灯。

配置流程

关于串口实验的说明

  1. 开发板,stm32f103zet6;
  2. 串口,USART1,挂接在APB1总线上,引脚为PA9(TX),PA10(RX);
  3. 串口调试助手,要在连接上USB线后才能打开;

配置流程

  1. 初始化时钟
    需要初始化GPIOA和USART1的时钟,都在APB2总线上;

  2. 初始化GPIOA端口
    PA9设置为复用推挽输出,要用它发送数据。PA10设置为浮空输入。关于端口的设置可以参考《stm32f10xxx参考手册》8.1.11章节。

  3. 初始化串口
    9600,8-N-1;无硬件控制流,读写均开启;

  4. 中断触发事件配置
    RXNE,接收缓冲区非空(有数据发过来);

  5. 优先级配置
    优先级分组,优先级设置;

  6. 串口使能

代码

  1. 串口部分
#include "usart.h"

u8 recv;

void USART1_Init(u32 bound)
{
		GPIO_InitTypeDef GPIO_InitStructure;
		USART_InitTypeDef USART_InitStructure;
		NVIC_InitTypeDef NVIC_InitStructure;
		/*时钟初始化*/
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
		/*端口初始化*/
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;		//下拉
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOA, &GPIO_InitStructure);
		/**/
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;		//浮空输入
		GPIO_Init(GPIOA, &GPIO_InitStructure);
		/*串口模式*/
		USART_InitStructure.USART_BaudRate = bound;										//波特率
		USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//数据位
		USART_InitStructure.USART_Parity = USART_Parity_No;		/*校验位*/
		USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
		USART_InitStructure.USART_StopBits  = USART_StopBits_1;			//停止位
		USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;		//模式读写
		USART_Init(USART1, &USART_InitStructure);
		
		
		USART_ClearFlag(USART1 ,USART_FLAG_TC);
		USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);		/*配置中断触发方式,接收触发
																												个人理解,当某个中断能产生中断事件的方式
																												不止一种时需要配置*/
		USART_Cmd(USART1, ENABLE);		/*让串口处于可以接收和发送数据的状态,个人理解,当某个中断出了
																		除了触发中断还可以做别的事情的时候,比如串口中断,在触发中断之前
																		还完成接收和发送数据的任务,这个中断需要使能*/																												
																					
		/*串口使能*/
		
				
		/*优先级配置*/
		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
		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);

}

/*终端服务子函数*/
void USART1_IRQHandler(void)
{		
		if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
		{
					recv = USART_ReceiveData(USART1);	/*接收数据*/
					USART_SendData(USART1, recv);			/*发送数据*/
					while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
		}
		USART_ClearFlag(USART1 ,USART_FLAG_TC);	
		
}

2.led部分

void LED_Init(void)
{	
		GPIO_InitTypeDef GPIO_InitStructure;
		/*开启GPIOC时钟*/
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
		/*GPIOC端口配置*/
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | \
											 GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
		GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOC, &GPIO_InitStructure);
		/*拉高PC0*/
		GPIO_SetBits(GPIOC, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | \
												GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
}

3.主函数

led用了宏定义,采用了位带操作,用的别人的代码,一般单片教程里都有,还是使用了systick定时器产生毫秒延时,不再列举。第一个LED灯用来显示程序正常运行。

#include "system.h"
#include "led.h"
#include "systick.h"
#include "usart.h"

int main()
{
		
		u8 i = 0, j = 1,  buf = 0;
		SysTick_Init(72);
		LED_Init();
		USART1_Init(9600);
		
		while(1)
		{
			
			i++;
			if(i % 20 == 0)
			{
					LED1 = !LED1;
			}
			do
			{
					
					buf = recv;
					buf = buf & (0x01 << j);
					if(buf != 0)
					{
							PCout(j) = 0;
					}
					else
					{
							PCout(j) = 1;
					}
					j++;
			}
			while(j < 8);
			j = 1;
			delay_ms(10);
		}
}

实验现象

从串口调试助手里面发送0x08(00001000)第四个LED灯亮

stm32学习--串口中断

stm32学习--串口中断

总结

写这篇文章的目的不是为了自己stm32学的多好,其实就是刚入门,是因为调试串口的时候遇到很多问题,比如串口卡在循环里,串口没有产生中断等,我个人的解决方案如下

  1. 串口中断没法启动。
    I、一定要好好检查你的串口和中断配置流程是否合理,时钟的配置是要放在最前面的,其他配置一般没有要求,但是可以参考别人的配置流程,减少出错的机会;
    II、检查中断服务函数是否有问题,中断的功能是否合理,一定要检查中断名字是否写正确,我就是在这卡了好长时间,在KEIL里,你就是把名字写错了,它也不会给你报错,只当你是自定义的函数,但是不会被识别为中断服务子函数,所以中断就是触发了,也不会执行中断函数,巨坑,兄弟们当心,还有就是只用程序烧录软件也可以调试串口,比较方便。如果是人串口调试助手要注意设置,要按照软件设置的模式配置串口调试助手,而且打开后似乎是要先勾选RTS,接触该状态,然后在取消勾选,即可正常工作。另外结束调试之后最好先关闭串口再拔掉USB线。

    最后,关于程序注释,我这种注释风格不太好,但是初学者总想把每一个语句的功能都搞懂,这样有利于后续开发,等熟悉了开发流程之后,就不必这样写了,只需注明函数的功能,以及调用了那些函数(自定义)用到的全局变量,是否改变了全局变量的值,输出和返回值。关于注释里面的个人理解,纯属个人想法,如有大佬发现错误,还请不吝赐教。