今回はスリープモードと、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 code
for(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 追加 続きの記事はこちらです。








コメント