一、中断-按键中断
(1)实验内容
用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。
程序:
//led.c
#include "stm32f10x.h" // Device header
uint16_t LED_Count;//计数器(中断触发次数)
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
//exti_key.c
#include "exti_key.h"
#include "misc.h"
void EXTI_Key_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;//定义配置初始化结构体
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //打开GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //打开AFIO时钟
//EXTI 和 NVIC已经默认开启时钟,不需要再开启
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; //对于外部中断来说,要选择浮空,上拉或者下拉输入其中一个模式,如果不清楚看ST参考手册GPIO外设配置表(113页EXTI输入线)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
//第三步,给AFIO配置,库函数文件在GPIO一个文件里
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);//用AFIO配置需要的中断引脚选择
//第四步配置EXTI,选择触发的方式
EXTI_InitStruct.EXTI_Line = EXTI_Line14;//选择PB14对应的14号线路;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;//开启中断
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;//选择是中断还是事件触发
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//配置为下降沿触发
EXTI_Init(&EXTI_InitStruct);
//第五步,配置NVIC(NVIC属于内核,库函数需要在杂项即misc.h里寻找)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//先配置一下指定中断分组(这里选择抢占优先级和响应优先级)
NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;//这里参数需要到整个文件搜索,不同类型的芯片终端通道列表不一样,选择MD的芯片找到10-15EXTI的通道
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
//指定优先级,因为只有一个中断源,优先级随意设置
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; //指定抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; //指定响应优先级
NVIC_Init(&NVIC_InitStruct);
}
//main.c
#include "stm32f10x.h" // Device header
#include "LED.h"
#include "exti_key.h"
int main(void)
{
LED_Init();
EXTI_Key_Init();
while(1)
{
}
while(1)
{
}
}
//第六步写中断函数(在启动文件startup里面找,找对应之前开启的10-15通道)
void EXTI15_10_IRQHandler(void)//必须无参无返回值
{
//因为有10-15EXTI都能进来所以一般首先要进行判断EXTI14的中断标志位是不是为1
if(EXTI_GetITStatus(EXTI_Line14)== SET)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==Bit_RESET)//判断现在是低电平还是高电平,对应开关等
{
GPIO_SetBits(GPIOA,GPIO_Pin_5);
//GPIO_ResetBits(GPIOA,GPIO_Pin_5);
}
else if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_4)==Bit_SET)
{
//GPIO_SetBits(GPIOA,GPIO_Pin_5);
GPIO_ResetBits(GPIOA,GPIO_Pin_5);
}
//每次执行中断后都应该清除中断标志位,不然会一直申请中断,程序就卡死在中断里面
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
(2)实现效果
led-A5,杜邦线-B14,因为没有按键所以在这里使用杜邦线代替。
接入5.5v时led正常亮起,接入G时led灭,不接入G正常亮起。
二、中断-串口中断
1.字符“s”
(1)实验内容
当stm32接收到1个字符“s”时,停止持续发送“hello windows!”; 当接收到1个字符“t”时,持续发送“hello windows!”。
程序:
//main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Serial.h"
uint8_t KeyNum;
uint8_t RxData;
extern uint8_t receiveFlag;
int main()
{
OLED_Init();
Serial_Init();
while(1)
if(receiveFlag==1)
{
printf("hello windows!\r\n");
OLED_Clear();
}
Delay_ms(500);
if(receiveFlag==0)
{
OLED_ShowNum(1,1,2,2);
}
Delay_ms(500);
}
}
//Serial.c
#include "stm32f10x.h" // Device header
#include <stdio.h>
#include <stdarg.h>
#include "string.h" //使用strcmp头文件
uint8_t Serial_RxFlag;
uint8_t receiveFlag=1;
/**
* 函 数:串口初始化
* 参 数:无
* 返 回 值:无
*/
void Serial_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //开启USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*GPIOA_9初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
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); //将PA9引脚初始化为复用推挽输出
//GPIOA_10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// //选择A6为亮灯口
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出模式
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO_Init(GPIOA, &GPIO_InitStructure);
/*USART初始化*/
USART_InitTypeDef USART_InitStructure; //定义结构体变量
USART_InitStructure.USART_BaudRate = 9600; //波特率
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流控制,不需要
USART_InitStructure.USART_Mode = USART_Mode_Tx|USART_Mode_Rx; //模式,选择收发模式
USART_InitStructure.USART_Parity = USART_Parity_No; //奇偶校验,不需要
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位,选择1位
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长,选择8位
USART_Init(USART1, &USART_InitStructure); //将结构体变量交给USART_Init,配置USART1
/*USART使能*/
USART_Cmd(USART1, ENABLE); //使能USART1,串口开始运行
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//启用USART1,查询中断标志位RXNE,一旦查询到置一,申请终端
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断优先级分组
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;//NVIC通道
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//高优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//低优先级
NVIC_Init(&NVIC_InitStructure);
}
/**
* 函 数:串口发送一个字节
* 参 数:Byte 要发送的一个字节
* 返 回 值:无
*/
void Serial_SendByte(uint8_t Byte)
{
USART_SendData(USART1, Byte); //将字节数据写入数据寄存器,写入后USART自动生成时序波形
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //等待发送完成
/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}
/**
* 函 数:串口发送一个数组
* 参 数:Array 要发送数组的首地址
* 参 数:Length 要发送数组的长度
* 返 回 值:无
*/
void Serial_SendArray(uint8_t *Array,uint16_t Length)//发送数组
{
uint16_t i;
for(i=0;i<Length;i++)
{
Serial_SendByte(Array[i]);
}
}
/**
* 函 数:串口发送一个字符串
* 参 数:String 要发送字符串的首地址
* 返 回 值:无
*/
void Serial_SendString(char *string)
{
uint8_t i;
for(i=0;string[i]!='\0';i++)
{
Serial_SendByte(string[i]);
}
}
/**
* 函 数:次方函数(内部使用)
* 返 回 值:返回值等于X的Y次方
*/
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1; //设置结果初值为1
while (Y --) //执行Y次
{
Result *= X; //将X累乘到结果
}
return Result;
}
/**
* 函 数:串口发送数字
* 参 数:Number 要发送的数字,范围:0~4294967295
* 参 数:Length 要发送数字的长度,范围:0~10
* 返 回 值:无
*/
void Serial_SendNumber(uint32_t Number,uint8_t Length)
{
uint8_t i;
for(i=0;i<Length;i++)
{
Serial_SendByte(Number/Serial_Pow(10,Length-i-1)%10+'0');//由十进制的高位到低位依次发送
//加上'0'就转换为ascll码的类型,从ascll码中字符0的位置开始算,也可以改为0x30.
}
}
/**
* 函 数:使用printf需要重定向的底层函数
* 参 数:保持原始格式即可,无需变动
* 返 回 值:保持原始格式即可,无需变动
*/ //将printf的底层重定向到自己的发送字节函数
//什么是重定向?重定向是指将fputc里面的输出指向目标设备。因printf函数调用了fputc,
//而fputc输出有默认指向的目标,且不同库中的fputc输出指向不同,所以需要重写fputc
int fputc(int ch,FILE *f) //printf重定向,为串口
{
Serial_SendByte(ch);
return ch;
}
/**
* 函 数:自己封装的prinf函数
* 参 数:format 格式化字符串
* 参 数:... 可变的参数列表
* 返 回 值:无
*/
void Serial_Printf(char *format, ...)
{
char string[100];
va_list arg; //参数列表变量
va_start(arg,format); //
vsprintf(string,format,arg);
va_end(arg);//释放参数表
Serial_SendString(string);//发送string
}
uint8_t Serial_GetRxFlag()
{
if(Serial_RxFlag==1)
{
Serial_RxFlag=0;
return 1;
}
return 0;
}
//发送s,t
发送s,t
void USART1_IRQHandler()//串口中断函数
{
if(USART_GetFlagStatus(USART1,USART_IT_RXNE)==SET)
{
uint8_t temp=USART_ReceiveData(USART1);
if(temp=='s')
{
receiveFlag=0;
}
if(temp=='t')
{
receiveFlag=1;
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
(2)实现效果
2.字符串“stop stm32!”
(1)实验内容
当stm32接收到字符“stop stm32!”时,停止持续发送“hello windows!”; 当接收到字符“go stm32!”时,持续发送“hello windows!”(提示:要将接收到的连续字符保存到一个字符数组里,进行判别匹配。写一个接收字符串的函数。
程序:
//发送stop,go
void USART1_IRQHandler()//串口中断函数
{
uint8_t receive[100];
if(USART_GetFlagStatus(USART1,USART_IT_RXNE)==SET)
{
static uint8_t x=0;
uint8_t temp=USART_ReceiveData(USART1);
if(temp!='\n') //字符串接收函数
{receive[x]=temp;
x++;
}
receive[x]='\0';
if(strcmp((char*)receive,"s")==0)
{
receiveFlag=0;
x=0;
}
else if(strcmp((char*)receive,"t")==0)
{
receiveFlag=1;
x=0;
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
(2)实现效果
三、参考文章
上述实验程序参考来源如下:
STM32以中断的方式点亮LED小灯(标准库)
初学STM32单片坤之中断入门
四、总结
通过这次实验我学习到了更多关于串口通信的知识。