STM32F103C8T6-arduino    
              本来のSTM32F103C8T6はこちらです

 Blue Pill(STM32F103C8T6)は興味深いモジュールですが、mbedは私にとって荷が重すぎます。自分が書いたコードがエラーになるのはわかりますが、利用したライブラリーでエラーストップすると回復の方法がありません。幸いarduinoで動きますから、この項ではarduino-IDEに絞って調べてみたいと思います。

 ここでは、arduino-IDEにSTM32F103C8T6をインストールしたものを使い、書き込みにはSTLink V2で行いますが、事前準備は別項STM32F103C8T6の設定によります。

 ○ Arduino-IDEのメモ  2019.07.04
 1 最初の実験用ボード  2019.06.29
 2 Lチカのテスト 2019.06.29 
 3 LCD表示 2019.06.29  
 4 シリアル通信テスト 2019.06.29   
 5 内蔵RTCのテスト 2019.06.30   
 6 気圧センサーbmp280のテスト 2019.07.05   
 7 SDカード記録のテスト 2019.07.06   
 8 MCUの内容表示(stm32flash.exeの実行) 2019.07.14   

 21 第2の実験ボード 2019.07.07   
 22 STM32duino実験ボード 2019.07.19   

 31 久しぶりSTM32duino 2021.07.23   






  Arduino-IDEのメモ   2019.07.04
ポートの指定: PA2、PB4のように書く。 PB_4、pb4 などは使えない。
ポートの設定:特に必要がない限り次の設定が良い
       serial1 PA9,PA10 // Tx,Rx   serial2 PA2,PA3 // Tx,Rx
       I2C PB7, PB6 // sda, scl 
       LCD PB10,PB11,PB12,PB13,PB14,PB15  // rs, e, d4-d7  
          LiquidCrystal lcd(PB10,PB11,PB12,PB13,PB14,PB15);//rs, en, d4, d5, d6, d7
       SD  PA7,PA6,PA5,PA4, "sd" //mosi,miso,sck,cs
デジタル入力ポート:pinMode(PBx, INPUT_PULLUP);で設定してスイッチを検知する digitalRead(PBx) が
       使える端子と使えない端子があるようだ。PB4,PB5,PA9,PA10,PA11,PA15は○、PB3,PA12は×。
シリアル入力 Serial2.parseInt():カンマ区切りの数字を数値として(文字列でなく)読み取れる。
       時刻入力に便利だった。
STM32F103C8T6端子配置図
        
コンソール・エラーペインの文字色:赤が見にくいので白文字に変える。Arduino/lib/theme/theme.txt の
       32行を console.error.color = #FFFFFF に変更する。
    






 1 最初の実験用ボード    2019.06.29
 以前に使ったボードを流用しています。
電源は、5V用LCDのために5Vを独立で供給しています。5V入力は Blue Pill の 5V端子とLCDのVccにつないでいます。(LCDは5Vですが、信号線は3.3Vでドライブすることになります。)
書き込み装置のSTLink-v2は電源のない接続をしています。USBコネクタは使用しません。
ボードの使用端子は
  USART2 - PA2:TX PA3:RX
  LCD - PB10:rs PB11:en PB12:D4 PB13:D5 PB14:D6 PB15:D7 
    付 LCDコネクタ 1:GND(黒) 2:Vcc(橙) 3:RS(茶) 4:EN(黄) 5:D4(緑) 6:D5(青) 7:D6(灰) 8:D7(白)
  I2C1 - PB6:SCL PB7:SDA (予定)
です。
リセットスイッチが押しにくいので左下に別途設けました。


Arduino-IDEの設定は、メニュー→ツールから
 →ボード で     Generic STM32F103C series を、
 →variant… で   64k Flash を、
 →Upload Method:で "STLink" を選びます。
     




 2 Lチカのテスト  2019.06.29
 Arduino-IDEのスケッチ例からblinkを選びました。ファイル名は Lchika_test01 としました。
スケッチです。
//  Blink
#define LED_BUITIN PC_13    // LEDはPC_13なので追加した。

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);                       // wait for a second
}
Arduino-IDEの→ボタン(コンパイルと書き込み)を押すことで問題なく書き込めました。boot sw は0,0のままです。
     




 3 LCD表示   2019.06.29
 スケッチ例LCDを利用。
'PB_10' was not declared in this scope
'PB_11' was not declared in this scope
が出たが、ツール→エンコーディングを修正 を実行してスケッチを書き直したところ問題なくコンパイルできた。

スケッチ:
// LCDtest01

#include <LiquidCrystal.h>
LiquidCrystal lcd(PB10,PB11,PB12,PB13,PB14,PB15);//rs, en, d4, d5, d6, d7

void setup() {
  lcd.begin(16, 2);
  lcd.print("hello, world! 2");
}

void loop() {
  lcd.setCursor(0, 1);
  lcd.print(millis() / 1000);
}

問題なく表示できました。
    





 4 シリアル通信テスト   2019.06.29
 ボードの端子は、USART2 PA2:TX PA3:RX を使用しています。PCとの接続は cp2102使用のUSB-シリアル変換器を使っています。
単純な表示と、キーボー入力を受けてアスキーコードが+1の文字を返すだけです。
//"serial_test01"

void setup(){
  Serial2.begin(9600);
  Serial2.println("Hello Serial 2");
}

