TMR0を使った7セグLEDのダイナミック点灯

はじめに

Arduinoでダイナミック点灯をやってみたが、これぐらいだったらPICでもできるのでは?と思い挑戦してみた。 Arduinoでの実装はこちら。

github.com

今回用意するのは

ダイナミック点灯

ダイナミック点灯とは、7セグメントLEDを順番にかつ高速に点灯させることにより、人間の目には同時に点灯しているように見せる点灯方式。

www.elec-hobbyist.com

タイマーの考え方

例えば、一秒毎にカウントアップする数字をダイナミック点灯で表示させることを考えた場合、カウント用のループ処理とダイナミック点灯用のループ処理が必要になるが、メインループの中で両方ともやることは出来ない。理由としてはダイナミック点灯を行う場合、7セグLEDに対して数ミリ秒間隔で交互に電力を供給する必要が出てくるが、この処理の中でスリープ1秒とかをいれてしまった日には、LEDセグメントはチカチカッと光って後半の7セグLEDだけが光る状態になる。そこで、メインループの中はダイナミック点灯用のループを回して、TMR0で1秒毎に割り込みをいれてカウントアップするようにする。

TMR0割り込み

このサイトが大変参考になる

smtengkapi.com

  • PIC16F628Aの内部クロック4MHz
  • 1命令の実行時間は0.25μs(1sec / 4,000,000Hz)
  • 動作クロック1MHz(4Mhz / 4クロック)
  • 1命令の実行時間は1μs (1sec / 1,000,000Hz)
  • カウンターは256でオーバーフロー

このまま愚直にカウンターを繰り上げていくと、256μsでオーバーフロー(割り込み発生)してしまう(カウンター8bitなので256がMAX) こうなると、1秒を検知するためには 1sec / 256( 1μs * 256回 ) μs = 1sec / 0.000256sec = 3906回割り込みをしなければいけなくなる。 これだと1秒を作るのに割り込みが入りすぎなので、プリスケーラというのを使ってクロックを間引く。

プリスケーラ(分周)

f:id:kyonta1022:20211104224629j:plain

仮にプリスケーラのマックス256で考えたとすると 1μs * 256 = 256μs となり、カウンターを繰り上げるのに256μsかかることになる。 これは、先程カウンタをマックスにするのにかかった時間そのものになる。

そうすると、1秒を作るために3906回必要だった割り込みが、1sec / 65,536 ( 1μs * プリスケーラ:256 * 256回 ) μs = 1sec / 0.065536sec = 15回の割り込みで十分となる。 なので、65msで割り込みが入ったら独自のカウンタを繰り上げて、それが15になったら秒数繰り上げ処理を行えば1秒(だいたい)を作れる。

※ このだいたい1秒は、カウンターの初期値を調整してぴったり1秒とかにすることもでき、それで精度の調整も可能になる(さらに精度を高める場合は内部クロックを外部クロックに変えるとかも必要になる) 今回利用しているPICは8bitPICなので、カウンターが256までだが、16bitPICを利用すれば65,536カウンターまで保持できるので、1secちょうどで割り込み発生させるとかが可能となっている。

実装

TMR0を利用するためにはいくつかレジスタを設定する必要がある。

まず、Global Interrupt EnableとTMR0 Overflow Interrupt Enableに1を立てる。 これで割り込み自体の有効化とTMR0の有効化をしている そのあとは、上で説明したプリスケーラの値を設定して、カウンターの初期値を設定する。

//GIE(Global Interrupt Enable bit):1, T0IE(TMR0 Overflow Interrupt Enable bit):1
INTCON = 0b10100000; 
// Prescaler Rate Select bits 111 -> 1 : 256
OPTION_REG = 0b10000111;
TMR0 = 0x00;

レジストリの設定を行ったあとは割り込み発生時に実行する関数を定義する。 T0IFはタイマー割り込みが入ると1になるので0に戻して再度割り込みを可能な状態にしておく。

void __interrupt() irt(void)
{
    T0IF = 0;
    tmr0_counter--;
    if (tmr0_counter == 0) {
        tmr0_counter = INTERRUPTS_COUNT_PER_SECOND;
        count_up();
    }
}

ここら辺のことを一通りやった上でダイナミック点灯を実現しているのが以下のソースになる。

gist.github.com

回路図

今回利用した7セグメントLEDは

  • DC Forward Current (IF) = 20mA 順方向電流
  • DC Forward Voltage (VF) = 3.3 〜 4.0V 電圧
  • Reverse Voltage (VR) = 5V 逆電圧

だったので、余裕を持って18mAとして (5V - 3.3V) / 0.018A = 94Ω で100Ωの抵抗を挟むことにする

f:id:kyonta1022:20211103202008p:plain

まとめ

昔PICで遊んでいた時は、ダイナミック点灯が難しいイメージだったが、原理を理解してTMR0の使い方を把握さえしてしまえば、難しいことは何もなかった。Arduinoで実装できればそれをPIC用にコンバートするだけ。