前文回顾---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);}
}