STM32--EXTI外部中断

前文回顾---STM32--GPIO

相关回顾--有关中断系统简介


目录

STM32中断

NVIC

 EXTI外部中断

AFIO

EXTI框图

旋转编码器简介

对射式红外传感器工程

代码:

旋转编码器工程

代码:


STM32中断

先说一下基本原理

1.中断请求发生:外部事件(如按键触发,定时器溢出等)引起中断请求信号;

2.中断向量表:中断发生后,CPU会根据中断向量表找到对应的中断服务程序入口。

3.中断服务程序执行:CPU会跳转到中断服务程序入口,执行相应的中断处理代码;

4.中断优先级:不同的中断源可能具有不同的优先级,可以通过设置优先级来控制中断的相对重要性。

5.中断嵌套:STM32支持中断的嵌套,即在一个中断服务程序中可以发生另一个中断程序,这种情况下需要使用优先级抢占和屏蔽来管理中断的执行程序;

6.中断清除:中断服务程序执行完成后,需要立即清除中断标志位,以便允许其他同优先级或更高优先级的中断发生。

在STM32中,有68个可屏蔽中断通道,每个中断通道包含16个可编程的优先等级;

具体使用哪条通道,需要查看手册;

 上面灰色部分的是内核的中断,白色部分就是STM32外设的中断;

由于STM32中断通道众多,所以就用了一个叫作嵌套向量中断器(NVIC)来进行管理;


NVIC

NVIC(Nested Vectored Interrupt Controller)是用于管理中断的硬件模块,它嵌套在ARM Cortex-M3上,是一个内核外设。NVIC负责中断向量表、中断优先级、中断使能以及中断处理的功能。

上图是NVIC的基本结构; 

在STM32中,NVIC的功能主要有以下几个方面:

1.中断向量表:NVIC管理中断向量表,它是一个存储中断服务程序入口的表格。当一个中断请求发生时,CPU会根据中断号从中断向量表中获取对应的中断的服务程序的入口地址,并跳转执行该程序。

2.中断优先级:NVIC允许设置不同中断源的优先级,以确定中断的相对重要性。通过配置优先级寄存器,可以设置每个中断源的优先级当多个中断同时发生时,具有最高优先级的中断将被立即处理。
3. 中断使能:NVIC控制中断的使能与禁止。通过设置使能寄存器,可以启用或禁用特定中断源的中断功能。禁用某个中断源后,即使该中断源发生,CPU也不会响应。
 

总的来说,有了NVIC,CPU就可以免去选择哪个中断通道这个步骤,让NVIC选择好后再进入到CPU

接下来看NVIC优先级的分组。

NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队。

 进行分组的目的是为了更灵活的管理和控制中断优先级。在程序中,会先进行分组,再让不同等级的去执行优先级。比如有多个中断源进入不同通道,那么抢占优先级就是来分不同身份的,身份相同归为一组,而数值越小代表身份越高;而响应优先级表示同等身份下的不同等级;

 EXTI外部中断

EXTI(EXternal Interrupt,外部中断)是STM32中用于处理外部事件触发的中断机制。它允许外部设备(如按键、传感器等)通过引脚信号触发中断,从而实现对这些事件的快速响应。

EXTI可以与GPIO引脚进行连接,作为外部中断的触发源。支持所有的GPIO口,但不允许相同的Pin同时触发中断;

EXTI有多种触发方式:支持上升沿触发、下降沿触发、边沿变化触发等,可以根据实际需求进行选择适合的触发方式。

EXTI可以与NVIC配合使用,实现中断的屏蔽和优先级抢占

这是EXTI的基本结构

最左边是GPIO口,通过AFIO中断引脚选择器来进行选择三个GPIO口的16个通道中的一个与EXTI进行连接。例如GPIOA,GPIOB、GPIOC各个的PIN0,只能有一个PIN0于EXTI0进行连接。之后通道就连接到EXTI边沿检测与控制电路上,最后连到NVIC。

