STM32F103

ArduinoSTM32

▼ 仕様

●BOOTジャンパ
起動モードを設定します。デフォルトでは BOOT 0 / BOOT 1 ジャンパ共に 0 側 (FLASH Boot) です。
MODE	BOOT0	BOOT1
FLASH	0	0
	0	1
ISP	1	0
SRAM	1	1

●SWD (Serial Wire Debug) ピン
デバッグ用の SWD ピンです。ST-Link 等を繋げるためのもので、一般的な USBシリアル変換モジュールを接続するものではありません。
Pin	名称
4	GND
3	DCLK
2	DIO
1	3V3

■画像の代わりになるテキスト■

●GPIO
PA0~PA15、PB0~PB15、PC13~PC15
・PC13はオンボードLEDが接続されている。
・PWM使用可能ピン PB0, PA7, PA6, PA3, PA2, PA1, PA0, PB7, PB6, PA10, PA9, PA8
・ADC使用可能ピン PB0, PA7, PA6 , PA5 , PA4 , PA3 , PA2 , PA1 , PA0

STM32には、5V電圧信号を入力できる端子があります。
具体的な端子はデータシートに「FT」(Five voltage Tolerant)と記載されています。
マイコンの電源電圧が3.6V以下であっても、この端子には5V電圧を印加できます。
PA8~15、PB2~4、PB6~15 については、5V入力が可能です。

ただし、その場合の入力閾値電圧は5V基準ではなく、マイコンの電源電圧VDD基準のままです。
一方、GPIOはNMOSオープン・ドレイン構成が可能です。
この場合、外部にプルアップ抵抗をつなぎ、プルアップ電圧を5Vにすると、見かけ上、5VのHighレベル信号を出力することができます。

<GPIOピン注意>
・PA8~PA11は、ブートローダーを導入している場合はUSBにて使用され使えない。
・PA15 / PB3 / PB4 を使用する場合
	JTAG デバッグ用に使われているため、下記操作が必要。

void setup() {
  disableDebugPorts();  // デバッグ無効
}

有効に戻す場合は、
enableDebugPorts();  // デバッグ有効

・PB2 は、BOOTジャンパピンの中央のピンになる。

●UART
3系統あります。
Serial (UART) に使えるピンはデフォルトで以下の通りです。
	UART1	UART2	UART3
TXD	PA9	PA2	PB10 (26)
RXD	PA10	PA3	PB11 (27)
RTS	PA12	PA1	PB14 (30)
CTS	PA11	PA0	PB13 (29)
CK	PA8	PA4	PB12 (28)

Serial オブジェクトは宣言なしに利用可能です。
ブートローダーを導入すると UART1 は USBシリアルに割り当てられ Serial として定義されます。
UART2 及び UART3 は Serial2 / Serial3 として定義されており、宣言なしに利用可能です。UART1 及び UART3 のピンは 5V トレラントです。
.begin()のconfigパラメータについて、7ビット設定等がサポートされていないためSERIAL_7N1等の一部設定が使用できません。

・スケッチ
void setup() {
  Serial.begin(9600, SERIAL_8N1);
  while (!Serial.isConnected()) delay(100); // シリアルが起動するまで待ち。これがないと起動直後の送信が抜ける。
}

●SPI
SPI に使えるピンはデフォルトで以下の通りです。

SPI	ピン
SCK	PA5
MISO	PA6
MOSI	PA7
SS	PA4

SPI2	ピン
SCK	PB12 (28)
MISO	PB14 (30)
MOSI	PB15 (31)
SS	PB12 (28)

SPI2 を使うには以下のように宣言します。
#include <SPI.h>
SPIClass SPI_2(2);

●I2C
I2C に使えるピンはデフォルトで以下の通りです。

定数	ピン
SDA	PB7 (23)
SCL	PB6 (22)

I2C はもう一系統使えます (I2C2)。

定数	ピン
SDA	PB11 (27)
SCL	PB10 (26)

I2C2 を使うには以下のように宣言します。

#include <Wire.h>
TwoWire Wire2(PB10, PB11, SOFT_STANDARD);

●RTC (Real Time Clock)
32.768kHz の外部オシレーターが実装されており、独立したタイマを使う事ができます。

