文章笔记源于——江科大自化协的视频
一. 中断系统
中断 : 在主程序运行过程中,出现特定的中断触发条件,使得CPU暂停当前正在运行的程序,而去处理中断程序,完成后,又返回原来被暂停的位置继续工作
中断优先 : 当有多个中断开始时,CPU会根据事情的轻重响应更加紧急的中断
中断嵌套 : 一个中断正常进行,又来一个更高级的中断,会先去做刚来的高级的中断,然后依次返回
一般中断函数都是在一个子函数里的,这个函数不需要我们调用,当中断来临时,自动由硬件调用这个函数
二. STM32的中断
1. 68个可屏蔽中断通道,包含EXTI,TIM,ADC,USART,SPI,IIC,RTC等多个外设
2. 使用NVIC统一管理中断,每个中断有16个可编程的优先等级,可对优先级分组
三 . EXTI简介
1. EXTI外部中断
2. EXTI可监测指定GPIO口的电平信号,当GPIO口的电平变化时,EXTI就立刻向NVIC发出中断申请,经过NVIC裁决后,让CPU执行中断程序
3. 触发方式 : 上升沿(低变高),下降沿(高变低),双边沿(前两个都可以),软件出发(写代码出发,和GPIO没关系)
4. GPIO : 支持所有GPIO口,但是相同的Pin不能触发中断
5.通道数 : 16个Pin,(外加PVD输出,RTC闹钟,USB唤醒,以太网唤醒)——蹭网
6.触发响应方式: 中断响应,事件响应
所以PA0 PB0 PC0只能有一个触发,不能同时触发
四. AFIO复用IO口
在STM32中,AFIO口主要完成两个任务:复用功能引脚重映射,中断引脚选择
五. 旋转编码器介绍
他是外部信号,这个信号是突发的,STM32只能被动读取,而且万一读取晚了,就会错过很多波形,所以我们就要考虑STM32的外部中断了
旋转编码器,是可以按下去的,这个时候他可以当作普通的按键来用
旋转编码器模块有5个引脚,分别是GND(-), VCC(+), SW, DT, CLK。其中VCC和GND用来接电源和地,按缩写SW应该是Switch(开关)、CLK是Clock(时钟)、DT是Data(数据)
A_CLK B_DT ,我们没有接C,因为不用他的开关(当然也是可以使用的)
接下来进入代码部分
1.主函数
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"
//给了Num赋值
int16_t Num;
int main(void)
{
//OLED ENCODER初始化,初始化完才能用
OLED_Init();
Encoder_Init();
OLED_ShowString(1, 1, "Num:");
while (1)
{
Num += Encoder_Get();
OLED_ShowSignedNum(1, 5, Num, 5);
}
}
2.Encode.c
#include "stm32f10x.h" // Device header
//定义一个带符号变量
int16_t Encoder_Count;
void Encoder_Init(void)
{
//两个中断的初始化代码,初始化时钟,GPIOB,AFIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//AFIO的部分
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
//指定的中断闲为EXTI_LINE1和EXTI_LINE0
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);
//中断分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//中断优先级,对两个通道分别设置优先级
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);
}
//把变量返回回去,返回变化值,所以返回count(这里是用了一个技巧,间接返回了count(把值付给temp让temp回去))
int16_t Encoder_Get(void)
{
int16_t Temp;
Temp = Encoder_Count;
Encoder_Count = 0;
return Temp;
}
//接下来是中断的中断函数
void EXTI0_IRQHandler(void)
{
//检查一下中断标志位
if (EXTI_GetITStatus(EXTI_Line0) == SET)
{
//判断一下另一个引脚的电平
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
//如果是就反转
Encoder_Count --;
}
//清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
//下面这个也是一样的,只是换了线,如果是9——15就把两个放一起,用一个中断就行
void EXTI1_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line1) == SET)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
{
//正转
Encoder_Count ++;
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
3.Encode.h
#ifndef __ENCODER_H
#define __ENCODER_H
void Encoder_Init(void);
int16_t Encoder_Get(void);
#endif