ロータリーエンコーダーの2相パルスをピン変化割り込みで取り込む
2020年8月11日:14pinのAVRマイコン、ATtiny24が動かん!
2019年9月29日:360度グルグル回したろ
に絡むんですが、1kHzのPWM波発生回路を作ろうとしています。
旭化成のAK7401 、PWMで角度を出力してくれます。
その回路検証に使います。
周波数が1kHzで、検出角によりPWMのデューティを10%から90%
まで可変。
デューティーの10%が0度に対応して、50%が180度で
359.9度が90%という仕様です。
このパルスを模擬的に出そうという回路です。
この角度の設定にロータリーエンコーダを使おうとしたのです。
右に左にぐるぐる回せば0~359度自在に設定という目論見です。
14pinのAVRマイコン、ATtiny24が動かん! では、ロータリーDIP SW
を使ったんですが、これは「リニア」に設定できません。
角度の設定ならやはりグルグル回しでしょう。
「確かどこかに残ってたはずや」っとパーツボックスを
探すと出てきました。
BOURNSのECW1J-B23-BC0024L 。
1回転が24クリック。
360度回そうとすると15回転。
ちょっとクリクリするのがたいへんか・・・
1クリックでこんなパルスが出てきます。
右回しと左回しで波形がちょいと異なります。
A相B相とも途中では止まりません。
両方信号オフ状態で止まります。
これ、2相パルスには違いないのですが、クリック機構があると
途中で止められないので、4逓倍は無理。
そこで・・・「つぶしてもエエやん」っと、基板部を固定してある
ポッチリを削ると、中身が出てきました。
A相・B相のコード板と接点。
そしてクリックはこんな機構。
スプリング部に出たチョッポリで回転軸のギザギザを押さえ
ています。
このスプリングを外せば、クリック無しで自由回転するんですが、
接点端子部をコード板に押さえているのもこの機構です。
スプリングを取ると接触が安定しません。
そこで、「ポッチリを反対にしてみたろ」っとスプリングを裏返したと
ころ、「まぁエエやん」っとクリック感無しで360度クルクル回転でき
るようになりました。
これで4逓倍すれば、3.75回転で360度の角度をセットできます。
回すとこんなパルスが出てきます。
ほんとは各パルス90度位相差なんですが、コード板の構造で
しょう、1箇所周期が長いところがあります。
接点のチャタリング、こんな様子です。
いつも出るわけじゃなく、「たまに出る」というヒゲですが、
CRでのチャタリング吸収回路は必須です。
ちなみにBOURNSのカタログにはこんな回路が。
・単純にCRで
10k+0.01uFですんで時定数0.1mSくらい。
・そして・・・あらま懐かしい「MC14490」 が出てきました。
チャタリング除去専用のICです。
大昔、使ったことあります。
C-MOS4000番シリーズの「けったいなIC」系列に登場する
石ですなぁ。
まずはArduino UNOでエンコーダーの計数を試してみます。
ネットを探すと・・・INT0・INT1エッジ割り込みを使った
例題ばかり。
面白くないんで、こんな接続に。
「ピン変化割り込み」を使ってみます。
ピン変化割り込みは、ポートB群、ポートC群、ポートD群、
3つの割り込みが独立して使えます。
ただし、それぞれのポート群の中での入力ピンは一つだけ。
(どのピンが変化したかのチェックを省略する処理では)
INT0とINT1はPD2、PD3でポートD群ですんでこれは除外。
PB0とPC0に2相パルスを入れることにします。
エンコーダ入力のテストはこんなスケッチ。
※PWM出力制御と設定角度の表示はこれから
とりあえずシリアル出力で。
/***** ロータリーエンコーダーテスト *****/
// A相+B相の4エッジを使って4逓倍
// ポート使用
// A0 PC0 A相入力 PCINT8
// D8 PB0 B相入力 PCINT0
// D13 PB5 LED テスト出力
// データ
#define ENC_TOP 359 // 360度回転 0~359
volatile word enc_cnt; // 0~359カウンタ
volatile byte f_cnt; // カウンタup/downフラグ
// ポート制御
#define INP_A0 (PINC & (1 << PC0)) // A相 A0入力 PC0 PCINT8
#define INP_D8 (PINB & (1 << PB0)) // B相 D8入力 PB0 PCINT0
#define D13_H (PORTB |= (1 << PB5)) // PB5 D13 H/L
#define D13_L (PORTB &= ~(1 << PB5)) // テストパルス
/***** エンコーダカウントアップ *****/
// 359度なら0に
void cntup(void)
{
if(enc_cnt >= ENC_TOP) enc_cnt = 0;
else enc_cnt++;
}
/***** エンコーダカウントダウン *****/
// 0度なら359に
void cntdn(void)
{
if(enc_cnt == 0) enc_cnt = ENC_TOP;
else enc_cnt--;
}
/***** カウント値読み出し *****/
// いったん割り込み禁止にして
word readcnt(void)
{
word d;
cli(); // ★いったん割り込み禁止して
d = enc_cnt; // 0~359度カウンタ読み出し
sei(); // 割り込み有効に戻す
return d;
}
/***** A相入力 ↑↓ PC0 A0 *****/
// CW : A↑ B=L / A↓ B=H
// CCW : A↑ B=H / A↓ B=L
ISR(PCINT1_vect)
{
D13_H; // (!!!)
if(INP_A0){ // A↑
if(!INP_D8) cntup(); // B=L CW, +1
else cntdn(); // B=H CCW,-1
}
else{ // A↓
if(INP_D8) cntup(); // B=H CW, +1
else cntdn(); // B=L CCW,-1
}
f_cnt = 1; // カウントup/down
D13_L; // (!!!)
}
/***** B相入力 ↑↓ PB0 D8 *****/
// CW : B↑ A=H / B↓ A=L
// CCW : B↑ A=L / B↓ A=H
ISR(PCINT0_vect)
{
D13_H; // (!!!)
if(INP_D8){ // B↑
if(INP_A0) cntup(); // A=H CW, +1
else cntdn(); // A=L CCW,-1
}
else{ // B↓
if(!INP_A0) cntup(); // A=L CW, +1
else cntdn(); // A=H CCW,-1
}
f_cnt = 1; // カウントup/down
D13_L; // (!!!)
}
/***** SETUP *****/
void setup()
{
pinMode(8, INPUT); // D8 PB0 PCINT0 B相入力
pinMode(A0, INPUT); // A0 PC0 PCINT8 A相入力
pinMode(13, OUTPUT); // D13 PB5 (test LED port)
// ピン変化割り込み
PCMSK0 = 0b00000001; // 1で許可
// +---- PCINT0 PB0 D8 B入力
PCMSK1 = 0b00000001; // 1で許可
// +---- PCINT8 PC0 A0 A入力
PCICR = 0b00000011; // 1で割り込み許可
// ||+---- PCIE0 PCINT0~7 (PB)
// |+----- PCIE1 PCINT8~14 (PC)
// +------ PCIE2 PCINT16~23 (PD)
// シリアル
Serial.begin(9600);
// 最初の表示
f_cnt = 1; // カウント値表示フラグon
}
/***** LOOP *****/
void loop()
{
if(f_cnt){ // カウンタup/downした
f_cnt = 0;
Serial.println(readcnt()); // カウント値をシリアル出力
}
}
INT0・INT1以外にエンコーダをもう一つ付けたいという時の
参考になりますでしょうか。
・カウントアップ・ダウンの処理
※おっと、2カ所ミスあり。
B相↓のとき「B相=」というところ「A相=」です。
・逆転する様子
入力のA相・B相パルスと、割り込み処理の関係、こんな具合です。
割り込みでD13・PB5ポートをon/offしています。
各エッジで割り込みが入っていることが見えます。
※注意点
0~359という16bit値を扱っていますが、Arduino-UNOは
8bitマイコン。
割り込みで処理される2バイト以上のデータを扱う時は、
メイン側での読み書きは必ず割込禁止状態でしなくちゃ
なりません。
ネットで見かけるロータリーエンコーダの処理、これを
していない例題が多数です。
・2019年3月25日:割り込みで処理させるwordデータの扱い
・2016年02月19日:Arduinoのタイマー処理
・2018年10月11日:魔法の言葉「volatile」
※2相パルス、ロータリーエンコーダー絡みの記事
・2014年07月01日:高速2相パルス発生回路
・2017年9月12日:C-MOS 2相パルスカウンタの入力部回路
・2010年01月30日:U/Dカウンタ
・2018年5月25日:アナログ入力で2相パルスの周波数と正転逆転を制御する発振器
・2018年6月17日:RX220マイコンで2相パルスカウント:グルグル回る角度を
※問題のある割り込み処理内データの読み出し例。
いずれも、読み書きのタイミングが「ゆっくり」だから
割り込みとの競合が露呈しないのでしょう。
割り込み内で下位桁からの桁上がりで上位桁が変化する
時などが問題。
割込を禁止しないで2バイト以上のデータを読み出すと、
そのタイミングで、割り込みによるカウントアップや
カウントダウンが起こると、誤ったデータを読み出
してしまいます。
下位バイトと上位バイトが処理される隙間に割り込みが
入るとアウトかも!という現象です。
16bitマイコンや32bitマイコンを触っていると、意識
しなくても正しい処理をしてくれるんですが、低機能
な8bitマイコンではしかたありません。
めったにないことでも、起こる可能性があるものは確実に
起こります。
それがプログラム(のバグ)というものです。
割り込みで処理される2バイト以上のデータを扱う時は、
めんどうがらずに「割込禁止」と「割込許可」を挿入のこと。
C言語のコンパイラ、変数にvolatileを付けていても、割り込み
との競合は防いでくれません。
プログラマの責任です。
・ラジオペンチ Arduinoでロータリーエンコーダーを使う
intのカウンター。
・ラジオペンチ ピンチェンジ割込みを使ってロータリーエンコーダーを読む (Arduino)
Xの変化は小さそうなんで8bit→16bitへの桁上がりはなさそうだけど・・・
if (X != 0) { // エンコーダーの値が変化していたら
data += X; ←★1
X = 0; ←★2
:
★1と★2の間に割り込みがあってXが増減したら、
★2でクリアされてしまいその増減が無視されてしまう・・・
この間でも,割込禁止・割込許可が必要。
・第二十一項 ロータリーエンコーダとノイズ対策・割り込み kusamura
longのカウンターを処理。
・ロータリーエンコーダを使う part 1 : 外部割込みとチャタリング対策 jumbleat
intのカウンター。
割り込み内でSerial.printを使うのは気持ち悪い。
・【Arduino】マウスホイール(ロータリーエンコーダ)の回転量を取得する - おもちゃラボ
intのカウンター。
・迷走の果て・Tiny Objects Arduinoでロータリーエンコーダを試す(2)
intのカウンター。
・ロータリーエンコーダテスト 割り込みを使う場合 ・ GitHub
intのカウンター。
| 固定リンク
「Arduino」カテゴリの記事
- Arduinoで「ボコスカハンマー」 あれれれれっ?!(2023.12.07)
- Arduino UNO R3で±19.9V表示電圧計(2023.10.14)
- 「御詠歌プレーヤー」の製作 (MP3-TF-16Pモジュールの使用例)(2023.08.10)
- Arduino UNO R3のソケット・・思えば違和感がぁ(2023.07.07)
- 初めて買ったArduino UNO・・・今は(2023.05.25)
「割り込み処理」カテゴリの記事
- 初めて買ったArduino UNO・・・今は(2023.05.25)
- 8ビットマイコンの割り込み処理・・・1バイトに収まるなら1バイトに(2023.03.01)
- 8bitマイコンにも16bitのメモリ読み書き命令があった(2022.10.14)
- 何度も言うぞ! Arduino(8bitマイコン)の割り込みには気をつけろ!(2022.10.11)
- ロータリーエンコーダーのチャタリング波形(2022.09.11)
コメント