diff options
author | Yannis Huber <[email protected]> | 2020-04-01 13:04:25 +0200 |
---|---|---|
committer | GitHub <[email protected]> | 2020-04-01 13:04:25 +0200 |
commit | 6e86daa95e2b0279d31c142b88c0e99e95cff6c0 (patch) | |
tree | 112ea477f9fb520710bf99c4f1184027e5f4fe27 | |
parent | 62e78c0a26935cf74bf25d29eb25e4562f21a39b (diff) | |
download | tinygo-6e86daa95e2b0279d31c142b88c0e99e95cff6c0.tar.gz tinygo-6e86daa95e2b0279d31c142b88c0e99e95cff6c0.zip |
riscv: add I2C support for the SiFive HiFive1 Rev B board
-rw-r--r-- | src/machine/board_hifive1b.go | 6 | ||||
-rw-r--r-- | src/machine/board_hifive1b_baremetal.go | 7 | ||||
-rw-r--r-- | src/machine/i2c.go | 2 | ||||
-rw-r--r-- | src/machine/machine_fe310.go | 145 |
4 files changed, 159 insertions, 1 deletions
diff --git a/src/machine/board_hifive1b.go b/src/machine/board_hifive1b.go index be0d7eacd..5ce9d7ca9 100644 --- a/src/machine/board_hifive1b.go +++ b/src/machine/board_hifive1b.go @@ -51,3 +51,9 @@ const ( SPI1_MOSI_PIN = D11 SPI1_MISO_PIN = D12 ) + +// I2C pins +const ( + I2C0_SDA_PIN = D18 + I2C0_SCL_PIN = D19 +) diff --git a/src/machine/board_hifive1b_baremetal.go b/src/machine/board_hifive1b_baremetal.go index 729e985de..7c88556f8 100644 --- a/src/machine/board_hifive1b_baremetal.go +++ b/src/machine/board_hifive1b_baremetal.go @@ -10,3 +10,10 @@ var ( Bus: sifive.QSPI1, } ) + +// I2C on the HiFive1 rev B. +var ( + I2C0 = I2C{ + Bus: sifive.I2C0, + } +) diff --git a/src/machine/i2c.go b/src/machine/i2c.go index 7270d918b..25d3180f6 100644 --- a/src/machine/i2c.go +++ b/src/machine/i2c.go @@ -1,4 +1,4 @@ -// +build avr nrf sam stm32,!stm32f4disco +// +build avr nrf sam stm32,!stm32f4disco fe310 package machine diff --git a/src/machine/machine_fe310.go b/src/machine/machine_fe310.go index c85fc8986..0a5048f54 100644 --- a/src/machine/machine_fe310.go +++ b/src/machine/machine_fe310.go @@ -4,6 +4,7 @@ package machine import ( "device/sifive" + "errors" "runtime/interrupt" ) @@ -181,3 +182,147 @@ func (spi SPI) Transfer(w byte) (byte, error) { // return data return byte(spi.Bus.RXDATA.Get() & sifive.QSPI_RXDATA_DATA_Msk), nil } + +// I2C on the FE310-G002. +type I2C struct { + Bus *sifive.I2C_Type +} + +// I2CConfig is used to store config info for I2C. +type I2CConfig struct { + Frequency uint32 + SCL Pin + SDA Pin +} + +var i2cAckExpectedError error = errors.New("I2C write error: expected ACK not NACK") + +// Configure is intended to setup the I2C interface. +func (i2c I2C) Configure(config I2CConfig) error { + var i2cClockFrequency uint32 = 32000000 + if config.Frequency == 0 { + config.Frequency = TWI_FREQ_100KHZ + } + + if config.SDA == 0 && config.SCL == 0 { + config.SDA = I2C0_SDA_PIN + config.SCL = I2C0_SCL_PIN + } + + var prescaler = i2cClockFrequency/(5*config.Frequency) - 1 + + // disable controller before setting the prescale registers + i2c.Bus.CTR.ClearBits(sifive.I2C_CTR_EN) + + // set prescaler registers + i2c.Bus.PRER_LO.Set(uint32(prescaler & 0xff)) + i2c.Bus.PRER_HI.Set(uint32((prescaler >> 8) & 0xff)) + + // enable controller + i2c.Bus.CTR.SetBits(sifive.I2C_CTR_EN) + + config.SDA.Configure(PinConfig{Mode: PinI2C}) + config.SCL.Configure(PinConfig{Mode: PinI2C}) + + return nil +} + +// Tx does a single I2C transaction at the specified address. +// It clocks out the given address, writes the bytes in w, reads back len(r) +// bytes and stores them in r, and generates a stop condition on the bus. +func (i2c I2C) Tx(addr uint16, w, r []byte) error { + var err error + if len(w) != 0 { + // send start/address for write + i2c.sendAddress(addr, true) + + // ACK received (0: ACK, 1: NACK) + if i2c.Bus.CR_SR.HasBits(sifive.I2C_SR_RX_ACK) { + return i2cAckExpectedError + } + + // write data + for _, b := range w { + err = i2c.writeByte(b) + if err != nil { + return err + } + } + } + if len(r) != 0 { + // send start/address for read + i2c.sendAddress(addr, false) + + // ACK received (0: ACK, 1: NACK) + if i2c.Bus.CR_SR.HasBits(sifive.I2C_SR_RX_ACK) { + return i2cAckExpectedError + } + + // read first byte + r[0] = i2c.readByte() + for i := 1; i < len(r); i++ { + // send an ACK + i2c.Bus.CR_SR.Set(^uint32(sifive.I2C_CR_ACK)) + + // read data and send the ACK + r[i] = i2c.readByte() + } + + // send NACK to end transmission + i2c.Bus.CR_SR.Set(sifive.I2C_CR_ACK) + } + + // generate stop condition + i2c.Bus.CR_SR.Set(sifive.I2C_CR_STO) + return nil +} + +// Writes a single byte to the I2C bus. +func (i2c I2C) writeByte(data byte) error { + // Send data byte + i2c.Bus.TXR_RXR.Set(uint32(data)) + + i2c.Bus.CR_SR.Set(sifive.I2C_CR_WR) + + // wait until transmission complete + for i2c.Bus.CR_SR.HasBits(sifive.I2C_SR_TIP) { + } + + // ACK received (0: ACK, 1: NACK) + if i2c.Bus.CR_SR.HasBits(sifive.I2C_SR_RX_ACK) { + return i2cAckExpectedError + } + + return nil +} + +// Reads a single byte from the I2C bus. +func (i2c I2C) readByte() byte { + i2c.Bus.CR_SR.Set(sifive.I2C_CR_RD) + + // wait until transmission complete + for i2c.Bus.CR_SR.HasBits(sifive.I2C_SR_TIP) { + } + + return byte(i2c.Bus.TXR_RXR.Get()) +} + +// Sends the address and start signal. +func (i2c I2C) sendAddress(address uint16, write bool) error { + data := (address << 1) + if !write { + data |= 1 // set read flag in transmit register + } + + // write address to transmit register + i2c.Bus.TXR_RXR.Set(uint32(data)) + + // generate start condition + i2c.Bus.CR_SR.Set((sifive.I2C_CR_STA | sifive.I2C_CR_WR)) + + // wait until transmission complete + for i2c.Bus.CR_SR.HasBits(sifive.I2C_SR_TIP) { + } + + return nil +} |