皆さま こんにちは。
今回は 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
です。
デバイスコードの読み取り
気圧データの読み取り
いかがでしたか?
皆さんはうまく読むことができましたか?
お疲れさまでした。