打印
[开发工具]

MBED基础教程(5)mbedGPIO中断应用

[复制链接]
2956|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
643757107|  楼主 | 2015-12-19 13:25 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
mbedGPIO中断应用
在前面的mbed数字输入输出初步应用中我们已经学会了如果通过读取管脚的电平状态来判断用户的输入,但我们发现这种实现方式会浪费大量的MCU时间在检查管脚的状态上,应该说应用效果并不理想,那么有没有另外的方式无需检查就可以处理管脚状态的变化呢,答案是肯定的,那就是MCU的中断系统。MCU的中断系统不但可以处理管脚的变化,还可以处理更复杂的计时器的变化、MCU通讯状态的变化等,是微处理器中最重要的概念之一。

我们把原先通过不断查询来处理事件的方式叫做轮询,它和中断方式一起构成微处理器最常用的事件处理解决方案。在现实生活中,中断和查询也是常用的事务处理方式,举个例子,假设我们5点要出门办事,如果采用轮询方式,那我们就需要定时地去查看当前的时间,一旦发现时间到了5点就出门;而如果采用中断方式,我们就可以订个闹钟,在其它时间我们并不需要关心时间问题,只有闹钟响了我们才中断当前的工作出门。微处理对于中断的处理和这类似,下图是微处理器中断响应的流程图(至于说怎么检测到中断是微处理器系统自动实现的,用户只要把某个中断设成可检测状态,用户的程序就能响应这个中断,当然,具体的中断类型和微处理器的型号相关):
中断处理流程中的中断处理向量是从中断处理向量表中查询的,该表设定了所有中断处理流程的入口,它是在mbed的启动代码中被初始化的,一般都在.S汇编文件中。
GPIO中断是微处理器中断系统中最简单也是最常用的中断类型,它可以让用户在某个管脚状态发生特定的变化时执行相应的代码。需要注意的是,并不是所有的GPIO管脚都具备中断处理能力,如xbed LPC1768的19,20管脚就不能当作中断管脚。
Mbed使用InterruptIn对象来处理GPIO中断,它提供的主要方法有:
类名
方法
用途
InterruptIn
InterruptIn(PinName pin);
构造函数,把pin管脚设成中断处理管脚
int read();
读取管脚的当前状态
void rise(void (*fptr)(void));
设置管脚上升沿触发的中断处理函数
void fall(void (*fptr)(void));
设置管脚下降沿触发的中断处理函数
void mode(PinMode pull);
设置管脚的模式,一般来说,上升沿处理应设成PullDown,下降沿触发设成PullUp
这样的话,我们就可以采用更好的方式来监测xbed LPC1768按钮的状态了,由于xbed LPC1768外部已经用上拉电阻固定到高电平,所以我们最好采用下降沿触发的方式,下面的代码可以实现用户每按一下,LED灯变换一次:
DigitalOut led(LED1);
InterruptIn btn(P2_8);
void flip()
{
    led=!led;
}
int main() {
    btn.fall(&flip);
    while (1)
                        ;
}
需要注意的是,设置管脚对应的中断处理函数只需一次都可以在程序运行的整个生命周期发挥作用,另外,while(1);这句话不可少,否则,相当于程序运行完退出,那么中断处理函数也就不会发挥作用了。
前面的GPIO中断演示了ARM单片机中断系统的简单应用,但在实际的应用中,我们有可能遇到这样的情况,多个中断同时发生或者系统在做中断处理的时候又有新的中断发生,此时系统会怎么处理呢。为了回答此问题,我们必须从ARM中断的优先级开始。首先我们来看一个例子:
DigitalOut led(LED1);
InterruptIn btn(P2_8);
void flip()
{
    led=!led;
    wait(1);
    led=!led;
}
int main() {
    btn.fall(&flip);
    while (1)
                        ;
}
该代码同样利用按钮实现LED的翻转,不过,它是每按一次,LED亮一秒钟。在这里,实际上用到了两种类型的中断,一种就是GPIO中断,另外一种则是wait函数用到的计时器中断,从此代码的运行效果上看,显然,在GPIO中断时又被新的中断打断了,这是因为计时器的中断优先级比外部中断高,可以实现嵌套中断,但如果你在中断的过程中再次按下按钮,你会发现对应的中断处理程序并没有执行,这是因为中断处理程序只能被比自己优先级高的中断打断。
ARM中断优先级及嵌套中断的具体描述已经超过了本书的内容,我们只需要记住,ARM的中断是有优先级的,而优先级的具体规定有些中断是固定的,而有些中断的优先级是用户可配置的。用户的中断处理程序只能被比自己中断优先级高的中断打断,而且可以实现循环嵌套,即新的中断处理程序又会被优先级更高的中断打断。
为了验证GPIO中断嵌套的效果,我们另外介绍和中断相关的方法:
__disable_irq():禁止所有可屏蔽中断;
__enable_irq()允许所有未屏蔽中断;
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)设置中断的优先级,IRQn表示需要设置的中断号,后面表示优先级,数字越小表示优先级越高。
下面是这三个方法的综合应用,这里用到了串口中断和GPIO中断,默认情况下,两者的中断优先级是一样的,所以谁也不能中断谁,现象就是led1亮的时候,led2就不会亮,但我们把uart0的优先级调高一下就可以了:
DigitalOut led1(LED1);
DigitalOut led2(LED2);
InterruptIn btn(P2_8);
Serial pc(USBTX,USBRX);
void btnflip()
{
    led1=!led1;
    wait(10);
    led1=!led1;
}
void uartflip()
{
    led2=!led2;
    pc.getc();
    wait(10);
    led2=!led2;
}
int main() {
    __disable_irq();
    wait(5);
    __enable_irq();
    btn.fall(&btnflip);
    pc.attach(&uartflip);
    NVIC_SetPriority(UART0_IRQn, 5);
    NVIC_SetPriority(EINT3_IRQn, 255);
    while (1)
                        ;
}

