PR 記事には広告が含まれています
スポンサーリンク
Translate

コピーリモコン 超低消費電力スリープと3種類の信号が出せるようになりました

スポンサーリンク

今回はスリープモードと、ON、常夜灯、OFFの3つの信号が出せるようにプログラムを改良します。

赤外線リモコンがコピーできた前回の記事はこちらです。

スポンサーリンク

Padaukのスリープ

Padaukマイコンのスリープには2種類あります。

stopexe

クロックの発振は継続しますが、システムクロックが停止しプログラムが停止します。レジスタやメモリ、カウンタ回路だけは電気が供給され続けます。

復帰要因

  • IOピンの状態変化
  • タイマー割り込み
  • コンパレータの状態変化

タイマー割り込みで復帰することから、特定の時間だけスリープして電力消費量を抑える時に使えそうです。

スリープしてもプログラムカウンタが保持されるため、復帰時はスリープ命令の次にある命令から実行されます。

stopexeでスリープ中の消費電流は、uAオーダーとなっています。

stopsys

クロックやカウンタも、全てが停止します。しかし、レジスタやメモリには電源が供給され値を保持します。

復帰要因

  • IOピンの状態変化

stopexeよりも機能が限定的なスリープです。クロックが停止しているので、復帰要因はIOピンの状態変化のみです。

しかし、レジスタやメモリの値を保持しているというのが特徴です。

スリープしてもプログラムカウンタが保持されるため、復帰時はスリープ命令の次にある命令から実行されます。このあたりはESP32のDeepSleepとは違いますね。

stopsysの消費電流

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 追加 続きの記事はこちらです。

Padaukマイコンの開発には、世界最強のSuper Easy PDK Programmerを使っています。