Arduino

2020年9月21日 (月)

『アトミック操作』・・・8bitマイコンに限り何か別の言い方なかったか?

2019年3月25日:割り込みで処理させるwordデータの扱い
に書きましたが、Arduino-UNOで使われているAtmega-328Pは
8ビットマイコンです。
ですんで、割り込みで処理される2バイト以上のデータをメイン側で
読み書きする時は「割り込み禁止状態にして」というのが基本です。

この手順を「何て言うの?」っと調べたら・・・
  ・アトミックに処理を
  ・アトミック操作
  ・不可分処理
っと、出てきます。

でも、アトミック操作とはで調べると、8ビットマイコンでの
割り込み処理の話が出てこずに、
  スレッドやプロセスや
  RISCやCISCや
  マルチスレッドやシングルスレッドや
っという8ビットマイコンの世界とはちょいと縁遠いお話し
ばかりが出てきます。

この「アトミック操作」ですが、昔々の8ビットマイコンの
世界じゃ(8080,6800~Z80あたりの年代で)なんかもっと他の
言い方、無かったでしょうか?

「8ビットマイコンの割り込み処理で多バイトデータを扱う時、
 メインでは割り込み禁止で読み書きを」を一言で表現できる
言葉、こんなの無かったですかね?

当時、アトミック処理や不可分処理なんて言葉で言わなかった
よう・・・

『アトミックにアクセス』なんて言い回し・・・
ラジオペンチさん に教えてもらった。
   (この↑記事のコメントの前にもあったはずだけど探し出せてない)


| | コメント (0)

2020年9月19日 (土)

クリック有りのロータリーエンコーダーで

2020年9月17日:ロータリーエンコーダーの2相パルス、クリック有りの場合は
を具体化。
Arduino-UNOを使ってこんな回路に。

Cc1_20200918181201
3桁の7seg LEDに設定角度(0~359度)を表示します。
実験中のバラック↓

Cc2_20200918181201
エンコーダは「読み取れるかどうかの実験のため」に
2回路付けてますが、設定値の表示は1系統だけ。
これをベースに電池駆動にしたジグを製作する予定です。

Arduino-UNO、クロックが16MHz。
PWMの周波数は1kHzが目標なんですが、PWMを450分割しな
ければなりません。
Duty 10%~90%を0度~360度にということで、こんな分割比
になります。→「45 + 360 + 45」
1kHzに近いところで16MHz÷450÷35で1015.9Hzになっています。
本チャンではクロック発振子を「7.2MHz」にして、
7.2MHz÷450÷16で1000.0Hzというふうにします。


なぜ「1kHz・10%~90%」のPWMかは、旭化成のAK7401のデータシートを
参照のこと。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2020年9月12日:ロータリーエンコーダーの2相パルスをピン変化割り込みで取り込む
で示した、メーカー推奨のチャタリング吸収回路・・・
  ※10kΩ抵抗に0.01uFコンデンサのCRローパスフィルタ。

B1_20200912144901
これだと、エンコーダのon/off波形が鈍ってしまって、ATmega328Pの
入力シュミット回路でミスが発生する(ことがある)のです。
ATmega32P、それぞれのデジタル入力にはシュミットゲートが入ってい
るのですが、シュミットのヒステリシス幅が小さいので、「鈍った波形」を
うまく処置(HかLかの決め打ち)ができない(ことがある)のです。

入力信号がなだらかに増減すると、スレッショルド電圧付近で
不安定に・・・
入力して出力するだけのテストプログラムを走らせると、
「振動波形」が現れる(ことがある)のです。

キレイな信号が入ることを前提としたエッジ検出入力回路、
これがスカタンすると、ちょいと問題。

ですんで、とりあえずは「内蔵プルアップ抵抗+外付け1000PF」
が「まだマシでした」ということで、試作したのです。
  ※内蔵プルアップ抵抗が20~50KΩ。
   直列抵抗無しでそのままGND間にコンデンサ。
コンデンサが無いと・・・そりゃひどいものです。
ソフトでどうにかしろ・・・ご勘弁を・・・・

ゲートICが増えることを許容して、HC14やHC132、4584、4093
などのちゃんとしたシュミット入力ゲートICを設けるべきでしょうね。
  ※仕事の設計だと「ケチらない!」。

エッジ検出による割り込み駆動だと、チャタリング吸収がちょいと
やりにくい。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

※スケッチはこちら → ダウンロード - test_enc3.c
  (ファイルタイプを「C」にしています)

特徴的なところを。

/*****  エンコーダカウントアップダウン     *****/
// 359度以上なら0に,0度未満なら359度に
// カウンタのポインターと加算値
// 加算値は±で
void cntupdn(volatile word *p, short a)
{
short d;
d = *p; // 現在値
d += a; // ±加算
if(a >= 0){ // count upの時
if(d > ENC_TOP) d -= (ENC_TOP + 1); // 0~359
}
else{ // count downの時
if(d < 0) d += (ENC_TOP + 1); // 0~359
}
*p = d;
}
/************************************/
/* 2相パルスA相↓エッジ割り込み */
/************************************/
// D13ポート 割り込みでH/Lパルス
// D12ポート カウントup/down処理でH/Lパルス
/***** INT0割り込み *****/
// ch1 A相↓エッジ, B相がHなら+1 Lなら-1
ISR(INT0_vect)
{
short d;
D13_H; // (!!!) LED on
if((tm_enc1 >= 5) && // 前のパルスから5ms経過で新↓エッジ
(INP_1A == 0)){ // A相がHならミストリガ
D12_H; // (!!!)
if(tm_enc1 >= 40) d = 1; // 40ms経過してたら+1
else d = 2; // 以内に操作したら+2
if(INP_1B) cntupdn(&enc_cnt1, d); // up
else cntupdn(&enc_cnt1, -d); // down
f_cnt = 1; // カウント値更新
tm_enc1 = 0; // タイマー1クリアー
D12_L; // (!!!)
}
D13_L; // (!!!) LED off
}

