stm32学习--串口中断
实验目的
显然不是纯粹为了钻研,有被毕设逼迫的成分,本来选择51,但是感觉51资源不太够用,而且51的逼格不够,答辩的话stm32感觉逼格高一些,但是也增加了风险,我对51的资源,寄存器,开发流程都相当熟悉,但是32的话仅仅还是入门,只能跟着比人用库开发,万一有懂行的老师,那就GG,所以无论什么原因,既然选择32,就要好好学习。闲话到此结束,上正文。
串口简介
stm32的串口资源还是比较丰富的,有三个同步异步串口,两个异步串口,这里针对的是stm32f103ZET6。分别挂接在APB1和APB2上,其中串口1挂接在APB2总线上,有更快的时钟频率,这里采用的是串口1。串口1有多种事件可以触发中断,这里采用的是接收缓冲区非空触发,即RXNE。即把接收消息放在中断里,也可以不采用中断的方式,在主函数循环里面轮询,效率相对较低,而且不能满足实时要求。实验采用的方法是由PC机向stm32发送一个字节的数据,然后判断字节内容,点亮LED灯。
配置流程
关于串口实验的说明
- 开发板,stm32f103zet6;
- 串口,USART1,挂接在APB1总线上,引脚为PA9(TX),PA10(RX);
- 串口调试助手,要在连接上USB线后才能打开;
配置流程
-
初始化时钟
需要初始化GPIOA和USART1的时钟,都在APB2总线上; -
初始化GPIOA端口
PA9设置为复用推挽输出,要用它发送数据。PA10设置为浮空输入。关于端口的设置可以参考《stm32f10xxx参考手册》8.1.11章节。 -
初始化串口
9600,8-N-1;无硬件控制流,读写均开启; -
中断触发事件配置
RXNE,接收缓冲区非空(有数据发过来); -
优先级配置
优先级分组,优先级设置; -
串口使能
代码
- 串口部分
#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学的多好,其实就是刚入门,是因为调试串口的时候遇到很多问题,比如串口卡在循环里,串口没有产生中断等,我个人的解决方案如下
-
串口中断没法启动。
I、一定要好好检查你的串口和中断配置流程是否合理,时钟的配置是要放在最前面的,其他配置一般没有要求,但是可以参考别人的配置流程,减少出错的机会;
II、检查中断服务函数是否有问题,中断的功能是否合理,一定要检查中断名字是否写正确,我就是在这卡了好长时间,在KEIL里,你就是把名字写错了,它也不会给你报错,只当你是自定义的函数,但是不会被识别为中断服务子函数,所以中断就是触发了,也不会执行中断函数,巨坑,兄弟们当心,还有就是只用程序烧录软件也可以调试串口,比较方便。如果是人串口调试助手要注意设置,要按照软件设置的模式配置串口调试助手,而且打开后似乎是要先勾选RTS,接触该状态,然后在取消勾选,即可正常工作。另外结束调试之后最好先关闭串口再拔掉USB线。最后,关于程序注释,我这种注释风格不太好,但是初学者总想把每一个语句的功能都搞懂,这样有利于后续开发,等熟悉了开发流程之后,就不必这样写了,只需注明函数的功能,以及调用了那些函数(自定义)用到的全局变量,是否改变了全局变量的值,输出和返回值。关于注释里面的个人理解,纯属个人想法,如有大佬发现错误,还请不吝赐教。