LSI Jiu-Jitsu

電子工作とブラジリアン柔術

STM32で遊ぶ (8) - I2CマスターRead

f:id:mohran:20200325222425j:plain

照度センサーを接続してI2Cリードの確認を行いました。

使用したモジュールはこちらです。
aitendoで購入しました。
www.aitendo.com

搭載しているフォトダイオードロームのBH1750と言う型番でした。
上のaitendoの商品ページにデータシートがリンクされています。

I2Cライトと同様にSPLに用意されている関数を使うことで簡単にI2Cリードを行えました。
使用した関数は

void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);
void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);

の4つです。
I2C_AcknowledgeConfigの第2引数にENABLEを設定するとAck送信、DISABLEを設定するとNack送信となるようですので、リードの最終バイト時にはDISABLEを設定するようにします。

BH1750の使い方としては、0x01をライトしてPower On状態にしてから計測モードをライトすることで計測開始となるようです。
計測モードはスタンダードの「H-Resolution Mode」を使用しました。
計測時間に120msecかかるので、TIM6を使って133msec待ってから16bitの測定結果をリードすることにしました。
f:id:mohran:20200325230920j:plain

MTregの値がデフォルトの69(0x45)だと測定結果を0.83倍することでLuxに変換できるようです。
f:id:mohran:20200325230932j:plain

プログラムはこのような感じで組みました。

#include <stdio.h>
#include "stm32f4xx.h"

#include "stm32f4xx_tim.h"
#include "stm32f4xx_i2c.h"

void __auto_semihosting(void) __attribute__((alias("main")));
extern void initialise_monitor_handles(void);

#define BH1750_W  ((0x23 << 1) | 0x0)  // 0100 0110
#define BH1750_R  ((0x23 << 1) | 0x1)  // 0100 0111

#define ONE_TIME_H2   0x21
#define ONE_TIME_H    0x20
#define ONE_TIME_L    0x23

int main(void){
  unsigned short result;
  float          lux;

  initialise_monitor_handles();

  // Clock Enable
  RCC->APB1ENR |= (RCC_APB1ENR_I2C1EN | RCC_APB1ENR_TIM6EN);
  RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;

  // PB8 - I2C1_SCL
  // PB9 - I2C1_SDA
  GPIOB->MODER   |= ((0x2 << 18) | (0x2 << 16));  // Alternate function mode
  GPIOB->PUPDR   |= ((0x1 << 18) | (0x1 << 16));  // Pull-up
  GPIOB->OTYPER  |= ((0x1 <<  9) | (0x1 <<  8));  // Output open-drain
  GPIOB->AFR[1]  |= ((0x4 <<  4) | (0x4 <<  0));  // AF4

  // TIM6
  TIM6->PSC = 9999;  // 11.111nsec(90MHz) * 10,000 = 111.111usec
  TIM6->ARR = 1200;  // 111.111usec * 1200 = 133.333msec
  TIM6->CNT = 0;
  TIM6->CR1 = 1;

  // I2C1 Reset
  I2C1->CR1   = (0x1 << 15);
  for(int i=0; i<1000; i++){ asm("NOP"); };
  I2C1->CR1   = (0x0 << 15);

  I2C1->CR2   = 45;
  I2C1->CCR   = ((0 << 15) | (0 << 14) | 226);  // 22.222nsec(45MHz) * 226 * 2 = 10usec(100KHz)
  I2C1->TRISE = 46;

  // I2C1 Enable
  I2C1->CR1 |= 1;

  // BH1750 Power ON
  I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
  I2C_SendData(I2C1, BH1750_W);    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
  I2C_SendData(I2C1, 0x01);        while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
  I2C_GenerateSTOP(I2C1, ENABLE);

  while(1){
    // BH1750 Measurement start
    I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
    I2C_SendData(I2C1, BH1750_W);    while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
    I2C_SendData(I2C1, ONE_TIME_H);  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
    I2C_GenerateSTOP(I2C1, ENABLE);

    // Wait 133msec
    TIM6->SR  = 0x0;
    TIM6->CNT = 0x0;
    while(!(TIM6->SR));

    // BH1750 Result read
    I2C_GenerateSTART(I2C1, ENABLE);      while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
    I2C_SendData(I2C1, BH1750_R);         while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
    I2C_AcknowledgeConfig(I2C1, ENABLE);  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
    result = I2C_ReceiveData(I2C1);  // High Byte
    result <<= 8;
    I2C_AcknowledgeConfig(I2C1, DISABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
    result |= I2C_ReceiveData(I2C1); // Low Byte
    I2C_GenerateSTOP(I2C1, ENABLE);

    lux = result * 0.83;

    printf("0x%04x : %d -- %f lux\n", result, result, lux);
  }

  return(0);
}


計測結果をprintfで表示します。
この際、0.83倍したfloatを表示できるようにするためにプロジェクトのプロパティからリンカオプションへ

-u _printf_float

を追加します。
f:id:mohran:20200325232729j:plain

測定結果のリード値とLuxへ変換した値が表示できました。
f:id:mohran:20200325232812j:plain

以前Amazonで購入した照度計がどこかにしまってあるので、今度探し出して値を比べてみたいと思います。

[参考記事]