aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorYannis Huber <[email protected]>2020-04-01 13:04:25 +0200
committerGitHub <[email protected]>2020-04-01 13:04:25 +0200
commit6e86daa95e2b0279d31c142b88c0e99e95cff6c0 (patch)
tree112ea477f9fb520710bf99c4f1184027e5f4fe27
parent62e78c0a26935cf74bf25d29eb25e4562f21a39b (diff)
downloadtinygo-6e86daa95e2b0279d31c142b88c0e99e95cff6c0.tar.gz
tinygo-6e86daa95e2b0279d31c142b88c0e99e95cff6c0.zip
riscv: add I2C support for the SiFive HiFive1 Rev B board
-rw-r--r--src/machine/board_hifive1b.go6
-rw-r--r--src/machine/board_hifive1b_baremetal.go7
-rw-r--r--src/machine/i2c.go2
-rw-r--r--src/machine/machine_fe310.go145
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
+}