・時刻スケッチ
#include <RTClock.h>
RTClock rtclock (RTCSEL_LSE); // initialise
char *wday[] = {"Mon","Tue","Wed","Thr","Fri","Sat","Sun"};  // 曜日設定

void setup() {
  struct tm_t t;  // メンバー変数定義
   t.year    = 2022 - 1970;  // 年   [1970からの経過年数]
   t.month   = 8;          // 月   [0-11] 0から始まることに注意
   t.day     = 24;         // 日   [1-31]
   t.weekday = 5;          // 曜日 [0-6] 0:月曜 ~ 6:日曜
   t.hour    = 12;         // 時   [0-23]
   t.minute  = 0;          // 分   [0-59]
   t.second  = 0;          // 秒   [0-59]
   rtc.setTime(t);         // 時刻の設定
}

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          // 秒
  );
  Serial.println(str);
  delay(1000);
}

・RTC割り込み
使える RTC には 4 種類ありますが、Blue Pill なら LSE を使うのがよさそうです。
ソースは、次の3つのいずれかです。
種類	定数
HSE	RTCSEL_HSE	128 分周の(高速外部オシレータクロック(High-Speed External Clock)
LSE	RTCSEL_LSE	低速外部オシレータクロック(Low-Speed External Clock) ※通常はこれを使用
LSI	RTCSEL_LSI	40 kHz 低速内蔵 RCオシレータクロック(Low-Speed Internal Clock)

割り込みタイミングは次の3つです。
アラーム割込み。ソフトウェアでプログラミング可能なアラーム割り込みを生成します。
秒割込み。プログラミング可能な周期(最大 1 秒)で周期的な割り込み信号を生成します。
オーバーフロー割り込み。内部のプログラマブルカウンタがゼロにロールオーバーしたことを検出します。

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

#include <RTClock.h>

RTClock rt (RTCSEL_LSE); // initialise

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

void sec_func () {  // 1秒割り込みルーチン
  digitalWrite(LED_RED,!digitalRead(LED_RED));
}

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

void alarm2_func () {
  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(sec_interrupt);  // 1秒割り込み開始
//  rt.attachAlarmInterrupt(alarm_func, rt.getTime()+10);
  rt.attachAlarmInterrupt(alarm_func);  // アラーム割り込み開始
  rt.createAlarm(alarm2_func, rt.getTime()+60);
  rt.setAlarmTime(rt.getTime()+10);

}

uint32 tt;
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());
  }
}

●バッテリーバックアップ回路
バッテリーバックアップは VBAT端子に1.8Vから3.6Vまでの電圧を供給(電池を繋ぐ事)で実現できます。
バックアップドメイン(バックアップレジスタとリアルタイムクロック)は、電源(VDD端子に供給される電源) の供給が停止した場合でも動作し続けます。
消費する電流は3.3V動作時で1.4uAになり、CR2032を使用した場合、計算上は約18年動作し続ける。

●バックアップレジスタ(BKP)
バックアップレジスタは、STM32F103C8T6の場合は20バイトあり、データレジスタはDR1~DR10になる。
大容量のチップは84バイト。

・スケッチ
#include <libmaple/bkp.h>

void setup() {
  bkp_init();   // BKPの初期化  
}

void loop() {
  // BKP 書き込み
  bkp_enable_writes();    // 書込み可能にする
  bkp_write(1, 0xFFFF);  // データの書込み(レジスタ1に0xFFFF書き込み)
  bkp_disable_writes();  // 書込み禁止にする    

  // BKP 読み込み
  for (int reg=1; reg <= BKP_NR_DATA_REGS; reg++) { //BKP_NR_DATA_REGSはBKPサイズ(STM32F103C8T6の場合は10)
    Serial.println(bkp_read(reg), DEC);  // データの読み込み
  }
}

●省電力機能
最高スピードである72MHzで駆動しているときでも36mAぐらいしか消費しませんが、さらにバッテリー等で長時間駆動させたい場合に有効です。
省電力モードに移行してもタイマー等は稼働しており、適宜復帰をすることが可能です。
SLEEP mode	5.5から14.4mA
STOP mode	14から24μA
STANDBY mode	2μA

