一.程序调试方式
二.OLED简介
stm32引脚上电后,如果不初始化,默认是浮空输入的模式,在这个模式下引脚不会输出电平
三. 中断系统
EXTI外部中断,TIM定时器,ADC模数转换器,USART串口,SPI通信,I2C通信,RTC实时时钟
灰色部分是内核里面的中断
NVIC是用于统一分配中断优先及和管理中断的,一个外设可能会占用多个外设通道
NVIC通过每个中断的优先级,分配中断,之后通过右边的一个输出口告诉CPU该处理哪一个中断
值越小,优先级越高
抢占优先级,有一个病人还在看病,而需要紧急的看病的病人直接让其靠边站,等到自己看完病再让旁边等的人看
响应优先级,需要紧急的看病的病人等到,目前正在看的那一个病人看完后再插队去看
EXTI
双边沿就是上升沿和下降沿都可以触发中断,软件触发就是通过一句程序代码触发中断
相同的Pin 不能同时触发,例 PA1,PB1,PC1.........不能同时用
一共支持16引脚中断+4个“蹭网”的
中断响应后信号会传输到CPU
事件响应(不会触发中断)则是触发其他外设的操作,比如触发ADC转换,触发DMA........,属于外设间的联合工作
内核里面的外设都是不需要开启时钟的
四.代码实现
(1)对射式红外计数
#include "stm32f10x.h" // Device header
#include "Delay.h"
uint16_t Count;
void CountSensor_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;//局部变量不要放在可执行语句之后
EXTI_InitTypeDef EXTI_InitStructure;//声明可能不会出现在块中的可执行语句之后
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能RCC
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*AFIO选择中断引脚*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
EXTI_InitStructure.EXTI_Line=EXTI_Line14;
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;//指定外部中断线为下降沿触发
EXTI_Init(&EXTI_InitStructure);
/*NVIC中断分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置NVIC为分组2
//即抢占优先级范围:0~3,响应优先级范围:0~3
//此分组配置在整个工程中仅需调用一次
//若有多个中断,可以把此代码放在main函数内,while循环之前
//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
/*NVIC配置*/
NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
}
uint16_t get(void)
{
return Count;
}
/**
* 函 数:EXTI15_10外部中断函数
* 参 数:无
* 返 回 值:无
* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
* 函数名为预留的指定名称,可以从启动文件复制
* 请确保函数名正确,不能有任何差异,否则中断函数将不能进入
*/
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line14)==SET)
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
{
Count++;
}
// Count++;
EXTI_ClearITPendingBit(EXTI_Line14);//清除外部中断14号线的中断标志位
//中断标志位必须清除
//否则中断将连续不断地触发,导致主程序卡死在中断
}
}
中断函数的二层if判断是在中断标志位置1后,判断PB14是否输出0,如果输出为0,那么电源灯亮,此时挡光片移开,count++;
(2)旋转编码计数
Encoser.c
#include "stm32f10x.h" // Device header
int16_t count;
void Encoder_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);
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_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);
}
int16_t Get(void)
{
int16_t Temp;
Temp = count;
count = 0;
return Temp;
}
void EXTI0_IRQHandler(void)//正转+1
{
if(EXTI_GetITStatus(EXTI_Line0)==SET)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0)
{
count++;
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
void EXTI1_IRQHandler(void)//反转-1
{
if(EXTI_GetITStatus(EXTI_Line1)==SET)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0)
{
count--;
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
//我的编码器的A和B与视频教程是相反的!!!
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"
int16_t NUM;
int main(void)
{
OLED_Init();
Encoder_Init();
OLED_ShowString(1,1,"Num:");
while(1)
{
NUM+=Get();
OLED_ShowSignedNum(1,5,NUM,3);
}
}
注:
在中断函数里面,最好不要执行耗时过长的代码;
最好不要在中断函数和主函数调用相同的函数或操作同一个硬件(如果即在主函数调用OLED,又在中断里调用OLED ,会造成主函数内容显示出现问题)
对于外部硬件,并没有在进入中断时,进行现场保护