今回はレジスタへのアクセスについてお話します。
投稿時の開発環境を記しておきます。
PC:Windows10 OS
IDE: STM32CubeIDE Version1.3.0
Configurator: STM32CubeMX Version5.6.0
Board: STM32Nucleo-F401RE
レジスタにアクセスする
これまでの記事は HAL や Mbed を使ったものばかりで直接レジスタを触るコードは書いていません。
私自身の考えでは HAL という便利なものがあるので、基本的にはそちらを使わせて頂こうと思っています。
でも、レジスタを直接触りたいことがあると思います。
そんな時にどうコードを書いていけば良いのか? 見ていきたいと思います。
紹介したボードのマイコン用日本語ドキュメントは RM0368 を見てください。
汎用タイマであるTIM2の CR2 と CNT レジスタを例に見ていきましょう。
13項に汎用タイマTIM2~5の説明があり、13.4.2項はTIM2~5 CR2レジスタについて書かれています。
また、13.4.10項はTIM2~5 CNTレジスタについて書かれています。
rw と書かれているものは読み書き可能であることを示しています。
まずレジスタの指定方法ですが、周辺機能->レジスタとします。
ここではTIM2について説明するので、例えばマニュアルの13.4.2項にあるCR2レジスタを指定するには TIM2->CR2 とします。
同じく13.4.10項のCNTレジスタを指定するには TIM2->CNT とします。
周辺機能はタイマやGPIOなど同じ機能が複数ある場合には周辺機能のおしりに数値がついたりアルファベッド(ABC…)がついたりします。
例.
xxxはレジスタと仮定します。
RTC->xxx
TIM2->xxx
GPIOA->xxx
32ビットでアクセスする
13.4.10項 カウンタレジスタは TIMx_CNT と書かれています。TIM2の場合、このレジスタは32ビットなので値を読むには例えば
uint32_t value = TIM2->CNT;
とします。値を書き込むには、例えば
TIM2->CNT = 0x00123456;
とします。書き込む値を16進表記で書きましたが、10進数でも2進数でも構いません。
アロー演算子 (->) で記述しているので TIM2 の部分はポインタになっていることがわかります。
stm32f401xe.hを見るとTIM2は以下のように定義されていて
#define TIM2 ((TIM_TypeDef *) TIM2_BASE)
TIM2 は TIM_TypeDef型構造体のポインタになっていることがわかりました。
具体的なアドレスは TIM2_BASE から追いかけていけばわかりますが、ここでは省略します。
TIM_TypeDefを見ると、各レジスタが構造体のメンバになっていることがわかります。
typedef struct
{
__IO uint32_t CR1;
__IO uint32_t CR2;
__IO uint32_t SMCR;
__IO uint32_t DIER;
__IO uint32_t SR;
__IO uint32_t EGR;
__IO uint32_t CCMR1;
__IO uint32_t CCMR2;
__IO uint32_t CCER;
__IO uint32_t CNT;
__IO uint32_t PSC;
__IO uint32_t ARR;
__IO uint32_t RCR;
__IO uint32_t CCR1;
__IO uint32_t CCR2;
__IO uint32_t CCR3;
__IO uint32_t CCR4;
__IO uint32_t BDTR;
__IO uint32_t DCR;
__IO uint32_t DMAR;
__IO uint32_t OR;
} TIM_TypeDef;
その他のペリフェラルも同じように定義されているので、同様に構造体のポインタでアクセスできるようになっていることがわかります。
アクセスする際には 周辺機能->レジスタ という表現を使えば良いことがわかりました。
ビット単位でアクセスする
レジスタの中は機能別にビットが区切られていることも少なくありません。
ここではTIM2の制御レジスタ2 CR2 について見ていきます。
まず1ビットの設定から
このレジスタのビット7に TI1S (TI1Selectの略でしょうか)という機能がつけられています。
アクセスの方法について理解することが目的ですから、その機能について理解する必要はありません。
stm32f401xe.hを見るとTIM2のTI1Sは以下のように定義されています。
#define TIM_CR2_TI1S_Pos (7U)
#define TIM_CR2_TI1S_Msk (0x1UL << TIM_CR2_TI1S_Pos)
#define TIM_CR2_TI1S IM_CR2_TI1S_Msk
Posはポジションを表していて、ビット7の位置にあるので 7U となっています。
Mskはマスクの意味で、値1を7回左シフトした値です。
そして TIM2_CR2_TI1S はマスクの値そのものですから、TI1Sに 1 をセットするには
TIM2->CR2 |= TIM_CR2_TI1S;
と書けば良いですし、 TI1Sに 0 をセット(クリア)するにはチルダを使って
TIM2->CR2 &= ~TIM2_CR2_TI1S_Msk;
と書きます。
上の例では1ビットで 0 か 1 かを決めましたが、次に複数のビットを設定する場合について見てみます。
上で紹介したレジスタのビット6-4でMMS(マスターモード選択)を設定することができます。
アクセスの方法について理解することが目的ですから、その機能について理解する必要はありません。
この部分のマクロは以下のように定義されています。
#define TIM_CR2_MMS_Pos (4U)
#define TIM_CR2_MMS_Msk (0x7UL << TIM_CR2_MMS_Pos)
#define TIM_CR2_MMS TIM_CR2_MMS_Msk
#define TIM_CR2_MMS_0 (0x1UL << TIM_CR2_MMS_Pos)
#define TIM_CR2_MMS_1 (0x2UL << TIM_CR2_MMS_Pos)
#define TIM_CR2_MMS_2 (0x4UL << TIM_CR2_MMS_Pos)
MMSは3ビットあり、設定禁止の値もないので 0~7 の値を設定することができます。
例えば 3 を設定するには
TIM2->CR2 &= ~TIM_CR2_TI1S_Msk;
TIM2->CR2 |= 3 << TIM_CR2_MMS_Pos;
と書きます。5を書くのは 3 の部分を 5 にすれば良いですね。
or するので、1ビットの場合と違い、複数のビットをセットする時には、まずそれらのビットをクリアしておく必要があります。
MMSに 0 をセット(クリア)するにはチルダを使って
TIM2->CR2 &= ~TIM_CR2_MMS_Msk;
と書きます。
プロジェクトを作成する
IDEを起動し、File- New – STM32 Project を選択し、Target Selection ウィンドウが出たら Board Selector タブを選択し Boards List から NUCLEO-F401RE を選択し Next ボタンを押します。
Project 名に F401TIM2 と入力し、Finishボタンを押します。
Initialize all peripherals with their default Mode ? と聞いてくるので Yesを押します。
This kind of project is associated with the STM32CubeMx perspective. Do you want to open this perspective now ? と聞いてくるので Yesを押します。
コードジェネレーターでTIM2を有効にする
レジスタの設定を行うにあたり、TIM2を有効にしておく必要があります。
Pinout & Configuration – Categories – Timers – TIM2 をクリックします。
TIM2 Mode and Configuration – Mode の Closk Source を Internal Clock に設定しビルドしてエラーがないことを確認しておきます。
コーディングする
main()のMX_TIM2_Init()の後に以下のコードを書きます。
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
TIM2->CNT = 0x12345678;
TIM2->CR2 |= TIM_CR2_TI1S; // CR2のTI1Sに1をセット
TIM2->CR2 &= ~TIM_CR2_TI1S_Msk; // 0をセット
TIM2->CR2 &= ~TIM_CR2_MMS_Msk;
TIM2->CR2 |= 3 << TIM_CR2_MMS_Pos; // CR2のMMSに3をセット
TIM2->CR2 &= ~TIM_CR2_MMS_Msk; // 0をセット
ビルドして実行する
ビルド後エラーがないことを確認し、Run – Resume(F8キー) します。
プログラムが停止している状態で、TIM2->CNT = 0x12345678;の行にブレークポイントを貼ります。
Run – Resume するとブレークポイントのところでプログラムが停止します。
View の右側の SFRs を選択し、レジスタのTIM2を選択します。
更に > CR2 の > をクリックし CR2 を展開しておきます。
SFR は Special Function Register の略でペリフェラル(周辺機能)用のレジスタです。
それに対して汎用レジスタは Resigers を選択することで確認することができます。
今回注目する部分に赤線を引いておきました。
これでレジスタの値を確認できるようになりました。
Step Over(F6キー)して1行ずつ実行し、各レジスタの値が思惑通りになっていることを確認します。
いかがでしたか?
思惑通りにレジスタの値を変更できましたか?
コメントを書く