今回はスリープモードと、ON、常夜灯、OFFの3つの信号が出せるようにプログラムを改良します。
赤外線リモコンがコピーできた前回の記事はこちらです。
Padaukのスリープ
Padaukマイコンのスリープには2種類あります。
stopexe
クロックの発振は継続しますが、システムクロックが停止しプログラムが停止します。レジスタやメモリ、カウンタ回路だけは電気が供給され続けます。
復帰要因
- IOピンの状態変化
- タイマー割り込み
- コンパレータの状態変化
タイマー割り込みで復帰することから、特定の時間だけスリープして電力消費量を抑える時に使えそうです。
スリープしてもプログラムカウンタが保持されるため、復帰時はスリープ命令の次にある命令から実行されます。
stopexeでスリープ中の消費電流は、uAオーダーとなっています。
stopsys
クロックやカウンタも、全てが停止します。しかし、レジスタやメモリには電源が供給され値を保持します。
復帰要因
- IOピンの状態変化
stopexeよりも機能が限定的なスリープです。クロックが停止しているので、復帰要因はIOピンの状態変化のみです。
しかし、レジスタやメモリの値を保持しているというのが特徴です。
スリープしてもプログラムカウンタが保持されるため、復帰時はスリープ命令の次にある命令から実行されます。このあたりはESP32のDeepSleepとは違いますね。
stopsysでのスリープ時の消費電流はstopexeより一桁低い、0.数uAオーダーとなっています。メモリの値が保持されていて、この値ってすごいですね。
プログラム
#include <pdk/device.h> #include <stdint.h> #include "auto_sysclock.h" #include "startup.h" #include "delay.h" #define IR_OUT 3 #define T2_BOUND 210 #define LED_PIN 5 #define LED_OFF() PA |= (1 << LED_PIN) #define LED_ON() PA &= ~(1 << LED_PIN) #define SW_PIN 4 #define SW_STATES() ( PA & (1 << SW_PIN) ) == 0 #define SW_PRESS 1 #define SW_RELEASE 0 #define T_TIME 375 //us #define IR_ON() TM2C = (uint8_t)(TM2C_CLK_IHRC | TM2C_MODE_PERIOD | TM2C_OUT_PA3) #define IR_OFF() TM2C = (uint8_t)(TM2C_CLK_IHRC | TM2C_MODE_PERIOD | TM2C_OUT_DISABLE) const uint8_t payload_6jo[] = { 0xff, 0x08, 0xa2, 0x88, 0x8a, 0x8a, 0x8a, 0xa2, 0xaa, 0xa8, 0x88, 0x8a, 0x28, 0xaa, 0x2a, 0xa2, 0x22, 0x88, 0x88, 0x80 }; const uint8_t payload_joyato[] = { 0xff, 0x08, 0xa2, 0x88, 0x8a, 0x8a, 0x8a, 0xa2, 0xaa, 0xa8, 0x88, 0x8a, 0x28, 0xa2, 0xaa, 0xa2, 0x88, 0x88, 0x88, 0x80 }; const uint8_t payload_power[] = { 0xff, 0x08, 0xa2, 0x88, 0x8a, 0x8a, 0x8a, 0xa2, 0xaa, 0xa8, 0x88, 0x8a, 0x28, 0x8a, 0xaa, 0xa8, 0x88, 0x88, 0x88, 0x80 }; const uint8_t* payload[] = { payload_6jo, payload_joyato, payload_power}; const uint8_t payload_length[] = { sizeof(payload_6jo), sizeof(payload_joyato), sizeof(payload_power)}; void transmit( const uint8_t* payload, const uint8_t length) { const uint8_t mask[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; //Transmit the codefor(uint8_t i=0 ; i<length ; i++) { for(uint8_t j=0; j<8 ; j++) { if( ( payload[i] & mask[j] ) != 0 ) { IR_ON(); }else{ IR_OFF(); } _delay_us(T_TIME); __wdreset(); //kick WDT } } } void main() { //Timer2 PAC |= ( 1 << IR_OUT); TM2C = (uint8_t)(TM2C_CLK_IHRC | TM2C_MODE_PERIOD | TM2C_OUT_DISABLE); TM2S = (uint8_t)(TM2S_SCALE_NONE | TM2S_PRESCALE_NONE | TM2S_PWM_RES_8BIT); TM2B = T2_BOUND; //for LED PAC |= ( 1 << LED_PIN); LED_OFF(); //for SWtich PAC &= ~(1 << SW_PIN); PAPH |= ( 1 << SW_PIN); //pullup PADIER = 0; PADIER |= ( 1 << SW_PIN); //input PBDIER = 0; //sleep at the boot if( SW_STATES() != SW_PRESS ) { _delay_ms(40); __stopsys(); } while(1) { for( int i=0 ; i<3 ; i++) { LED_ON(); transmit( payload[i], payload_length[i]); LED_OFF(); //Waiting for the release of the button _delay_ms(40); while(SW_STATES() == SW_PRESS) { _delay_ms(20); __wdreset(); //kick WDT } _delay_ms(40); __stopsys(); } } } // Startup code - Setup/calibrate system clock unsigned char STARTUP_FUNCTION(void) { // Initialize the system clock (CLKMD register) with the IHRC, ILRC, or EOSC clock source and correct divider. // The AUTO_INIT_SYSCLOCK() macro uses F_CPU (defined in the Makefile) to choose the IHRC or ILRC clock source and divider. // Alternatively, replace this with the more specific PDK_SET_SYSCLOCK(...) macro from pdk/sysclock.h PDK_USE_FACTORY_IHRCR_16MHZ(); PDK_USE_FACTORY_BGTR(); AUTO_INIT_SYSCLOCK(); CLKMD &= ~CLKMD_ENABLE_WATCHDOG; // Disenable WDT // Insert placeholder code to tell EasyPdkProg to calibrate the IHRC or ILRC internal oscillator. // The AUTO_CALIBRATE_SYSCLOCK(...) macro uses F_CPU (defined in the Makefile) to choose the IHRC or ILRC oscillator. // Alternatively, replace this with the more specific EASY_PDK_CALIBRATE_IHRC(...) or EASY_PDK_CALIBRATE_ILRC(...) macro from easy-pdk/calibrate.h AUTO_CALIBRATE_SYSCLOCK(2800); return 0; // Return 0 to inform SDCC to continue with normal initialization. }
スリープには消費電流のより低いstopsysを使います。
スリープからの復帰はIOピンの状態変化となります。スリープから復帰するピンを指定するには、PADIERレジスタのピン番号に1をセットします。指定以外のピンの状態変化で復帰しないように、そのほかのピン番号には0でクリアしておきます。(62,63行目)
PFS122の6ピンマイコンを今回は使います。6ピンマイコンにはポートBはありませんが、PFS122にはポートBも存在するモデルがあり、6ピンマイコンでもシリコンチップ上にはポートBが存在します。このためPBDIERレジスタも0でクリアしておかないといけません。(64行)
電池をセットしてマイコンが起動しても信号が出ないように、起動時でスイッチが押されていなかったら、すぐにスリープするようにしてあります。(67〜70行目)
スイッチが押されると73行目からプログラムが実行されます。
78行目でデータが送信され、スイッチが離されるまで待ってスリープします。(82〜90行目)
スイッチが押される度に、電源ON、常夜灯、電源OFFの3つの信号を順番に繰り返します。
プログラムの実行
スリープ時の消費電流
スリープ時に消費電流を測ってみました。0.5uAです。メモリの状態を保持したままのスリープで0.5uAですからすごいですね。
シーリングライトをコントロール
スイッチを1回押すとシーリングライトがONしました。
2回目は常夜灯に切り替わります。
3回目はOFFです。
目的通り、ON、常夜灯、OFFを繰り返すようになりました。
さて次回は
次回は、スイッチをタッチスイッチに変更して、どこを触ってもスイッチが入るようにしたいと思います。
2025.4.19 追加 続きの記事はこちらです。
コメント