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_TX_PIN = D1
|
||||||
UART_RX_PIN = D0
|
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
|
package machine
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ package machine
|
||||||
import (
|
import (
|
||||||
"device/arm"
|
"device/arm"
|
||||||
"device/sam"
|
"device/sam"
|
||||||
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CPU_FREQUENCY = 48000000
|
const CPU_FREQUENCY = 48000000
|
||||||
|
@ -407,3 +408,238 @@ func handleUART0() {
|
||||||
bufferPut(byte((sam.SERCOM0_USART.DATA & 0xFF)))
|
bufferPut(byte((sam.SERCOM0_USART.DATA & 0xFF)))
|
||||||
sam.SERCOM0_USART.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC
|
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()
|
initClocks()
|
||||||
initRTC()
|
initRTC()
|
||||||
initUARTClock()
|
initUARTClock()
|
||||||
|
initI2CClock()
|
||||||
|
|
||||||
// connect to UART
|
// connect to UART
|
||||||
machine.UART0.Configure(machine.UARTConfig{})
|
machine.UART0.Configure(machine.UARTConfig{})
|
||||||
|
@ -306,3 +307,17 @@ func initUARTClock() {
|
||||||
sam.GCLK_CLKCTRL_CLKEN)
|
sam.GCLK_CLKCTRL_CLKEN)
|
||||||
waitForSync()
|
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()
|
||||||
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче