tinygo/src/machine/machine_k210.go
Ayke van Laethem 06564cbdb2 Switch default frequency to 4MHz
Let's use the same default frequency everywhere, for consistency.
It could be any frequency, but 4MHz is already used for other chips and
it seems like a reasonable frequency to me (not too fast for most chips
but still reasonably fast). Oh, and 4MHz is slow enough that it can be
inspected by a Saleae Logic 4 (that sadly has been discontinued).
2020-10-18 22:14:21 +02:00

635 строки
17 КиБ
Go

// +build k210
package machine
import (
"device/kendryte"
"device/riscv"
"errors"
"runtime/interrupt"
)
func CPUFrequency() uint32 {
return 390000000
}
type PinMode uint8
type fpioaPullMode uint8
type PinChange uint8
// Pin modes.
const (
PinInput PinMode = iota
PinInputPullUp
PinInputPullDown
PinOutput
)
// FPIOA internal pull resistors.
const (
fpioaPullNone fpioaPullMode = iota
fpioaPullDown
fpioaPullUp
)
// GPIOHS pin interrupt events.
const (
PinRising PinChange = 1 << iota
PinFalling
PinToggle = PinRising | PinFalling
)
var (
errUnsupportedSPIController = errors.New("SPI controller not supported. Use SPI0 or SPI1.")
errI2CTxAbort = errors.New("I2C transmition has been aborted.")
)
func (p Pin) setFPIOAIOPull(pull fpioaPullMode) {
switch pull {
case fpioaPullNone:
kendryte.FPIOA.IO[uint8(p)].ClearBits(kendryte.FPIOA_IO_PU & kendryte.FPIOA_IO_PD)
case fpioaPullUp:
kendryte.FPIOA.IO[uint8(p)].SetBits(kendryte.FPIOA_IO_PU)
kendryte.FPIOA.IO[uint8(p)].ClearBits(kendryte.FPIOA_IO_PD)
case fpioaPullDown:
kendryte.FPIOA.IO[uint8(p)].ClearBits(kendryte.FPIOA_IO_PU)
kendryte.FPIOA.IO[uint8(p)].SetBits(kendryte.FPIOA_IO_PD)
}
}
// SetFPIOAFunction is used to configure the pin for one of the FPIOA functions.
// Each pin on the Kendryte K210 can be configured with any of the available FPIOA functions.
func (p Pin) SetFPIOAFunction(f FPIOAFunction) {
kendryte.FPIOA.IO[uint8(p)].Set(fpioaFuncDefaults[uint8(f)])
}
// FPIOAFunction returns the current FPIOA function of the pin.
func (p Pin) FPIOAFunction() FPIOAFunction {
return FPIOAFunction((kendryte.FPIOA.IO[uint8(p)].Get() & kendryte.FPIOA_IO_CH_SEL_Msk))
}
// Configure this pin with the given configuration.
// The pin must already be set as GPIO or GPIOHS pin.
func (p Pin) Configure(config PinConfig) {
var input bool
// Check if the current pin's FPIOA function is either GPIO or GPIOHS.
f := p.FPIOAFunction()
if f < FUNC_GPIOHS0 || f > FUNC_GPIO7 {
return // The pin is not configured as GPIO or GPIOHS.
}
// Configure pin.
kendryte.FPIOA.IO[uint8(p)].SetBits(kendryte.FPIOA_IO_OE_EN | kendryte.FPIOA_IO_IE_EN | kendryte.FPIOA_IO_ST | kendryte.FPIOA_IO_DS_Msk)
switch config.Mode {
case PinInput:
p.setFPIOAIOPull(fpioaPullNone)
input = true
case PinInputPullUp:
p.setFPIOAIOPull(fpioaPullUp)
input = true
case PinInputPullDown:
p.setFPIOAIOPull(fpioaPullDown)
input = true
case PinOutput:
p.setFPIOAIOPull(fpioaPullNone)
input = false
}
if f >= FUNC_GPIO0 && f <= FUNC_GPIO7 {
// Converts the IO pin number in the effective GPIO number (based on the FPIOA function).
gpioPin := uint8(f - FUNC_GPIO0)
if input {
kendryte.GPIO.DIRECTION.ClearBits(1 << gpioPin)
} else {
kendryte.GPIO.DIRECTION.SetBits(1 << gpioPin)
}
} else if f >= FUNC_GPIOHS0 && f <= FUNC_GPIOHS31 {
// Converts the IO pin number in the effective GPIOHS number (based on the FPIOA function).
gpioPin := uint8(f - FUNC_GPIOHS0)
if input {
kendryte.GPIOHS.INPUT_EN.SetBits(1 << gpioPin)
kendryte.GPIOHS.OUTPUT_EN.ClearBits(1 << gpioPin)
} else {
kendryte.GPIOHS.OUTPUT_EN.SetBits(1 << gpioPin)
kendryte.GPIOHS.INPUT_EN.ClearBits(1 << gpioPin)
}
}
}
// Set the pin to high or low.
func (p Pin) Set(high bool) {
// Check if the current pin's FPIOA function is either GPIO or GPIOHS.
f := p.FPIOAFunction()
if f < FUNC_GPIOHS0 || f > FUNC_GPIO7 {
return // The pin is not configured as GPIO or GPIOHS.
}
if f >= FUNC_GPIO0 && f <= FUNC_GPIO7 {
gpioPin := uint8(f - FUNC_GPIO0)
if high {
kendryte.GPIO.DATA_OUTPUT.SetBits(1 << gpioPin)
} else {
kendryte.GPIO.DATA_OUTPUT.ClearBits(1 << gpioPin)
}
} else if f >= FUNC_GPIOHS0 && f <= FUNC_GPIOHS31 {
gpioPin := uint8(f - FUNC_GPIOHS0)
if high {
kendryte.GPIOHS.OUTPUT_VAL.SetBits(1 << gpioPin)
} else {
kendryte.GPIOHS.OUTPUT_VAL.ClearBits(1 << gpioPin)
}
}
}
// Get returns the current value of a GPIO pin.
func (p Pin) Get() bool {
// Check if the current pin's FPIOA function is either GPIO or GPIOHS.
f := p.FPIOAFunction()
if f < FUNC_GPIOHS0 || f > FUNC_GPIO7 {
return false // The pin is not configured as GPIO or GPIOHS.
}
var val uint32
if f >= FUNC_GPIO0 && f <= FUNC_GPIO7 {
gpioPin := uint8(f - FUNC_GPIO0)
val = kendryte.GPIO.DATA_INPUT.Get() & (1 << gpioPin)
} else if f >= FUNC_GPIOHS0 && f <= FUNC_GPIOHS31 {
gpioPin := uint8(f - FUNC_GPIOHS0)
val = kendryte.GPIOHS.INPUT_VAL.Get() & (1 << gpioPin)
}
return (val > 0)
}
// Callbacks to be called for GPIOHS pins configured with SetInterrupt.
var pinCallbacks [32]func(Pin)
// SetInterrupt sets an interrupt to be executed when a particular pin changes
// state.
//
// You can pass a nil func to unset the pin change interrupt. If you do so,
// the change parameter is ignored and can be set to any value (such as 0).
// If the pin is already configured with a callback, you must first unset
// this pins interrupt before you can set a new callback.
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {
// Check if the pin is a GPIOHS pin.
f := p.FPIOAFunction()
if f < FUNC_GPIOHS0 || f > FUNC_GPIOHS31 {
return ErrInvalidDataPin
}
gpioPin := uint8(f - FUNC_GPIOHS0)
// Clear all interrupts.
kendryte.GPIOHS.RISE_IE.ClearBits(1 << gpioPin)
kendryte.GPIOHS.FALL_IE.ClearBits(1 << gpioPin)
kendryte.GPIOHS.HIGH_IE.ClearBits(1 << gpioPin)
kendryte.GPIOHS.LOW_IE.ClearBits(1 << gpioPin)
// Clear all the pending bits for this pin.
kendryte.GPIOHS.RISE_IP.SetBits(1 << gpioPin)
kendryte.GPIOHS.FALL_IP.SetBits(1 << gpioPin)
kendryte.GPIOHS.HIGH_IP.SetBits(1 << gpioPin)
kendryte.GPIOHS.LOW_IP.SetBits(1 << gpioPin)
if callback == nil {
if pinCallbacks[gpioPin] != nil {
pinCallbacks[gpioPin] = nil
}
return nil
}
if pinCallbacks[gpioPin] != nil {
// The pin was already configured.
// To properly re-configure a pin, unset it first and set a new
// configuration.
return ErrNoPinChangeChannel
}
pinCallbacks[gpioPin] = callback
// Enable interrupts.
if change&PinRising != 0 {
kendryte.GPIOHS.RISE_IE.SetBits(1 << gpioPin)
}
if change&PinFalling != 0 {
kendryte.GPIOHS.FALL_IE.SetBits(1 << gpioPin)
}
handleInterrupt := func(inter interrupt.Interrupt) {
pin := uint8(inter.GetNumber() - kendryte.IRQ_GPIOHS0)
if kendryte.GPIOHS.RISE_IE.HasBits(1 << pin) {
kendryte.GPIOHS.RISE_IE.ClearBits(1 << pin)
// Acknowledge interrupt atomically.
riscv.AsmFull(
"amoor.w {}, {mask}, {reg}",
map[string]interface{}{
"mask": uint32(1 << pin),
"reg": &kendryte.GPIOHS.RISE_IP.Reg,
})
kendryte.GPIOHS.RISE_IE.SetBits(1 << pin)
}
if kendryte.GPIOHS.FALL_IE.HasBits(1 << pin) {
kendryte.GPIOHS.FALL_IE.ClearBits(1 << pin)
// Acknowledge interrupt atomically.
riscv.AsmFull(
"amoor.w {}, {mask}, {reg}",
map[string]interface{}{
"mask": uint32(1 << pin),
"reg": &kendryte.GPIOHS.FALL_IP.Reg,
})
kendryte.GPIOHS.FALL_IE.SetBits(1 << pin)
}
pinCallbacks[pin](Pin(pin))
}
var ir interrupt.Interrupt
switch f {
case FUNC_GPIOHS0:
ir = interrupt.New(kendryte.IRQ_GPIOHS0, handleInterrupt)
case FUNC_GPIOHS1:
ir = interrupt.New(kendryte.IRQ_GPIOHS1, handleInterrupt)
case FUNC_GPIOHS2:
ir = interrupt.New(kendryte.IRQ_GPIOHS2, handleInterrupt)
case FUNC_GPIOHS3:
ir = interrupt.New(kendryte.IRQ_GPIOHS3, handleInterrupt)
case FUNC_GPIOHS4:
ir = interrupt.New(kendryte.IRQ_GPIOHS4, handleInterrupt)
case FUNC_GPIOHS5:
ir = interrupt.New(kendryte.IRQ_GPIOHS5, handleInterrupt)
case FUNC_GPIOHS6:
ir = interrupt.New(kendryte.IRQ_GPIOHS6, handleInterrupt)
case FUNC_GPIOHS7:
ir = interrupt.New(kendryte.IRQ_GPIOHS7, handleInterrupt)
case FUNC_GPIOHS8:
ir = interrupt.New(kendryte.IRQ_GPIOHS8, handleInterrupt)
case FUNC_GPIOHS9:
ir = interrupt.New(kendryte.IRQ_GPIOHS9, handleInterrupt)
case FUNC_GPIOHS10:
ir = interrupt.New(kendryte.IRQ_GPIOHS10, handleInterrupt)
case FUNC_GPIOHS11:
ir = interrupt.New(kendryte.IRQ_GPIOHS11, handleInterrupt)
case FUNC_GPIOHS12:
ir = interrupt.New(kendryte.IRQ_GPIOHS12, handleInterrupt)
case FUNC_GPIOHS13:
ir = interrupt.New(kendryte.IRQ_GPIOHS13, handleInterrupt)
case FUNC_GPIOHS14:
ir = interrupt.New(kendryte.IRQ_GPIOHS14, handleInterrupt)
case FUNC_GPIOHS15:
ir = interrupt.New(kendryte.IRQ_GPIOHS15, handleInterrupt)
case FUNC_GPIOHS16:
ir = interrupt.New(kendryte.IRQ_GPIOHS16, handleInterrupt)
case FUNC_GPIOHS17:
ir = interrupt.New(kendryte.IRQ_GPIOHS17, handleInterrupt)
case FUNC_GPIOHS18:
ir = interrupt.New(kendryte.IRQ_GPIOHS18, handleInterrupt)
case FUNC_GPIOHS19:
ir = interrupt.New(kendryte.IRQ_GPIOHS19, handleInterrupt)
case FUNC_GPIOHS20:
ir = interrupt.New(kendryte.IRQ_GPIOHS20, handleInterrupt)
case FUNC_GPIOHS21:
ir = interrupt.New(kendryte.IRQ_GPIOHS21, handleInterrupt)
case FUNC_GPIOHS22:
ir = interrupt.New(kendryte.IRQ_GPIOHS22, handleInterrupt)
case FUNC_GPIOHS23:
ir = interrupt.New(kendryte.IRQ_GPIOHS23, handleInterrupt)
case FUNC_GPIOHS24:
ir = interrupt.New(kendryte.IRQ_GPIOHS24, handleInterrupt)
case FUNC_GPIOHS25:
ir = interrupt.New(kendryte.IRQ_GPIOHS25, handleInterrupt)
case FUNC_GPIOHS26:
ir = interrupt.New(kendryte.IRQ_GPIOHS26, handleInterrupt)
case FUNC_GPIOHS27:
ir = interrupt.New(kendryte.IRQ_GPIOHS27, handleInterrupt)
case FUNC_GPIOHS28:
ir = interrupt.New(kendryte.IRQ_GPIOHS28, handleInterrupt)
case FUNC_GPIOHS29:
ir = interrupt.New(kendryte.IRQ_GPIOHS29, handleInterrupt)
case FUNC_GPIOHS30:
ir = interrupt.New(kendryte.IRQ_GPIOHS30, handleInterrupt)
case FUNC_GPIOHS31:
ir = interrupt.New(kendryte.IRQ_GPIOHS31, handleInterrupt)
}
ir.SetPriority(5)
ir.Enable()
return nil
}
type UART struct {
Bus *kendryte.UARTHS_Type
Buffer *RingBuffer
}
var (
UART0 = UART{Bus: kendryte.UARTHS, Buffer: NewRingBuffer()}
)
func (uart UART) Configure(config UARTConfig) {
// Use default baudrate if not set.
if config.BaudRate == 0 {
config.BaudRate = 115200
}
// Use default pins if not set.
if config.TX == 0 && config.RX == 0 {
config.TX = UART_TX_PIN
config.RX = UART_RX_PIN
}
config.TX.SetFPIOAFunction(FUNC_UARTHS_TX)
config.RX.SetFPIOAFunction(FUNC_UARTHS_RX)
div := CPUFrequency()/config.BaudRate - 1
uart.Bus.DIV.Set(div)
uart.Bus.TXCTRL.Set(kendryte.UARTHS_TXCTRL_TXEN)
uart.Bus.RXCTRL.Set(kendryte.UARTHS_RXCTRL_RXEN)
// Enable interrupts on receive.
uart.Bus.IE.Set(kendryte.UARTHS_IE_RXWM)
intr := interrupt.New(kendryte.IRQ_UARTHS, UART0.handleInterrupt)
intr.SetPriority(5)
intr.Enable()
}
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
rxdata := uart.Bus.RXDATA.Get()
c := byte(rxdata)
if uint32(c) != rxdata {
// The rxdata has other bits set than just the low 8 bits. This probably
// means that the 'empty' flag is set, which indicates there is no data
// to be read and the byte is garbage. Ignore this byte.
return
}
uart.Receive(c)
}
func (uart UART) WriteByte(c byte) {
for uart.Bus.TXDATA.Get()&kendryte.UARTHS_TXDATA_FULL != 0 {
}
uart.Bus.TXDATA.Set(uint32(c))
}
type SPI struct {
Bus *kendryte.SPI_Type
}
// SPIConfig is used to store config info for SPI.
type SPIConfig struct {
Frequency uint32
SCK Pin
SDO Pin
SDI Pin
LSBFirst bool
Mode uint8
}
// Configure is intended to setup the SPI interface.
// Only SPI controller 0 and 1 can be used because SPI2 is a special
// peripheral-mode controller and SPI3 is used for flashing.
func (spi SPI) Configure(config SPIConfig) error {
// Use default pins if not set.
if config.SCK == 0 && config.SDO == 0 && config.SDI == 0 {
config.SCK = SPI0_SCK_PIN
config.SDO = SPI0_SDO_PIN
config.SDI = SPI0_SDI_PIN
}
// Enable APB2 clock.
kendryte.SYSCTL.CLK_EN_CENT.SetBits(kendryte.SYSCTL_CLK_EN_CENT_APB2_CLK_EN)
switch spi.Bus {
case kendryte.SPI0:
// Initialize SPI clock.
kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_SPI0_CLK_EN)
kendryte.SYSCTL.CLK_TH1.ClearBits(kendryte.SYSCTL_CLK_TH1_SPI0_CLK_Msk)
// Initialize pins.
config.SCK.SetFPIOAFunction(FUNC_SPI0_SCLK)
config.SDO.SetFPIOAFunction(FUNC_SPI0_D0)
config.SDI.SetFPIOAFunction(FUNC_SPI0_D1)
case kendryte.SPI1:
// Initialize SPI clock.
kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_SPI1_CLK_EN)
kendryte.SYSCTL.CLK_TH1.ClearBits(kendryte.SYSCTL_CLK_TH1_SPI1_CLK_Msk)
// Initialize pins.
config.SCK.SetFPIOAFunction(FUNC_SPI1_SCLK)
config.SDO.SetFPIOAFunction(FUNC_SPI1_D0)
config.SDI.SetFPIOAFunction(FUNC_SPI1_D1)
default:
return errUnsupportedSPIController
}
// Set default frequency.
if config.Frequency == 0 {
config.Frequency = 4000000 // 4MHz
}
baudr := CPUFrequency() / config.Frequency
spi.Bus.BAUDR.Set(baudr)
// Configure SPI mode 0, standard frame format, 8-bit data, little-endian.
spi.Bus.IMR.Set(0)
spi.Bus.DMACR.Set(0)
spi.Bus.DMATDLR.Set(0x10)
spi.Bus.DMARDLR.Set(0)
spi.Bus.SER.Set(0)
spi.Bus.SSIENR.Set(0)
spi.Bus.CTRLR0.Set((7 << 16))
spi.Bus.SPI_CTRLR0.Set(0)
spi.Bus.ENDIAN.Set(0)
return nil
}
// Transfer writes/reads a single byte using the SPI interface.
func (spi SPI) Transfer(w byte) (byte, error) {
spi.Bus.SSIENR.Set(0)
// Set transfer-receive mode.
spi.Bus.CTRLR0.ClearBits(0x3 << 8)
// Enable/disable SPI.
spi.Bus.SSIENR.Set(1)
defer spi.Bus.SSIENR.Set(0)
// Enable/disable device.
spi.Bus.SER.Set(0x1)
defer spi.Bus.SER.Set(0)
spi.Bus.DR0.Set(uint32(w))
// Wait for transfer.
for spi.Bus.SR.Get()&0x05 != 0x04 {
}
// Wait for data.
for spi.Bus.RXFLR.Get() == 0 {
}
return byte(spi.Bus.DR0.Get()), nil
}
// I2C on the K210.
type I2C struct {
Bus *kendryte.I2C_Type
}
// I2CConfig is used to store config info for I2C.
type I2CConfig struct {
Frequency uint32
SCL Pin
SDA Pin
}
// Configure is intended to setup the I2C interface.
func (i2c I2C) Configure(config I2CConfig) error {
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
}
// Enable APB0 clock.
kendryte.SYSCTL.CLK_EN_CENT.SetBits(kendryte.SYSCTL_CLK_EN_CENT_APB0_CLK_EN)
switch i2c.Bus {
case kendryte.I2C0:
// Initialize I2C0 clock.
kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_I2C0_CLK_EN)
kendryte.SYSCTL.CLK_TH5.ReplaceBits(0x03, kendryte.SYSCTL_CLK_TH5_I2C0_CLK_Msk, kendryte.SYSCTL_CLK_TH5_I2C0_CLK_Pos)
// Initialize pins.
config.SDA.SetFPIOAFunction(FUNC_I2C0_SDA)
config.SCL.SetFPIOAFunction(FUNC_I2C0_SCLK)
case kendryte.I2C1:
// Initialize I2C1 clock.
kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_I2C1_CLK_EN)
kendryte.SYSCTL.CLK_TH5.ReplaceBits(0x03, kendryte.SYSCTL_CLK_TH5_I2C1_CLK_Msk, kendryte.SYSCTL_CLK_TH5_I2C1_CLK_Pos)
// Initialize pins.
config.SDA.SetFPIOAFunction(FUNC_I2C1_SDA)
config.SCL.SetFPIOAFunction(FUNC_I2C1_SCLK)
case kendryte.I2C2:
// Initialize I2C2 clock.
kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_I2C2_CLK_EN)
kendryte.SYSCTL.CLK_TH5.ReplaceBits(0x03, kendryte.SYSCTL_CLK_TH5_I2C2_CLK_Msk, kendryte.SYSCTL_CLK_TH5_I2C2_CLK_Pos)
// Initialize pins.
config.SDA.SetFPIOAFunction(FUNC_I2C2_SDA)
config.SCL.SetFPIOAFunction(FUNC_I2C2_SCLK)
}
div := CPUFrequency() / config.Frequency / 16
// Disable controller before setting the prescale register.
i2c.Bus.ENABLE.Set(0)
i2c.Bus.CON.Set(0x63)
// Set prescaler registers.
i2c.Bus.SS_SCL_HCNT.Set(uint32(div))
i2c.Bus.SS_SCL_LCNT.Set(uint32(div))
i2c.Bus.INTR_MASK.Set(0)
i2c.Bus.DMA_CR.Set(0x03)
i2c.Bus.DMA_RDLR.Set(0)
i2c.Bus.DMA_TDLR.Set(0x4)
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 {
// Set peripheral address.
i2c.Bus.TAR.Set(uint32(addr))
// Enable controller.
i2c.Bus.ENABLE.Set(1)
if len(w) != 0 {
i2c.Bus.CLR_TX_ABRT.Set(i2c.Bus.CLR_TX_ABRT.Get())
dataLen := uint32(len(w))
di := 0
for dataLen != 0 {
fifoLen := 8 - i2c.Bus.TXFLR.Get()
if dataLen < fifoLen {
fifoLen = dataLen
}
for i := uint32(0); i < fifoLen; i++ {
i2c.Bus.DATA_CMD.Set(uint32(w[di]))
di += 1
}
if i2c.Bus.TX_ABRT_SOURCE.Get() != 0 {
return errI2CTxAbort
}
dataLen -= fifoLen
}
// Wait for transmition to complete.
for i2c.Bus.STATUS.HasBits(kendryte.I2C_STATUS_ACTIVITY) || !i2c.Bus.STATUS.HasBits(kendryte.I2C_STATUS_TFE) {
}
if i2c.Bus.TX_ABRT_SOURCE.Get() != 0 {
return errI2CTxAbort
}
}
if len(r) != 0 {
dataLen := uint32(len(r))
cmdLen := uint32(len(r))
di := 0
for dataLen != 0 || cmdLen != 0 {
fifoLen := i2c.Bus.RXFLR.Get()
if dataLen < fifoLen {
fifoLen = dataLen
}
for i := uint32(0); i < fifoLen; i++ {
r[di] = byte(i2c.Bus.DATA_CMD.Get())
di += 1
}
dataLen -= fifoLen
fifoLen = 8 - i2c.Bus.TXFLR.Get()
if cmdLen < fifoLen {
fifoLen = cmdLen
}
for i := uint32(0); i < fifoLen; i++ {
i2c.Bus.DATA_CMD.Set(0x100)
}
if i2c.Bus.TX_ABRT_SOURCE.Get() != 0 {
return errI2CTxAbort
}
cmdLen -= fifoLen
}
}
return nil
}