STM32 LLでSPI通信してみる 後編

  • 2021.01.15
  • LL
STM32 LLでSPI通信してみる 後編

皆さま こんにちは。

今回は LL SPI通信の後編です。

LLってな~に?という方は HALとLL の記事をご覧ください。

投稿時の開発環境を記しておきます。

PC:Windows10 OS
IDE: STM32CubeIDE Version1.5.0
Configurator: STM32CubeMX Version6.1.0
Board: STM32Nucleo-F401RE

コーディングしてみる

Project Explorer の Core – Inc を右クリックし New – Header File を選択し spi.h を入力して Finish を押します。

Project Explorer の Core – Src を右クリックし New – Source File を選択し spi.c を入力して Finish を押します。

h と c それぞれ以下のようにコードを書きます。

spi.h

#ifndef SRC_SPI_H_
#define SRC_SPI_H_

#include <stdint.h> // uint8_t

#define WHO_AM_I 0x0f
#define CTRL_REG1 0x20
#define WAKE_UP 0x90
#define P_ADRS 0x28

#define LPS25HB_DEVICE_CODE 0xbd

void SPI_Comm(void);
uint8_t SPI_LPS25HB_Init(void);
void select(void);
void deSelect(void);

uint8_t send(uint8_t data);

#endif /* SRC_SPI_H_ */

spi.c


#include "spi.h"
#include "stm32f4xx_ll_spi.h"
#include "stm32f4xx_ll_gpio.h"
#include "stm32f4xx.h"

uint8_t SPI_LPS25HB_Init(void)
{
  uint8_t retCode;

  select();
  send(WHO_AM_I | 0x80);
  retCode = send(0);
  deSelect();

  select();
  send(CTRL_REG1);
  send(WAKE_UP);
  deSelect();
  HAL_Delay(1200);

  return retCode;
}

void SPI_Comm(void)
{
  uint8_t sbuf[16];
  uint8_t rbuf[16];

  uint8_t err = 0;
  uint8_t h, m, l;
  uint32_t press;

  sbuf[0] = WHO_AM_I | 0x80;	// Who am I ?

  if (SPI_LPS25HB_Init() == LPS25HB_DEVICE_CODE)
  {
    err = 0;
  }
  else
  {
    err = 1;
  }

  while (1)
  {
    select();
    send((P_ADRS | 0xc0));
    l = send(0);
    m = send(0);
    h = send(0);
    deSelect();

    press = h << 16 | m << 8 | l;
    press >>= 12; // 1/4096

    HAL_Delay(1200);
  }
}

uint8_t send(uint8_t data)
{
  if (LL_SPI_IsActiveFlag_RXNE(SPI1) == SET)
  {
    LL_SPI_ReceiveData8(SPI1);
  }
  LL_SPI_TransmitData8(SPI1, data);
  while( LL_SPI_IsActiveFlag_TXE(SPI1) == RESET)
  {
    ;
  }
  while( LL_SPI_IsActiveFlag_RXNE(SPI1) == RESET)
  {
    ;
  }
    return LL_SPI_ReceiveData8(SPI1);
}

void select(void)
{
  if (LL_SPI_IsEnabled(SPI1) == RESET)
  {
    LL_SPI_Enable(SPI1);
  }
  HAL_Delay(1);
  LL_GPIO_ResetOutputPin(GPIOA, GPIO_PIN_4);
}

void deSelect(void)
{
  LL_GPIO_SetOutputPin(GPIOA, GPIO_PIN_4);
}

main.c

while()ループの手前に

LL_GPIO_SetOutputPin(GPIOA, GPIO_PIN_4); // CS=H
SPI_Comm();

を記述します。

ビルドして実行する

ビルドしてエラーがないことを確認し、 Run – DEbug を選択します。

main.cの SPI_Comm()にブレークポイントを貼り、 Run – Resume を選択しプログラムを実行します。

停止したところで、Window – Show View – SFRs を選択し、

SPI1 の CR1が 0x031f 、CR2が 0x0000 であることを確認します。

F5キーを押してSPI_Comm()の中をステップ実行していきます。

以下の関数の戻り値が LPS25HB_DEVICE_CODE を返して err = 0 になることを確認します。

if (SPI_LPS25HB_Init() == LPS25HB_DEVICE_CODE)
{
  err = 0;
}
else
{
  err = 1;
}

ステップ実行していき、press がそこそこの気圧(単位hPa)になれば成功です。

プログラムの概要

SPI_LPS25HB_Init()関数

select()関数

CSをLに下げる処理だけを書いていたのですが、おかしな動きが見つかったので修正しました。
CSをLに下げる前にSPIがイネーブルになっているか確認し、なっていなければイネーブルにします。
(send()関数内にこの処理を入れると、そこでクロックが動いてしまう不具合が見つかったので、こちらに移動しました)
if()は、なくてもいいかも知れませんね。。
その後、CSをLに下げます。

WHO_AM_I(0x0f) は気圧センサーのレジスタのひとつです。
レジスタのアドレスは6ビットで構成されています。
気圧センサーにアクセスするために、まずレジスタに書き込みを行います。
書き込む際に、書き込みの命令なのか、読み出しの命令なのかを指定します。
読み出しの場合にはレジスタに 0x80 を OR した値を書き込みます。
読み出しの際に更に 0x40を OR すると読み出す度にアドレスが自動でインクリメントされます。

まず、 WHO_AM_I | 0x80 を書き込んで、デバイスコードを読み込む指定をします。
0xbd が読めれば成功です。

send()関数

まず受信バッファにデータが残っていたら、カラ読みして捨てます。
そしてデータを送信し、送信バッファが空になるまで待ちます。
次に受信バッファにデータが入るまで待ち、データを受信します。

この受信したデータはコマンドによって必要な場合もあれば、不要な場合(ゴミ)もあります。

※SPIの特徴ですが、データの送信を開始するためにクロックが生成されます。
そのクロックで送信と同時に受信も行います。
UARTと違ってSPIでは単独に受信できません。従って受信するために疑似データを送信する必要があります。

2回目の send(0) の 引数0が疑似データです。
このsend(0)はデバイスコードを読むために疑似データを送信しています。

次の send(CTRL_REG1) でコントロールレジスタ1への書き込みを指定しています。
そして send(WAKE_UP) でパワーダウン状態を解除し、1秒毎に出力するように設定します。

deSelect()関数

CS=Hに戻します。

まずSPI_LPS25HB_Init()が正しい値を返すことを確認しておきます。

気圧を読んでみる

while (1)
{
  select();
  send((P_ADRS | 0xc0));
  l = send(0);
  m = send(0);
  h = send(0);
  deSelect();

  press = h << 16 | m << 8 | l;
  press >>= 12; // 1/4096

  HAL_Delay(1200);
}

send((P_ADRS | 0xc0)) で気圧を読む指定をします。
P_ADRS は 0x28 です。気圧のデータは3バイト構成で、レジスタは 0x28, 0x29, x02a です。
0xc0 を or して指定することにより、この後 0x28, 0x29, 0x2a の値を続けて読むことができます。

読んだ値を 4096 で割ることにより気圧[hPa]を求めることができます。

ロジアナで波形を観測してみたので、画像を貼っておきます。
波形は上から
CS
CLK
MOSI
MISO
です。

デバイスコードの読み取り

気圧データの読み取り

いかがでしたか?
皆さんはうまく読むことができましたか?

お疲れさまでした。

LLカテゴリの最新記事