割り込み処理の確認のため、
・D13(LEDポート)に割り込み応答パルスを出力。
・D12に、カウントアップダウンが成功したらパルスを出力。

誤パルス除去のため、
・前パルスより5ms内の新パルスは無視。
・A相立ち下がりエッジの確認。
  割込直後のA相がHなら無視。

回転をちょっとスピードアップするため、40ms内のカウントアップ
ダウンを「±2」に。
ラジオペンチさんところ の、「50ms内は10倍速」という処理を
真似たのですが、でも10倍速は早すぎました!

試しに使った「クリック有り」のロータリーエンコーダー
(BOURNSE社製)の回転、素早く回すと1クリック
数ミリ秒で回せます。

こんな感じ。
A000_20200919102201

△(1)」のところで、立ち下がりではなく立ち上がりで
割り込みが入ったことが見えています。
△(2)」ではラインが太く見えているので、割り込みが続け
さまに2発入ったようです。
  ※ATmega328Pのシュミット入力のヒステリシスが
   もうちょい大きければ避けられたかも。

このくらいのミスパルスなら、先ほどの「誤パルス除去」
が防いでくれて、「◎」のカウントだけが有効になっています。

シュミット入力のミス、もうちょい時間を広げると
こんな感じです。
右端の「△」部がミストリガー。
チャタリング除去用のコンデンサで立ち上がりが波形が
鈍っているせいかと。

A001_20200919102901
「誤パルス除去」処理で、「◎」の1発カウントだけになって
いて、なんとか正しくカウントが進みました。



3桁7seg LEDのダイナミック表示はタイマー2割り込み
を使ってます。
コンペアマッチAで1ms(1kHz)周期を作ります。
その割り込みでLEDをブランクに。

そして、20us後に起動するコンペアマッチB割り込みで、
LEDのセグメントとコモンラインを駆動して点灯します。
この中で1msのタイマーをカウント。

3バイトの「led_bff[]」に書いたLEDのセグメント点灯情報を
割り込みが勝手に表示するという仕掛け。

ライブラリは使わず、ATmega328Pのレジスターを直接操作。
処理の遅いdigitalWritedigitalReadも使いません。

/*******************************/
/* タイマー2割り込み */
/*******************************/
// 1kHz(1mS)で割り込み
// コンペアAでLEDブランクに
// 20us後のコンペアBでLED点灯
/***** タイマー2コンペアマッチA割込み *****/
// LED出力ブランクに
ISR(TIMER2_COMPA_vect)
{
PORTC |= 0b00111111; // セグメントをオフ(Hでoff)
// ++++++---- seg F~A
PORTB |= 0b00001000;
// +------- seg G
PORTD |= 0b01110000; // COM駆動オフ(Hでoff)
// +++-------- COM3,2,1
}
/***** タイマー2コンペアマッチB割込み *****/
// ブランク後20usに起動
// LED出力オンとタイマー処理
ISR(TIMER2_COMPB_vect)
{
byte d;
d = ~led_bff[led_ptr]; // Lアクティブ
PORTC &= (d | 0b11000000); // bit6~0:seg F~A
d >>= 3; // 3bit右に
PORTB &= (d | 0b11110111); // seg G
d = led_com[led_ptr]; // COM1~COM3
PORTD &= d; // Lでオン
// 次桁
led_ptr++; // 次ポインター
if(led_ptr >= DIMSIZ(led_bff)) led_ptr = 0;
// タイマー処理
if(tm_enc1 != 255) tm_enc1++; // タイマー1 +1
if(tm_enc2 != 255) tm_enc2++; // タイマー2 +1
}


3桁数字の表示ルーチンはこれだけ。
下位桁からセグメント情報に変換し、上位はゼロサプレス処理
しています。

/*******************************/
/* LED表示 */
/*******************************/
/***** LED 3桁変換表示 *****/
void led3disp(word d)
{
byte i;
for(i = 0; i < 3; i++){ // 3桁 LSBから
if((i != 0) && (d == 0)) // ゼロサプレス
led_bff[i] = 0b00000000; // ブランク
else
led_bff[i] = led_seg[d % 10]; // 0~9
d /= 10; // 1/10
}
}

3バイトのled_bff[]に表示データ(LEDのセグメントデータ)を一度
書き込めば、あとは勝手にタイマー2割り込みが表示処理を実行し
てくれます。
毎回表示ルーチンを呼ばなくても、メモリーの内容に従い割り込みが
勝手に表示してくれるのです。

 ※「delay()」を使って表示タイミングを作っているような
  スケッチは実用的ではありません。
  しかし、「arduino 7seg LED ダイナミックスキャン」を検索
  しても、なかなかタイマー割り込みによる表示実行例に行き着
  きません。
  もう一つ。
  このようなスピード優先、マイコンチップの機能を直接操作する
  ようなプログラムでは、汎用的関数digitalWriteなんかを使わな
  くってもイイじゃないですか。
  速度優先で「素」のチップを制御するということで、ポートの
  直接操作、これでエエでしょう。
 

