第22回 LED点滅プログラム完成
このSPLLENは「Software PLL Enable」の略で、このビットでPLLを使用する/しないの設定ができます。ただし、コンフィグレーション設定でPLLをONにした場合はこのビットの設定は無効で、常にPLLが使用されます(つまり周波数は常に4倍される)。コンフィグレーション設定でPLLをOFFにした場合、プログラム動作中にPLLをONにしたい場合は、このビットを1にするとPLLが有効になります。0を設定した場合はPLLは無効です。
IRCF(第6〜第3ビット)設定名称は「IRCF」ですが、データシートでは「Internal Oscillator Frequency Select(内部発振周波数選択)」と説明されています(後でデータシートを確認します)。
なんだか設定名称と英語があってないですよね。「IRCF」は、「Internal RC oscillator Frequency」の略で、日本語に無理やり訳すと、「内部の抵抗とコンデンサを使用した発振モジュールの周波数」という感じでしょうか。(設定名称と英語は合わせた方がいいような…)
第6ビット第5ビット第4ビット第3ビット周波数0000または131kHz001031.25kHz (MF)001131.25kHz (HF)010062.5kHz (MF)0101125kHz (MF)0110250kHz (MF)0111500kHz (MF)デフォルト1000125kHz (HF)1001250kHz (HF)1010500kHz (HF)10111MHz (HF)今回使用11002MHz (HF)11014MHz (HF)11108MHz (HF)111116MHz (HF)1MHzの場合は、第6ビットから第3ビットは「1011」という設定 にすればよいことがわかります。
ところで、すぐにお気づきかもしれませんが、31.25kHzとか、125kHzとか、他の周波数もそうですが、2回出てきてますよね。例えば250kHzに設定したい場合、「0110の250kHz (MF) 」と「1001の250kHz (HF) 」の設定があります。これって、どちらに設定すればよいのでしょうか。
結論としては、「 MF 」と「 HF 」の両方がある周波数では、基本的には「 MF 」を使用するようにします。ただし、プログラムによっては「 HF 」を選択した方が良いケースもあります。
周波数の右側の文字を見ると、「 MF 」「 HF 」の他に一番最初の31kHzのところに「 LF 」がありますよね。
最初に「 MF 」、「 HF 」、「 LF 」は何を意味しているか、ここから解明していきましょう。
- 低周波モジュール(Low Frequency = LF)31kHzの周波数を発生させるモジュールです。あまり精度は良くありません。ウォッチドッグタイマーなどに使用されます。
- 中周波モジュール(Middle Frequency = MF)500kHzの周波数を発生させるモジュールです。工場出荷時に調整されますので、ある程度の精度は期待できます。プログラム実行に使用します。この周波数を元に、この半分の250kHz、さらに半分の125kHz、という感じで31.25kHzまでの周波数を生成します。
- 高周波モジュール(High Frequency = HF)16MHzの周波数を発生させるモジュールです。こちらも工場出荷時に調整されますので、ある程度の精度は期待できます。プログラム実行に使用します。この周波数を元に、この半分の周波数である8MHz、さらに半分の4MHz、という感じで125kHzまでと、その半分の半分の31.25kHzの周波数を生成します。
周波数の右側の文字、「 MF 」などは、どの周波数モジュールを使用して生成しているか、を意味しています。例えば、「250kHz (MF) 」であれば、中周波モジュールの500kHzのクロックを元に生成している250kHz、という意味です。
周波数は、数値が大きいほど(周波数が高いほど)と消費電力が多くなります。つまり、同じ250kHzを指定するにしても「250kHz (HF) 」は「250kHz (MF) 」よりも消費電力が多くなります。ということは、250kHzにしたい場合、消費電力の少ない「250kHz (MF) 」を使用した方がよいことになります。となると、ますます「250kHz(HF)」は必要なさそうですよね。
今回製作するタイマーのように、プログラム動作中は周波数を変更しない場合、「 MF 」と「 HF 」の2種類があれば、消費電力の少ない「 MF 」で問題ないかな、と思います。
周波数の切り替えは、同じクロック発生モジュールであれば、一瞬で周波数が変更されます。例えば、「1MHz (HF) 」から「500kHz (HF) 」というように、高周波モジュールを使用する周波数同士の切り替えは一瞬です。
しかし、例えば「1MHz (HF) 」から「500kHz (MF) 」というように周波数切り替えに伴い、モジュールが変更される場合は、切り替えの時間がかかります。(ほんの少しですが…)
かなり込み入った説明になってしまいましたが、結論は「 MF 」があれば「 MF 」を使うのが無難かな、という感じです。
SCS(第1ビット・第0ビット)「SCS」は「System Clock Select」です。(クロック元の指定ってコンフィグレーション設定でも出てきたような…)
第1ビット第2ビット意味10または1内部クロックを使用01Timer1を使用00コンフィグレーション設定のFOSCに従う 内部クロック設定のまとめ OSCCON = 0b01011000; (Microchip Technology社 PIC12F1822データシートより引用・加工)最初の レジスタ構成 の部分は、3行で構成さています。一番上の行は、OSCCONレジスタの各ビットの「読み書き属性」と「デフォルト値」の説明です。
読み書き属性の行の意味は、次の レジスタ構成の記号の意味 に説明されています。この記号の意味を以下にまとめます。
記号意味内容RReadable Bit読取り可能ビット(この書き込みしても反映されない)WWritable Bit書込み可能ビットUUnimplemented Bit無効ビット(読み出すと常にゼロ、書き込みしても反映されない)uUnchanged値変更不可xUnknown不定-n /nValue at POR&BOROther ResetsR/Wなどのあとに書かれている「-0/0」などの数字は、スラッシュの左側の数字は電源投入時または電源低下リセット時のデフォルト値、スラッシュの右側の数字はその他のリセット時のデフォルト値1Bit is set1がセットされる0Bit is cleared0がセットされる ピンの機能設定 アナログかデジタルか「RA」という文字のついたピンは、デジタル制御ができる ことを示しています。ピンの機能を見ると、電源ピンを除くすべてのピン(2番ピン〜7番ピン)に対して、「RA0」から「RA5」が割り当てられていることがわかります。
「AN」という文字のついたピンは、アナログ制御ができる ことを示しています。アナログ制御ができるピンは、デジタルピンよりも少なく、「AN0」から「AN3」までの4ピンです。また、デジタルピンと番号がずれている点にも注意してください。
入力か出力か ピン設定の概要ピンのデジタル/アナログの設定は「ANSELA」レジスタで行います。ANSELAは「Analog Select(Port A)」の略です。直訳すると「アナログ選択」ですが、意味的には「アナログ/デジタルの選択」という感じです。
ピンのアナログ・デジタル設定(ANSELA) ANSELA = 0b00000000; ANSELA = 0x00; ピンの入力・出力設定 TRISA = 0b00001000; ANSELAとTRISAのデータシート (Microchip Technology社 PIC12F1822データシートより引用) (Microchip Technology社 PIC12F1822データシートより引用) ピンの初期設定 LATA5 = 1; LATA5 = 0;参考ですが、あとでブザーをRA4ピン(3番ピン)に接続します。ブザーをONにする場合は、 LATA4 = 1; 、OFFにする場合は LATA4 = 0; と書くことになります。
LATA5 = 0; 動作処理- 950ms消灯状態にする
- LATA5 = 1; でLEDを点灯させる
- そのまま50ms点灯状態にしておく
- LATA5 = 0; でLEDを消灯させる
- ❶に戻る
教えるには、 _XTAL_FREQ にクロック周波数(単位はHz)を #define で定義します。
#define _XTAL_FREQ 1000000プログラム完成ですが…
- 内部クロックの誤差今回はPICマイコンの内部クロックを使用しましたが、内部クロックはある程度、誤差があります。正確に時間を計測する、つまり正確なクロック信号を得るには、時計用の32.768kHzの水晶発振子を使用したりする必要があります。ただ、タイマー程度でしたら内部クロックでも十分実用になると思います。
- プログラムの誤差作成したプログラムでは、正確には1秒の処理を繰り返すようにはなっていません。 while(1) 、 LATA5=1 , LATA5=0 、という処理はほんの少しだけ時間がかかります。 __delay_ms() でぴったり1秒時間待ちしていますので、それにプラスして、 while 、 LATA5 代入の処理分時間がかかっていることになります。ただ、これらの処理にかかる時間は非常に短いのでタイマー程度でしたらこのような実装で問題ありません。
更新履歴
日付内容2016.10.15新規投稿2018.11.24プログラムテンプレートをMPLABX IDE v5.10版に更新2019.6.20誤記訂正(OSCCONのクロックソースのtimer1使用設定の誤記訂正)2025.4.10誤植訂正&内容補足 古い順 一番投票が多い 本文中にフィードバック 全てのコメントを見るお尋ねします。 今回のを参考にして、LEDを4個を流れる点灯を作りたいと思っています RA5~RA2のピンを使用してそれぞれのピンの点灯時間、消灯時間をずらすプログラムを くめば大丈夫でしょか? プログラムは全くのド素人なので ご指導を、お願いします
RA5〜RA2に接続した4個のLEDを制御して光が流れるようにするには、LEDのONとOFFをプログラムにすれば問題ないです。 ただ、次のように制御すると、より光が移動しているように見えると思います。 (RA5とRA4のLEDが隣り合っていると仮定しています)
1. RA5のLEDをON 2. しばらく時間待ち 3. RA4のLEDを先にON 4. 少しだけ時間待ちをする(隣り合う2個のLEDが同時に点灯している時間を設けるところがポイント) 5. RA5のLEDをOFF 6. この後、1から5を隣り合うLEDで繰り返し
// RA5からRA4への光の移動 LATA5 = 1; __delay_ms(300); // 実際の見え方によってこの時間を調整します LATA4 = 1; __delay_ms(50); // 光が移動したように見えるようにオーバーラップの時間を作る LATA5 = 0;
// RA4からRA3への移動 // 上と同じように作成する __delay_ms(300); // 実際の見え方によってこの時間を調整します LATA3 = 1; __delay_ms(50); // 光が移動したように見えるようにオーバーラップの時間を作る LATA4 = 0;
ご連絡ありがとうございます。 現在、トラック野郎の模型を作製していまして 電飾をLEDで再現したくてたどり着いたのが『ツール・ラボ』さんのHPなのです
イメージとして ● ●○○○→○●●○○○→○○●●○→○○○●●(●がOFF,○がON) と流れる様にしたいのですがプログラム初心者な物でLED1つは何とか 理解できたのですが、とはいってもHPの例だけですが そこからはほぼ真白な状態です。。。。 お手数おかけしますが 全体のプログラムの組み方を押してて頂けますでしょうか。 宜しくお願い致します。
お世話になります。 下記の様なプログラムを考えてみました。 ON OFFのみですがどうでしょうか?
while(1) __delay_ms(500); // 500ms時間待ち LATA1 = 1; // LEDをON __delay_ms(2000); // 2000ms時間待ち LATA1 = 0; // LEDをOFF >
while(2) __delay_ms(1000); // 1000ms時間待ち LATA2 = 1; // LEDをON __delay_ms(1000); // 1000ms時間待ち LATA2 = 0; // LEDをOFF >
while(3) LATA3 = 1; // LEDをON __delay_ms(500); // 500ms時間待ち LATA 3= 0; // LEDをOFF __delay_ms(1000); // 1000ms時間待ち LATA 3= 1; // LEDをON __delay_ms(500); // 500ms時間待ち LATA = 3; // LEDをOFF >
while(4) LATA4 = 1; // LEDをON __delay_ms(1000); // 1000ms時間待ち LATA4 = 0; // LEDをOFF >
while(5) LATA5 = 1; // LEDをON __delay_ms(1500); //1 500ms時間待ち LATA = 5; // LEDをOFF __delay_ms(500); //500ms時間待ち >
( 1) ○○○○○ : 全てのLEDをOFFにする ( 2) ↓ : 1000ms待つ ( 3) ●○○○○ : LED1をONにする ( 4) ↓ : 1000ms待つ ( 5) ●●○○○ : LED2をONにする ( 6) ↓ : 1000ms待つ ( 7) ○●●○○ : LED1をOFF、LED3をONにする ( 8) ↓ : 1000ms待つ ( 9) ○○●●○ : LED2をOFF、LED4をONにする (10) ↓ : 1000ms待つ (11) ○○○●● : LED3をOFF、LED5をONにする (12) ↓ : 1000ms待つ (13) ○○○○● : LED4をOFFにする (14) ↓ : 1000ms待つ
( 7) ○●●○○ LATA1 = 0 ; LATA3 = 1 ;
( 9) ○○●●○ LATA2 = 0 ; LATA4 = 1 ;
(11) ○○○●● LATA3 = 0 ; LATA5 = 1 ;
while(1) // (1)の制御 LATA = 0 ; __delay_ms(1000) ;
// (3)の制御 LATA1 = 1 ; __delay_ms(1000) ;
// (5)の制御 LATA2 = 1 ; __delay_ms(1000) ;
// (7)の制御 LATA1 = 0 ; LATA3 = 1 ; __delay_ms(1000) ;
// (9)の制御 LATA2 = 0 ; LATA4 = 1 ; __delay_ms(1000) ;
// (11)の制御 LATA3 = 0 ; LATA5 = 1 ; __delay_ms(1000) ;
// (3)の制御 LATA4 = 0 ; __delay_ms(1000) ; >
LEDの動作を個別に指示をすのでなく、 一連のLEDの動作を while(1)で支持(流れ)を指定し囲むのですね
ご指摘のように while(1)で組みますと、とてもシンプルで わかりやすいです。点灯のパターンと速度もこの組み方ですと 応用が簡単に変更が可能ですね。
また、サンプルプログラムでは__delay_ms(1000)と書きましたが、変更するときに大変ですので、プログラム先頭で #define INTERVAL 1000 などと定義しておき、時間待ちは __delay_ms(INTERVAL); のようにした方がいいと思いますので、ご検討くださればと思います。
#define INTERVAL 1000の定義はとても便利です
第17回の「プログラムのコピペ」の記事で36、37行の//LEDを消灯する LATA1 =0;の 下に指示をすればよろしいのでしょうか?
たびたび済みません プログラムを確認したところ ピンの入力・出力設定ですがRA3 PINは入力のみLEDの接続はダメとなるのでしょうか LED5個の接続ですとRA3以外 RA0~RA5の5つに接続するプログラムを 組めば大丈夫でしょうか?
このシリーズ記事の範囲外の内容になってしまい申し訳ないのですが、RA0、RA1ピンを使用する場合、書き込み時の回路とLED制御時の回路を分ける必要があります。 方法はいくつかありますが、手取り早い方法としては、書き込み用のブレッドボード回路とLED制御用のブレッドポード回路を分ける、という方法があります。 手順としては、最初にPIC12F1822を書き込み用のブレッドボード回路にセットしてプログラムを書き込みます。 書き込み後、PIC12F1822をLED制御用のブレッドポード回路に移し替えて電池を接続して動作させます。
LED点灯のマイコン制御は 凄い技ですね とても幅広く活用が出来そうです。
無事に書き込み動作、出来ました。 まずはHPのサンプルを参考に2個の点灯の プログラムを組んでみました。
使用しているマイコンは「PIC18F14K22」で MPLAB X IDEのMCCで初期化コードを生成しています。
アルゴリズムは下記のようにしています。 ・タイマ0を使って1msecの周期割込みを発生 ・割込み処理内で1msecカウンタgMSecTimeをインクリメント ・mainループで1msecカウンタgMSecTimeを監視 ・1000msec経過したら、LED(IOポートRA2)をトグル
—以下、ソースコード————— uint16_t gMSecTime;
// タイマー割込み関数 void Timer0IntFunc(void) gMSecTime++; >
/* Main application */ void main(void) uint16_t t_led_tgl = 0; // time scale for LED uint16_t diff;
// Initialize the device SYSTEM_Initialize();
// Enable the Global Interrupts INTERRUPT_GlobalInterruptEnable();
// Enable the Peripheral Interrupts INTERRUPT_PeripheralInterruptEnable();
while (1) diff = gMSecTime – t_led_tgl; // 経過時間計算 if( diff >= 1000 )< // 1000msec経過?
// ログ出力 sprintf(line_buf, “%u,%u,%u\r\n”, gMSecTime, t_led_tgl, diff ); TxString(line_buf,strlen(line_buf));
t_led_tgl = gMSecTime; // 時間再計測 >
—以下、ログ出力—————————– 11195,10195,1000 12216,11216,1000 13056,12237,1074 ★この時、時間が短くなる。 14077,13077,1000 15098,14098,1000 16119,15119,1000
★の箇所で gMSecTime:13056 t_led_tgl:12237 なので、 diff:819 となり、ifの条件に引っかからないはずなのですが diff:1074 となっておりifの条件に引っかかってトグル処理を実行しています。 引き算の結果が時々おかしくなる!?
気になっているのは、 PIC18Fは8bitマイコンですが gMSecTimeが16bitなので 何か弊害があるのでは・・・ と思っています。
長々と申し訳ありません。 先人たちの考えをお聞きしたいので よろしくお願い致します。
グローバル変数 volatile uint16_t gMSecTime;
main関数のローカル変数 volatile uint16_t t_led_tgl = 0; // time scale for LED volatile uint16_t diff;
解決策ですが、下記の処理を割込み禁止にすることで正常動作しました。 diff = gMSecTime – t_led_tgl; // 経過時間計算
簡単にいうと gMSecTimeは16bit変数ですがマイコンは8bitなので 下位バイトと上位バイトを読み込む間に割込みが入ると計算が異常になる という事象が発生していたと思われます。
これまで組込みで32bitCPUしか扱った経験が有りませんでしたので 8bitや16bitCPUはこんなケアもしなきゃならないのか・・・ と少し戸惑いました。
Gemini_mk2- 950ms消灯状態にする
- 「LATA5 = 1;」でLEDを点灯させる
- そのまま50ms点灯状態にしておく
- 「LATA5 = 0;」でLEDを消灯させる
- (1)に戻る
内部クロックOSCCONの設定で「10」の「Timer1を使用」の表記がありますが、「10」とは1ビット目と0ビット目と言う意味で良いでしょうか、前の説明は設定値なので「01」では?と勘違いしそうな・・・ でも、とっても素人に優しく説明して頂き非常に感謝しています。 これからも是非サポート宜しくお願いします。
>>今回は1MHZ = 10000000Hzにしましたので、 この部分、桁が1つ多いのではないでしょうか。