●タンパー
タンパーピンにエッジが検出されるとバックアップレジスタの中身が消去されます。
製品の外装を開封されると困る場合などに使用できます。

●タイマー(HardwareTimer)
Timer1 ~ Timer4 が用意されています(大容量ボードではさらにTimer5 ~Timer8 が利用可能です)。
Timer1(Timer8)は高機能タイマ、Timer2 ~ Timer5 は汎用タイマ、Timer6、Timer7 は基本タイマです。
これらの高機能タイマ、汎用タイマ、基本タイマはHardwareTimerクラスでは全て同じタイマとして利用出来ます。

・タイマ割込みをLEDを点滅させる例
#define LED_PIN  PC13

void handle_timer() {
  static uint8_t sw = LOW;
  sw = !sw;
  digitalWrite(LED_PIN, sw);
}
 
void setup() {
  pinMode(LED_PIN, OUTPUT);
  Timer1.pause();                   // タイマー停止
  Timer1.setPeriod(200 * 1000);     // 200ミリ秒でオバーフロー
  Timer1.attachInterrupt(           // 割り込みハンドラの登録
      TIMER_UPDATE_INTERRUPT,       // 呼び出し条件は、カウンターオーバーフロー更新時
      handle_timer                  // 呼び出す関数
    );  
  Timer1.refresh();                 // タイマ更新
  Timer1.resume();                  // タイマースタート
}

void loop() {  }

●外部割り込み
どのピンも外部割り込みに使用できますが、いくつかの制限があります。
一度に最大16個の外部割り込みを使用できます。 さらに、使用する16ピンを選ぶことはできません。
EXTIラインごとに1つのピンしか外部割り込みに使用できないためです

●HID (Human Interface Device)
STM32F103 ボードで HID を扱うには以下のライブラリを導入します。
USB HID library for STM32F1 (GitHub)

・HID キーボード
以下のスケッチは PA0 に繋がれたスイッチが押された時だけ "Hello,world." を出力するスケッチです。

#include <USBHID.h>
const int BUTTON = PA0;

void setup() {
  pinMode(BUTTON, INPUT_PULLUP);
  USBHID_begin_with_serial(HID_KEYBOARD);
  Keyboard.begin();
  delay(5000);
}

void loop() {
  if (digitalRead(BUTTON) == LOW) {
    Keyboard.println("Hello,world.");
    delay(1000);
  }  
}


・HID マウス
PA0 に繋がれたスイッチが押された時だけマウスカーソルが (512, 512) へ移動するスケッチです。

#include <USBHID.h>
const int BUTTON = PA0;
const uint8_t reportDescription[] = {
  HID_ABS_MOUSE_REPORT_DESCRIPTOR(HID_MOUSE_REPORT_ID)
};
HIDAbsMouse mouse;

void setup() {
  pinMode(BUTTON, INPUT_PULLUP);
  USBHID_begin_with_serial(reportDescription, sizeof(reportDescription));
}

void loop() {
  if (digitalRead(BUTTON) == LOW) {
    mouse.move(512, 512);
    delay(1000);
  }  
}

・HID ジョイスティック
ArudinoIDEにて、ファイル ⇒ スケッチ例 ⇒ USBComposite for STM32F1] にある simplejoystick サンプルスケッチは
Maple という名前のゲームコントローラーとして認識され、スケッチを実行すると 0.5 秒おきに左右にレバーが入ります。

●EEPROM emulation機能
Flashメモリの一部をEEPROMとして使う機能です。

#include <EEPROM.h>
#define EPSIZE 254

void setup() {
  uint16 Address = 0x10;  // 0x10~0x10D
  uint16 Data = 0xFFFF;
  uint16 Status;

  Status = EEPROM.write(Address, Data);  // 書き込み
  Status = EEPROM.read(Address, &Data);  // 読み込み
}

void loop() {
}

●ソフトリセット
CortexM0の場合は#include "core_cm0.h"
CortexM3の場合は#include "core_cm3.h"
CortexM4の場合は#include "core_cm4.h"

NVIC_SystemReset();


Homeへ