タイマーと割り込み関連の初期設定もそんなにややこしいこ
とはありません。
ATmega328Pのマニュアルをよく読めば、へんにライブラリーを
探すより早いかと。
マイコンが持つ機能、これを「素」で使います。

//  タイマー1 PWM出力 (A:PB1,B:PB2)
// エンコーダ値 0~359度でPWM 10%~90%を出力
// 16MHz/35/450 = 1015.9kHz
// ICR1AのTOP値は450*35 - 1
// 16MHz / 450
TCCR1A = 0b11110010;
// |||| ++---- PWM 高速PWM ICR1 モード
// ||++-------- PWM B (Negモード)
// ++---------- PWM A (Negモード)
TCCR1B = 0b00011001;
// || ||+++---- クロックセレクト 16MHz / 1 16MHz
// || ++------- PWM ICR1 モード
// |+---------- ICES1
// +----------- ICNC1
ICR1 = PWMTOP; // 0~449で0%~99.78%
OCR1A = OCR1B = 0xFFFF; // PWM off
// タイマー2
TCCR2A = 0b00000010; // モード設定
// |||| ++--- WGM: CTCモード
// ||++------- COM0B
// ++--------- COM0A
TCCR2B = 0b00000100;
// |+++--- CS:1/64 : 250kHz 4uS
// +------ WGM02
OCR2A = 250 - 1; // 1kHz(1mS)
OCR2B = 5 - 1; // 20uS
TIMSK2 = 0b00000110;
// |+----- OCIE2A コンペアマッチA割り込み有効
// +------ OCIE2B コンペアマッチB割り込み有効
// INT0,INT1 ↓エッジ割り込みに
EICRA = 0b00001010; // 割り込み入力エッジ
// ||++---- ISC0 INT0 ↓
// ++------ ISC1 INT1 ↓
EIMSK = 0b00000011; //外部割り込み有効
// |+---- INT0
// +----- INT1

 

| | コメント (0)

2020年9月17日 (木)

ロータリーエンコーダーの2相パルス、クリック有りの場合は

2020年9月12日:ロータリーエンコーダーの2相パルスをピン変化割り込みで取り込む

2020年9月16日:ロータリーエンコーダーの2相パルスをタイマー割り込みで
は、クリック無しでグルグル回しのロータリーエンコーダーを
想定しています。
ですんで、A相とB相の↑↓各エッジ、4つを使ってカウントし、
いわゆる4逓倍の処理を考えました。
1回転24パルスのロータリーエンコーダーだと1回転96パルスになります。

この、小型エンコーダー(コパルRE12D-300) だと、
1回転300パルスが1200パルスに。
ちょっと得した気分。

で、「クリック有りのロータリーエンコーダー」だったらどうした
ものかというお話しをちょっと。
観測波形を見ますと、静止時はA相・B相ともHレベルを保持。
左右どちらかの1クリックで2相パルスが出現という仕掛けに
なっています。

1クリックでA相とB相両方にLパルスが生じます。
その「後先」±1が決まります。
ですので、判断はどこか1つのエッジだけを使えば良いわけです。
例えばこんな具合。

A000_20200917095401
A001

下側波形の立ち下がりを使うことにして、このパルスを検出した時、
反対相のH/LによってCWかCCWを決めます。
ただし、クリックのメカ的な機構によりけっこう早い応答が必要。
パルスの抜けを防ぐにはエッジ検出は割り込みでの処理が必須でしょう。

この場合、割り込みが必要なのは片側の相だけ。
反対相はH/Lを入力するだけになりますんで、割り込みは不要。
また、エッジを検出しないほうの相が変化しても、カウント処理には
関係ありません。
  ※4逓倍処理なら、片相が変化するだけで±1します。
   しかし、エッジ検出が1つの場合だと、相が進まずに
   その片相だけがH/Lを繰り返すとミスカウントしてし
   まいます。
   クリック有りのロータリーエンコーダーはメカ的
   信号の相が進むようになっていますので、片相だけ
   の変化は心配しなくてかまいません。
     ※クリック無しのだと簡単にはいかない

ArduinoのINT0とINT1の割り込み駆動は両エッジだけでなく、
↑エッジ、↓エッジが設定できますので、↑↓どのエッジで
駆動されたのかの判断は不要です。
クリック有りタイプのロータリーエンコーダーだと2つ接続
できるということになります。


| | コメント (0)

2020年9月16日 (水)

ロータリーエンコーダーの2相パルスをタイマー割り込みで

2020年9月12日:ロータリーエンコーダーの2相パルスをピン変化割り込みで取り込む
2020年9月14日:「チャタリング除去回路」じゃなくって「チャタリング発生回路」をどうぞ
2020年9月15日:今度はチャタリング除去、その考え方

これらの続きということで、2kHzのタイマー割り込み(ATmega328Pのタイマー2)で
ロータリーエンコーダの入力を処理してみました。
   ※無理やりチャタリングを入れてます
そのスケッチを示します。

A相入力がA0、B相がA1。
D8出力とD9出力にチャタリング除去して平滑化したA相とB相を出力。
D10出力はA相B相の↑↓エッジ検出タイミング。
D13出力は(LED)は2kHzタイマー割り込み処理でパルスを出力しています。

