STM32 LLのI2C メモリーアクセス関数

  • 2021.01.30
  • LL
STM32 LLのI2C メモリーアクセス関数

前回の記事 で LL I2C の概要は理解できたと思います。

今回はLL を使ってI2Cメモリー(EEPROM)にアクセスする関数をつくってみました。

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

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

さっとつくったので動作確認を行ってからお使いください。

コーディングしてみる

define とプロトタイプ

#define DEVICE_ADDR 0xa0
#define SIZE 64
void i2cSetAck(I2C_TypeDef *I2Cx, uint32_t ack);
void i2cStart(I2C_TypeDef *I2Cx);
void i2cStopAfterWriting(I2C_TypeDef *I2Cx);
void i2cStopAfterReading(I2C_TypeDef *I2Cx);
void i2cWrite(I2C_TypeDef *I2Cx, uint8_t data);
void i2cAddress(I2C_TypeDef *I2Cx, uint8_t addr);
uint8_t i2cRead(I2C_TypeDef *I2Cx);
void i2cLLMemWrite(I2C_TypeDef *I2Cx, uint8_t devAdrs, uint16_t memAdrs, uint8_t *buffer, uint16_t size);
void i2cLLMemRead(I2C_TypeDef *I2Cx, uint8_t devAdrs, uint16_t memAdrs, uint8_t *buffer, uint16_t size);

関数の実装

void i2cLLMemWrite(I2C_TypeDef *I2Cx, uint8_t devAdrs, uint16_t memAdrs, uint8_t *buffer, uint16_t size)
{
  i2cSetAck(I2Cx, LL_I2C_ACK);
  i2cStart(I2Cx);
  i2cAddress(I2Cx, devAdrs);

  uint8_t tempAdrs = memAdrs >> 8;
  i2cWrite(I2Cx, tempAdrs);
  tempAdrs = memAdrs & 0xff;
  i2cWrite(I2Cx, tempAdrs);

  for (int i = 0; i < size; i++)
  {
    i2cWrite(I2Cx, *buffer++);
  }
  i2cStopAfterWriting(I2Cx);
}

void i2cLLMemRead(I2C_TypeDef *I2Cx, uint8_t devAdrs, uint16_t memAdrs, uint8_t *buffer, uint16_t size)
{
  i2cSetAck(I2Cx, LL_I2C_ACK);
  i2cStart(I2Cx);
  i2cAddress(I2Cx, devAdrs);

  uint8_t tempAdrs = memAdrs >> 8;
  i2cWrite(I2Cx, tempAdrs);
  tempAdrs = memAdrs & 0xff;
  i2cWrite(I2Cx, tempAdrs);

  i2cStart(I2Cx);	// Restart

  if (1 == size)
  {
    i2cSetAck(I2Cx, LL_I2C_NACK);
  }

  i2cAddress(I2Cx, devAdrs | 0x1);	// Readの指示

  for (int i = 0; i < size; i++)
  {
    if ((size != 1) && (size - 1 == i))
    {
      i2cSetAck(I2Cx, LL_I2C_NACK);
    }
    *buffer++ = i2cRead(I2Cx);
  }
  i2cStopAfterReading(I2Cx);
}

void i2cSetAck(I2C_TypeDef *I2Cx, uint32_t ack)
{
  LL_I2C_AcknowledgeNextData(I2Cx, ack);
}

void i2cStart(I2C_TypeDef *I2Cx)
{
  LL_I2C_GenerateStartCondition(I2Cx);	// Start Condition 発行
  while (!LL_I2C_IsActiveFlag_SB(I2Cx))	// 認識するまで待つ
  {
    ;
  }
}

void i2cStopAfterWriting(I2C_TypeDef *I2Cx)
{
  while (!LL_I2C_IsActiveFlag_TXE(I2Cx))
  {
    ;
  }
  LL_I2C_GenerateStopCondition(I2Cx);	// Stop Condition 発行
}

void i2cStopAfterReading(I2C_TypeDef *I2Cx)
{
  LL_I2C_GenerateStopCondition(I2Cx);	// Stop Condition 発行
}

void i2cWrite(I2C_TypeDef *I2Cx, uint8_t data)
{
  while (!LL_I2C_IsActiveFlag_TXE(I2Cx))
  {
	  ;
  }
  LL_I2C_TransmitData8(I2Cx, data);
  while (!LL_I2C_IsActiveFlag_BTF(I2Cx))
  {
	  ;
  }
}

uint8_t i2cRead(I2C_TypeDef *I2Cx)
{
  while (!LL_I2C_IsActiveFlag_RXNE(I2Cx))
  {
	  ;
  }
  return LL_I2C_ReceiveData8(I2Cx);
}

void i2cAddress(I2C_TypeDef *I2Cx, uint8_t addr)
{
  LL_I2C_TransmitData8(I2Cx, addr);
  while (!LL_I2C_IsActiveFlag_ADDR(I2Cx))
  {
    ;
  }
  LL_I2C_IsActiveFlag_BUSY(I2Cx);	// Read SR2 (Dummy)
}

動作確認用のコード
err が 0 なら成功です。

uint8_t sBuf[SIZE] = {0};
uint8_t rBuf[SIZE] = {0};

for (int i = 0; i < SIZE; i++)
{
  sBuf[i] = i;
}

i2cLLMemWrite(I2C1, DEVICE_ADDR, 0, sBuf, SIZE);
HAL_Delay(5); // ちょい休憩
i2cLLMemRead(I2C1, DEVICE_ADDR, 0, rBuf, SIZE);

int err = 0;

for (int i = 0; i < SIZE; i++)
{
  if (sBuf[i] != rBuf[i])
  {
    err |= 1;
  }
}

コメント

ライト後にリードする前に「ちょい休憩」が必要なようです。
リード時に、どこかで NAK を送ってスレーブに終了を伝えますが、そのあたりが少しややこしいです。

エラーやタイムアウトの処理は実装されていません。

それからページを超えた連続のアクセスはできませんのでご注意ください。
今回使ったメモリーは最大 64 です。

いかがでしたか、うまく読み書きできましたか?

LLカテゴリの最新記事