PIC 16F1827時計のソフトウエア開発 (9)
今回は、予告した秒針もどきの表示ルーチンについて、説明したいと思います。
SetLAT()とLEDset()という2つのルーチンを追加します。
/*
* item=0:0~5時のLED、1:6~11時とコロン
*
*/
void SetLAT(int item, unsigned int data) {
LATA = ~((0x40 << item) | (data >> (item * 8) & 0x000f));
LATB = ~((data >> (4 + item * 8)) & 0x000f) & (~MASKPB3) | (LATB & MASKPB3);
//dataの上位4ビットを反転してPB3~0、PB7~4はHigh
if (AlarmOn && item == 1) {
//コロンを消灯してみる
LATB2 = 1;
}
}
/*
* 12+2個のLEDを光らせる
* item=0:0~5時のLED、1:6~11時とコロン
*/
int LEDset(int item, unsigned char data) {
if (PatternMode & 0x0100) {
if (LEDpatternAlarm[PatternPos] == 0xffff) PatternPos = 0;
SetLAT(item, LEDpatternAlarm[PatternPos]);
return;
}
switch (PatternMode) {
case 1:
if (LEDpattern1[PatternPos] == 0xffff) PatternPos = 0;
SetLAT(item, LEDpattern1[PatternPos]);
break;
case 2:
if (LEDpattern2[PatternPos] == 0xffff) PatternPos = 0;
SetLAT(item, LEDpattern2[PatternPos]);
break;
case 3:
if (LEDpattern3[PatternPos] == 0xffff) PatternPos = 0;
SetLAT(item, LEDpattern3[PatternPos]);
break;
case 4:
if (LEDpattern4[PatternPos] == 0xffff) PatternPos = 0;
SetLAT(item, LEDpattern4[PatternPos]);
break;
default:
//5秒間は、1つのLEDを点滅
if ((Bright[Pattern[1][Cnt]] & BrightPos) == 0) data = 0;
//dataの下位4ビットと、itemで指定された上位を合わせて反転出力
LATA = ~((0x40 << item) | (data & 0x0f));
LATB = ~(data >> 4) & (~MASKPB3) | (LATB & MASKPB3); //dataの上位4ビットを反転してPB3~0、PB7~4はHigh
if (AlarmOn && item == 1) {
//コロンを消灯してみる
LATB2 = 1;
}
break;
}
}
SetLATは、LEDsetから呼び出されています。実際にPICのポートに出力しているルーチンとなります。
LEDsetは、周辺のLEDをどのように光らせるかの設定を行っています。
LED発光パターンとして、LEDpattern1~4、LEDpatternAlarmとか、Brigtという配列定数を使っています。
私の設定について、以下に示します。
//LEDの輝度
const unsigned int Bright[] = {
0x0000, 0x0001, 0x0101, 0x0111,
0x1111, 0x1115, 0x1515, 0x1555,
0x5555, 0x5557, 0x5757, 0x5777,
0x7777, 0x777f, 0x7f7f, 0x7fff,
0xffff,
};
//LEDの発光パターン
const unsigned char Pattern[PatternMAX][10] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{16, 16, 16, 16, 8, 0, 0, 0, 0, 0},
{16, 14, 13, 11, 10, 8, 6, 4, 2, 0},
{0, 2, 4, 6, 8, 10, 11, 13, 14, 16},
{16, 16, 16, 16, 16, 16, 16, 16, 16, 16},
};
//パターンの定義、0xffffで終端とするので、長さは任意でOK
//
//16bitのデータでLED0-11を光らせるパターンを設定。1で光る。
//bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
//LED -- -- 11 10 9 8 7 6 - - 5 4 3 2 1 0 各時刻の所のLEDを示す
//注意:PatternPosのデータが
//振り子型のパターン
//2sで1周期
const unsigned int LEDpattern1[] = {
//0s 0.1s 0.2s 0.3s 0.4s 0.5s 0.6s 0.7s 0.8s 0.9s
0x0010, 0x0010, 0x0030, 0x0020, 0x0120, 0x0100, 0x0300, 0x0200, 0x0600, 0x0400,
0x0400, 0x0400, 0x0600, 0x0200, 0x0300, 0x0100, 0x0120, 0x0020, 0x0030, 0x0010,
0xffff,
};
//十字の位置の4LEDが回るように光るパターン
const unsigned int LEDpattern2[] = {
//0s 0.1s 0.2s 0.3s 0.4s 0.5s 0.6s 0.7s 0.8s 0.9s
0x0909, 0x0909, 0x0909, 0x0909, 0x0909, 0x1212, 0x1212, 0x1212, 0x1212, 0x1212,
0x2424, 0x2424, 0x2424, 0x2424, 0x2424, 0xffff,
};
//上から下へ流れるように落ちるパターン
const unsigned int LEDpattern3[] = {
//0s 0.1s 0.2s 0.3s 0.4s 0.5s 0.6s 0.7s 0.8s 0.9s
0x0001, 0x2003, 0x2002, 0x3006, 0x1004, 0x180c, 0x0c18, 0x0630, 0x0320, 0x0100,
0xffff,
};
//ぐるぐる回っているように光るパターン
const unsigned int LEDpattern4[] = {
//0s 0.1s 0.2s 0.3s 0.4s 0.5s 0.6s 0.7s 0.8s 0.9s
0x2001, 0x0003, 0x0006, 0x001c, 0x0030, 0x0120, 0x0300, 0x0600, 0x1c00, 0x3000,
0xffff,
};
//アラーム用:十字の位置の4LEDが回るように光るパターン
const unsigned int LEDpatternAlarm[] = {
//0s 0.1s 0.2s 0.3s 0.4s 0.5s 0.6s 0.7s 0.8s 0.9s
0x0909, 0x0909, 0x1212, 0x1212, 0x2424, 0x2424, 0xffff,
};
最初のBrightとPatternは、秒針を示すための5回ずつ光らせるものです。
Patternが1秒間にどのような輝度で光らせるかを定義しており、0は消灯、16だと常に光らせるという感じにして、輝度を表現できるようにしています。Bright[]では、さらに短い時間で、1になっているビットなら点灯、0なら消灯で、Bright[16]は、0xffffなので、常に光らせることになり、Bright[4]なら、0x1111なので、2進数に直すと、0001 0001 0001 0001となり、4回に1回しか光らせません。
つまり、25%しか点灯しなくなるので、ダイナミック点灯しているLEDが暗く見えるようになることを期待しています。
ダイナミック点灯には、1ms毎の割り込みを使っています。10ms周期で、処理が1周するようにしています。今どのLED群を対象としているかを示している変数が、ScanTimer2です。
以下の表に示したような処理が行われます。
| ScanTimer2 | 処理内容 |
| 0 | 周辺の0時~5時の部分の6つのLEDを点灯 |
| 1 | 周辺の6時~11時とコロンを加えた8つのLEDを点灯 |
| 2 | 時の1の位の7セグメントLEDを点灯 |
| 3 | 時の10の位の7セグメントLEDを点灯 |
| 4 | 分の1の位の7セグメントLEDを点灯 |
| 5 | 分の10の位の7セグメントLEDを点灯 |
| 6 | ScanTimer2=2と同じ |
| 7 | ScanTimer2=3と同じ |
| 8 | ScanTimer2=4と同じ |
| 9 | ScanTimer2=5と同じ |
周辺のLEDの方が輝度が高かったので、10回中1回だけ光らせ、7セグのLEDは、10回中2回光らせました。
先ほど説明した周辺のLEDの輝度は、この10msに1回光らせるのを毎回行うのを最大輝度として、16ビットの定数を使って、最小の輝度では、16回つまり160msに1回光らせられるようにしているのです。
これらを今までのソースに追加してコンパイルするのですが、少し待って下さい。そのままコンパイルしても、前と動作は変わりませんので。
LEDset()を呼び出す必要があります。main()内の無限ループしているルーチン内に、LEDsetをコメントアウトしている所が2か所あるはずです。ここを生かします。
具体的には、
修正前:
if (ScanTimer2 == 0) ;//LEDset(0, (1 << ss));
else if (ScanTimer2 == 1) ;//LEDset(1, (1 << (ss - 6)) | 0xc0);
修正後:
if (ScanTimer2 == 0) LEDset(0, (1 << ss));
else if (ScanTimer2 == 1) LEDset(1, (1 << (ss - 6)) | 0xc0);
として下さい。これで、コンパイルして、PIC時計に書き込みます。
さて、どういう動作になるかというと、通常は、5秒ずつ1つのLEDが点滅を繰り返します。
赤か白ボタンを押すと、そのパターンが変わります。
先ほど提示した配列定数は、LEDpattern1~4まで、4種類ありました。
1: 振り子のような表示
2: 十字の位置の4つのLEDが回転するパターン
3: 上から下へ流れるように落ちるパターン
4: 1秒で、1周、ぐるぐる回るように光るパターン
以上のような表示パターンを作ってみました。
動画を表示させるため、初めてユーチューブに登録してみました。
いかがでしょうか。映像編集が素人ですが、PIC時計がどのようにLEDを表示させているかの雰囲気を理解いただければよいので、ご容赦下さい。
今回は、これで終了とさせていただきます。
この記事へのコメント