/*****  ロータリーエンコーダーテスト    *****/
// A相+B相の4エッジを使って4逓倍
// タイマー2割り込みでチャタリング除去とともにエッジ検出
// ポート使用
// A0 PC0 A相入力 (pull up)
// A1 PC1 B相入力 (pull up)
// D8 PB0 A相入力安定モニター出力
// D9 PB1 B相入力安定モニター出力
// D10 PB2 エッジ検出出力
// D13 PB5 LED 割り込みタイミング出力
// タイマー2周波数設定 → OCR2A = 125 - 1; // 2kHz(0.5mS) ★1
// ポート制御
#define D8_H (PORTB |= (1 << PB0)) // PB0 A相入力安定モニター出力
#define D8_L (PORTB &= ~(1 << PB0)) //
#define D9_H (PORTB |= (1 << PB1)) // PB1 B相入力安定モニター出力
#define D9_L (PORTB &= ~(1 << PB1)) //
#define D10_H (PORTB |= (1 << PB2)) // PB2 エッジ検出出力
#define D10_L (PORTB &= ~(1 << PB2)) //
#define D13_H (PORTB |= (1 << PB5)) // PB5 (LED)割り込みタイミング
#define D13_L (PORTB &= ~(1 << PB5)) //
// 配列のデータ数を返すマクロ
#define DIMSIZ(a) (sizeof(a)/sizeof(*a))

/******************************/
/* エンコーダ入力 */
/******************************/
// データ
#define ENC_TOP 359 // 360度回転 0~359
volatile word enc_cnt; // 0~359カウンタ
volatile byte f_cnt; // カウンタup/down検出フラグ
// A相 B相入力 on,offチェック (Hで1)
#define INP_A (PINC & (1 << PC0)) // A相入力チェック
#define INP_B (PINC & (1 << PC1)) // B相
// ↑↓エッジのビット位置
#define ENC_A 0 // A相↑
#define ENC_B 1 // B相↓
// bit0,1にエンコーダ入力データが入る
volatile byte enc_sft[4]; // チャタリング除去用シフトデータ
// 4回ともH/L同じなら安定
volatile byte enc_inp; // 入力のon/off安定状態
// エッジをチェック
/***** エンコーダカウントアップ *****/
// 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;
}
/***** エンコーダ入力スキャン *****/
// タイマー割り込みで呼出
// 4サイクルでチャタリング除去
// ↑↓エッジを見つけてup/downカウント
// CW : A↑ B=L , A↓ B=H , B↑ A=H , B↓ A=L
// CCW : A↑ B=H , A↓ B=L , B↑ A=L , B↓ A=H
void encscan(void)
{
static byte p = 0; // 最新入力書き込み位置
static byte q1 = 0; // 安定したエンコーダ入力
byte d0, d1; // L安定,H安定 ↓↑エッジ
byte n, q2; // n:一時データ、q2:エッジ検出用
// エンコーダA,B相入力
n = 0;
if(INP_A) n |= 0b00000001; // 0:A相
if(INP_B) n |= 0b00000010; // 1:B相
enc_sft[p] = n; // 最新エンコーダ入力状態
// シフトデータをANDとORして変化点を見つける ★2
d0 = d1 = enc_sft[0]; // d0:全L / d1:全Hチェック
d0 |= enc_sft[1]; // [0]~[3]の4シフトデータ
d1 &= enc_sft[1];
d0 |= enc_sft[2];
d1 &= enc_sft[2];
d0 |= enc_sft[3];
d1 &= enc_sft[3];
n = (~d0) | d1; // 1のところが安定データ
q2 = q1; // 前の状態
q1 = (q2 & (~n)) | (enc_sft[3] & n); // 新入力状態確定
// シフトデータのポインタ+1
p++; // 次書き込み位置
if(p >= DIMSIZ(enc_sft)) p = 0; // ポインタ一周
// A相/B相安定状態をモニター出力
if(q1 & (1 << ENC_A)) D8_H; // A相 D8
else D8_L;
if(q1 & (1 << ENC_B)) D9_H; // B相 D9
else D9_L;
// エッジチェック
d1 = (~q2) & ( q1); // ↑エッジをチェック ★3
d0 = ( q2) & (~q1); // ↓エッジ
if(d0 | d1){ // エッジあり
D10_H; // A相かB相に↑↓エッジあり
f_cnt = 1; // カウントup/down検出
if(d1 & (1 << ENC_A)){ // A相↑ ★4
if(!(q1 & (1 << ENC_B))) cntup(); // B=L CW, +1
else cntdn(); // B=H CCW,-1
}
if(d1 & (1 << ENC_B)){ // B相↑
if( q1 & (1 << ENC_A)) cntup(); // A=H CW, +1
else cntdn(); // A=L CCW,-1
}
if(d0 & (1 << ENC_A)){ // A相↓
if( q1 & (1 << ENC_B)) cntup(); // B=H CW, +1
else cntdn(); // B=L CCW,-1
}
if(d0 & (1 << ENC_B)){ // B相↓
if(!(q1 & (1 << ENC_A))) cntup(); // A=L CW, +1
else cntdn(); // A=H CCW,-1
}
}
else{
D10_L; // エッジなし
}
}
/*******************************/
/* タイマー2割り込み */
/*******************************/
/***** タイマー2コンペアマッチA割込み *****/
// 2kHz(0.5mS)で割り込み
ISR(TIMER2_COMPA_vect)
{
D13_H; // (!!!)
encscan(); // エンコーダ入力処理
D13_L; // (!!!)
}
/*******************************/
/* SETUP */
/*******************************/
/***** SETUP *****/
void setup()
{
// ポートI/O指定
PORTB = 0b00000000; // data/pull up
DDRB = 0b00111111; // I/O (0:in 1:out)
// |||||+---- PB0 IO8 out A相入力安定モニター出力
// ||||+----- PB1 IO9 out B相入力安定モニター出力
// |||+------ PB2 IO10 out ↑↓エッジありモニター出力
// ||+------- PB3 IO11 out
// |+-------- PB4 IO12 out
// +--------- PB5 IO13 out (LED) 割り込みタイミング
PORTC = 0b00111111; // data/pull up
DDRC = 0b00000000; // I/O (0:in 1:out)
// |||||+---- PC0 AD0 in A相入力(pull up)
// ||||+----- PC1 AD1 in B相入力
// |||+------ PC2 AD2 in
// ||+------- PC3 AD3 in
// |+-------- PC4 AD4 in
// +--------- PC5 AD5 in
PORTD = 0b11111111; // data/pull up
DDRD = 0b00000010; // I/O (0:in 1:out)
// |||||||+---- PD0 IO0 in RXD
// ||||||+----- PD1 IO1 out TXD
// |||||+------ PD2 IO2 in
// ||||+------- PD3 IO3 in
// |||+-------- PD4 IO4 in
// ||+--------- PD5 IO5 in
// |+---------- PD6 IO6 in
// +----------- PD7 IO7 in
// タイマー2
TCCR2A = 0b00000010; // モード設定
// |||| ++--- WGM: CTCモード
// ||++------- COM0B
// ++--------- COM0A
TCCR2B = 0b00000100;
// |+++--- CS:1/64 : 250kHz
// +------ WGM02
OCR2A = 125 - 1; // 2kHz(0.5mS) ★1
TIMSK2 = 0b00000010;
// +----- OCIE2A コンペアマッチA割り込み有効
// シリアル
Serial.begin(9600);
// 最初の表示
delay(100); // 入力安定にちょい時間待ち(timer0使う)
cli(); // いったん割り込み禁止して
enc_cnt = 0; // カウンタをゼロクリア
sei(); // 割り込み有効に戻す
f_cnt = 1; // カウント値表示フラグon
}
/*******************************/
/* LOOP */
/*******************************/
/***** LOOP *****/
void loop()
{
if(f_cnt){ // カウンタup/downした
f_cnt = 0;
Serial.println(readcnt()); // カウント値をシリアル出力
}
}
/*===== end of "test_enc2.ino" =====*/