void loop() {
    Serial2.println("Hello World!");
    while(1) {
//      Serial2.println();
      Serial2.write(Serial2.read() + 1);
    }
}
     



 5 内蔵RTCのテスト   2019.06.30
 内蔵RTCがあると知りテストしました。
ハードウエアの追加が必要です。
 1 ボードの3V3端子からVB端子(RTC用電源端子)へショットキーダイオードとそれと並列に0.1uFコンデンサをつなぎます。電源offのときにRTC用電池から3V3本体へ電流が流れるのを防ぐためでしょう。0.1uは雑音防止?
 2 RTC用電源電池の+からショットキーダイオードを経由してVB端子につなぎます。
 3 RTC用電源電池の−とGNDをつなぎます。
なお、電池は臨時に単4電池2本にしています。

スケッチはwebを調べたところ たま吉さん のサイトからスケッチをいただきました。無事実行できたのでシリアルだけでなくLCDに時刻表示を追加しました。

スケッチです。
//RTCtest01

#include <RTClock.h>
#include <time.h>
#include <LiquidCrystal.h>

RTClock rtc(RTCSEL_LSE);
LiquidCrystal lcd(PB10,PB11,PB12,PB13,PB14,PB15);//rs, en, d4, d5, d6, d7
char *wday[] = {"Mon","Tue","Wed","Thr","Fri","Sat","Sun"};

// セットアップ
void setup() {
  struct tm_t t;  
  Serial2.begin(115200);
  while (!Serial2) delay(100);

  t.year    = 2019-1970;  // 年   [1970からの経過年数]
  t.month   = 6;          // 月   [0-11] 0から始まることに注意
  t.day     = 30;         // 日   [1-31]
  t.weekday = 6;          // 曜日 [0-6] 0:月曜 〜 6:日曜
  t.hour    = 20;         // 時   [0-23]
  t.minute  = 5;          // 分   [0-59]  
  t.second  = 0;          // 秒   [0-59] 
//  rtc.setTime(t);         // 時刻の設定    ←時刻を設定するときはコメントアウトしない

  lcd.begin(16, 2);
  lcd.print("hello, world! 2");
}

void loop() {
   char str[64];  
   struct tm_t st;   
   rtc.getTime(st);      // 時刻取得  
       
   sprintf(str, "%04d/%02d/%02d [%s] %02d:%02d:%02d",
      st.year+1970,      // 西暦年
      st.month,          // 月
      st.day,            // 日
      wday[st.weekday],  // 曜日
      st.hour,           // 時
      st.minute,         // 分
      st.second          // 秒
   );
   Serial2.println(str);

   sprintf(str, "%04d %02d %02d %s ",
      st.year+1970,      // 西暦年
      st.month,          // 月
      st.day,            // 日
      wday[st.weekday]  // 曜日
   );
  lcd.setCursor(0, 0);
  lcd.print(str);

   sprintf(str, "%02d:%02d:%02d        ",
      st.hour,           // 時
      st.minute,         // 分
      st.second          // 秒
   );
  lcd.setCursor(0, 1);
  lcd.print(str);
   
   delay(1000);
}
問題はプログラムの重さです。これだけで 37kB(56%)を使っています。時計を作るならともかく、他の処理に時刻を与えるにはおもすぎるのではないかと思います。なお、表示間隔にdelay(1000)を使っているので変更が必要です。

(2019.07.02) 表示方法の変更
 プログラムが非常に重くなるのは sprintf() 関数ではないかと思い、この関数を使わず一つずつ lcd.print() で書いてみました。その結果、18kBあまりとなりかなり小さくなりました。
//RTCtest03

#include <RTClock.h>
#include <time.h>
#include <LiquidCrystal.h>

RTClock rtc(RTCSEL_LSE);
LiquidCrystal lcd(PB10,PB11,PB12,PB13,PB14,PB15);//rs, en, d4, d5, d6, d7
char *wday[] = {"Mon","Tue","Wed","Thr","Fri","Sat","Sun"};

// セットアップ
void setup() {
  struct tm_t t;  
  Serial2.begin(115200);
  while (!Serial2) delay(100);

  t.year    = 2019-1970;  // 年   [1970からの経過年数]
  t.month   = 6;          // 月   [0-11] 0から始まることに注意
  t.day     = 30;         // 日   [1-31]
  t.weekday = 6;          // 曜日 [0-6] 0:月曜 〜 6:日曜
  t.hour    = 20;         // 時   [0-23]
  t.minute  = 5;          // 分   [0-59]  
  t.second  = 0;          // 秒   [0-59] 
//  rtc.setTime(t);         // 時刻の設定      設定時はコメントアウトしない

  lcd.begin(16, 2);
  lcd.print("hello, world! ");
}

void loop() {
   char str[64];  
   struct tm_t st;   
   rtc.getTime(st);      // 時刻取得  
       
  lcd.setCursor(0, 0);   // 1行目に年月日を表示
  lcd.print(st.year+1970);
  lcd.print(" ");
  if(st.month<10)lcd.print(" "); lcd.print(st.month);//1桁のときは空白を入れる
  lcd.print(" ");
  if(st.day<10)lcd.print(" "); lcd.print(st.day);
  lcd.print("     ");


  lcd.setCursor(0, 1);  // 2行目に時分秒を表示
  if(st.hour<10)lcd.print(" "); lcd.print(st.hour);
  lcd.print(":");
  if(st.minute<10)lcd.print("0"); lcd.print(st.minute);
  lcd.print(":");
  if(st.second<10)lcd.print("0"); lcd.print(st.second);
  lcd.print("        ");
  
   delay(1000);
}
Arduinoに変換するために12kBほど必要としているようですから、RTCの読み取りとlcd表示に6kBほどを要していることになります。全容量64kBから考えると許容範囲ということになるのでしょうか。

 表示方法の変更 その2
