machine/samd21: I2C implementation
Signed-off-by: Ron Evans <ron@hybridgroup.com>
Этот коммит содержится в:
родитель
38c5e384af
коммит
3ebf464da2
4 изменённых файлов: 258 добавлений и 1 удалений
|
@ -29,3 +29,9 @@ const (
|
|||
UART_TX_PIN = D1
|
||||
UART_RX_PIN = D0
|
||||
)
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA_PIN = 22 // // SDA: SERCOM3/PAD[0]
|
||||
SCL_PIN = 23 // // SCL: SERCOM3/PAD[1]
|
||||
)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// +build avr nrf stm32f103xx
|
||||
// +build avr nrf sam stm32f103xx
|
||||
|
||||
package machine
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ package machine
|
|||
import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const CPU_FREQUENCY = 48000000
|
||||
|
@ -407,3 +408,238 @@ func handleUART0() {
|
|||
bufferPut(byte((sam.SERCOM0_USART.DATA & 0xFF)))
|
||||
sam.SERCOM0_USART.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC
|
||||
}
|
||||
|
||||
// I2C on the SAMD21.
|
||||
type I2C struct {
|
||||
Bus *sam.SERCOM_I2CM_Type
|
||||
}
|
||||
|
||||
// Since the I2C interfaces on the SAMD21 use the SERCOMx peripherals,
|
||||
// you can have multiple ones. we currently only implement one.
|
||||
var (
|
||||
I2C0 = I2C{Bus: sam.SERCOM3_I2CM}
|
||||
)
|
||||
|
||||
// I2CConfig is used to store config info for I2C.
|
||||
type I2CConfig struct {
|
||||
Frequency uint32
|
||||
SCL uint8
|
||||
SDA uint8
|
||||
}
|
||||
|
||||
const (
|
||||
// Default rise time in nanoseconds, based on 4.7K ohm pull up resistors
|
||||
riseTimeNanoseconds = 125
|
||||
|
||||
// wire bus states
|
||||
wireUnknownState = 0
|
||||
wireIdleState = 1
|
||||
wireOwnerState = 2
|
||||
wireBusyState = 3
|
||||
|
||||
// wire commands
|
||||
wireCmdNoAction = 0
|
||||
wireCmdRepeatStart = 1
|
||||
wireCmdRead = 2
|
||||
wireCmdStop = 3
|
||||
)
|
||||
|
||||
const i2cTimeout = 1000
|
||||
|
||||
// Configure is intended to setup the I2C interface.
|
||||
func (i2c I2C) Configure(config I2CConfig) {
|
||||
// Default I2C bus speed is 100 kHz.
|
||||
if config.Frequency == 0 {
|
||||
config.Frequency = TWI_FREQ_100KHZ
|
||||
}
|
||||
|
||||
// reset SERCOM3
|
||||
i2c.Bus.CTRLA |= sam.SERCOM_I2CM_CTRLA_SWRST
|
||||
for (i2c.Bus.CTRLA&sam.SERCOM_I2CM_CTRLA_SWRST) > 0 ||
|
||||
(i2c.Bus.SYNCBUSY&sam.SERCOM_I2CM_SYNCBUSY_SWRST) > 0 {
|
||||
}
|
||||
|
||||
// Set i2c master mode
|
||||
//SERCOM_I2CM_CTRLA_MODE( I2C_MASTER_OPERATION )
|
||||
i2c.Bus.CTRLA = (sam.SERCOM_I2CM_CTRLA_MODE_I2C_MASTER << sam.SERCOM_I2CM_CTRLA_MODE_Pos) // |
|
||||
|
||||
i2c.SetBaudRate(config.Frequency)
|
||||
|
||||
// Enable I2CM port.
|
||||
// sercom->USART.CTRLA.bit.ENABLE = 0x1u;
|
||||
i2c.Bus.CTRLA |= sam.SERCOM_I2CM_CTRLA_ENABLE
|
||||
for (i2c.Bus.SYNCBUSY & sam.SERCOM_I2CM_SYNCBUSY_ENABLE) > 0 {
|
||||
}
|
||||
|
||||
// set bus idle mode
|
||||
i2c.Bus.STATUS |= (wireIdleState << sam.SERCOM_I2CM_STATUS_BUSSTATE_Pos)
|
||||
for (i2c.Bus.SYNCBUSY & sam.SERCOM_I2CM_SYNCBUSY_SYSOP) > 0 {
|
||||
}
|
||||
|
||||
// enable pins
|
||||
GPIO{SDA_PIN}.Configure(GPIOConfig{Mode: GPIO_SERCOM})
|
||||
GPIO{SCL_PIN}.Configure(GPIOConfig{Mode: GPIO_SERCOM})
|
||||
}
|
||||
|
||||
// SetBaudRate sets the communication speed for the I2C.
|
||||
func (i2c I2C) SetBaudRate(br uint32) {
|
||||
// Synchronous arithmetic baudrate, via Arduino SAMD implementation:
|
||||
// SystemCoreClock / ( 2 * baudrate) - 5 - (((SystemCoreClock / 1000000) * WIRE_RISE_TIME_NANOSECONDS) / (2 * 1000));
|
||||
baud := CPU_FREQUENCY/(2*br) - 5 - (((CPU_FREQUENCY / 1000000) * riseTimeNanoseconds) / (2 * 1000))
|
||||
i2c.Bus.BAUD = sam.RegValue(baud)
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// wait until transmission complete
|
||||
timeout := i2cTimeout
|
||||
for (i2c.Bus.INTFLAG & sam.SERCOM_I2CM_INTFLAG_MB) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("I2C timeout on ready to write data")
|
||||
}
|
||||
}
|
||||
|
||||
// ACK received (0: ACK, 1: NACK)
|
||||
if (i2c.Bus.STATUS & sam.SERCOM_I2CM_STATUS_RXNACK) > 0 {
|
||||
return errors.New("I2C write error: expected ACK not NACK")
|
||||
}
|
||||
|
||||
// write data
|
||||
for _, b := range w {
|
||||
err = i2c.WriteByte(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = i2c.signalStop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(r) != 0 {
|
||||
// send start/address for read
|
||||
i2c.sendAddress(addr, false)
|
||||
|
||||
// wait transmission complete
|
||||
for (i2c.Bus.INTFLAG & sam.SERCOM_I2CM_INTFLAG_SB) == 0 {
|
||||
// If the slave NACKS the address, the MB bit will be set.
|
||||
// In that case, send a stop condition and return error.
|
||||
if (i2c.Bus.INTFLAG & sam.SERCOM_I2CM_INTFLAG_MB) > 0 {
|
||||
i2c.Bus.CTRLB |= (wireCmdStop << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Stop condition
|
||||
return errors.New("I2C read error: expected ACK not NACK")
|
||||
}
|
||||
}
|
||||
|
||||
// ACK received (0: ACK, 1: NACK)
|
||||
if (i2c.Bus.STATUS & sam.SERCOM_I2CM_STATUS_RXNACK) > 0 {
|
||||
return errors.New("I2C read error: expected ACK not NACK")
|
||||
}
|
||||
|
||||
// read first byte
|
||||
r[0] = i2c.readByte()
|
||||
for i := 1; i < len(r); i++ {
|
||||
// Send an ACK
|
||||
i2c.Bus.CTRLB &^= sam.SERCOM_I2CM_CTRLB_ACKACT
|
||||
|
||||
i2c.signalRead()
|
||||
|
||||
// Read data and send the ACK
|
||||
r[i] = i2c.readByte()
|
||||
}
|
||||
|
||||
// Send NACK to end transmission
|
||||
i2c.Bus.CTRLB |= sam.SERCOM_I2CM_CTRLB_ACKACT
|
||||
|
||||
err = i2c.signalStop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte to the I2C bus.
|
||||
func (i2c I2C) WriteByte(data byte) error {
|
||||
// Send data byte
|
||||
i2c.Bus.DATA = sam.RegValue8(data)
|
||||
|
||||
// wait until transmission successful
|
||||
timeout := i2cTimeout
|
||||
for (i2c.Bus.INTFLAG & sam.SERCOM_I2CM_INTFLAG_MB) == 0 {
|
||||
// check for bus error
|
||||
if (sam.SERCOM3_I2CM.STATUS & sam.SERCOM_I2CM_STATUS_BUSERR) > 0 {
|
||||
return errors.New("I2C bus error")
|
||||
}
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("I2C timeout on write data")
|
||||
}
|
||||
}
|
||||
|
||||
if (i2c.Bus.STATUS & sam.SERCOM_I2CM_STATUS_RXNACK) > 0 {
|
||||
return errors.New("I2C write error: expected ACK not NACK")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendAddress 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
|
||||
}
|
||||
|
||||
// wait until bus ready
|
||||
timeout := i2cTimeout
|
||||
for (i2c.Bus.STATUS&(wireIdleState<<sam.SERCOM_I2CM_STATUS_BUSSTATE_Pos)) == 0 &&
|
||||
(i2c.Bus.STATUS&(wireOwnerState<<sam.SERCOM_I2CM_STATUS_BUSSTATE_Pos)) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("I2C timeout on bus ready")
|
||||
}
|
||||
}
|
||||
i2c.Bus.ADDR = sam.RegValue(data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) signalStop() error {
|
||||
i2c.Bus.CTRLB |= (wireCmdStop << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Stop command
|
||||
timeout := i2cTimeout
|
||||
for (i2c.Bus.SYNCBUSY & sam.SERCOM_I2CM_SYNCBUSY_SYSOP) > 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("I2C timeout on signal stop")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) signalRead() error {
|
||||
i2c.Bus.CTRLB |= (wireCmdRead << sam.SERCOM_I2CM_CTRLB_CMD_Pos) // Read command
|
||||
timeout := i2cTimeout
|
||||
for (i2c.Bus.SYNCBUSY & sam.SERCOM_I2CM_SYNCBUSY_SYSOP) > 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errors.New("I2C timeout on signal read")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i2c I2C) readByte() byte {
|
||||
for (i2c.Bus.INTFLAG & sam.SERCOM_I2CM_INTFLAG_SB) == 0 {
|
||||
}
|
||||
return byte(i2c.Bus.DATA)
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ func init() {
|
|||
initClocks()
|
||||
initRTC()
|
||||
initUARTClock()
|
||||
initI2CClock()
|
||||
|
||||
// connect to UART
|
||||
machine.UART0.Configure(machine.UARTConfig{})
|
||||
|
@ -306,3 +307,17 @@ func initUARTClock() {
|
|||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
waitForSync()
|
||||
}
|
||||
|
||||
func initI2CClock() {
|
||||
// Turn on clock to SERCOM3 for I2C0
|
||||
sam.PM.APBCMASK |= sam.PM_APBCMASK_SERCOM3_
|
||||
|
||||
// Use GCLK0 for SERCOM3 aka I2C0
|
||||
// GCLK_CLKCTRL_ID( clockId ) | // Generic Clock 0 (SERCOMx)
|
||||
// GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is source
|
||||
// GCLK_CLKCTRL_CLKEN ;
|
||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_SERCOM3_CORE << sam.GCLK_CLKCTRL_ID_Pos) |
|
||||
(sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
||||
sam.GCLK_CLKCTRL_CLKEN)
|
||||
waitForSync()
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче