前回の記事 で 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 です。
いかがでしたか、うまく読み書きできましたか?