文字列(配列)に1文字ずつ代入して、文字列を表示する方法をとってみました。
16780バイトと更に小さくなりました。シリアルへの出力を考えるとこのほうが良いのかもしれません。
//RTCtest04

#include <RTClock.h>
#include <time.h>
#include <LiquidCrystal.h>

RTClock rtc(RTCSEL_LSE);
LiquidCrystal lcd(PB10,PB11,PB12,PB13,PB14,PB15);//rs, en, d4, d5, d6, d7
char *wday[] = {"Mon","Tue","Wed","Thr","Fri","Sat","Sun"};

// セットアップ
void setup() {
  struct tm_t t;  
  Serial2.begin(115200);
  while (!Serial2) delay(100);

  t.year    = 2019-1970;  // 年   [1970からの経過年数]
  t.month   = 6;          // 月   [0-11] 0から始まることに注意
  t.day     = 30;         // 日   [1-31]
  t.weekday = 6;          // 曜日 [0-6] 0:月曜 〜 6:日曜
  t.hour    = 20;         // 時   [0-23]
  t.minute  = 5;          // 分   [0-59]  
  t.second  = 0;          // 秒   [0-59] 
//  rtc.setTime(t);         // 時刻の設定

  lcd.begin(16, 2);
  lcd.print("hello, world! 2");
}

void loop() {
   char str[64],s[17];  
   struct tm_t st;   
   rtc.getTime(st);      // 時刻取得  
       
  s[0]='2';
  s[1]='0';
  s[2]=((st.year-30)/10)+48;
  s[3]=((st.year-30)%10)+48;
  s[4]=' ';
  s[5]=((st.month)/10)+48;
  s[6]=((st.month)%10)+48;
  s[7]=' ';
  s[8]=((st.day)/10)+48;
  s[9]=((st.day)%10)+48;
  s[10]=' ';
  s[11]=' ';
  s[12]=' ';
  s[13]=' ';
  s[14]=' ';
  s[15]=' ';
  s[16]='\0';
  lcd.setCursor(0, 0);
  lcd.print(s);
  
  s[0]=((st.hour)/10)+48;
  s[1]=((st.hour)%10)+48;
  s[2]=':';
  s[3]=((st.minute)/10)+48;
  s[4]=((st.minute)%10)+48;
  s[5]=':';
  s[6]=((st.second)/10)+48;
  s[7]=((st.second)%10)+48;
  s[8]=' ';
  s[9]=' ';
  s[10]=' ';
  s[11]=' ';
  s[12]=' ';
  s[13]=' ';
  s[14]=' ';
  s[15]=' ';
  s[16]='\0';

  lcd.setCursor(0, 1);
  lcd.print(s);

   delay(1000);
}  

(2019.07.04) シリアルによる時刻設定と分未満四捨五入ボタンの設置
PB4ポートをソフトウエアプルアップして、GND間にタクトスイッチを入れて、シリアルによる時刻設定のスイッチとしました。
PB5ポートをソフトウエアプルアップして、GND間にタクトスイッチを入れて、時刻の分未満を四捨五入するスイッチとしました。

時刻設定はserial2をteratarmで開いて(115200)PB4ポートのswを押します。ボードから"Time Set YY,MM,DD,hh,mm,ss"と表示されるので西暦の下2桁、月日時分秒をそれぞれ2桁でコンマで区切って入力すると時刻が設定されます。

誤差が出た時の修正のために、微小な時刻修正のために分未満を四捨五入するスイッチをPB5に設けました。10分の位の繰り上がりは配慮していないので分1桁が9のときは注意が必要です(0まで待つ)。

21kB弱に収まりました。Arduino変換に12kBほど消費するとしてRTCで9kBほど使っているのでしょうか。あと40kBあまり残っているので良しとしましょう。 printf と String は消費が大きいです。printfは20kBほど、Stringは2kBほどフラッシュを消費するようです。

スケッチです
//RTCtest05

#include <RTClock.h>
#include <time.h>
#include <LiquidCrystal.h>

RTClock rtc(RTCSEL_LSE);
LiquidCrystal lcd(PB10, PB11, PB12, PB13, PB14, PB15); //rs, en, d4, d5, d6, d7
//char *wday[] = {"Mon", "Tue", "Wed", "Thr", "Fri", "Sat", "Sun"};

void setup() {
  pinMode(PB4, INPUT_PULLUP);
  pinMode(PB5, INPUT_PULLUP);
  pinMode(PC13, OUTPUT);

  Serial2.begin(115200);
  while (!Serial2) delay(100);

  lcd.begin(16, 2);
  lcd.print("hello, world! 2");
}

