PICに再び挑戦しているものの、再挑戦の動機となったI2Cのスレーブ化は問題解決ができず行き詰まり中。
気晴らし?に、PICによるRaspberryPiの間欠動作を行いました。
以前、RaspberryPi Zeroの間欠動作をRaspberryPi Picoでした事がありますが、Picoを使った理由は技能的にMicroPythonしか出来なかったから。今ならPICでもいけるんじゃないかと。
行ったこととして、PIC単体で可能なウォッチドックタイマーとスリープを使って、なんちゃって間欠動作回路を組みました。
この回路で、RaspberryPi Zero Wを、大体30分に1度動作させる間欠動作させた結果、1900mAのニッケル水素電池3本で、165時間(6.9日)稼働しました。
過去、市販の間欠動作デバイスであるEveryUSBで同じことをやった時の稼働時間は119時間でした。今回、RaspberryPi OSをDesktopではなくLiteに入れ替えてあるので、まったくの同条件ではなく、起動にかかっている消費電力が違うのかもしれませんが、それでもなかなか長生きしました。
ちゃんとやろうとすれば、タイマー機能やアラーム機能が付いているリアルタイムクロックICを使う方が、省電力だし正確だし良いと思いますが、、、まぁ、PICは安いし、他の機能を付与できるし、使う場面もあるかも?
■ 回路
【パーツ】
・PIC:PIC12F1840
・Nch MOSFET:2SK4017
・抵抗 100Ω、100kΩ
・電気二重層コンデンサ 5F
(・RaspberryPi Zero W)
PIC12F1840を使ったのは、手持ちがそれだったというだけで特に選んだわけではありません。
要件としては、ウォッチドックタイマーとEEPROMがあれば良いです。
この回路、パスコンしないと、少し電池の電圧が下がっただけでRaspberryPi ZeroWが正常に動作しません。LEDを見る限り、起動途中で落ちてる感じ。電池直結だと正常に起動するので、MOSFETの影響と思いますが、理由はよくわかりません。
最初、100uFのコンデンサでパスコンしたのですが、ちょこっとしか効果がなく、手元にあった5Fのスーパーキャパシタを使うことで、長いこと起動するようにできました。Zeroの起動時の出力に耐えれば良いので、多分ここまで容量が大きくなくてもいいんじゃないかな。
ただ、ログができなくなった時の電池の電圧はまだ3.5V(1本1.16V)程で、その状態で電池と直結させると、まだ起動するので、もっと上手いやり方があるような気がします。
昇圧回路を挟んだ方が、電圧変換時に電力ロスするにしても、使える容量が増えて、長生きするかも。
■ 電池
1900mAのスタンダードのエネループです。新品ではありません。
■ ラズパイ設定
Zeroは、起動後サーバーにアクセスしログを残し、シャットダウンするようにします。OSはRaspberryPi OS Liteをインストールした直後です。
具体的な設定方法は、以前の記事参照。
■ プログラム
段階的にテストしていったので、そのまま記載します。
まず負荷にLEDを接続して、1s毎にLチカさせるテスト。
参考サイト:
PIC16F1829でウォッチドックタイマーとスリープ機能を利用して低消費電力なLチカをさせてみた
https://asukiaaa.blogspot.com/2020/06/pic16f1829l.html
なお、configの説明は、下記サイトのコピペさせてもらってます。
PIC12F1840 i2cの設定とPWMの設定 (XC8編)
https://karappi.mydns.jp/kousaku/pic12f1840/pic12f1840_c.html
#include <xc.h> //config1 #pragma config FOSC = INTOSC //Oscillator Selection 内部クロック使用 #pragma config WDTE = ON //Watchdog Timer off #pragma config PWRTE = ON //Power up timer on(スイッチを入れた直後電源が安定するまで待つ) #pragma config MCLRE = OFF //MCLR PIN off (ハードウェアリセットのピンの用途を無しにしてdigital pinとして使えるようにする) #pragma config CP = OFF //CODE PROTECT OFF プログラムの読み出しのプロテクトoff #pragma config CPD = OFF //Data Protect OFF データ領域の読み出しのプロテクトoff #pragma config BOREN = ON // Brownout on (もし電源が不安定のとき一時停止する) #pragma config CLKOUTEN = OFF // CLOCK 信号の外部出力をOFF #pragma config IESO = OFF // 2段階クロックoff(立ち上がりで即安定する内部クロックを使いその後外部クロックに切り替える設定) #pragma config FCMEN = OFF //Fale-safe Clock Monitor off(外部クロックが壊れたとき内部クロックに切り替える設定) //config2 #pragma config WRT = OFF //Write Protection プログラム領域の書き込み禁止 off #pragma config PLLEN = OFF //クロック逓倍off (onにすると発生したクロックが4倍になる) #pragma config STVREN = ON //スタック領域をオーバーしたらリセットする(offは何もしない) #pragma config BORV = LO //Brounout電圧設定 (HI:電源電圧がちょっとさがるとリセット、LO:うんと下がるとリセット) #pragma config LVP = OFF //プログラム書き込み電圧(ON;低電圧書き込み有効 マイコンの電圧で書き込める off:低電圧書き込み無効 PICKIT3のときoffに) #define _XTAL_FREQ 31250 void main(void) { OSCCON = 0b00010000;//オシレータ31.25kHz ANSELA = 0x00;//全ピン デジタルI/Oピン TRISA = 0x00;//全てのピンを出力 PORTA = 0x00;//全てのピンを初期化(LOW(0)に設定) WDTCONbits.SWDTEN = 1; // WDTを有効 WDTCONbits.WDTPS = 0b01010;// 1:32768 ( インターバル 1 s typ.) while(1){ RA0 = 1; __delay_ms(100); RA0 = 0; SLEEP(); } return; }
ウォッチドックタイマーで可能なインターバルは256秒までなので、EEPROMにカウントを記録して、インターバル時間をのばします。
テストとして、1秒のカウントを4回して4秒待機、1秒のLED点灯を、5秒毎に繰り返すプログラムにしました。
参考サイト:
PICマイコン入門 第14回 EEPROM(2) 〜タイマー設定値保存機能を追加する〜
https://tool-lab.com/pic-app-14/
電子工作室 ウォッチドッグタイマ
http://www.picfun.com/pic18/pic18xx13.html
#include <xc.h> //config1 #pragma config FOSC = INTOSC //Oscillator Selection:内部クロック使用 #pragma config WDTE = ON //Watchdog timer #pragma config PWRTE = ON //Power up timer(スイッチを入れた直後電源が安定するまで待つ) #pragma config MCLRE = OFF //MCLR PIN off (ハードウェアリセットのピンの用途を無しにしてdigital pinとして使えるようにする) #pragma config CP = OFF //CODE PROTECT (プログラムの読み出しのプロテクト) #pragma config CPD = OFF //Data Protect (データ領域の読み出しのプロテクト) #pragma config BOREN = ON // Brownout (もし電源が不安定のときは一時停止) #pragma config CLKOUTEN = OFF // CLOCK(信号の外部出力) #pragma config IESO = OFF // 2段階クロック(立ち上がりで即安定する内部クロックを使いその後外部クロックに切り替える) #pragma config FCMEN = OFF //Fale-safe Clock Monitor(外部クロックが壊れたとき内部クロックに切り替える) //config2 #pragma config WRT = OFF //Write Protection(プログラム領域の書き込み):禁止 #pragma config PLLEN = OFF //クロック逓倍off (onにすると発生したクロックが4倍になる) #pragma config STVREN = ON //スタック領域をオーバーしたらリセットする(offは何もしない) #pragma config BORV = LO //Brounout電圧設定 (HI:電源電圧がちょっとさがるとリセット、LO:うんと下がるとリセット) #pragma config LVP = OFF //プログラム書き込み電圧(ON;低電圧書き込み有効 マイコンの電圧で書き込める off:低電圧書き込み無効 PICKIT3のときoffに) #define _XTAL_FREQ 31250 void main(void) { OSCCON = 0b00010000;//オシレータ31.25kHz ANSELA = 0x00;//全ピン デジタルI/Oピン TRISA = 0x00;//全てのピンを出力 PORTA = 0x00;//全てのピンを初期化(LOW(0)に設定) WDTCONbits.SWDTEN = 1; // WDTを有効 WDTCONbits.WDTPS = 0b01010;// 1:32768 (インターバル 1s typ.) unsigned char count; while(1){ count = eeprom_read(0); count++; if(count>5){ RA0 = 1; __delay_ms(1000); eeprom_write(0, 0); RA0 = 0; }else{ eeprom_write(0, count); } SLEEP(); } return; }
やっと本題の大体30分に1回、1分間通電する間欠起動タイマー
#include <xc.h> //config1 #pragma config FOSC = INTOSC //Oscillator Selection:内部クロック使用 #pragma config WDTE = ON //Watchdog timer #pragma config PWRTE = ON //Power up timer(スイッチを入れた直後電源が安定するまで待つ) #pragma config MCLRE = OFF //MCLR PIN off (ハードウェアリセットのピンの用途を無しにしてdigital pinとして使えるようにする) #pragma config CP = OFF //CODE PROTECT (プログラムの読み出しのプロテクト) #pragma config CPD = OFF //Data Protect (データ領域の読み出しのプロテクト) #pragma config BOREN = ON // Brownout (もし電源が不安定のときは一時停止) #pragma config CLKOUTEN = OFF // CLOCK(信号の外部出力) #pragma config IESO = OFF // 2段階クロック(立ち上がりで即安定する内部クロックを使いその後外部クロックに切り替える) #pragma config FCMEN = OFF //Fale-safe Clock Monitor(外部クロックが壊れたとき内部クロックに切り替える) //config2 #pragma config WRT = OFF //Write Protection(プログラム領域の書き込み):禁止 #pragma config PLLEN = OFF //クロック逓倍off (onにすると発生したクロックが4倍になる) #pragma config STVREN = ON //スタック領域をオーバーしたらリセットする(offは何もしない) #pragma config BORV = LO //Brounout電圧設定 (HI:電源電圧がちょっとさがるとリセット、LO:うんと下がるとリセット) #pragma config LVP = OFF //プログラム書き込み電圧(ON;低電圧書き込み有効 マイコンの電圧で書き込める off:低電圧書き込み無効 PICKIT3のときoffに) #define _XTAL_FREQ 31250 void main(void) { OSCCON = 0b00010000;//オシレータ31.25kHz ANSELA = 0x00;//全ピン デジタルI/Oピン TRISA = 0x00;//全てのピンを出力 PORTA = 0x00;//全てのピンを初期化(LOW(0)に設定) WDTCONbits.SWDTEN = 1; // WDTを有効 WDTCONbits.WDTPS = 0b10010;// 1:8388608 (インターバル 256s typ.) unsigned char count,i; while(1){ count = eeprom_read(0); count++; if(count>7){ RA0 = 1; for(i=0;i<60;i++){ //60秒待機 __delay_ms(1000); CLRWDT(); } eeprom_write(0, 0); RA0 = 0; }else{ eeprom_write(0, count); } SLEEP(); } return; }
昔はなぜこの程度のことができなかったのだろうと思いますが^^;、、、直接的にやりたい事の情報が得られるようになった恩恵ですね!
冒頭の稼働時間の実験の時は、前回条件と合わせるため、30分毎に3分間通電する設定にしました。
せっかくPICを使うのであれば、電圧取ったり、ラズパイの死活確認とか色々した方が、使う意義がある気がしますが、自分の能力的な限界を感じるので、ここまでにします。
最後にインターバル時間に影響するクロック周波数とウォッチドックタイマのカウントのレジスタをメモしておきます(主に自分用に)。
OSCCON = 0b00010000;
赤の部分
000x = 31 kHz LF
0010 = 31.25 kHz MF
0011 = 31.25 kHz HF(1)
0100 = 62.5 kHz MF
0101 = 125 kHz MF
0110 = 250 kHz MF
0111 = 500 kHz MF (リセット時の既定値)
1000 = 125 kHz HF(1)
1001 = 250 kHz HF(1)
1010 = 500 kHz HF(1)
1011 = 1 MHz HF
1100 = 2 MHz HF
1101 = 4 MHz HF
1110 = 8 MHz または 32 MHz HF(セクション 5.2.2.1「HFINTOSC」参照)
1111 = 16 MHz HF
ウォッチドッグ タイマ制御レジスタ
WDTCONbits.WDTPS = 0b10010;
00000 = 1:32 ( インターバル 1 ms typ.)
00001 = 1:64 ( インターバル 2 ms typ.)
00010 = 1:128 ( インターバル 4 ms typ.)
00011 = 1:256 ( インターバル 8 ms typ.)
00100 = 1:512 ( インターバル 16 ms typ.)
00101 = 1:1024 ( インターバル 32 ms typ.)
00110 = 1:2048 ( インターバル 64 ms typ.)
00111 = 1:4096 ( インターバル 128 ms typ.)
01000 = 1:8192 ( インターバル 256 ms typ.)
01001 = 1:16384 ( インターバル 512 ms typ.)
01010 = 1:32768 ( インターバル 1 s typ.)
01011 = 1:65536 ( インターバル 2 s typ.) デフォルト
01100 = 1:131072 (217) ( インターバル 4 s typ.)
01101 = 1:262144 (218) ( インターバル 8 s typ.)
01110 = 1:524288 (219) ( インターバル 16 s typ.)
01111 = 1:1048576 (220) ( インターバル 32 s typ.)
10000 = 1:2097152 (221) ( インターバル 64 s typ.)
10001 = 1:4194304 (222) ( インターバル 128 s typ.)
10010 = 1:8388608 (223) ( インターバル 256 s typ.)