STM32F103を使ってみる

STANDBY中のタイマー精度


STM32にはいくつかの省電力機能が有ります。
こ ちらこちらに詳しい解説が有りま す。
こ ちらの解説書(5.2.3章)に各モードの消費電流が記載されています。
SLEEP mode(72Mhz)
5.5から14.4mA
STOP mode
14から24μA
STANDBY mode
2μA

こちらにSTOP mode/STANDBY modeに移行するためのライブラリが公開されています。
そこで消費電流が一番小さいSTANDBY mode時のタイマー精度を測定してみました。
なお、STANDBY modeに移行するには以下の関数を使用しますが、指定できる秒数の精度が8ビット(最大255秒)なので
この関数は使わずに、直接createAlarm()を使います。

void sleepAndWakeUp(SleepMode mode, RTClock *rt, uint8_t seconds) {
  rt->createAlarm(&noop, rt->getTime() + seconds);
  goToSleep(mode);
}

STANDBY modeからの復帰方法についてはこち らに解説が公開されています。
いずれの方法で復帰してもスケッチの先頭から再開しますが、このときシリアルモニターの初期化がうまく行われず、
シリアルモニターには何も表示されません。
そこで、再開したことが分かるように、W5500イーサネットモジュールを使って、MQTTのPublishを行います。
Sleep時間は3600秒とし、起動直後からPublish実行までの処理時間(大体1秒)を補正しています。
/*
 STANDBY mode時のタイマー精度を測定する
 
 PIN Connections (Using STM32F103):

   W5X00  -  STM32F103
   ---------------------
   VCC    -  3.3V
   GND    -  GND
   SS     -  Pin PA4
   SCLK   -  Pin PA5
   MISO   -  Pin PA6
   MOSI   -  Pin PA7
   RST    -  PullUp

*/
#include <SPI.h>
#include <Ethernet_STM.h>   // https://github.com/rogerclarkmelbourne/Arduino_STM32
#include <PubSubClient.h> // https://github.com/knolleary/pubsubclient
#include <STM32Sleep.h>     // https://github.com/chacal/stm32sleep
#include <RTClock.h>        // https://github.com/rogerclarkmelbourne/Arduino_STM32/

#define MQTT_SERVER     "192.168.10.40"
//#define MQTT_SERVER     "broker.hivemq.com"
//#define MQTT_SERVER     "iot.eclipse.org"
#define MQTT_PORT       1883
#define MQTT_KEEP_ALIVE 60
#define MQTT_PUB_TOPIC  "stm32f103/STANDBY" // You can change
#define MQTT_WILL_TOPIC "stm32f103/STANDBY" // You can change
#define MQTT_WILL_MSG   "" // You can change
#define STOP_BUTTON     PB0 // 0: Disable STOP_BUTTON
#define RUNNING_LED     PB1 // 0: Disable RUNNING_LED
#define ALARM_SECOND    3600

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
#if defined(WIZ550io_WITH_MACADDRESS) // Use assigned MAC address of WIZ550io
;
#else
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
#endif 

EthernetClient ethClient;
PubSubClient pubsubClient(ethClient);

RTClock rt(RTCSEL_LSE);

static void noop() {};

void errorDisplay(char* buff) {
  int stat = 0;
  Serial.print("Error:");
  Serial.println(buff);
  while(1) {
    if (RUNNING_LED) {
      digitalWrite(RUNNING_LED,stat);
      stat = !stat;
      delay(100);
    }
  }
}

void setup() {
  unsigned long Time1;
  unsigned long Time2;

  Time1 = millis();
  delay(1000);
  Serial.begin(9600);

  pinMode(STOP_BUTTON,INPUT);
  int wk = digitalRead(STOP_BUTTON);
  if (wk) {
    while(1) {
      Serial.println("HALT!!");
      delay(1000);
    }
  }

  if (RUNNING_LED) {
    pinMode(RUNNING_LED,OUTPUT);
    digitalWrite(RUNNING_LED,LOW);
  }

  // start Ethernet and UDP
#if defined(WIZ550io_WITH_MACADDRESS)
  if (Ethernet.begin() == 0) {
#else
  if (Ethernet.begin(mac) == 0) {
#endif 
    errorDisplay("Failed to configure Ethernet using DHCP");
  }

  Serial.print("My IP: ");
  Serial.println(Ethernet.localIP());
  Serial.print("Netmask: ");
  Serial.println(Ethernet.subnetMask());
  Serial.print("GW IP: ");
  Serial.println(Ethernet.gatewayIP());
  Serial.print("DNS IP: ");
  Serial.println(Ethernet.dnsServerIP());

  pubsubClient.setServer(MQTT_SERVER, MQTT_PORT);

  char clientid[30];
  IPAddress ip = Ethernet.localIP();
  Serial.print(ip[0]);
  Serial.print(".");
  Serial.print(ip[1]);
  Serial.print(".");
  Serial.print(ip[2]);
  Serial.print(".");
  Serial.println(ip[3]);
  sprintf(clientid,"STM32-%03d",(int)ip[3]);
  Serial.print("clientid=");
  Serial.println(clientid);
 
  Serial.print("Attempting MQTT connection...");
  // Attempt to connect
  if (!pubsubClient.connect(clientid,MQTT_WILL_TOPIC,0,0,MQTT_WILL_MSG)) {
    errorDisplay("connect Fail");
  }
  Serial.println("connected");

  char payload[50];
  snprintf (payload, 75, "hello world!! I'm STM32F103");
  Serial.print("Publish message: ");
  Serial.println(payload);
  if (!pubsubClient.publish(MQTT_PUB_TOPIC, payload)) {
    errorDisplay("publish fail");
  }

  Time2 = millis();
  Serial.print("Timee2 - Time1=");
  Serial.println(Time2-Time1);
  int procTime = (Time2-Time1)/1000;
  Serial.print("procTime=");
  Serial.println(procTime);
 
  rt.createAlarm(&noop, rt.getTime() + ALARM_SECOND - procTime);
  goToSleep(STANDBY);
}

void loop() { }  // This is never run

しばらく動かすと、以下の様に9時8分16秒に始めた通信が、24時間後には9時9分24秒になっています。
24時間で1分ほど時計が進むことが分かりました。
STANDBY中のタイマーの精度はあまりよくないです。


続く...