一、 外部中断点亮LED
1、用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。
GPIOA端5号管脚接一个LED,GPIOB端口14号引脚接一个开关。
exti_key.h文件
#ifndef __EXTI_KEY_H
#define __EXTI_KEY_H
#include "stm32f10x.h"
void EXTI_Key_Init(void);
#endif
exti.key.c文件
#include "stm32f10x.h"
uint16_t LED_Count;//计数器(中断触发次数)
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; //对于外部中断来说,要选择浮空,上拉或者下拉输入其中一个模式
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_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;
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"
int main(void)
{
LED_Init();
while(1)
{
}
}
//中断函数
void EXTI15_10_IRQHandler(void)//必须无参无返回值
{
//因为有10-15EXTI都能进来所以一般首先要进行判断EXTI14的中断标志位是不是为1
if(EXTI_GetITStatus(EXTI_Line14)== SET)
{
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);
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_14)==Bit_SET)
{
//GPIO_SetBits(GPIOA,GPIO_Pin_5);
GPIO_ResetBits(GPIOA,GPIO_Pin_5);
}
//每次执行中断后都应该清除中断标志位,不然会一直申请中断,程序就卡死在中断里面
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
实验结果:
2、如果完成后,尝试在main函数while循环中加入一个串口每隔1s 发送一次字符的代码片段,观察按键中断对串口发送是否会带来干扰或延迟。
USART_Init_Config.c文件
#include <stdio.h>
#include "USART_Init_Config.h"
void USART1_Init(void)
{
//1.定义GPIO和串口对象
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//2.开启GPIOA和USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
//3.USART设置RX/TX
//USART1_TX,默认情况下复用PA9引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1_RX,默认情况下复用PA10引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
//4.USART1参数配置
//设置波特率为9600
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据位占8位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //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);//初始化串口1
// 嵌套向量中断控制器组选择
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;// 配置USART为中断源
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;// 抢断优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;// 响应优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;// 使能中断
NVIC_Init(&NVIC_InitStructure);// 初始化配置NVIC
//使能串口接收中断
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
//使能串口1
USART_Cmd(USART1, ENABLE);
}
//发送一个字节
void Usart_SendByte(USART_TypeDef * pUSARTx,uint8_t ch)
{
USART_SendData(pUSARTx,ch);// 发送一个字节数据到USART
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE)==RESET);// 等待发送数据寄存器为空
}
//发送字符串
void USART_SendString(USART_TypeDef * pUSARTx,char *str)
{
unsigned int k=0;
do
{
Usart_SendByte(pUSARTx,*(str+k));
k++;
}while(*(str+k)!='\0');
// 等待发送完成
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}
//微秒级的延时
void delay_us(uint32_t delay_s)
{
volatile unsigned int i;
volatile unsigned int t;
for (i = 0; i < delay_s; i++)
{
t = 11;
while (t != 0)
{
t--;
}
}
}
//毫秒级的延时函数
void delay_ms(uint16_t delay_ms)
{
volatile unsigned int num;
for (num = 0; num < delay_ms; num++)
{
delay_us(1000);
}
}
USART_Init_Config.h文件
#ifndef __USART_INIT_CONFIG_H
#define __USART_INIT_CONFIG_H
#include "stm32f10x.h"
void USART1_Init(void);
void Usart_SendByte(USART_TypeDef * pUSARTx,uint8_t ch);
void USART_SendString(USART_TypeDef * pUSARTx,char *str);
void delay_ms(uint16_t delay_ms);
#endif
main.c文件
头文件添加
#include "USART_Init_Config.h"
在while循环中添加
USART_SendString(USART1,"Hello World");
delay_ms(1000);
实验结果表明中断函数的加入并不会影响串口通信,按键中断对串口发送不会带来干扰或延迟。
二、中断方式实现串口通信
采用串口中断方式重做上周查询方式的串口通信作业,分别实现:
(1)当stm32接收到1个字符“s”时,停止持续发送“hello windows!”; 当接收到1个字符“t”时,持续发送“hello windows!”(提示:采用一个全局标量做信号灯);
(2)当stm32接收到字符“stop stm32!”时,停止持续发送“hello windows!”; 当接收到字符“go stm32!”时,持续发送“hello windows!”(提示:要将接收到的连续字符保存到一个字符数组里,进行判别匹配。写一个接收字符串的函数。
(1)添加USART_Init_Config.c文件和USART_Init_Config.h文件(上文)
(2)main.c文件
#include "stm32f10x.h"
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include "USART_Init_Config.h"
// 全局变量作为标志
volatile uint8_t flag = 0;
volatile bool stringReceived = false;
char receivedString[20]; // 存放接收到的字符串
int stringIndex = 0;
// 中断服务例程
void USART1_IRQHandler(void) {
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
char data = USART_ReceiveData(USART1);
// 处理单字符命令
if(data == 's') flag= 0;
else if(data == 't') flag = 1;
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
/*
// 处理字符串
if(data == '\n' || data == '\r') {
receivedString[stringIndex] = '\0'; // 字符串结束
stringReceived = true;
} else {
receivedString[stringIndex++] = data;
if(stringIndex >= 19) stringIndex = 0; // 防止数组越界
}
*/
}
}
// 主循环
int main(void) {
// ... 初始化代码 ...
USART1_Init();
while(1) {
if(flag ==1) {
USART_SendString(USART1, "hello windows!\r\n");
delay_ms(1000);
}
/*
if(stringReceived) {
// 检查接收到的字符串
if(strcmp(receivedString, "stop stm32!") == 0) {
flag = 0;
} else if(strcmp(receivedString, "go stm32!") == 0) {
flag = 1;
}
// 重置字符串接收标志和索引
stringReceived = false;
stringIndex = 0;
}
*/
}
}
实验结果:
总结
学习到了用标准库的方式来实现中断,利用中断来控制led和实现串口通信,但也存在不懂的地方和不足之处。