★1でタイマー2割り込みの周波数を設定。
2kHz(0.5ms)にしています。
チャタリング除去用のシフトレジスタが4段ですので、入力した信号が2mS遅れます。
これで2相パルスに対する応答速度が決まります。
ダイヤルを早く回すと追いつかないという状態に。

★2がチャタリング除去の処理。
ANDとORを重ねながらシフトレジスタを読み出しています。

★3がエッジ検出処理。
チャタリング除去されて確定したq1とq2からエッジを得ます。

★4で、A相B相の↑↓エッジ(4つある)から、相対する相の
状態を見て、カウントアップとカウントダウンの処理を行っています。


その状態。 オシロスコープで。
A000

一番下の波形、このタイミングでカウンタのup/downが
行われて、カウント値をシリアル出力しています。

エンコーダのぐるぐる回しをちょいと早くすると、
こんな感じに。
A003

模擬的なチャタリングをうまく取り除いている様子が見えます。
しかし、応答速度はこのあたりが限界。
対策は、タイマー割り込みの周期をもっと短くっという方法に
なるのですが、毎割り込みで取られる時間がちょいもったいな
いかと。
パルスが入ってカウント処理されると10usくらいです。
待機時は7~8usくらい。

割り込み機能の無い入力ピンを使っての2相パルスカウント、
高速応答はできませんが、手動で操作するエンコーダだと
こんなものでしょう。

シフトレジスタは8bitです。
今は2bitしか使っていないんで、エンコーダは4系統まで
拡張できるかと。


※試行錯誤

シフトレジスタのAND、OR処理、Cの書き方を
ちょいと変えると、出てくるコードが変わりました。
これが最初の。
enc_sft[0]~enc_sft[3]の並びをうまくやってくれるかと、
コンパイラに期待しましたが・・・

//  シフトデータをANDとORして変化点を見つける ★2
d0 = d1 = enc_sft[0]; // d0:全L / d1:全Hチェック
d0 |= enc_sft[1]; // [0]~[3]の4シフトデータ
d1 &= enc_sft[1];
d0 |= enc_sft[2];
d1 &= enc_sft[2];
d0 |= enc_sft[3];
d1 &= enc_sft[3];
n = (~d0) | d1; // 1のところが安定データ
q2 = q1; // 前の状態
q1 = (q2 & (~n)) | (enc_sft[3] & n); // 新入力状態確定

500: 90 91 18 01 lds r25, 0x0118 ;enc_sft[0]
504: 20 91 19 01 lds r18, 0x0119 ;enc_sft[1]
508: 29 2b or r18, r25
50a: 80 91 19 01 lds r24, 0x0119 ;enc_sft[1]
50e: 98 23 and r25, r24
510: 80 91 1a 01 lds r24, 0x011A ;enc_sft[2]
514: 28 2b or r18, r24
516: 80 91 1a 01 lds r24, 0x011A ;enc_sft[2]
51a: 98 23 and r25, r24
51c: 40 91 1b 01 lds r20, 0x011B ;enc_sft[3]
520: 80 91 1b 01 lds r24, 0x011B ;enc_sft[3]
524: 24 2b or r18, r20
526: 20 95 com r18
528: 98 23 and r25, r24
52a: 92 2b or r25, r18
52c: 80 91 17 01 lds r24, 0x0117 ;q1
530: 20 91 1b 01 lds r18, 0x011B ;enc_sft[3]
534: 49 2f mov r20, r25
536: 40 95 com r20
538: 48 23 and r20, r24
53a: 92 23 and r25, r18
53c: 94 2b or r25, r20
53e: 90 93 17 01 sts 0x0117, r25 ;q1

