搜索
bottom↓
回复: 9

Reading falling / rising edges without using interrupt on change

[复制链接]

出0入0汤圆

发表于 2010-7-28 08:26:02 | 显示全部楼层 |阅读模式
a lot of times, you want to read if a pin has gone through a transition (high->low, or low->high). some times, your mcu may have interrupt-on-change functionality for that.

for those mcus that do not have IOC functionality, or you don't have space for IOC, you can implement it fairly easily, as shown below.

=========code===============

#define PORT_TYPE                        unsigned int                //works for 32-bit ports
//#define PORT_TYPE                        unsigned char                //works for 8-bit ports


//set bits where the pins have gone through a low-high or high-low transition
PORT_TYPE port_get_changing(PORT_TYPE port, PORT_TYPE pins) {
        PORT_TYPE tmp;
        static PORT_TYPE pins_prev=0x00;

        tmp=pins_prev;                                        //save pins_prev
        pins_prev=port & pins;                        //read pins from port
        return (pins_prev ^ tmp);                //return rising or falling edge
}

//return 1 on bits where the pins have gone through a high-low transition
PORT_TYPE port_get_falling(PORT_TYPE port, PORT_TYPE pins) {
        PORT_TYPE tmp;
        static PORT_TYPE pins_prev=0x00;

        tmp=pins_prev;                                        //save pins_prev
        pins_prev=port & pins;                        //read pins from port
        return (pins_prev ^ tmp) & tmp;        //return falling edge
}

//set bits where the pins have gone through a low-high transistion
PORT_TYPE port_get_rising(PORT_TYPE port, PORT_TYPE pins) {
        PORT_TYPE tmp;
        static PORT_TYPE pins_prev=0x00;

        tmp=pins_prev;                                        //save pins_prev
        pins_prev=port & pins;                        //read pins from port
        return (pins_prev ^ tmp) & pins_prev;        //return rising edge
}
===========end of code============

port_get_rising(port, pins) will return 0x08 if port.7 has gone through a low->high transition.
similarly, port_get_changing(port, pins) will return 0x28 if port.9 and port.7 have gone through either a high->low or low->high transition.

here is a piece of code that demonstrates how it works.

=====demo code===========
//demo program
int main(void) {                                        //main

        mcu_init();
        while (1) {
                if (port_get_changing(KEY_PORT, KEYs) & KEY1) IO_FLP(LED_PORT, LED1);          //flip led1 where key1 has changed
                if (port_get_falling(KEY_PORT, KEYs) & KEYs)  IO_FLP(LED_PORT, LED2);        //flip led2 when key1 or key2 goes high->low
                if (port_get_rising(KEY_PORT, KEYs) & KEY2)   IO_FLP(LED_PORT, LED3);        //flip led3 when key2 goes low->high
                //do something else
        }
}
=========end code==========

KEY1 and KEY2 are two input sources that go from 0->1 and then 1->0 at varying frequencies.

LED1 is programmed to flip its state whenever KEY1 has gone through a transition;
LED2 flips if either KEY1 or KEY2 goes from high to low;
LED3 flips only if KEY2 goes low->high.

here is the simulation.



(原文件名:LPC2106 get_key.PNG)


since the code is entirely C, it is highly portable to other platforms.

enjoy.

出0入0汤圆

 楼主| 发表于 2010-8-8 12:05:18 | 显示全部楼层
I was asked how I would read a rotary encoder. so here is my solution.

a rotary encoder has two output, A and B. they go high / low, 90 degrees apart.

so if you turn a rotary encoder clock-wide, you will see the following output:

A:0110
B:0011

or '00, '10, '11, '01.

so if you see a sequency of '00 followed by '10,  you know that the rotary encoder has been turned clock-wise by 1 click.

alternatively, if you see a sequence of '11 followed by '10, you know that the rotary encoder has been turned counter clock-wise by 1 click.

thus, you can use a table to detonate the value of those clicks, based on the previous and current read-outs of A and B pins:

