こちらはrogy Advent Calendar X日目の記事です.
今回は秋月電子通商で販売されているSTM32L010F4P6を使った際,レジスタを叩く(HALすら使わずに)ことになったので,その時の知見を備忘録がてら残しておこうと思います.誰得…?
どうしてそんなことになってしまったのか
もともと工大祭とMaker Fairに向けてライブ振動子(下図)なるものを作っていたんですが,今年は100台の振動子をつくると,見栄を張って宣言しまいました.
2か月前くらいになってようやく手を付け始めて(はよやれ)とりあえず部品選定となりました.
前作ではPIC12F1822を使ってレジスタ叩いて開発していたのですが,IO8ピンではサイリウム単色点灯しかできない,かつプログラムメモリや計算速度が足らず小数を無理やり整数にして表現するなど,流石にスペック不足でした.
てことで昔使ってたPIC16F1827あたりを使おうかと思ったら…
お値段が高い!!!
君180円くらいだったよね??なんでDegikeyで260円もするの???
そのくらい誤差ではと思うかもしれませんが,今回は100台生産するので180円と260円では最終的に8000円の差になってきます.303k8にしたら78,000円…
で,秋月で色々見ていた時に見つけたのがSTM32L010F4P6でした.
何と200円(2023年12月現在)で16F1827より安くIOが16ピン,しかも軽く調べるとmbed(現Keil Studio)で開発できるとの記事が!!
32ビットで小数計算もきっと速い,そしてなによりmbedでいいならもうレジスタを叩かなくて済む!!
勝ったも同然の気持ちでL010を100個2万円で購入し,回路の設計・発注も行いました.
で,動作確認用のコード書いてコンパイルしたら…
そこで目にしたのは,Keil Studioの出力から吐かれる大量の No space in execution regions
のエラーたちでした.
コードのミスも疑いましたが,Lチカくらいは問題なくコンパイルできる(この段階で12kB/16kBとか使う)のにI2Cのクラス追加しただけでこうなるとか,それらしい質問をしている人がいるとかでメモリ不足が問題と判断しました.
さらにHAL(Hardware Abstraction Layer)を使ってコーディングしてもやはりメモリ不足になり,マイコン買いなおすのも回路再設計もきつかったので,レジスタ叩きに落ち着きました.悲しい.
100個なんて買う前に動確とれよという方,仰るとおりですが,時間もちゃんと調べる時間もあまりなく根性で実装することになりました.
下準備
ここからが本題です.
まずは開発環境としてSTM32CubeIDEをインストールしておきます.
ついでに,STM32CubeProgrammerもいざというときのために入れておくとよいでしょう.今回はレジスタを直接触るので,普通の書き込みができなくなる事故が発生する場合があります(これでめちゃくちゃ時間とかした).詳しくは後述.
また,レジスタ叩く上でリファレンスマニュアルを手元に置いておくと便利です.STM32L0X0のものは英語版です.日本語のSTM32L0X3のマニュアルも大変便利ですので,2つともダウンロードしておくと幸せになれます.
日本語の方が読みやすいですが,最後はL0x0の方で確認をとるべきです(ちょいちょい違って苦しみました).
回路
なんか色々書いてありますが殆ど気にする必要はありません.横着して振動子の回路図をそのままのっけてありますすみません.フルカラーLEDとかI2Cとか色々機能つけてるのがメモリ不足の原因です.
最低限の書き込み関係の部分は
- PB9-BOOT0 : 10kΩの抵抗でプルダウン(DNPとかかれているR7は未実装.なくてもよさそうです)
- NRST:リセットスイッチを取り付け(重要).念のため10kΩの抵抗でプルアップ.
- PA13:ST-LinkのSWDIOなどと接続
- PA14 : ST-LinkのSWCLKなどと接続
あとは電源とパスコンを取り付けておけば大丈夫かと思います.
NRSTに取り付けるリセットスイッチですが,先述した通り書き込みができなない(ST-Linkと接続できない)場合に非常に重要になります.ST-Linkと接続できない場合の対処法はこちらに書かれています.本当に助かりました…
以下では,PC15にLEDがついているとして話を進めます.
Lチカ
それではいよいよ本題のレジスタ叩きに移っていきます.
STM32のレジスタを叩く際には,エビデイエビナイ電子工作様のブログが大変参考になります.
まずはCubeIDEで新規のプロジェクトを作り,STM32L010F4P6を選んで進みます.
するとCubeIDEの画面が出てきますので,適当にピンをいじって保存するとCore/Src/main.cの中身が自動生成されます.
ここで,折角自動生成してもらったコードをすべて消去し,真っ白にします.
そして以下のコードを入力し,ビルドして書き込めば,PC15のLEDが点滅するかと思います.
#include "stm32l0xx.h"
int main(void)
{
// RCC(Reset and Clock Control)クロックを有効化
RCC->IOPENR |= RCC_IOPENR_GPIOCEN;
GPIOC->MODER &= ~GPIO_MODER_MODE15_Msk;
GPIOC->MODER |= (GPIO_MODE_OUTPUT_PP << GPIO_MODER_MODE15_Pos);
while (1)
{
GPIOC->BSRR |= GPIO_BSRR_BS_15;
for(int i = 0; i<10000; i++);
GPIOC->BRR |= GPIO_BRR_BR_15;
for(int i = 0; i<10000; i++);
}
}
まずレジスタ操作について,マニュアルのRCC_IOPENRレジスタを見るとこんな感じになっています.
当たり前ですが本当に32bitあるんですね…8bitのPICマイコンの時は PORTA = 0b00001111;とか2進数で書けましたが,32ビットでは2進数で書くのは厳しいですね.実はちょっとやろうとしてやめた
各レジスタにはRCC->IOPENRのようにアローでアクセスします.
基本的にレジスタ単位で操作するので,いじりたい対象以外のビットを保持するよう,ORやANDで値を書き換えていきます.
(レジスタ) |= (対象ビット)
でセット(1にする)でき,(レジスタ) &= ~(対象ビット)
でクリア(0にする)できます.賢い…
で,RCC_IOPENRレジスタですが,各機能にクロックを供給する設定を行います.省電力のために各機能へのクロック供給は標準でFalseになっているらしいです(もれなく私はここで引っ掛かりました).
さて,GPIOCにクロックを供給してほしいので,2番ビットをセットする必要があります.
基本的には RCC->IOPENR |= (1<<2);
と書けばよさそうです.
ですがこの書き方だと都度マニュアルを見ないと何の操作をしているのかわかりません.
便利なことに,stm32l010x4.hというヘッダファイルの中に色々定義がしてあって,今回のRCC_IOPENR_GPIOCENのように何をやっているのかわかりやすく記述できます.賢い.
続いてGPIOの設定ですが,MODERレジスタで入出力モードの設定をします.15番ピンを操作したいので,MODE15をいじります.
ここで,いったんマスクしてからビットセットするのは面倒なように見えますが,慣れないうちはこの手間を踏んだ方がいいと思います.
A0からA3まで出力モードにしたかった私がマスクを面倒がって,GPIOA->MODER = 0xFFFF FFF0
; とやったことで,一度書き込むと二度と書き込みできない魔のプログラムを生成したことがあります.
(しかも原因に気づかずマイコン壊れたと思ったので,5つくらいのマイコンにこのコード書きこんであわあわした.上記のリセット押しながら接続で復旧できます).
ポートAのリセット値を見ると全部がFではなく,書き込み関係で重要な場所があると考えられます.気をつけましょう(自戒).
最後に,while文の中でGPIOC_BSRRレジスタとGPIOC->BRRレジスタで出力状態を変更します.
前者をセットするとピンがHigh状態になり,後者をセットするとピンがLow状態になります.
なお,わざわざ別のレジスタを使わなくてもGPIOx_ODRレジスタを操作することでも,出力状態を変化させることができます.
最後に
本当はタイマ関連のこととかもっとL010に特化した話を書くべきだった気がします.また時間とやるきがあれば書きますので,ご容赦ください.お疲れさまでした.
開発・執筆の際に参考にさせていただいたページ様
- STM32 Nucleoをデバッガ ST-Linkとして使う方法
- STM32L010F4P6を使ってみた
- Nucleoで自作基板のSTMマイコンにmbedのプログラムを書き込む方法
- STM32CubeIDEでST-Link-V2が接続できない事象の対処方法
- STM32L010のMbedでの新規Lチカコンパイル (STM32L010F4P6)
他多数
(余談)ChatGPTを用いたレジスタ叩き
実は,上記のコードくらいならChatGPT君が自動生成してくれます.(お願いの文中に回路って書いてあるけどプログラムの間違いですね…)
ちゃんとdelay関数も準備してくれます.
こういうのって例えばC言語で電卓つくるとか汎用的なものは得意だと思ってましたが,マイコンの種別指定して,かつ情報少なそうなレジスタ叩きでも使えるんですねびっくりしました.
これで出てきたコードはちゃんと動くものでした.
レジスタ操作では,欲しい機能を動かすために必要なレジスタの全容をつかめるまではなかなか苦労する印象があります.そこがショートカットされるのは大変便利です.提案されたコードを見ながら,マニュアルを読んでレジスタを確認することができます.
個人的に面白いのは,面倒なレジスタ操作をなくすために上位のライブラリ等が出てきたのに,より上位にChatGPTみたいなのが来た現在では,逆にレジスタ操作するの簡単になったんだなという所です.
とはいえ自動生成コードも完璧でなく,実はこの後I2Cやったときに,ChatGPTコードで動かずかなり苦戦しましたので,お願いすればなんでもできるという段階ではまだなさそうです.参考までに.