void loop() {
  char s1[17], s2[17];
  struct tm_t t, st;
  int sec;

  //時刻設定
  if (digitalRead(PB4) == 0) {
    digitalWrite(PC13, 0);

    Serial2.println("Set Time. YY,MM,DD,hh,mm,ss");
    while (Serial2.available() < 18) {};

    t.year    = Serial2.parseInt() + 30; // 年   [1970からの経過年数]
    t.month   = Serial2.parseInt() - 1;        // 月   [0-11] 0から始まることに注意
    t.day     = Serial2.parseInt();         // 日   [1-31]
    t.weekday = 0;          // 曜日 [0-6] 0:月曜 〜 6:日曜
    t.hour    = Serial2.parseInt();// 時   [0-23]
    t.minute  = Serial2.parseInt();          // 分   [0-59]
    t.second  = Serial2.parseInt();          // 秒   [0-59]
    rtc.setTime(t);         // 時刻の設定
  }
  else digitalWrite(PC13, 1);

  //四捨五入
  if (digitalRead(PB5) == 0) {
    if (st.second > 29) st.minute  += 1;
    st.second  = 0;
    rtc.setTime(st);         // 時刻の設定
  }

  rtc.getTime(st);      // 時刻取得
  if (st.second != sec) {//LCD表示
    s1[0] = '2';
    s1[1] = '0';
    s1[2] = ((st.year - 30) / 10) + 48;
    s1[3] = ((st.year - 30) % 10) + 48;
    s1[4] = ' ';
    s1[5] = ((st.month + 1) / 10) + 48;
    s1[6] = ((st.month + 1) % 10) + 48;
    s1[7] = ' ';
    s1[8] = ((st.day) / 10) + 48;
    s1[9] = ((st.day) % 10) + 48;
    s1[10] = ' ';
    s1[11] = ' ';
    s1[12] = ' ';
    s1[13] = ' ';
    s1[14] = ' ';
    s1[15] = ' ';
    s1[16] = '\0';
    lcd.setCursor(0, 0);
    lcd.print(s1);

    s2[0] = ((st.hour) / 10) + 48;
    s2[1] = ((st.hour) % 10) + 48;
    s2[2] = ':';
    s2[3] = ((st.minute) / 10) + 48;
    s2[4] = ((st.minute) % 10) + 48;
    s2[5] = ':';
    s2[6] = ((st.second) / 10) + 48;
    s2[7] = ((st.second) % 10) + 48;
    s2[8] = ' ';
    s2[9] = ' ';
    s2[10] = ' ';
    s2[11] = ' ';
    s2[12] = ' ';
    s2[13] = ' ';
    s2[14] = ' ';
    s2[15] = ' ';
    s2[16] = '\0';
    lcd.setCursor(0, 1);
    lcd.print(s2);
    sec = st.second;
  }
  //  Serial2.print(s1);
  //  Serial2.println(s2);

  //  delay(1000);
}
    



 6 気圧センサーbmp280のテスト   2019.07.05
 気圧センサーbmp280のモジュールがあったので追加しました。


使用したボードは以前に他の目的に使ったもので、I2C端子の並びがbmp280と合わなかったので配線を変更しました。
ボードの電源はbluePillの3.3V変換の電流容量が小さいために、汎用電源3.3Vとしました。LCDは5V必要なのでステップアップモジュールで昇圧しています。この状態で3.3Vは80mAほど流れています。

ソフトウエアのライブラリーについては、以前にAdafruit_BME280_Library-master.zipとAdafruit_Sensor-master.zipをダウンロードにし、解凍したものをlibrariesフォルダにおいてありますのでそのまま利用しました。

bmp280を使うために必要なことはスケッチの赤字部分だけのようです。思ったよりも簡単に結果が得られました。ライブラリーがよくできているからでしょう。プログラムの勉強にはなりませんが。