该代码还应用了屏蔽中断功能,其现象就是前5秒系统不能响应用户按键和串口发送,其中的UART0_IRQn中断号就对应着UART0,而EINT3_IRQn,对应着所有管脚中断。


沙发
643757107|  楼主 | 2015-12-19 14:39 | 只看该作者
mbed-rtos在中断服务程序中的应用
mbed-rtos提供的同步机制和通讯机制同样也可以应用在中断服务程序中,但考虑到中断服务程序必须尽快返回,所以互斥锁机制不能用,而且所以涉及到需要等待的场合必须立刻返回,我们来看下面的示例代码,其效果和采用多线程方式实现是一样的:

#include "rtos.h"
typedef struct {
    uint32_t length;
    char str[255];
} message_t;
Mail<message_t, 16> queue;
Serial pc(USBTX,USBRX);
int32_t bufindex =-1;
char serialdata[255];
void serialhandler () {
    serialdata[++bufindex]=pc.getc();
    if (serialdata[bufindex]=='\n' || bufindex==255)
    {
                        message_t *message = queue.alloc();
                        message->length = bufindex;
                        memcpy(message->str,serialdata,bufindex);
                        queue.put(message);
                        bufindex=-1;
    }
}
int main (void) {
    pc.attach(&serialhandler);
    while (true) {
                        osEvent evt = queue.get();
                        if (evt.status == osEventMail) {
                                            message_t *message = (message_t*)evt.value.p;
                                            pc.printf("User input length is %d.\n",message->length);
                                            for (uint8_t i=0;i<message->length;i++)
                                                                 pc.putc(message->str);
                                            pc.printf("\n");
                                            queue.free(message);
                        }
                        Thread::yield();
    }
}
         当然,不同的中断服务程序之间也可以用Queue或Mail实现通讯,但需要注意的是,在使用这两者的get函数时一定要立即返回,示例代码如下:
#include "rtos.h"
typedef struct {
    uint32_t length;
    char str[255];
} message_t;
Mail<message_t, 16> queue;
Serial pc(USBTX,USBRX);
int32_t bufindex =-1;
Ticker tick;
char serialdata[255];
void serialhandler () {
    serialdata[++bufindex]=pc.getc();
    if (serialdata[bufindex]=='\n' || bufindex==255)
    {
                        message_t *message = queue.alloc();
                        message->length = bufindex;
                        memcpy(message->str,serialdata,bufindex);
                        queue.put(message);
                        bufindex=-1;
    }
}
void serialout()
{
    osEvent evt = queue.get(0);
    if (evt.status == osEventMail) {
                        message_t *message = (message_t*)evt.value.p;
                        pc.printf("User input length is %d.\n",message->length);
                        for (uint8_t i=0;i<message->length;i++)
                                            pc.putc(message->str);
                        pc.printf("\n");
                        queue.free(message);
    }
}
int main (void) {
    pc.attach(&serialhandler);
    tick.attach(&serialout,1);
    Thread::wait(osWaitForever);
}


使用特权

评论回复
板凳
643757107|  楼主 | 2015-12-19 16:27 | 只看该作者
中断相关的方法:
__disable_irq():禁止所有可屏蔽中断;
__enable_irq():允许所有未屏蔽中断;
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority):设置中断的优先级,IRQn表示需要设置的中断号,后面表示优先级,数字越小表示优先级越高。
好简单啊,这是面向对象吗?是不是瞬间觉得ARM的开发简单要死了。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

213

主题

3783

帖子

11

粉丝