天天看点

stm32学习--串口中断代码

实验目的

显然不是纯粹为了钻研,有被毕设逼迫的成分,本来选择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线。

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

继续阅读