STM32 学习笔记_5 调试方法;外部中断

调试

OLED:方便,试试更新,但是显示框小。

串口:数据全,但是带电脑不方便。

MDK 自带 debug

OLED 调试

4个引脚的:3.3~5V 电压,地,SCL SDA 的 IIC。

我们把 GND-VCC-SCL-SDA 接到 B6-B9 上,B6 B7接高低电平。代码和51非常像,先使用老师的代码调试即可。

DeBug 调试

编译后点击放大镜d的符号。会显示汇编代码寄存器等信息,这些C编程都不用太管。

debug 从左到右的按钮意思:复位,全速运行,停止,向下一行,跳出当前函数,跳转到光标指定位置。

窗口部分:命令窗口,反汇编窗口,符号窗口(里面可以查看一些寄存器的结构,右键添加到 watch 窗口还可以实时查看值)。

Peripherals-System View 里面可以查看外设实时的值。

中断

可以使程序先停止运行去处理一些事情。

中断优先级:有一定的优先级。

中断嵌套:低优先级中断处理时,可以被高优先级中断中断。

看门狗中断:程序定期喂狗,一段时间不喂触发,可能是卡死了,可以加一些校验是否卡死的逻辑。

PVD 中断:电量低触发,可以提醒充电。

NVIC 可以对中断的优先级分组,他接收多个中断,按优先级顺序分配给 CPU。0最高,15最低。优先级相同的按中断号排队。

有两种调度方法,一种是插队的,需要把当前中断处理完再调度下一个;另一种是不插队的,可以打断当前中断(抢占)。

EXTI

检测 GPIO 电平变化触发中断(PVD RTC USB Ethernet 也包含在内),可以上升 下降 双边沿 或软件触发。

所有 GPIO 引脚都可以,但是同 Pin 的不可以同时做中断引脚(PA1 PB1)。

32有两种触发响应的方式,一种是中断响应,发生响应时去 CPU 触发中断。一种是事件响应,发生响应时转而去触发其他的事件。

外部中断只有 16 个Pin,所以所有 GPIO 通过 AFIO 中断引脚选择,挑选一个 Pin 到 EXTI 边沿检测中。

AFIO 还有一个功能,让有复用功能的引脚重映射的功能。

image-20230423185714575

为了节省线路,9-5, 15-10 会触发同一个中断函数,因此需要通过标志位区分。

image-20230423190008581

边沿检测电路和软件中断时间寄存器只要有一个是1,就让或门结果=1.

触发事件就走下路,触发中断就走中路。

中断屏蔽寄存器:是否开启中断。

请求挂起寄存器:是否开启了当前引脚的中断。

什么时候用外部中断?

比如突发的需要读取的转瞬即逝的信号。比如旋转编码器。

按键需要处理消抖,而且也不是转瞬即逝,建议持续循环读取或者定时器中断读取。

旋转编码器

光栅式内部有红外光,旋转时会遮挡-接收-遮挡-接收,进而测定旋转速度。但是这种无法测定方向。

机械触点式旋转时依次接通触点,且左右旋转不同向会产生有相位差的方波。不适合高速旋转的电机测速。

其内部还有一个按键。

霍尔传感器式感应内部磁铁的旋转。

对射式红外传感器

很简单,内部有一个发送和接收红外线的部分,挡住时信号就会变化。

代码:对射式红外传感器

首先介绍一下 GPIO 里和中断相关的函数。

image-20230424062717573

我们设置中断源就需要最后一个函数,这个函数操作 AFIO 选脚。

//1. RCC 2. GPIO Mode 3. AFIO choose a pin 4. EXTI set trigger mode and IT response/Event response 5. set NVIC priority
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    
    //EXTI NVIC don't need open RCC.
    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);
    
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);

然后看一下 stm32f10x_exti.h 的内容:

image-20230424063229137

复位,初始化,结构体赋初值,软件中断,查看和清除标志位状态,查看和清除中断标志位状态。

EXTI_Initstruct 包含的参数:中断线(决定引脚),线命令(启用或禁用),中断模式(事件,中断),触发(上升,下降,双边沿)。

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 中断。中断函数信息在 misc.h 中:

image-20230424064523788

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//each chip can only set this group once. 2 means 2 bits for pre-emption priority, 2 bits for subpriority.
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;//choose a bit to open it
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//可以去查看一下设置的 EXTI_Mode 允许的范围
    NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
    NVIC_Init(&NVIC_InitStructure);

这样中断初始化函数就定义好了,

接下来写中断函数,中断函数名字固定,在启动文件中,以 IRQHandler 结尾的函数。找到对应端口的。

我们要先判断一下是不是当前的中断线(因为10-15通用),再清除标志位。

void EXTI15_10_IRQHandler(void){
    
    
    if(EXTI_GetITStatus(EXTI_Line14)==SET){
    
    
        EXTI_ClearITPendingBit(EXTI_Line14);
    }
}

然后在 main.c 中 include 该文件,并打开调试,在中断函数里打一个断点,然后全速运行,试一下挡住红外线会不会在中断处中断。

image-20230424070621393

成功停下了~

然后我们写一个示例,触发一次中断计数器++,并在 OLED 上打印计数器值。

则我们在 CountSensor 里定义一个全局变量,且写一个 Get 函数。

uint16_t Count_Get(void){
    
    
    return count;
}

main 里获取这个值并展现在 OLED 上就能记录了有几次挡住。

这里不建议在中断函数里对 OLED 进行操作,因为我们的中断本来就是暂停程序去执行的,如果程序正处理着 OLED 函数,被我们中断了,我们又在中断里操作了 OLED,就会出问题。因此我们这里采取的是设定一个变量,主函数可以访问其值,然后在主函数中再操作 OLED。

代码:旋转编码器

机械旋转编码器左拧右拧时触发触点接通。我们设置左拧-1,右拧+1,根据 OLED 上的数值判断现在拧到什么地步了。

数据处理:检测到左拧(A下降沿,B低电平),返回-1,右拧(B下降沿,A低电平),返回+1,主函数 count+返回值。

下图为右拧(顺时针),先检测B下降沿,再检测A低电平,即可确认确实是顺时针,count++。

img

#include "stm32f10x.h"                  // Device header

int16_t count;
void Encoder_Init(void){
    
    
    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);
    
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);
    
    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 Counter_Get(void){
    
    
    int16_t temp=count;
    count=0;
    return temp;
}

void EXTI0_IRQHandler(void){
    
    
    if(EXTI_GetITStatus(EXTI_Line0)==SET){
    
    
        if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1)==0){
    
    count--;
        }
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

void EXTI1_IRQHandler(void){
    
    
    if(EXTI_GetITStatus(EXTI_Line1)==SET){
    
    
        if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0)==0){
    
    count++;
        }
        EXTI_ClearITPendingBit(EXTI_Line1);
    }
}

#include "stm32f10x.h"
#include "Delay.h"
#include "Encoder.h"
#include "OLED.h"
int main(void){
    
    
    int16_t count;
    OLED_Init();
    OLED_ShowChar(1,1,'A');
    OLED_ShowString(1,3,"Hello World!");
    Encoder_Init();
    while(1){
    
    
        count+=Counter_Get();
        OLED_ShowNum(2,1,count,5);
    }
}

猜你喜欢

转载自blog.csdn.net/jtwqwq/article/details/130363303