別の書き方で。
こっちのほうがメモリの読み出しを省いている
分、ちょいだけと早そう。


// シフトデータをANDとORして変化点を見つける ★2
d0 = d1 = enc_sft[0]; // d0:全Lチェック,d1:全Hチェック
n = enc_sft[1]; // [0]~[3]の4シフトデータ
d0 |= n;
d1 &= n;
n = enc_sft[2];
d0 |= n;
d1 &= n;
n = enc_sft[3];
d0 |= n;
d1 &= n;
n = (~d0) | d1; // 1のところが安定データ
q2 = q1; // 前の状態
q1 = (q2 & (~n)) | (enc_sft[3] & n); // 新入力状態確定


500: 90 91 18 01 lds r25, 0x0118 ;enc_sft[0]
504: 80 91 19 01 lds r24, 0x0119 ;enc_sft[1]
508: 29 2f mov r18, r25
50a: 28 2b or r18, r24
50c: 89 23 and r24, r25
50e: 40 91 1a 01 lds r20, 0x011A ;enc_sft[2]
512: 24 2b or r18, r20
514: 98 2f mov r25, r24
516: 94 23 and r25, r20
518: 80 91 1b 01 lds r24, 0x011B ;enc_sft[3]
51c: 28 2b or r18, r24
51e: 20 95 com r18
520: 89 23 and r24, r25
522: 92 2f mov r25, r18
524: 98 2b or r25, r24
526: 80 91 17 01 lds r24, 0x0117 ;q1
52a: 20 91 1b 01 lds r18, 0x011B ;enc_sft[3]
52e: 49 2f mov r20, r25
530: 40 95 com r20
532: 48 23 and r20, r24
534: 92 23 and r25, r18
536: 94 2b or r25, r20
538: 90 93 17 01 sts 0x0117, r25 ;q1

 

 

| | コメント (0)

2020年9月15日 (火)

今度はチャタリング除去、その考え方

2020年9月12日:ロータリーエンコーダーの2相パルスをピン変化割り込みで取り込む
に絡んで、まずはこんなものを作りました。
2020年9月14日:「チャタリング除去回路」じゃなくって「チャタリング発生回路」をどうぞ

エンコーダのA相・B相信号、チャタリングを取り除きながら
エッジ検出してCW・CCW方向のパルス計数をする方法。
このように考えて作っています。

・INT0やINT1、あるいはピン変化割り込みを使う場合は、
 ハードウェア的にチャタリングを取り除いておかないと
 不要パルスが入った時にミスカウントするかも。

・これらの直接的パルス駆動割り込みを使わない場合は、
 タイマー割り込みでの定期的なA相・B相信号の入力処理
 が必要。

・定期的な処理ができるのなら、チャタリング除去も
 ソフトでできる。

・ただし・・・「入力波形が安定するまで時間待ち」など
 という恥ずかしい処理は×。

・こんなロジックでチャタリングを防ぎます。

C1_20200915170901

とりあえずシフトレジスタは4段。
タイマー割り込みの周期で入力信号をシフトしていきます。
4つとも全部がH、あるいは全部がLなら信号が安定した
ということで、入力を確定。
H/Lが混じっていたら不安定=チャタリング中ということで
安定していた時の昔のデータを用います。

スイッチ入力だと、1mSサイクルで10段ほど読んでおけば
ひどいチャタリングのあるスイッチでも大丈夫。
スイッチのオートリピートや長押し処理も、タイマー割り込み内
で完結します。

そして、エンコーダのA相・B相から正逆をカウントする処理は、
そのパルスエッジの検出が不可欠です。

これもやはりシフトレジスタを使って処理、こんな
「回路」で↑↓エッジを作り出します。

C2_20200915170901

このロジックをマイコンの命令に置き換えてプログラム
するわけです。

チャタリング除去の所、全Lを検出している「負論理4入力のAND」
は「正論理4入力・負論理出力のOR」と等価です。
これもプログラムの命令で実現します。

実際のプログラムの様子はこの次に。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
※2相パルスの弁別回路について
2017年9月12日:C-MOS 2相パルスカウンタの入力部回路
2017年3月6日:29年前に製作したツール

| | コメント (0)

2020年9月12日 (土)

ロータリーエンコーダーの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

11_20200912143701

1回転が24クリック。
360度回そうとすると15回転。
   ちょっとクリクリするのがたいへんか・・・

1クリックでこんなパルスが出てきます。
右回しと左回しで波形がちょいと異なります。
A2_20200912143801

A1_20200912143801
A相B相とも途中では止まりません。
両方信号オフ状態で止まります。
これ、2相パルスには違いないのですが、クリック機構があると
途中で止められないので、4逓倍は無理。

そこで・・・「つぶしてもエエやん」っと、基板部を固定してある
ポッチリを削ると、中身が出てきました。
A相・B相のコード板と接点。
13_20200912143901

そしてクリックはこんな機構。
14_20200912144001