==========code=============
const signed char ABs_states[]={0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
=========end of code=======

essentially, we are saying that if ABs = '0010 (0x02), we are go increment the encoder read-out by 1: ABs_states[ABs]=1, per the table above.

and if ABs='1110 (0x0e), ABs_states[ABs]=-1, and we are to decrement the encoder read-out by 1.

so here is the routine that returns the value of a rotary encoder:

========code=============

//determine increment / decrement of the encoder
unsigned char encoder_read(PORT_TYPE port, PORT_TYPE pins) {
        const signed char ABs_states[]={0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
        static unsigned char encoder_output=0x00;
        static unsigned char ABs=0x00;                                //AB key read out, Previous in the high 2 bits and current in the low two bits;
        unsigned char tmp;

        ABs <<=2;                                                //left 2 bits now contain the previous AB key read-out;
        tmp=IO_GET(port, pins);                        //read ab pins
        if (tmp & KEY_A) ABs |= 0x02;                        //set the 1st bit if A is high now;
        if (tmp & KEY_B) ABs |= 0x01;                        //set the 0th bit if B is high;
        ABs &= 0x0f;                                        //only retain ABs' last 4 bits (A_previous, B_previous, A_current, B_current)
        encoder_output += ABs_states[ABs];
        return encoder_output;
        //return ABs;
}
===========end of code====================

出0入0汤圆

 楼主| 发表于 2010-8-8 12:10:12 | 显示全部楼层
here is an example of how the routine can be used.

it specifies that

1) pin A of the encoder is connected to P.0 of a lpc2106
2) pin B of the encoder is connected to P.6 of a lpc2106
3) the output from the encoder is on P.8-P.15 of the same lpc2106.

==========code=============

#include <lpc210x.H>

//hardware configuration
#define OUT_PORT                        IOPIN                                        //leds on p1
#define OUT_DDR                                IODIR
#define OUTs                                0xff00                                        //output on pin 8 - 15

#define KEY_PORT                        IOPIN                                         //inputs on p0
#define KEY_DDR                                IODIR
#define KEY1                                (1<<0)
#define KEY2                                (1<<6)
#define KEYs                                (KEY1 | KEY2)

#define KEY_A                                KEY1                                //encoder's output A to KEY1
#define KEY_B                                KEY2                                //encoder's output B to KEY2
//end of hardware configuration

//port related macros
#define IO_FLP(port, bits)        port ^= (bits)                //flip bits on port
#define IO_SET(port, bits)        port |= (bits)                //set bits on port
#define IO_CLR(port, bits)        port &=~(bits)                //clear bits on port
#define IO_GET(port, bits)        (port & (bits))                //get bits on port
#define IO_IN(ddr, bits)        ddr &=~(bits)                //bits as input
#define IO_OUT(ddr, bits)        ddr |= (bits)                //bits as output

//define port type:
//"unsigned int" (a 32-bit type) for 32-bit ports
//"unsigned char" for 8-bit ports
//change PORT_TYPE if you are compiling for a differrent platform

#define PORT_TYPE                        unsigned long                //works for 32-bit ports
//#define PORT_TYPE                        unsigned char                //works for 8-bit ports


//determine increment / decrement of the encoder
unsigned char encoder_read(PORT_TYPE port, PORT_TYPE pins) {
        const signed char ABs_states[]={0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
        static unsigned char encoder_output=0x00;
        static unsigned char ABs=0x00;                                //AB key read out, Previous in the high 2 bits and current in the low two bits;
        unsigned char tmp;

        ABs <<=2;                                                //left 2 bits now contain the previous AB key read-out;
        tmp=IO_GET(port, pins);                        //read ab pins
        if (tmp & KEY_A) ABs |= 0x02;                        //set the 1st bit if A is high now;
        if (tmp & KEY_B) ABs |= 0x01;                        //set the 0th bit if B is high;
        ABs &= 0x0f;                                        //only retain ABs' last 4 bits (A_previous, B_previous, A_current, B_current)
        encoder_output += ABs_states[ABs];
        return encoder_output;
        //return ABs;
}


//initialize the mcu
void mcu_init(void) {
        IO_CLR(OUT_PORT, OUTs);                        //clear LED
        IO_OUT(OUT_DDR, OUTs);                        //leds as output
        IO_IN(KEY_DDR, KEYs);                        //keys as input
}

//demo program
int main(void) {                                        //main

        mcu_init();
        while (1) {
               
                OUT_PORT = encoder_read(KEY_PORT, KEY_A | KEY_B)<<8; //output encoder read-out on p8-p15
                //do something else
        }
}
=============end code===========

as the code is entirely C, you can easily port it to other mcus.

the encoder_read() is self-contained so you can easily make the code into a header / c file to be included in your own project.

enjoy.
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-2 10:32

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表