void loop() {関数で勘違いのため時間が取られました。このloopを通常のmain()のなかの while(1){ と勘違いしていたのです。LCDの表示を1秒に1回(秒の変わり目に)と考えたのですがどうしても思う通りになりません。時間が経過した後、Arduinoのloop()は変数を毎回初期設定してしまうことだと気づきました。loop()のなかでループするときは while(1){ を必ず書くことにしましょう。

ここまでの実装でフラッシュは 27588バイト、42%使用です。まだ余裕があるようです。

スケッチです。
//RTCtest06

#include <RTClock.h>
#include <time.h>
#include <LiquidCrystal.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
//#include <Streaming.h>      // http://arduiniana.org/libraries/streaming/

Adafruit_BMP280 bme; // I2C pppppppppp
RTClock rtc(RTCSEL_LSE);
LiquidCrystal lcd(PB10, PB11, PB12, PB13, PB14, PB15); //rs, en, d4, d5, d6, d7
//char *wday[] = {"Mon", "Tue", "Wed", "Thr", "Fri", "Sat", "Sun"};

void setup() {
  pinMode(PB4, INPUT_PULLUP);
  pinMode(PB5, INPUT_PULLUP);
  pinMode(PC13, OUTPUT);

  Serial2.begin(115200);
  while (!Serial2) delay(100);

  lcd.begin(16, 2);
  //  lcd.print("hello, world! 2");

  bme.begin();
}

void loop() {
  char s1[17], s2[17];
  struct tm_t t, st;
  int sec, f1, f2, flg;

  while (1) {
    //時刻設定
    if (digitalRead(PB4) == 0) {
      digitalWrite(PC13, 0);

      Serial2.println("Set Time. YY,MM,DD,hh,mm,ss");
      while (Serial2.available() < 18) {};

      t.year    = Serial2.parseInt() + 30; // 年   [1970からの経過年数]
      t.month   = Serial2.parseInt() - 1;        // 月   [0-11] 0から始まることに注意
      t.day     = Serial2.parseInt();         // 日   [1-31]
      t.weekday = 0;          // 曜日 [0-6] 0:月曜 〜 6:日曜
      t.hour    = Serial2.parseInt();// 時   [0-23]
      t.minute  = Serial2.parseInt();          // 分   [0-59]
      t.second  = Serial2.parseInt();          // 秒   [0-59]
      rtc.setTime(t);         // 時刻の設定
    }
    else digitalWrite(PC13, 1);

    //四捨五入
    if (digitalRead(PB5) == 0) {
      if (st.second > 29) st.minute  += 1;
      st.second  = 0;
      rtc.setTime(st);         // 時刻の設定
    }

    rtc.getTime(st);      // 時刻取得
    f2 = f1; f1 = st.second; flg = 0; if (f1 != f2)flg = 1;
    if (flg == 1) {
      s1[0] = '2';
      s1[1] = '0';
      s1[2] = ((st.year - 30) / 10) + 48;
      s1[3] = ((st.year - 30) % 10) + 48;
      s1[4] = ' ';
      s1[5] = ((st.month + 1) / 10) + 48;
      s1[6] = ((st.month + 1) % 10) + 48;
      s1[7] = ' ';
      s1[8] = ((st.day) / 10) + 48;
      s1[9] = ((st.day) % 10) + 48;
      s1[10] = ' ';
      s1[11] = '\0';
      s1[12] = ' ';
      s1[13] = ' ';
      s1[14] = ' ';
      s1[15] = ' ';
      s1[16] = '\0';
      lcd.setCursor(0, 0);
      lcd.print(s1);

      s2[0] = ((st.hour) / 10) + 48;
      if ((st.hour / 10 ) == 0) s2[0] = ' ';
      s2[1] = ((st.hour) % 10) + 48;
      s2[2] = ':';
      s2[3] = ((st.minute) / 10) + 48;
      s2[4] = ((st.minute) % 10) + 48;
      s2[5] = ':';
      s2[6] = ((st.second) / 10) + 48;
      s2[7] = ((st.second) % 10) + 48;
      s2[8] = ' ';
      s2[9] = ' ';
      s2[10] = '\0';
      s2[11] = ' ';
      s2[12] = ' ';
      s2[13] = ' ';
      s2[14] = ' ';
      s2[15] = ' ';
      s2[16] = '\0';
      lcd.setCursor(0, 1);
      lcd.print(s2);

      //  Serial2.print(s1);
      //  Serial2.println(s2);

      lcd.setCursor(11, 0);
      int pres = bme.readPressure() / 100 + 4;
      lcd.print(pres);

      lcd.setCursor(11, 1);
      int temp = bme.readTemperature() * 10;
      lcd.print(temp / 10);
      lcd.print('.');
      lcd.print(temp % 10);
    }
  }
  //  delay(1000);
}
    






 7 SDカード記録のテスト   2019.07.06
 SDカード記録のテストをしました。
ハードウエアは以前に結線していたとおり、mosi:PA7 miso:PA6 sck:PA5 cs:PA4 です。
ソフトウエアについては、SD.h と SPI.h が必要ですが以前にribrariesに登録してあるものを使いました。
リストの赤字部分が関係するところです。mosi,miso,sck はシステムで決まっているようですが cs だけは下記のように2回にわたって指定しなければならないようです。

単純に1秒に1回の割合で時、分、秒を記録しただけですが、ファイルが有れば追加で記録されました。なお、読み出しはPCで直接行いますのでプログラムは考えていません。
//RTCtest07

#include <RTClock.h>
#include <time.h>
#include <LiquidCrystal.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#include <SPI.h>
#include <SD.h>
//#include <Streaming.h>      // http://arduiniana.org/libraries/streaming/

Adafruit_BMP280 bme; // I2C pppppppppp
const int chipSelect = PA4;  // SD CS
RTClock rtc(RTCSEL_LSE);
LiquidCrystal lcd(PB10, PB11, PB12, PB13, PB14, PB15); //rs, en, d4, d5, d6, d7
//char *wday[] = {"Mon", "Tue", "Wed", "Thr", "Fri", "Sat", "Sun"};

void setup() {
  pinMode(PB4, INPUT_PULLUP);
  pinMode(PB5, INPUT_PULLUP);
  pinMode(PC13, OUTPUT);

  Serial2.begin(115200);
  while (!Serial2) delay(100);

  lcd.begin(16, 2);
  //  lcd.print("hello, world! 2");

  bme.begin();
  pinMode(PA4, OUTPUT);  // set OUTPUT for SPI

  if (!SD.begin(chipSelect)) {
    Serial2.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial2.println("card initialized.");
}

void loop() {
  char s1[17], s2[17];
  struct tm_t t, st;
  int sec, f1, f2, flg;

  while (1) {
    //時刻設定
    if (digitalRead(PB4) == 0) {
      digitalWrite(PC13, 0);

      Serial2.println("Set Time. YY,MM,DD,hh,mm,ss");
      while (Serial2.available() < 18) {};

      t.year    = Serial2.parseInt() + 30; // 年   [1970からの経過年数]
      t.month   = Serial2.parseInt() - 1;        // 月   [0-11] 0から始まることに注意
      t.day     = Serial2.parseInt();         // 日   [1-31]
      t.weekday = 0;          // 曜日 [0-6] 0:月曜 〜 6:日曜
      t.hour    = Serial2.parseInt();// 時   [0-23]
      t.minute  = Serial2.parseInt();          // 分   [0-59]
      t.second  = Serial2.parseInt();          // 秒   [0-59]
      rtc.setTime(t);         // 時刻の設定
    }
    else digitalWrite(PC13, 1);

    //四捨五入
    if (digitalRead(PB5) == 0) {
      if (st.second > 29) st.minute  += 1;
      st.second  = 0;
      rtc.setTime(st);         // 時刻の設定
    }

    rtc.getTime(st);      // 時刻取得
    f2 = f1; f1 = st.second; flg = 0; if (f1 != f2)flg = 1;
    if (flg == 1) {
      s1[0] = '2';
      s1[1] = '0';
      s1[2] = ((st.year - 30) / 10) + 48;
      s1[3] = ((st.year - 30) % 10) + 48;
      s1[4] = ' ';
      s1[5] = ((st.month + 1) / 10) + 48;
      s1[6] = ((st.month + 1) % 10) + 48;
      s1[7] = ' ';
      s1[8] = ((st.day) / 10) + 48;
      s1[9] = ((st.day) % 10) + 48;
      s1[10] = ' ';
      s1[11] = '\0';
      s1[12] = ' ';
      s1[13] = ' ';
      s1[14] = ' ';
      s1[15] = ' ';
      s1[16] = '\0';
      lcd.setCursor(0, 0);
      lcd.print(s1);

      s2[0] = ((st.hour) / 10) + 48;
      if ((st.hour / 10 ) == 0) s2[0] = ' ';
      s2[1] = ((st.hour) % 10) + 48;
      s2[2] = ':';
      s2[3] = ((st.minute) / 10) + 48;
      s2[4] = ((st.minute) % 10) + 48;
      s2[5] = ':';
      s2[6] = ((st.second) / 10) + 48;
      s2[7] = ((st.second) % 10) + 48;
      s2[8] = ' ';
      s2[9] = ' ';
      s2[10] = '\0';
      s2[11] = ' ';
      s2[12] = ' ';
      s2[13] = ' ';
      s2[14] = ' ';
      s2[15] = ' ';
      s2[16] = '\0';
      lcd.setCursor(0, 1);
      lcd.print(s2);

      //  Serial2.print(s1);
      //  Serial2.println(s2);

      lcd.setCursor(11, 0);
      int pres = bme.readPressure() / 100 + 4;
      lcd.print(pres);

      lcd.setCursor(11, 1);
      int temp = bme.readTemperature() * 10;
      lcd.print(temp / 10);
      lcd.print('.');
      lcd.print(temp % 10);
      
      File dataFile = SD.open("abc112.txt", FILE_WRITE);
      if (dataFile) {
        dataFile.print(st.hour); dataFile.print(" ");
        dataFile.print(st.minute); dataFile.print(" ");
        dataFile.print(st.second); dataFile.print(" ");
        dataFile.println(" ");
        dataFile.close();
      }
    }
  }
  //  delay(1000);
}
    




 8 MCUの内容表示(stm32flash.exeの実行) 2019.07.14   


  MCUの内容表示(stm32flash.exeの実行)   2019.07.14
 STM32F103C8T6 BluePill の Micro Controller Unit について stm32flash.exeを実行してスペックを表示することに成功しました。
何度か試みてうまく行かなかったのですが、指示通りに正しく操作するとできました。はじめはエラーになりましたが、電源を入れ直すと正しく動きました。その結果です。本当に128kBあるのでしょうか?
1 boot0を'1'にセットする。
2 serial1にUSB-シリアルをつなぐ。(1に注意)
3 D:\xxxxxxx\stm32flash-0.5-win64 フォルダ内でコマンドプロンプトを開く。
  このフォルダ内に generic_boot20_pc13.bin をコピーしてある。
4 "stm32flash.exe -f -v -w generic_boot20_pc13.bin COM5"を実行する。COM番号注意。
5 エラーなら電源を入れ直して実行する。
6 次の結果が得られた。

D:\xxxxxx\stm32flash-0.5-win64>stm32flash.exe -f -v -w generic_boot20_pc13.bin COM5
stm32flash 0.5

http://stm32flash.sourceforge.net/

Using Parser : Raw BINARY
Interface serial_w32: 57600 8E1
Version      : 0x22
Option 1     : 0x00
Option 2     : 0x00
Device ID    : 0x0410 (STM32F10xxx Medium-density)
- RAM        : 20KiB  (512b reserved by bootloader)
- Flash      : 128KiB (size first sector: 4x1024)
- Option RAM : 16b
- System RAM : 2KiB
Write to memory
Erasing memory
Wrote and verified address 0x08001bc0 (100.00%) Done.
その後、\stm32flash-0.5-win64>stm32flash.exe -f COM5 でよいことがわかった。(bootスイッチ注意)
D:\xxxxxxx\stm32flash-0.5-win64>stm32flash.exe -f COM5
stm32flash 0.5

http://stm32flash.sourceforge.net/

Interface serial_w32: 57600 8E1
Version      : 0x22
Option 1     : 0x00
Option 2     : 0x00
Device ID    : 0x0410 (STM32F10xxx Medium-density)
- RAM        : 20KiB  (512b reserved by bootloader)
- Flash      : 128KiB (size first sector: 4x1024)
- Option RAM : 16b
- System RAM : 2KiB
    






 21 第2の実験ボード   2019.07.07
 mbedで実験していたボードを流用してBluePillのArduinoテストをしてきましたが、一応のテストが終わったので今後の実験のために新しいボードを作りました。
少し使って、三端子レギュレーターが故障したために取り除いたモジュールを使っています。三端子は手持ちのL1087 3V3 800mA を追加しました。USB端子に5V電源で3.3V動作をしています。
BOOT端子は、ST-Link専用で使うために、両方共取り除いて0に固定しています。
現在接続しているのは、serial1(TX:PA9 RX:PA10)、スイッチ(reset PB4 PB5 pullup)、OLED(I2C SCK:PB6 SDA:PB7)の3つです。


写真でわかるようにユニバーサル基板を2つに切っています。モジュールの裏面が少し見えるためと、パターンの有効利用です。強度は大丈夫なようです。

デジタル入力スイッチとLチカ: 問題なく実行できました。

シリアル通信: 少し引っかかりました。前回はシリアル2に配線していましたのでserial2でアクセスできたのですが、今回はシリアル1につないでいますから serial でやってみたところ全く通信ができません。故障・配線間違いなど考えましたが関係無さそうです。ネットの記事を見ていて serial1 と 1 をつけなければならないことを知りました。serial1を使うと問題なく通信できました。

有機EL 0.96インチ前の実験で文字表示ができましたのでそのままのスケッチを書き込んでみました。結果は問題なく文字表示ができました。環境設定は前の実験時に出来上がっていたようです。

画面を書き換えるときは、ループの中ではじめに display.clearDisplay(),display.display() を置いて画面消去をすると毎回「消して、新たに書く」ことになるので表示が見苦しくなります。
これを避けるためには、ループのはじめで display.clearDisplay() を実行してバッファを空にし(表示は残っている)、バッファに次のテキストを書き込んでから display.display() で上書きをするとよいようです。すなわち、ループの中で「画面を消す」必要はないようです。バッファをクリアしないで上書きすると前の文字と重なります。グラフィックディスプレイだからでしょう。

フラッシュ使用量は28460バイト(43%)となりました。OLED文字表示で約13kBほど消費しますがまずまずといったところでしょうか。
// oled_01.ino  DrawText

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

Adafruit_SSD1306 display(-1);//リセット端子なし

void setup() {
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);//3V3電源,スレーブ0X3C
}

void loop() {
  int i = 0;
  display.display();//デフォルトロゴ表示。最初に書けばcppから。
  delay(2000);
  display.clearDisplay();// 画面表示をクリア
  display.display();

  while (1) {
    display.clearDisplay();//            バッファクリア

    display.setTextSize(2);// テキストサイズを設定  以下テキストをバッファに書き込み
    display.setTextColor(WHITE);// テキスト色を設定
    display.setCursor(0, 0);// テキストの開始位置を設定

    display.println("Hello");// 1行目に"Hello"を表示

    display.setCursor(0, 20);// 2行目に
    display.print("World! ");
    display.println(i);

    display.setCursor(0, 40);// 3行目に
    display.println("10:59:28am");          バッファに書き込み終わり

    display.display();//            描画バッファの内容を画面に表示

    delay(1000);
    i++;

  }
}

    






 22 STM32duino実験ボード   2019.07.19
 個々の実験をするにはBluePillの各端子からリードを取り出したくなります。が、最初のボードでははんだ付けが必要なので不便です。解体して作り直しました。
端子ごとにソケットを用意してピンを差し込めるように考えました。また、基本的な電源、シリアル、I2Cは使いやすいように専用のコネクタとしました。汎用のスイッチもPB4とPB5につけました。I2Cは電源が左のものと右のものがあることが後でわかったので電源を左右に付けたコネクタも準備しました。
BluePillは接着剤で止めています(やや危ないか?)。


稼働テストとして、DS3231によるRTCを試しました。BluePillのRTCは精度があまり良くないようで、かつ、リセットで1秒ほど狂います。精度に定評があるDS3231を使いました。これのライブラリもあるようですが私には難解なので以前にメモリマップから求めたI2C直接の読み書きで行っています。
表示はシリアル1とOELDとしましたが、アダプタでI2C使用にしたLEDがありましたので同時にテストしました。結果としては良いのですが、立ち上がりに問題があるらしくリセットでうまく起動しない時があるようです。RTCを引き抜いたりすると正常に起動することもありました。原因は不明です。
プログラムはこの様になりました。


(2022.09.23) 時間が経つと内容を忘れますので回路図を書きました。以前の写真より赤青発光ダイオードと8ピンのLCDソケットを追加しています。


    









 31 久しぶりに   2021.07.23

 ながらく休んでしまいました。体力がなくなった、netで話をする相手がいなくなった、など理由はありますが、想像した遊びは ほとんどしたからかもしれません。しかし、はんだごてを持つ楽しみは今も変わりません。
最近は誤嚥性の肺炎で入院をしたり、慢性の肺炎が見つかったり、更に膝の軟骨が薄くなって歩けなくなったりと散々で、週1回の仕事もままなりません。
 できることが少なくなってきたのでもう一度楽しんでみたいと自分のページを読みながら考えました。以前のように記事を読んでもらえる人はいなくなりましたがやはり書くことにしましょう。

今までに触ったMCはいくつかありますが、STM32F103C8T6 から以前を思い出すことにしました。幸いPCには開発環境が残っています。arduino言語はわかりやすいのでこれを使うことにしましょう。

 01 Lチカ 2021.07.23   
 02 Serial通信 2021.07.25   
 03 LCD表示 2021.07.25   





 01 Lチカ   2021.07.23

まずはLチカの復習からです。ボードはこれを使うことにしました。

MC基板のブートSWは2つとも'0'位置です。
書き込みにはSTLinkを使います。 arduinoの「ツール」の設定は
  ボード: Generic STM32F103C series
  Variant: STM32F103C8 (20k RAM, 64k Flash)
  Upload method: "STLink"
  CPU Speed(MHz): 72MHz (Normal)
  Optimize: "smallest(default)"
  シリアルポート: 無関係
で自動書き込み(丸に→)できます。

このようなプログラムになりました。
// L-chika
void setup() {
  pinMode(PA1, OUTPUT);
  pinMode(PA3, OUTPUT);
}
void loop() {
  digitalWrite(PA1, LOW);   // ON
  delay(2000);              
  digitalWrite(PA1, HIGH);  // OFF
  delay(1000);              

  digitalWrite(PA3, LOW);   // ON
  delay(1300);              
  digitalWrite(PA3, HIGH);  // OFF
  delay(500);               
}
多くを忘れていて、これだけ書くのにも大変でした。    






 02 Serial通信   2021.07.25
シリアル通信は、
  1 通信端子はSerial1(TX1:PA9 RX1:PA10)を用いた。
  2 USB-シリアル変換は CH340 で、TXとRXのみを使った。
  3 PC側はTeratermを使った。

PCからの入力待ちルーチンを使っています。while (!Serial1. available());です。
複数動作をするためのメニューも考えてみました。
プログラムです。
//プログラム"serial_test03"

void setup() {
  Serial1.begin(9600);
  while (!Serial1) delay(100);
  Serial1.println("Serial1 OK ");
}

void loop() {
  int i;
  char a;

  Serial1.println("Hello World!");

  while (1) {
    Serial1.println(" menu 1--3 ?");
    while (!Serial1. available());
    a = Serial1.read();
    Serial1.println();

    if (a == '1') Serial1.println(" good morning");
    if (a == '2') Serial1.println(" afternoon tea");
    if (a == '3') Serial1.println(" night music");

    delay(1000);
  }
}
arduinoプログラムを少しずつ思い出しています。     







 03 LCD表示   2021.07.25

 LCDはそのまま使う場合とI2Cコンバーターを付加して使う場合があります。
I2Cコンバーターを使う場合について調べました。
LCDは青色白抜きバックライト付きで2行16文字です。コンバーターはPCF8541Tがついたものです。
これはアドレスがデフォルトで0x27です。ジャンパピンで0x20から0x26に変更できます。
テストプログラムは次のようになりました。
// program:wire01-20210725

#include <Wire.h>           // I2Cのヘッダファイル
#include <LiquidCrystal_I2C.h>         //  液晶のヘッダファイル
LiquidCrystal_I2C lcd(0x27, 16, 2);    //  LCDの登録 I2Cアドレス、文字数、行数

void setup() {
  Wire.begin();                        // join i2c bus (address optional for master)
  lcd.begin();                         // LCD開始
  lcd.backlight();                     // バックライトON
  lcd.clear();                         // 消去
  lcd.setCursor(0, 1);                 // カーソル 0文字位置 2行目
  lcd.print("Hello world...");         // 出力
}

void loop() {
  lcd.setCursor(0,0);                  // カーソル 0文字位置 1行目  (文字位置0から, 行0から)
  lcd.print("OK! good.");
  
  while(1){};
}
LiquidCrystal_I2C には次の命令があります(全部ではありません)。
 begin(adds,cols, rows);
 backlight();
 setCursor(col, row);
 home();
 clear();
 print(data)   数値、文字列
 blink();
 noBlink();
 cursor();
 noCursor();

通常はこの程度で使えると思います。

通常のLCD表示(I2Cでない)もしてみました。
ポートは、PB10〜PB15の6つを使っています。この順で、rs、en、D4、D5、D6、D7です。
プログラム例です。
// program: LCD-nomal01-20210725

#include 
LiquidCrystal lcd(PB10, PB11, PB12, PB13, PB14, PB15); //(rs, enable, d4, d5, d6, d7)

void setup() {
  lcd.begin(16, 2);
}

void loop() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("hello world!");

  lcd.setCursor(0, 1);
  lcd.print("Good Morning!");

  while (1) {}
}

私自身は、LiquidCrystal lcd()、lcd.begin()、 lcd.clear()、lcd.setCursor()、lcd.print() くらいしか使いません。
その他はArduino日本語リファレンスに詳しく書かれています。     

































工事中























 2 予    定   2016.00.00
    









inserted by FC2 system