STM32F103を使ってみる

RTCのアラーム機能


STM32 にはRTCが実装されています。
Arduino_STM32 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に要する処理時間は無関係になります。
#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_TOPIC      "stm32f103/ALARM" // You can change
//#define MQTT_WILL_MSG   "I am leaving..." // 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 putChar(char c) {
  char tmp[10];
  if ( c == 0x0a) {
    Serial.println();
  } else if (c == 0x0d) {
  
  } else if ( c < 0x20) {
    uint8_t cc = c;
    sprintf(tmp,"[0x%.2X]",cc);
    Serial.print(tmp);
  } else {
    Serial.print(c);
  }
}

//Wait for specific input string until timeout runs out
bool waitForString(char* input, int length, unsigned int timeout) {
  unsigned long end_time = millis() + timeout;
  char current_byte = 0;
  int index = 0;

   while (end_time >= millis()) {
  
      if(Serial2.available()) {
      
        //Read one byte from serial port
        current_byte = Serial2.read();
//        Serial.print(current_byte);
        putChar(current_byte);
        if (current_byte != -1) {
          //Search one character at a time
          if (current_byte == input[index]) {
            index++;
          
            //Found the string
            if (index == length) {            
              return true;
            }
          //Restart position of character to look for
          } else {
            index = 0;
          }
        }
      }
  }
  //Timed out
  return false;
}

void getResponse(int timeout){
  char c;
  bool flag = false;
  char tmp[10];
 
  long int time = millis() + timeout;
  while( time > millis()) {
    if (Serial2.available()) {
      flag = true;
      c = Serial2.read();
      if (c == 0x0d) {
         
      } else if (c == 0x0a) {
        Serial.println();
      } else if ( c < 0x20) {
        uint8_t cc = c;
        sprintf(tmp,"[0x%.2X]",cc);
        Serial.print(tmp);
      } else {
        Serial.print(c);
      }
    } // end if
  } // end while
  if (flag) Serial.println();
}

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 clearBuffer() {
  while (Serial2.available())
    Serial2.read();
//  Serial.println("");
}

int buildConnect(byte *buf, int keep_alive, char *client_id, char *will_topic, char *will_msg) {
  int rlen = 12;
  int pos = 14;

  int client_id_len = strlen(client_id);
//  Serial.println(client_id_len);
  buf[pos++] = 0x00;
  buf[pos++] = client_id_len;
  for(int i=0;i<client_id_len;i++) {
    buf[pos++] = client_id[i];
  }
  rlen = rlen + 2 + client_id_len;
 
  int will_topic_len = strlen(will_topic);
//  Serial.println(will_topic_len);
  int will_msg_len = strlen(will_msg);
//  Serial.println(will_msg_len);

  if (will_topic_len > 0 && will_msg_len > 0) {
    buf[pos++] = 0x00;
    buf[pos++] = will_topic_len;
    for(int i=0;i<will_topic_len;i++) {
      buf[pos++] = will_topic[i];
    }
    buf[pos++] = 0x00;
    buf[pos++] = will_msg_len;
    for(int i=0;i<will_msg_len;i++) {
      buf[pos++] = will_msg[i];
    }
    rlen = rlen + 2 + will_topic_len + 2 + will_msg_len;
  }

  buf[0] = 0x10;
  buf[1] = rlen;
  buf[2] = 0x00;
  buf[3] = 0x06;
  buf[4] = 'M';
  buf[5] = 'Q';
  buf[6] = 'I';
  buf[7] = 's';
  buf[8] = 'd';
  buf[9] = 'p';
  buf[10] = 0x03;
  buf[11] = 0x02;
  if (will_topic_len > 0 && will_msg_len > 0) buf[11] = 0x06;
  buf[12] = 0x00;
  buf[13] = keep_alive;
  return buf[1] + 2;
}

int buildPublish(byte *buf, char *topic, char *msg) {
  int tlen = strlen(topic);
  for(int i=0;i<tlen;i++) {
    buf[4+i] = topic[i];
  }
  int mlen = strlen(msg);
  for(int i=0;i<mlen;i++) {
    buf[4+tlen+i] = msg[i];
  }
  buf[0] = 0x30;
  buf[1] = tlen + mlen + 2;
  buf[2] = 0x00;
  buf[3] = tlen;
  return buf[1] + 2; 
}

int buildSubscribe(byte *buf, char *topic) {
  int tlen = strlen(topic);
  for(int i=0;i<tlen;i++) {
    buf[6+i] = topic[i];
  }
  buf[0] = 0x82;
  buf[1] = tlen + 5;
  buf[2] = 0x00;
  buf[3] = 0x01;
  buf[4] = 0x00;
  buf[5] = tlen;
  buf[tlen+6] = 0x00;
  return buf[1] + 2; 
}


void hexDump(byte *buf, int msize) {
  Serial.print("\nmsize=");
  Serial.println(msize);
  for(int i=0;i<msize;i++) {
    Serial.print(buf[i],HEX);
    Serial.print(" ");
  }
  Serial.println();
}

int getIpAddress(char *buf, int szbuf, int timeout) {
  Serial2.print("AT+CIPSTA?\r\n");
  int len=0;
  int pos=0;
  char line[128];
  
  long int time = millis();

  while( (time+timeout) > millis()) {
    while(Serial2.available())  {
      char c = Serial2.read(); // read the next character.
      if (c == 0x0d) {
        
      } else if (c == 0x0a) {
        Serial.print("Read=[");
        Serial.print(line);
        Serial.println("]");
        if (strncmp(line,"+CIPSTA:ip:",11) == 0) {
          strcpy(buf,&line[12]);
          len = strlen(buf) - 1;
          buf[len] = 0;
        }
        if (strcmp(line,"OK") == 0) return len;
        pos=0;
        line[pos]=0;
      } else {
        line[pos++]=c;
        line[pos]=0;
      }
    }
  }
  return len;
}

int getMacAddress(char *buf, int szbuf, int timeout) {
  Serial2.print("AT+CIPSTAMAC?\r\n");
  int len=0;
  int pos=0;
  char line[128];
  
  long int time = millis();

  while( (time+timeout) > millis()) {
    while(Serial2.available())  {
      char c = Serial2.read(); // read the next character.
      if (c == 0x0d) {
        
      } else if (c == 0x0a) {
        Serial.print("Read=[");
        Serial.print(line);
        Serial.println("]");
        if (strncmp(line,"+CIPSTAMAC:",11) == 0) {
          strcpy(buf,&line[12]);
          len = strlen(buf) - 1;
          buf[len] = 0;
        }
        if (strcmp(line,"OK") == 0) return len;
        pos=0;
        line[pos]=0;
      } else {
        line[pos++]=c;
        line[pos]=0;
      }
    }
  }
  return len;
}

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-%03d-%03d-%03d",(int)ip[0],(int)ip[1],(int)ip[2],(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 msg[75];
 
  if (alarmFlag) {
    for (int i=0;i<10;i++) {
      Serial.print("Attempting MQTT connection...");
      // Attempt to connect
      if (pubsubClient.connect(clientid,MQTT_TOPIC,0,0,MQTT_WILL_MSG)) break;
      delay(1000);
    }
    if (!pubsubClient.connected()) {
      errorDisplay("connect Fail");
    }
    Serial.println("connected");

    alarmFlag = 0;
    snprintf (msg, 75, "hello world!! I'm STM32F103");
    Serial.print("Publish message: ");
    Serial.println(msg);
    if (!pubsubClient.publish(MQTT_TOPIC, msg)) {
      errorDisplay("publish fail");
    }
    pubsubClient.disconnect();
  }
}

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


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

続く...