Help writing I2C library

Hi :wave:

As a matter of fact I’ve just finished up with a C++ HAL driver for the I2C core of the FE310-G002. The high-level usage of the code looks like this:

#include <cassert>
#include <snowfox/hal/riscv64/FE310/Io.h>
#include <snowfox/hal/riscv64/FE310/Clock.h>
#include <snowfox/hal/riscv64/FE310/I2cMaster.h>
/* ... */
using namespace snowfox::hal;
/* ... */
static uint32_t const HFXOSCIN_FREQ_Hz     =  16'000'000UL;
static uint32_t const CORECLK_FREQ_Hz      = 200'000'000UL;

static uint8_t  const M24LR_ADDRESS_SYSTEM = (0x57 << 1);
static uint16_t const M24LR_REG_ICREF      = 0x091C;
static uint8_t  const M24LR_IC_REF         = 0x5A;
/* ... */
uint8_t m24lr_readByte(uint16_t const reg_addr)
{
  i2c_master.begin(M24LR_ADDRESS_SYSTEM, false);

  uint8_t const reg_addr_msb = static_cast<uint8_t>((reg_addr & 0xFF00) / 256);
  uint8_t const reg_addr_lsb = static_cast<uint8_t>((reg_addr & 0x00FF));

  i2c_master.write(reg_addr_msb);
  i2c_master.write(reg_addr_lsb);

  uint8_t data = 0;
  i2c_master.requestFrom(M24LR_ADDRESS_SYSTEM, &data, 1);

  return data;
}
/* ... */
FE310::Clock     clock     (&PRCI_HFXOSCCFG, &PRCI_PLLCFG, &PRCI_PLLOUTDIV, HFXOSCIN_FREQ_Hz);
FE310::I2cMaster i2c_master(&I2C0_PRESC_LOW, &I2C0_PRESC_HIGH, &I2C0_CONTROL, &I2C0_DATA, &I2C0_CMD_STATUS, &GPIO0_IOF_EN, &GPIO0_IOF_SEL, CORECLK_FREQ_Hz);
/* ... */
int snowfox_main()
{
  clock.setClockFreq(static_cast<uint8_t>(FE310::ClockId::coreclk), CORECLK_FREQ_Hz);

  i2c_master.setI2cClock(interface::I2cClock::F_100_kHz);

  uint8_t const m24lr_ref_id = m24lr_readByte(M24LR_REG_ICREF);
  assert(m24lr_ref_id == M24LR_IC_REF);

  for(;;) { }
  return 0;
}

The full example code can be found here.

If you want to compile it for your HiFive 1 Rev. B you’ve got to follow the following steps:

  • Download/Install RISCV64 Compiler (you probably already did that)
sudo wget https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-8.2.0-2019.02.0-x86_64-linux-ubuntu14.tar.gz
sudo tar -xzvf riscv64-unknown-elf-gcc-8.2.0-2019.02.0-x86_64-linux-ubuntu14.tar.gz
export PATH=$PATH:/opt/riscv64-unknown-elf-gcc-8.2.0-2019.02.0-x86_64-linux-ubuntu14/bin
  • Clone the Snowfox Repository with all examples
git clone --recurse-submodules https://github.com/snowfox-project/snowfox.git
cd snowfox
  • Build the SiFive FE310-G002 I2C example (the compiled binary is located in build/bin/hal-fe310-i2c )
.ci/script/run-build-example.sh examples/hal/FE310/hal-fe310-i2c

If you want to take a look at the code you’ll find it here. The code is contained in files I2CMaster.cpp, I2CMasterBase.cpp and I2CMasterLowLevel.cpp .

One thing that’s a bit tricky is that the command and status register are found at the same address and you access the command register by writing to it and the status register by reading from it. So if you are used to setting bits the usual way, that is reg |= (1<<bit_pos) you’ll be in for a surprise since that will be expanded to reg = reg | (1<<bit_pos) and I think the problem is now obvious :wink:

2 Likes