#include "stm32f10x.h" // Device header
// 学习EXTI 外部中断的配置与使用。
/*
// GPIOB 是APB2的外设。APB2, APB1, AHB
// 如果不确定外设挂载在那个总线上,不知道开启那个总线的时钟,就跳转到函数定义,看参数取值范围,既可以确定该总线上有哪些外设。
1,配置RCC,把使用到的GPIO外设时钟开启。
2,配置GPIO,端口选择输入模式。
3,配置AFIO,选择我们用的这一路GPIO PIn ,连接到后面的EXTI。
4,配置EXTI,选择边沿触发方式。触发响应方式(中断响应)
5,配置NVIC,优先级的配置。
6,最后根据NVIC,外部中断信号就能进CPU了。
*/
/*
void GPIO_AFIODeInit(void); 复位AFIO
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); 锁定GPIO配置的。防止意外更改。
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState); 配置AFIO事件输出功能。
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState); // 用来进行引脚重映射。(重映射的方式, 新的状态)
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource); // 用来进行配置AFIO的数据选择器,来选择中断源来自那个引脚。(配置中断用到的函数)
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface); // 和以太网有关的。
void EXTI_DeInit(void); // 把exti配置都清除,恢复成上电默认的状态。// 外设初始化基本都有着三个函数。很像。
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct); // 根据结构体里的参数,初始化exti。配置中断主要用的就是这个函数。
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct); // 调用这个函数可以把参数传递的结构体变量赋一个默认值。
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line); // 软件触发外部中断。调用这个函数,参数给一个指定的中断线,就能软件触发一次这个外部中断。
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line); // 库函数的模板函数。可以获取指定的标志位是否置1.
void EXTI_ClearFlag(uint32_t EXTI_Line); // 对置1的标志位清楚。 在主函数中查看或清除挂起标志位。
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line); // 获取中断标志位是否被置1。在中断中查看或清除标志位/
void EXTI_ClearITPendingBit(uint32_t EXTI_Line); // 清除中断挂起标志位。都是对状态寄存器的读写。下两个只能读取跟中断有关的标志位,并且对中断是否允许做了判断。
// 上两个是一般的读取状态寄存器,没有额外的处理,能不能触发中断标志位都能读取。
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); // 中断分组
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct); // 根据结构图里面指定的参数,初始化NVIC
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset); // NVIC,设置中断向量表
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState); // 系统低功耗配置
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource); //
*/
void count_sensor_init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 开启APB2外设时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);// 开启AFIO 时钟.
// EXTI (NVIC内核的外设)时钟一直是开启的。
// 2配置GPIO
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14; // 用的PIN14
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
// 配置AFIO
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14); // 由于用到的是GPIOB所以把X改成B,PIN14 .代表链接PB14号口的第14个中断线路。
// 配置EXTI
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line14; // 中断线
EXTI_InitStruct.EXTI_LineCmd = ENABLE; // 开启中断
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising; // 触发方式
EXTI_Init(&EXTI_InitStruct);
/*
EXTI_Trigger_Rising = 0x08,
EXTI_Trigger_Falling = 0x0C,
EXTI_Trigger_Rising_Falling = 0x10
*/
// 配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 两位抢占,两位响应。分组方式,整个芯片只能用一种。
NVIC_InitTypeDef NVIC_InitStruct;
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);
}
uint16_t num ;
// 在stm32中中断函数名字都是固定的。中断函数都是无参数,无返回值的。名字不要写错。
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line14) == SET) // 判断EXTI_Line14的中断标志位是否为1,
{ // 为1,就是触发中断了;为1,就可以执行中断了。
num++ ;
// 清除中断标志位。因为只要中断标志位置1了,程序就会跳到中断函数,不清除中断,程序会卡死在中断函数中。
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
uint16_t get_num (void)
{
return num ;
}
#include "stm32f10x.h" // Device header
int16_t encoder_count;
// AB 相都触发中断,只有B下降沿,a为低:正转。
// A下降沿,B低,反转。这样正反转,都转到位了才进中断执行加减。int16_t encoder_count = 0;
void encoder_init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 开启APB2外设时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);// 开启AFIO 时钟.
// EXTI (NVIC内核的外设)时钟一直是开启的。
// 2配置GPIO
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; // pin0 A相;PIN1B相。
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
// 配置AFIO
//GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0 | GPIO_PinSource1); // 由于用到的是GPIOB所以把X改成B,PIN14 .代表链接PB14号口的第14个中断线路。
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0 );
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1 );
// 配置EXTI
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line0 | EXTI_Line1; // 中断线
EXTI_InitStruct.EXTI_LineCmd = ENABLE; // 开启中断
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling; // 触发方式
EXTI_Init(&EXTI_InitStruct);
/*
EXTI_Trigger_Rising = 0x08,
EXTI_Trigger_Falling = 0x0C,
EXTI_Trigger_Rising_Falling = 0x10
*/
// 配置NVIC
// 同时初始化exti0 与 exti1
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 两位抢占,两位响应。分组方式,整个芯片只能用一种。
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn; // 指定中断通道,开启或关闭。EXTI0_IRQHandler
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; // 指定中断通道使能或失能。
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 指定所选通道的抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 与响应优先级。
NVIC_Init(&NVIC_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn; // 指定中断通道,开启或关闭。EXTI1_IRQHandler
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; // 指定中断通道使能或失能。
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 指定所选通道的抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; // 与响应优先级。
NVIC_Init(&NVIC_InitStruct);
}
int16_t get_encoder(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) == RESET) // A相的下降沿,B为低电平,代表正转。
{
encoder_count++;
}
EXTI_ClearITPendingBit(EXTI_Line0); // 标志位置0
}
}
void EXTI1_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line1) == SET)
{
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == RESET) // B相的下降沿,A为低电平,代表反转。
{
encoder_count-- ;
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
#include "stm32f10x.h" // Device header
#include "delay.h"
#include "led.h"
#include "key.h"
#include "buzzer.h"
#include "light.h"
#include "oled.h"
#include "countSensor.h"
#include "encoder.h"
/*
中断函数尽量简短快速。中断是用来处理突发事件,如果长时间在中断函数里主程序会受到严重的阻塞。
最好不要在主函数与中断函数调用相同的函数,或者操作同一个硬件。
在中断函数中操作变量或标志位,减少代码之间的耦合性,让各个模块之间相互独立,仅使用变量,标志位作为接口。使结构清晰,代码强健。
*/
int main (void)
{
int16_t num ;
OLED_Init();
key_init();
encoder_init();
OLED_ShowString(1, 1, "Count:");
while(1)
{
// num = get_num();
num += get_encoder();
OLED_ShowSignedNum(1, 7, num, 5); // 因为16bit最大值65535
}
}