在边沿检测还有一些绿色的外部中断通道。在STM32的外部中断中,可以从低功耗模式的停止模式来唤醒STM32。对于PVD电源电压检测,当电源从低电压恢复时,就需要PVD借助外部中断退出停止模式;对于RTC来说,有时为了省电,定了一个闹钟之后STM32就会进入停止模式,闹钟响起时再唤醒,就需要借助外部中断。对于这些通道,我们在这做一下简单了解就行。

AFIO

 AFIO主要用于引脚复用功能的选择和重定义

在STM32中,AFIO主要完成两个任务:复用功能引脚重映射中断引脚选择

 简单的来说就是GPIO口打通了引脚口与外设的连接,对于要实现EXTI这个中断功能,就需要AFIO,将引脚口拓展多了一个功能作用,然后EXTI就能与引脚口进行连接,引脚口就多了这项功能。

EXTI框图

右下角有20根输入线,输入线进入边沿检测电路,选择不同的触发方式;接着进入或门,只要有一个高电平1,那么输出高电平1,只有全部输入线为低电平0,才能输出0;接着兵分两路,连接着请求挂起寄存器的是触发中断的,脉冲发生器是事件中断;请求挂起寄存器相当于一个请求中断标志位,如果中断挂起寄存器置1,那么将会向左走进入与中断屏蔽寄存器进入一个与门,如果与门一条输入线为1,对于与门来说,任意数与1等于任意数;一条输入线为0,那么输出将会是0;下面的事件中断也是一样,与事件屏蔽寄存器相连到与门,通过脉冲发生器给出一个电平脉冲,用来触发外设的动作;最上面是APB总线,我们可以通过APB总线来访问到EXTI。

旋转编码器简介

 对于STM32来说,想要获取信号是外部驱动很快的突发信号;像旋转编码器的输出信号,我可以保持不拧它,那么这时候不需要STM32做任何事,一旦进行旋转,那么就会有很多脉冲信号需要STM32进行接收,这个脉冲信号发送的很快,就需要要求STM32快速的接收,像这个要求快的,就需要用到STM32的外部中断。

旋转编码器:用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向

类型:机械触点式/霍尔传感器式/光栅式

我们使用的是第二张图的旋转编码器,左边是它的外观,右边是它的内部结构;是利用金属触点来进行通断的它是一种机械触点式编码器,

这是内部的连接方式,中间圆点相当一个按键;

 

这是编码盘,上面附有金属触点,通过旋转可以让金属触点与 金属弹片接触来触发;并且这个编码盘经过设计的,能让两个触点的通断产生一个九十度的相位差。

当正转时,A向引脚输出一个方波波形,同时B向引脚输出一个相差九十度的方波波形,B向方波波形滞后九十度

当反转时, A向引脚输出一个方波波形,同时B向引脚输出一个相差九十度的方波波形,B向方波波形超前九十度

 这种称为正交波形,这样就能够识别方向;


对射式红外传感器工程

我们将实现通过传感器电平的变化,让其屏幕的数字加一;

连接方式:

代码:

CountSensor.h

#ifndef __COUNTSENSOR_H__
#define __COUNTSENSOR_H__void CountSensor_Init();
uint16_t CountSensor_Get();#endif

 CountSensor.c

