STM32F103を使ってみる

RTClockライブラリ


STM32にはRTCが実装されています。
Maple CoreにはRTCを利用するためのRTClock ライブラリが含まれています。
RTClockライブラリについてはこ ちらに詳しい解説が有ります。たま吉さんに感謝です。
この解説書によると、以下の4つのアラーム割り込みがあります。
@ 秒割り込み 1秒ごとに割り込みが発生します
A オーバーフロー割り込み 起動から128000秒後に発生します
128000秒ってどういう意味でしょうか??
B RTCアラームグローバル割り込み 通常の割り込みです
C RTCアラーム特殊割り込み アラーム復帰時は再起動します
割り込み関数が実行されることはありません
STANDBY機能と一緒に使われる事が多いです

そこで、以下のスケッチで@BCの動作を確認してみました。
#include <RTClock.h>

RTClock rt (RTCSEL_LSE); // initialise
uint32 tt;

#define LED_RED PA0
#define LED_BLUE PA1
#define LED_GREEN PA2

void red () {
  digitalWrite(LED_RED,!digitalRead(LED_RED));
}

void blue () {
  digitalWrite(LED_BLUE,!digitalRead(LED_BLUE));
//  rt.attachAlarmInterrupt(blue,rt.getTime()+10);
  rt.setAlarmTime(rt.getTime()+10);
}

void green () {
  digitalWrite(LED_GREEN,!digitalRead(LED_GREEN));
}

void setup()
{
  pinMode(LED_RED, OUTPUT);
  pinMode(LED_BLUE, OUTPUT);
  pinMode(LED_GREEN, OUTPUT);
  digitalWrite(LED_RED,LOW);
  digitalWrite(LED_BLUE,LOW);
  digitalWrite(LED_GREEN,LOW);
 
  rt.attachSecondsInterrupt(red);
//  rt.attachAlarmInterrupt(blue, rt.getTime()+10);
  rt.attachAlarmInterrupt(blue);
  rt.createAlarm(green, rt.getTime()+60);
  rt.setAlarmTime(rt.getTime()+10);

}


void loop()
{
 
  if (rt.getTime()!=tt)
  {
    tt = rt.getTime();
    
    Serial.print("time is: ");
//    Serial.println(tt);
    Serial.print(rt.hour());
    Serial.print(":");
    Serial.print(rt.minute());
    Serial.print(":");
    Serial.println(rt.second());
  }
}

スケッチを実行すると1秒ごとにLED_BLUEが点滅し、10秒ごとにLED_BLUEとLED_GREENが点滅します。
RTCアラームグローバル割り込みとRTCアラーム特殊割り込みは同時には使えません。
後から指定した方が有効になります。



次にRTCアラームグローバル割り込みのタイマー精度を測定してみました。
タイマー割り込みが発生したとき、W5500イーサネットモジュールを使って、MQTTのPublishを行い、Linuxの時間と比較します。
アラーム発生間隔は3600秒としました。
アラーム発生と同時に次回のアラームを設定しているので、Publishに要する処理時間は無関係になります。
/*
 RTCアラームグローバル割り込みのタイマー精度を測定する
 
 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>
#include <RTClock.h>

#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/ALARM" // You can change
#define MQTT_WILL_TOPIC "stm32f103/ALARM" // You can change
#define MQTT_WILL_MSG   "" // You can change
#define RUNNING_LED     PA0

// 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);
long alarmDelay = 3600;
//long alarmDelay = 60;
int  alarmFlag = 0;
char clientid[30];

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

void running_func () {
  digitalWrite(RUNNING_LED,!digitalRead(RUNNING_LED));
}

void publish_func () {
  alarmFlag = 1;
  rt.setAlarmTime(rt.getTime()+alarmDelay);
}

void setup() {

  delay(1000);
  Serial.begin(9600);

  pinMode(RUNNING_LED, OUTPUT);
  digitalWrite(RUNNING_LED,LOW);

  // start Ethernet
#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);

  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);

  rt.attachSecondsInterrupt(running_func);
  rt.attachAlarmInterrupt(publish_func);
  rt.setAlarmTime(rt.getTime()+alarmDelay);
}

void loop() {
  char payload[75];
 
  if (alarmFlag) {
    for (int i=0;i<10;i++) {
      Serial.print("Attempting MQTT connection...");
      // Attempt to connect
      if (pubsubClient.connect(clientid,MQTT_WILL_TOPIC,0,0,MQTT_WILL_MSG)) break;
      delay(1000);
    }
    if (!pubsubClient.connected()) {
      errorDisplay("connect Fail");
    }
    Serial.println("connected");

    alarmFlag = 0;
    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");
    }
    pubsubClient.disconnect();
  }
}

24時間ほど動かすと、以下の様に22秒ほど時計が進むことが分かりました。
1時間で1秒の誤差なのでまあまあの精度です。


次回はSTANDBY中のタイマーの精度を紹介します。

続く...