搜索
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-7-28 10:38:25 | 显示全部楼层
楼主给出了在 MCU 不具备(或不使用其)电平变化中断功能情况下,实现对输入口电平变化的跟踪监测的编程思路和示范程序。纯粹的C码,可以方便地用于其它平台。值得大家学习、借鉴。
    遗憾的是,我自学时没有选择C,只能通过给出的注释去理解了。
    谢谢 millwood 。

出0入0汤圆

发表于 2010-7-29 07:55:14 | 显示全部楼层
millwood0 能否讲些您自己的故事, 除了您的程序和回帖,所知甚少. 您也从不参与坛子里面的水贴

出0入0汤圆

发表于 2010-7-29 11:00:28 | 显示全部楼层
回复【楼主位】millwood0
-----------------------------------------------------------------------
/*
KEY1 and KEY2 are two input sources that go from 0->1 and then 1->0 at varying frequencies.
KEY1与KEY2从0 -> 1,与1 ->0 是两个不同频率变化的输入源。

LED1 is programmed to flip its state whenever KEY1 has gone through a transition; // LED1翻转随着KEY1变化而变化
LED2 flips if either KEY1 or KEY2 goes from high to low; //无论KEY1或KEY2由高->低变化都会使LED2翻转
LED3 flips only if KEY2 goes low->high.  //只有KEY2由低->高,LED3将翻转。
*/
看着图形胡乱翻译硬让自己理解,见笑了。
KEYs是什么?
if (port_get_changing(KEY_PORT, KEYs) & KEY1) // KEYs = KEY1;  ?
if (port_get_falling(KEY_PORT, KEYs) & KEYs)  // KEYs = KEY1|KEY2;  ?
if (port_get_rising(KEY_PORT, KEYs) & KEY2)   // KEYs = KEY2;  ?

出0入0汤圆

发表于 2010-7-29 15:39:57 | 显示全部楼层
KEY1 and KEY2 are two input sources that go from 0->1 and then 1->0 at varying frequencies.
    //KEY1 和 KEY2 是从低到高又从高到低变化着的、频率不同的两个输入信号源。
LED1 is programmed to flip its state whenever KEY1 has gone through a transition;
    //LED1 是这样被编程的——只要 KEY1 发生了变化(不论从高到低还是从低到高),LED1 的状态就翻转(亮起或熄灭)。
LED2 flips if either KEY1 or KEY2 goes from high to low;
    //(LED2 是这样被编程的——)如果 KEY1 或 KEY2 任何一个发生了从高到低的变化,LED2 的状态就翻转。
LED3 flips only if KEY2 goes low->high.
    //(LED3 是这样被编程的——)仅当 KEY2 发生了从低到高的变化,LED3 的状态才会翻转。


回复【3楼】wangqh1983
KEYs是什么?
-----------------------------------------------------------------------

if (port_get_changing(KEY_PORT, KEYs) & KEY1)
    //输入信号有发生变化,并且是第一输入口发生了变化(而不论其发生了怎样的变化),......
if (port_get_falling(KEY_PORT, KEYs) & KEYs)
    //输入信号有发生从高到低的变化,并且不论是哪个输入口发生了这样的变化,......
if (port_get_rising(KEY_PORT, KEYs) & KEY2)
    //输入信号有发生从低到高的变化,并且是第二输入口发生了这样的变化,......

不懂C的我,不知这样理解对不对。

出0入0汤圆

发表于 2010-7-29 16:44:57 | 显示全部楼层
回复【4楼】JQ_Lin
KEY1 and KEY2 are two input sources that go from 0->1 and then 1->0 at varying frequencies.  
    //KEY1 和 KEY2 是从低到高又从高到低变化着的、频率不同的两个输入信号源。
LED1 is programmed to flip its state whenever KEY1 has gone through a transition;  
    //LED1 是这样被编程的——只要 KEY1 发生了变化(不论从高到低还是从低到高),LED1 的状态就翻转(亮起或熄灭)。
LED2 flips if either KEY1 or KEY2 goes from high to low;  
    //(LED2 是这样被编程的——)如果 KEY1 或 KEY2 任何一个发生了从高到低的变化,LED2 的状态就翻转。
LED3 flips only if KEY2 goes low->high.  
    //(LED3 是这样被编程的——)仅当 KEY2 发生了从低到高的变化,LED3 的状态才会翻转。


回复【3楼】wangqh1983  
KEYs是什么?
-----------------------------------------------------------------------

if (port_get_changing(KEY_PORT, KEYs) & KEY1)
    //输入信号有发生变化,并且是第一输入口发生了变化(而不论其发生了怎样的变化),......
if (port_get_falling(KEY_PORT, KEYs) & KEYs)
    //输入信号有发生从高到低的变化,并且不论是哪个输入口发生了这样的变化,......
if (port_get_rising(KEY_PORT, KEYs) & KEY2)
    //输入信号有发生从低到高的变化,并且是第二输入口发生了这样的变化,......

不懂C的我,不知这样理解对不对。
-----------------------------------------------------------------------

JQ_Lin翻译愿意了,哈哈。我看着单词用理解意思表达一下好像好一点,用愿意读起来别扭!

下面的我就不是想翻译了,
KEYs是什么?我是想问这个变量时怎么来的,所指的是什么?函数的形参需要给定值的/或者在前面有#define定义,看到函数里定义的是Pins,应该指的是某一端口的某个管脚信息!看到下图有KEY1、KEY2否则这个参数同样也是无法理解的。我问的就是这个意思,所以语句后的双斜线才这样写!

出0入0汤圆

发表于 2010-7-29 18:17:03 | 显示全部楼层
回复【5楼】wangqh1983
-----------------------------------------------------------------------

哈哈,
我【4楼】回复的前半部分,也是用理解意思表达的,只是我的表述与你的表述有区别而已。就用你自己的理解和不别扭的表述吧。
你【3楼】那后半部分的问题,现在知道了,恕我没有看懂。

出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.

出0入0汤圆

发表于 2010-8-8 15:31:24 | 显示全部楼层
回复【8楼】millwood0
-----------------------------------------------------------------------

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

本版积分规则

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

GMT+8, 2024-5-2 09:33

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

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