スプリング部に出たチョッポリで回転軸のギザギザを押さえ
ています。
このスプリングを外せば、クリック無しで自由回転するんですが、
接点端子部をコード板に押さえているのもこの機構です。
スプリングを取ると接触が安定しません。

そこで、「ポッチリを反対にしてみたろ」っとスプリングを裏返したと
ころ、「まぁエエやん」っとクリック感無しで360度クルクル回転でき
るようになりました。

これで4逓倍すれば、3.75回転で360度の角度をセットできます。
回すとこんなパルスが出てきます。
A3_20200912144201
ほんとは各パルス90度位相差なんですが、コード板の構造で
しょう、1箇所周期が長いところがあります。

接点のチャタリング、こんな様子です。
A4_20200912144301
いつも出るわけじゃなく、「たまに出る」というヒゲですが、
CRでのチャタリング吸収回路は必須です。
ちなみにBOURNSのカタログにはこんな回路が。

・単純にCRで
 10k+0.01uFですんで時定数0.1mSくらい。
B1_20200912144901

・そして・・・あらま懐かしいMC14490 が出てきました。
B2
チャタリング除去専用のICです。
大昔、使ったことあります。
C-MOS4000番シリーズの「けったいなIC」系列に登場する
石ですなぁ。

まずはArduino UNOでエンコーダーの計数を試してみます。
ネットを探すと・・・INT0・INT1エッジ割り込みを使った
例題ばかり。

面白くないんで、こんな接続に。
ピン変化割り込み」を使ってみます。

B5

ピン変化割り込みは、ポート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以外にエンコーダをもう一つ付けたいという時の
参考になりますでしょうか。

・カウントアップ・ダウンの処理
B11_20200912150301
   ※おっと、2カ所ミスあり。
    B相↓のとき「B相=」というところ「A相=」です。

・逆転する様子
B12_20200912150301
入力のA相・B相パルスと、割り込み処理の関係、こんな具合です。
割り込みでD13・PB5ポートをon/offしています。
各エッジで割り込みが入っていることが見えます。

A6_20200912145501

※注意点
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のカウンター。

| | コメント (0)

2020年9月 9日 (水)

書籍でもやってる「1/1023」と「map(x, 0, 1023, 0, 255)」

ちょっと気になってたんで、Arduino関連の書籍を図書館にリクエストしてました。
とりあえずこの2冊。

(1)Arduino電子工作実践講座

A11_20200909091401

(2)Arduinoをはじめよう

A21_20200909091401

(1)からはこれ。
analogReadで読んだA/D値を電圧値へ変換する処理。
1/1023」と「Vref値の決め打ち」が蔓延っています。

A12_20200909091501


(2)では。
10bit→8bitへのスケーリングで「map」関数。
  map(val, 0, 1023, 0, 255);

A22_20200909091501

※関連
Arduino なんとかして誤用を正したい:A/Dの1/1023とmap関数
ミスが広まる 1/1023 vs 1/1024
Arduino 10bit A/D値をmap関数でスケーリングする例
線形補間って「LERP」って言うんだ!


| | コメント (0)

2020年8月29日 (土)

Arduino-UNO 12bit×4chアナログ データロガー計画中

Arduino-UNO + SDカードでシリアルデータロガー 完成形 今度こそ
でArduino-UNOを使ったSDカードの読み書きがうまく行ったんで、
今度はアナログ値のデータロガーかと、構想中です。

◎要求仕様
・10bitでは不足だっ。
 だもんで、ATmega328Pの10bit A/Dは使わず、
 12bitのA/Dコンバータを外付けっと。
・4chは欲しいぞ。
 だもんで、A/DはMCP3204。
・リファレンス電圧は4.096Vで。
 ちょっとエエのを使おうか
・入力レンジをどうしよう・・・
 悩み中。
 とりあえず±2Vレンジと±20Vレンジで。

フロントエンドはこんな回路か・・・

50_20200829161201
±2Vレンジなら±2.048Vで最小桁が1mV。
±20Vなら10mVを読めるかと。
いわゆる 3・1/2桁 相当。

昔々、各チャンネルのGNDをアイソレートして
別電源でも測れるようにっと、こんな回路も描いたけど・・・

53_20200829161301

入力レンジ切替回路の参考図。
51_20200829161301
2015年04月22日:A/Dコンバータの入力レンジを拡大する方法

これ↓はプリンタシールド応用チャートレコーダの
52_20200829161301
2014年01月25日:プリンタシールド、付加回路


| | コメント (2)

2020年8月24日 (月)

Arduino PWM波形のデューティー比測定 これで完成形か?

とりあえず「シリアル出力」するデューティーサイクル
測定回路、これで完成形にしておきます。

・プログラム  ダウンロード - duty_checker4.c
  ※ファイルタイプを「.c」にしています。
   「.ino」だとArduinoのIDEが立ち上がっちゃうんで。

※改善点

・タイマー1のインプットキャプチャーでのカウント
 を16bitに。
 オーバーフロー割り込みとの衝突が無くなったので
 短パルスのミスが改善。 2μsのパルスも安定。

・しかし、16bitカウントにすると、低い周波数だとオーバー
 フローしてしまい、測定できなくなる。
   ※これを避けるために32bitカウントにしてた。
 そこでタイマー1のクロック(↑↓エッジでこれを数える)
 を16MHzだけでなく、外部からクロックを供給するように
 した。
 このクロックを下げると、低い周波数でもデューティーの
 測定ができる。
 
