マイコンでチャタリング除去
昔書いたソースを眺めていたら、こういう方法でチャタリング除去をしていました。
方法
ポートを3回見て、3回とも0だったら0とみなす。3回とも1だったら1とみなす。それ以外だったら前回の値と同じとみなす。
真理値表を書くと以下のようになります。
out0は前回みなした値。p2は2回前のポートの値。p1は1回前のポートの値。p0は現在のポートの値。out1は今回みなす値。
真理値表
out0 | p2 | p1 | p0 | out1 |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 1 | 0 |
0 | 0 | 1 | 0 | 0 |
0 | 0 | 1 | 1 | 0 |
0 | 1 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 0 |
0 | 1 | 1 | 0 | 0 |
0 | 1 | 1 | 1 | 1 |
1 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 1 | 1 |
1 | 0 | 1 | 0 | 1 |
1 | 0 | 1 | 1 | 1 |
1 | 1 | 0 | 0 | 1 |
1 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 0 | 1 |
1 | 1 | 1 | 1 | 1 |
ここからout1を得る論理式を導きます。
まず真理値表からカルノー図を書きます。
論理式
out1 = out0・p2 + out0・p1 + out0・p0 + p2・p1・p0
C言語によるソース
#include "3694s.h" /** * チャタリング除去。タイマーで10msごとに呼び出す。 */ unsigned char polling(void) { static unsigned char out = 0xff, p2 = 0xff, p1 = 0xff; unsigned char p0 = IO.PDR5.BYTE; out = (out & p2) | (out & p1) | (out & p0) | (p2 & p1 & p0); p2 = p1; p1 = p0; return out; }
おまけ GCCが吐き出した上記のソースのアセンブラを眺めてみる
out1 = out0(p2+p1+p0) + p2・p1・p0 のように最適化されてますね。
$ h8300-hms-gcc -S -mh -mn -O1 test.c $ cat test.s .file "test.c" .h8300hn .section .data out___0: .byte -1 p2___1: .byte -1 p1___2: .byte -1 .section .text .align 1 .global _polling _polling: mov.l er6,@-er7 mov.w r7,r6 mov.l er4,@-er7 mov.b @-40:8,r4l mov.b @p2___1,r0l mov.b @p1___2,r1l mov.b r0l,r2l or r1l,r2l or r4l,r2l mov.b @out___0,r3l and r3l,r2l and r1l,r0l and r4l,r0l or r0l,r2l mov.b r2l,@out___0 mov.b r1l,@p2___1 mov.b r4l,@p1___2 mov.b @out___0,r0l extu.w r0 mov.l @er7+,er4 mov.l @er7+,er6 rts .end .ident "GCC: (GNU) 3.4.6"
おまけ2 スイッチが押されたときにフラグを立てる(試してません)
#include "3694s.h" union { unsigned char BYTE; struct { unsigned char B7:1; unsigned char B6:1; unsigned char B5:1; unsigned char B4:1; unsigned char B3:1; unsigned char B2:1; unsigned char B1:1; unsigned char B0:1; } BIT; } SW = {0}; void polling(void) { static unsigned char q1 = 0xff, q0 = 0xff, p2 = 0xff, p1 = 0xff; unsigned char p0 = IO.PDR5.BYTE; q0 = (q0 & p2) | (q0 & p1) | (q0 & p0) | (p2 & p1 & p0); SW.BYTE &= q1 & ~q0; q1 = q0; p2 = p1; p1 = p0; }
2017-01-02追記
ポート見るの,2回でいいような気がしてきました。
真理値表
out0 | p1 | p0 | out1 |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 0 | 1 | 0 |
0 | 1 | 0 | 0 |
0 | 1 | 1 | 1 |
1 | 0 | 0 | 0 |
1 | 0 | 1 | 1 |
1 | 1 | 0 | 1 |
1 | 1 | 1 | 1 |
カルノー図
out0\p1 p0 | 00 | 01 | 11 | 10 |
---|---|---|---|---|
0 | ○ | |||
1 | ○ | ○ | ○ |
論理式
out1 = p0・p1 + out0・p0 + out0・p1
ソース
#include "3694s.h" #include <stdint.h> /** * チャタリング除去。タイマーで10msごとに呼び出す。 */ uint8_t polling() { static uint8_t out = 0xff; static uint8_t p1 = 0xff; uint8_t p0 = IO.PDR5.BYTE; out = (p0 & p1) | (out & p0) | (out & p1); p1 = p0; return out; }
なんならビット演算しなくてもいい気がしてきました。
ソース
#include "3694s.h" #include <stdint.h> /** * チャタリング除去。タイマーで10msごとに呼び出す。 */ uint8_t polling() { static uint8_t out = 0xff; static uint8_t p1 = 0xff; uint8_t p0 = IO.PDR5.BYTE; if (p0 == p1) { out = p0; } p1 = p0; return out; }