#include "stm32f10x.h"               // Device headeruint16_t CountSensor_Count;void CountSensor_Init()
{//开启APB2时钟外设开关RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//选择GPIO输出模式和引脚,并初始化GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;   //上拉输入GPIO_InitStructure.GPIO_Pin=GPIO_Pin_14;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);//选择用作EXIT线的GPIO的引脚GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);//选择EXTI触发模式和引脚线并初始化EXTI_InitTypeDef EXTI_InitStructure;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_PriorityGroupConfig(NVIC_PriorityGroup_2);//选择优先级高低以及初始化NVIC_InitTypeDef NVIC_InitStructure;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);}void EXTI15_10_IRQHandler()
{//检查指定的EXTI行是否断言,中断函数if(EXTI_GetITStatus(EXTI_Line14)==SET){//触发可能会导致混乱,增加判断语句使数据稳定if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0){CountSensor_Count++;}清除行标志位EXTI_ClearITPendingBit(EXTI_Line14);}
}//获取次数
uint16_t CountSensor_Get()
{return CountSensor_Count;
}

首先需要对外部中断进行初始化,我们需要开启APB2的外设时钟开关

接着对GPIO引脚初始化,然后让EXTI连接到GPIO口上,

 接着对EXTI进行初始化,最后选择NVIC优先级即可。

 NVIC初始化;

 

然后操作执行中断函数中的程序,由于我们是以边沿触发方式来计次的,这种方式难免会有些不稳定,所有就多加一条判断语句,下降沿后,GPIO口数据将会是低电平,补充上去使计次稳定;

OELD

#ifndef __OLED_H__
#define __OLED_H__void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line,uint8_t Column,char Char);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);#endif

 

#ifndef __OLED_FONT_H
#define __OLED_FONT_H/*OLED字模库,宽8像素,高16像素*/
const uint8_t OLED_F8x16[][16]=
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//  00x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//! 10x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//" 20x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//# 30x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,//$ 40xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00,//% 50x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10,//& 60x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//' 70x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00,//( 80x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00,//) 90x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00,//* 100x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00,//+ 110x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00,//, 120x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,//- 130x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,//. 140x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00,/// 150x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,//0 160x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//1 170x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,//2 180x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,//3 190x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,//4 200x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,//5 210x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,//6 220x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,//7 230x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,//8 240x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,//9 250x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,//: 260x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00,//; 270x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00,//< 280x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,//= 290x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00,//> 300x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00,//? 310xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00,//@ 320x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,//A 330x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00,//B 340xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00,//C 350x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00,//D 360x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00,//E 370x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00,//F 380xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00,//G 390x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20,//H 400x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//I 410x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,//J 420x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,//K 430x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00,//L 440x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00,//M 450x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00,//N 460xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00,//O 470x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00,//P 480xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00,//Q 490x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20,//R 500x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00,//S 510x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//T 520x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//U 530x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00,//V 540xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00,//W 550x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20,//X 560x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//Y 570x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00,//Z 580x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00,//[ 590x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00,//\ 600x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00,//] 610x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//^ 620x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,//_ 630x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//` 640x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20,//a 650x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00,//b 660x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00,//c 670x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20,//d 680x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00,//e 690x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//f 700x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00,//g 710x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//h 720x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//i 730x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,//j 740x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00,//k 750x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//l 760x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F,//m 770x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//n 780x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//o 790x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00,//p 800x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80,//q 810x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00,//r 820x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00,//s 830x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00,//t 840x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20,//u 850x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00,//v 860x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00,//w 870x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00,//x 880x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00,//y 890x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00,//z 900x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40,//{ 910x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,//| 920x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00,//} 930x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//~ 94
};#endif
#include "stm32f10x.h"                  // Device header
#include "OLED_Font.h"//引脚配置
#define OLED_W_SCL(x)  GPIO_WriteBit(GPIOB,GPIO_Pin_8,(BitAction)(x))
#define OLED_W_SDA(x)  GPIO_WriteBit(GPIOB,GPIO_Pin_9,(BitAction)(x))//引脚初始化
void OLED_I2C_Init()
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_Init(GPIOB,&GPIO_InitStructure);OLED_W_SCL(1);OLED_W_SDA(1);
}/*** @brief  I2C开始* @param  无* @retval 无*/
void OLED_I2C_Start(void)
{OLED_W_SDA(1);OLED_W_SCL(1);OLED_W_SDA(0);OLED_W_SCL(0);
}/*** @brief  I2C停止* @param  无* @retval 无*/
void OLED_I2C_Stop(void)
{OLED_W_SDA(0);OLED_W_SCL(1);OLED_W_SDA(1);
}/*** @brief  I2C发送一个字节* @param  Byte 要发送的一个字节* @retval 无*/
void OLED_I2C_SendByte(uint8_t Byte)
{uint8_t i;for (i = 0; i < 8; i++){OLED_W_SDA(Byte & (0x80 >> i));OLED_W_SCL(1);OLED_W_SCL(0);}OLED_W_SCL(1);	//额外的一个时钟,不处理应答信号OLED_W_SCL(0);
}/*** @brief  OLED写命令* @param  Command 要写入的命令* @retval 无*/
void OLED_WriteCommand(uint8_t Command)
{OLED_I2C_Start();OLED_I2C_SendByte(0x78);  //发送从机地址OLED_I2C_SendByte(0x00);  //写命令OLED_I2C_SendByte(Command);OLED_I2C_Stop();
}/*** @brief  OLED写数据* @param  Data 要写入的数据* @retval 无*/
void OLED_WriteData(uint8_t Data)
{OLED_I2C_Start();OLED_I2C_SendByte(0x78);OLED_I2C_SendByte(0X40);OLED_I2C_SendByte(Data);OLED_I2C_Stop();}/*** @brief  OLED设置光标位置* @param  Y 以左上角为原点,向下方向的坐标,范围:0~7* @param  X 以左上角为原点,向右方向的坐标,范围:0~127* @retval 无*/
void OLED_SetCursor(uint8_t Y,uint8_t X)
{OLED_WriteCommand(0xB0|Y);								//设置Y位置OLED_WriteCommand(0x10|((X&0xF0)>>4));    //设置X位置高4位OLED_WriteCommand(0x00|(X&0x0F));					//设置X位置低4位}/*** @brief  OLED清屏* @param  无* @retval 无*/
void OLED_Clear(void)
{uint8_t i,j;for(i=0;i<8;i++){OLED_SetCursor(i,0);for(j=0;j<128;j++){OLED_WriteData(0x00);}}
}/*** @brief  OLED显示一个字符* @param  Line 行位置,范围:1~4* @param  Column 列位置,范围:1~16* @param  Char 要显示的一个字符,范围:ASCII可见字符* @retval 无*/
void OLED_ShowChar(uint8_t Line,uint8_t Column,char Char)
{uint8_t i;OLED_SetCursor((Line-1)*2,(Column-1)*8);			//设置光标位置上半部分for(i=0;i<8;i++){OLED_WriteData(OLED_F8x16[Char-' '][i]);		//显示上半部分}OLED_SetCursor((Line-1)*2+1,(Column-1)*8);			//设置光标位置在下半部分for(i=0;i<8;i++){OLED_WriteData(OLED_F8x16[Char-' '][i+8]);	//显示下半部分}}/*** @brief  OLED显示字符串* @param  Line 起始行位置,范围:1~4* @param  Column 起始列位置,范围:1~16* @param  String 要显示的字符串,范围:ASCII可见字符* @retval 无*/
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{uint8_t i;for (i = 0; String[i] != '\0'; i++){OLED_ShowChar(Line, Column + i, String[i]);}
}/*** @brief  OLED次方函数* @retval 返回值等于X的Y次方*/
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{uint32_t Result = 1;while (Y--){Result *= X;}return Result;
}/*** @brief  OLED显示数字(十进制,正数)* @param  Line 起始行位置,范围:1~4* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:0~4294967295* @param  Length 要显示数字的长度,范围:1~10* @retval 无*/
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{uint8_t i;for (i = 0; i < Length; i++)							{OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');}
}/*** @brief  OLED显示数字(十进制,带符号数)* @param  Line 起始行位置,范围:1~4* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:-2147483648~2147483647* @param  Length 要显示数字的长度,范围:1~10* @retval 无*/
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{uint8_t i;uint32_t Number1;if (Number >= 0){OLED_ShowChar(Line, Column, '+');Number1 = Number;}else{OLED_ShowChar(Line, Column, '-');Number1 = -Number;}for (i = 0; i < Length; i++)							{OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');}
}/*** @brief  OLED显示数字(十六进制,正数)* @param  Line 起始行位置,范围:1~4* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:0~0xFFFFFFFF* @param  Length 要显示数字的长度,范围:1~8* @retval 无*/
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{uint8_t i, SingleNumber;for (i = 0; i < Length; i++)							{SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;if (SingleNumber < 10){OLED_ShowChar(Line, Column + i, SingleNumber + '0');}else{OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');}}
}/*** @brief  OLED显示数字(二进制,正数)* @param  Line 起始行位置,范围:1~4* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:0~1111 1111 1111 1111* @param  Length 要显示数字的长度,范围:1~16* @retval 无*/
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{uint8_t i;for (i = 0; i < Length; i++)							{OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');}
}/*** @brief  OLED初始化* @param  无* @retval 无*/
void OLED_Init(void)
{uint32_t i, j;for (i = 0; i < 1000; i++)			//上电延时{for (j = 0; j < 1000; j++);}OLED_I2C_Init();			//端口初始化OLED_WriteCommand(0xAE);	//关闭显示OLED_WriteCommand(0xD5);	//设置显示时钟分频比/振荡器频率OLED_WriteCommand(0x80);OLED_WriteCommand(0xA8);	//设置多路复用率OLED_WriteCommand(0x3F);OLED_WriteCommand(0xD3);	//设置显示偏移OLED_WriteCommand(0x00);OLED_WriteCommand(0x40);	//设置显示开始行OLED_WriteCommand(0xA1);	//设置左右方向,0xA1正常 0xA0左右反置OLED_WriteCommand(0xC8);	//设置上下方向,0xC8正常 0xC0上下反置OLED_WriteCommand(0xDA);	//设置COM引脚硬件配置OLED_WriteCommand(0x12);OLED_WriteCommand(0x81);	//设置对比度控制OLED_WriteCommand(0xCF);OLED_WriteCommand(0xD9);	//设置预充电周期OLED_WriteCommand(0xF1);OLED_WriteCommand(0xDB);	//设置VCOMH取消选择级别OLED_WriteCommand(0x30);OLED_WriteCommand(0xA4);	//设置整个显示打开/关闭OLED_WriteCommand(0xA6);	//设置正常/倒转显示OLED_WriteCommand(0x8D);	//设置充电泵OLED_WriteCommand(0x14);OLED_WriteCommand(0xAF);	//开启显示OLED_Clear();				//OLED清屏
}

main.c

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "CountSensor.h"
int main()
{OLED_Init();CountSensor_Init();while(1){OLED_ShowNum(1,1,CountSensor_Get(),5);}
}

旋转编码器工程

通过旋转编码器,在屏幕上显示旋转计次,正向旋转加一,反向旋转减一;

连接方式:

代码:

Encounter 

#ifndef __ENCOUNTER_H__
#define __ENCOUNTER_H__void Encounter_Init();
int16_t CountGet();#endif
#include "stm32f10x.h"         // Device headerint16_t count;void Encounter_Init()
{//开启APB2时钟外设开关RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//选择GPIO输出模式和引脚,并初始化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);//选择用作EXIT线的GPIO的引脚GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);//选择EXTI触发模式和引脚线并初始化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);}int16_t CountGet()
{//int16_t tmp=count;//count=0;return count;
}
void EXTI0_IRQHandler()
{//如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动if(EXTI_GetITStatus(EXTI_Line0)==SET){if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0){if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0){count--;}}}EXTI_ClearITPendingBit(EXTI_Line0);
}void EXTI1_IRQHandler()
{if(EXTI_GetITStatus(EXTI_Line1)==SET){if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0){if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0){count++;}}}EXTI_ClearITPendingBit(EXTI_Line1);
}

这里初始化与上面差不多,只不过这里有两个中断源;正转的时候,A向波形呈现低电平状态,B向波形正在下降沿;反转的时候,B向波形呈现低电平状态,A向波形正在下降沿;

main.c

 

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Encounter.h"
int main()
{//int16_t sum=0;OLED_Init();Encounter_Init();while(1){// sum+=CountGet();OLED_ShowSignedNum(1,1,CountGet(),5);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/84081.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Profibus-DP转modbus RTU网关modbus rtu和tcp的区别

捷米JM-DPM-RTU网关在Profibus总线侧实现主站功能&#xff0c;在Modbus串口侧实现从站功能。可将ProfibusDP协议的设备&#xff08;如&#xff1a;EH流量计、倍福编码器等&#xff09;接入到Modbus网络中&#xff1b;通过增加DP/PA耦合器&#xff0c;也可将Profibus PA从站接入…

MyBatis操作数据库常见用法总结2

文章目录 1.动态SQL使用什么是动态sql为什么用动态sql标签拼接标签拼接标签拼接标签拼接标签拼接 补充1&#xff1a;resultType和resultMap补充2&#xff1a;后端开发中单元测试工具使用&#xff08;Junit框架&#xff09; 1.动态SQL使用 以insert标签为例 什么是动态sql 是…

golang协程池(goroutine池)ants库实践

golang中goroutine由运行时管理&#xff0c;使用go关键字就可以方便快捷的创建一个goroutine,受限于服务器硬件内存大小&#xff0c;如果不对goroutine数量进行限制&#xff0c;会出现Out of Memory错误。但是goroutine泄漏引发的血案&#xff0c;想必各位gopher都经历过&#…

易服客工作室:初学者的终极WordPress SEO教程(入门指导)

改善WordPress SEO对于获得更多网站流量至关重要。可悲的是&#xff0c;大多数WordPress SEO指南对于新用户来说太技术性了。 如果您认真考虑增加网站流量&#xff0c;则需要注意WordPress SEO最佳做法。 在本WordPress SEO教程中&#xff0c;我们将分享WordPress SEO的主要技…

创建Springboot+vue3项目

项目概述创建springboot项目加入mybatis-plus支持1.加入依赖代码2.创建数据库实例3.yml文件的配置4.编写测试代码5.测试结果 创建vue项目报错错误一错误二错误三 项目概述 后端&#xff1a;Springboot、mybatis-plus、java 前端&#xff1a;nodejs、vue脚手架、element-ui 数据…

【Spring Boot 源码学习】自动装配流程源码解析(上)

自动装配流程源码解析&#xff08;上&#xff09; 引言往期内容主要内容1. 自动配置开关2. 加载自动配置组件3. 自动配置组件去重 总结 引言 上篇博文&#xff0c;笔者带大家从整体上了解了AutoConfigurationImportSelector 自动装配逻辑的核心功能及流程&#xff0c;由于篇幅…

HCIP-linux和kvm(ks配置文件自动化安装及console连虚拟机有问题)

1、linux linux安装教程参考&#xff0c;https://blog.51cto.com/cloudcs/5245337 yum源配置 本地yum源配置&#xff1a; 8版本配置&#xff1a;将光盘iso挂载到某个目录&#xff0c;/dev/cdrom是/dev/sr0软链接&#xff0c;# mount /dev/cdrom /mnt&#xff0c;# ls /mnt Ap…

C#导入数据使用Task异步处理耗时任务

C#多线程中&#xff0c;我们可以使用async和await来异步处理耗时任务。 现在我们打开一个Excel表格&#xff0c;将Excel表格的每一行数据进行处理&#xff0c;并存储到数据库中 新建Windows应用程序DataImportDemo&#xff0c;.net framework 4.6.1 将默认的Form1重命名为Fo…

数组slice、splice字符串substr、split

一、定义 这篇文章主要对数组操作的两种方法进行介绍和使用&#xff0c;包括&#xff1a;slice、splice。对字符串操作的两种方法进行介绍和使用&#xff0c;包括&#xff1a;substr、split (一)、数组 slice:可以操作的数据类型有&#xff1a;数组字符串 splice:数组 操作数组…

小程序wx:else提示 Bad attr `wx

问题&#xff1a;以下wx:for里的wx:if &#xff0c; wx:else 会报这个错&#xff1a;Bad attr wx <scroll-view class"scroll1" scroll-x enable-flex"true"><view wx:if"{{playlist.length>0}}" class"item" wx:for"…

SonarQube安装与Java、PHP代码质量分析扫描

文章目录 1、下载安装1.1、SonarQube下载1.2、SonarQube安装1.3、SonarQube中文汉化1.4、SonarScanner扫描器 2、扫描项目2.1、java代码扫描2.2、php代码扫描 1、下载安装 SonarQube负责存储代码数据、收集数据、分析代码和生成报告等。 1.1、SonarQube下载 下载地址&#x…

MPP架构和Hadoop架构的区别

1. 架构的介绍 mpp架构是将许多数据库通过网络连接起来&#xff0c;相当于将一个个垂直系统横向连接&#xff0c;形成一个统一对外的服务的分布式数据库系统。每个节点由一个单机数据库系统独立管理和操作该物理机上的的所有资源&#xff08;CPU&#xff0c;内存等&#xff09…

【QT】 QT开发PDF阅读器

很高兴在雪易的CSDN遇见你 &#xff0c;给你糖糖 欢迎大家加入雪易社区-CSDN社区云 前言 本文分享QT开发PDF阅读器技术&#xff0c;希望对各位小伙伴有所帮助&#xff01; 感谢各位小伙伴的点赞关注&#xff0c;小易会继续努力分享&#xff0c;一起进步&#xff01; 你的点…

使用chatGPT-4 畅聊量子物理学

与chatGPT深入研究起源、基本概念&#xff0c;以及海森堡、德布罗意、薛定谔、玻尔、爱因斯坦和狄拉克如何得出他们的想法和方程。 1965 年&#xff0c;费曼&#xff08;左&#xff09;与朱利安施温格&#xff08;未显示&#xff09;和朝永信一郎&#xff08;右&#xff09;分享…

数据分析-python学习 (1)numpy相关

内容为&#xff1a;https://juejin.cn/book/7240731597035864121的学习笔记 导包 import numpy as np numpy数组创建 创建全0数组&#xff0c;正态分布、随机数组等就不说了&#xff0c;提供了相应的方法通过已有数据创建有两种 arr1np.array([1,2,3,4,5]) 或者datanp.loadt…

当我准备出门时,发现了......我可以用Python实现12306自动买票

前言 不知道大家有没有之前碰到这样的情况&#xff0c;打算去某一个地方当你规划好了时间准备去买票的时候&#xff0c;你想要的那一列往往没有你想要的票了&#xff0c;尤其是国庆七天假和春节半月假&#xff0c;有时候甚至买不到规定计划时间内的票&#xff0c;真的是太烦躁…

列队 Queue 接口概述

在Java中&#xff0c;Queue&#xff08;队列&#xff09;是一种基本的数据结构&#xff0c;用于按照先进先出&#xff08;FIFO&#xff09;的顺序存储元素。Java提供了多种实现Queue接口的类&#xff0c;以下是几种常见的实现方式&#xff1a; LinkedList&#xff1a;LinkedLis…

RISC-V公测平台发布 · 使用YCSB测试SG2042上的MySQL性能

实验介绍&#xff1a; YCSB&#xff08;全称为Yahoo! Cloud Serving Benchmark&#xff09;&#xff0c;该性能测试工具由Java语言编写&#xff08;在之前的MC文章中也提到过这个&#xff0c;如果没看过的读者可以去看看之前MC那一期&#xff09;&#xff0c;主要用于云端或者…

【位操作符的几种题型】

位操作符的几种题型 目录 题型一&#xff1a;寻找“单身狗”。 题型二&#xff1a;计算一个数在二进制中1的个数 题型三&#xff1a;不允许创建临时变量&#xff0c;交换两个整数的内容 题型一&#xff1a;寻找“单身狗”。 1.1题目解析 在一个整型数组中&#xff0c;只有…

SUB-1G SOC芯片DP4306F 32 位 ARM Cortex-M0+内核替代CMT2380F32

DP4306F是一款高性能低功耗的单片集成收发机&#xff0c;集成MO核MCU&#xff0c;工作频率可覆盖200MHiz^ 1000MHz。 支持230/408/433/470/868/915频段。该芯片集成了射频接收器、射频发射器、频率综合器、GFSK调制器、GFSK解调器等功能模块。通过SPI接口可以对输出功率、频道选…