・Arduino UNOのD5とD11をつなぐ
      D5 PD5 T1    クロック入力
      D11 PB3 OC2A   8MHz~100kHzクロック出力

  タイマー1:インプットキャプチャー ICP1入力
   16MHz内部クロックあるいはT1外部クロック入力を切替

  タイマー2 :1/1でCTCモード OC2Aで方形波発生
   これをT1に接続 この出力クロック周波数を切替

Pp1

・周波数の選択はシリアル入力で。
 「0~7」の数字を入力すると切り替え。
   0 : 16MHz
   1 :  8MHz
   2 :  4MHz
   3 :  2MHz
   4 :  1MHz
   5 : 500kHz
   6 : 200kHz
   7 : 100kHz
 この8種類。 1Hz程度でもカウント可能。

・測定例 9600bpsでシリアル出力
  Duty cyc checker (2020-08-24) ←起動メッセージ
  CLK=16MHz            ←初期値
   12.50%  0.250kHz  8000 56004
   12.50%  0.250kHz  8000 56004 ←1秒ごとに表示
   12.50%  0.250kHz  8000 56003
  CLK=8MHz            ←1 数字入力
   12.50%  0.250kHz  4000 28002
  CLK=4MHz            ←2
   12.50%  0.250kHz  2000 14001
  CLK=2MHz            ←3
   12.50%  0.250kHz  1000 7001
  CLK=1MHz            ←4
   12.50%  0.250kHz  500 3500
  CLK=500kHz           ←5
   12.50%  0.250kHz  250 1750
  CLK=200kHz           ←6
   12.50%  0.250kHz  100  700
  CLK=100kHz           ←7
   12.50%  0.250kHz   50  350
  CLK=16MHz           ←0
   12.50%  0.250kHz  8001 56003
    |    |   |  +-- L区間パルス数
    |    |   H区間パルス数
  デューティ比 周波数

  Overflow pulse  ↑↓エッジ間に2回以上の
           タイマー1のオーバーフロー
           が起こった。
  No pulse     1秒内に入力パルスが無い。

※その他
 ・ICP1以外の割り込みは禁止。
 ・システムタイマーのタイマー0の割り込みも止めてるんで、
  一部のタイマー関数は使えない。
 ・シリアル送受信も独自のもので。
    serial.printは割り込みを使うんで。


※関連
Arduino PWM波形のデューティー比測定 妥協点
Arduino UNOのPWM出力を(ちょっと精密に)確かめる
Arduinoから「タイマー0」を取り上げる(ユーザーが使う)


| | コメント (0)

2020年8月21日 (金)

Arduino PWM波形のデューティー比測定 妥協点

あれこれ試してきましたが・・・
仕様的な妥協点を。

・最低測定周波数を下げる。
  現在は16MHzクロックを32bitのカウンタで
  数えています。
  本来、ATmega328Pのタイマー1は16bit。
  これを拡幅するために、オーバーフロー割り
  込みで32bitの上位桁をカウントアップしてる
  わけです。
  この処理がインプットキャプチャー割り込み
  の応答を遅らせています。
  ということは、16bitカウンタだけにして
  オーバーフロー割り込みを無くせば、
  短パルス測定時のじゃまが無くなります。
  その場合、16MHz/65536で約244Hzが1周期。
  タイマー1,2のanalogWriteのPWM周期、約490Hz
  もカウントできるかっと。

・低い周波数を計りたい時は
  インプットキャプチャーしているタイマー1の
  クロック周波数を下げれば対応できます。
  16bit分解能は同じ。
  TCCR1Bのプリスケーラ選択で、
   1/1 1/8 1/64 1/256 1/1024
  が選べますが、ちょいと飛びすぎ
  T1入力を外部クロックにできますんで、ここに
  タイマー2のアウトプットコンペア出力をつないで
  おけば、
   内部で1/1=16MHz 1/8=2MHz
   外から8MHz 1MHz 100kHz
  などとして、カウントクロックを選べます。

まぁ。こんなところでしょうか。

ArduinoのanalogWriteが出すデューティを計るということで、
あれこれ試しました。

しかしこの実験、もう一つ別の目的があるのです。
旭化成のAK7401 という磁気センサー。
  (360度グルグル回ります)
このIC、周波数1kHzのPWM出力で回転角を出力するのです。
その検証用ジグの試作に絡むのです。

2019年9月29日:360度グルグル回したろ
   ↑は3線デジタルでしたが、PWMでも出ているのです。
・PWMの良い所・・・フォトカプラで絶縁できる。

※関連
インプットキャプチャー割り込みを「ISR_NAKED」で (デューティー比測定)
Arduino デューティー計測のためのインプットキャプチャータイミング
Arduino UNOのPWM出力を(ちょっと精密に)確かめる
14pinのAVRマイコン、ATtiny24が動かん!
   ↑ これはPWM出力

※豆知識
Arduino、「analogWrite」のPWM周波数、
 タイマー0が約980Hz
 タイマー1と2が約490Hz
と、記されています。

なぜこの周波数か・・・もうちょい桁を増やして・・・
  タイマー0は   16MHz÷64÷256で976.5625Hz
  タイマー1と2は 16MHz÷64÷255÷2で490.1960784Hz
これそのままだと中途半端な周波数と感じますでしょう。

でも、逆数をとって「周期」にしてみると・・・
  タイマー0は1.024mS。タイマー1と2が2.04ms
むちゃすっきりした数字になるのです。
  ※このこと、皆さんあまり言ってないような・・・

| | コメント (0)

より以前の記事一覧