|
| 1 | +// +build mimxrt1062 |
| 2 | + |
| 3 | +package machine |
| 4 | + |
| 5 | +// I2C peripheral abstraction layer for the MIMXRT1062 |
| 6 | + |
| 7 | +import ( |
| 8 | + "device/nxp" |
| 9 | + "errors" |
| 10 | +) |
| 11 | + |
| 12 | +const ( |
| 13 | + TWI_FREQ_DEFAULT = TWI_FREQ_100KHZ // default to StandardMode (100 kHz) |
| 14 | + TWI_FREQ_1MHZ = 1000000 // hardware supports FastModePlus (1 MHz) |
| 15 | +) |
| 16 | + |
| 17 | +var ( |
| 18 | + ErrI2CNotConfigured = errors.New("I2C interface is not yet configured") |
| 19 | +) |
| 20 | + |
| 21 | +// I2CConfig is used to store config info for I2C. |
| 22 | +type I2CConfig struct { |
| 23 | + Frequency uint32 |
| 24 | + SDA Pin |
| 25 | + SCL Pin |
| 26 | +} |
| 27 | + |
| 28 | +func (c I2CConfig) getPins() (sda, scl Pin) { |
| 29 | + if 0 == c.SDA && 0 == c.SCL { |
| 30 | + // default pins if none specified |
| 31 | + return I2C_SDA_PIN, I2C_SCL_PIN |
| 32 | + } |
| 33 | + return c.SDA, c.SCL |
| 34 | +} |
| 35 | + |
| 36 | +type I2C struct { |
| 37 | + Bus *nxp.LPI2C_Type |
| 38 | + |
| 39 | + // these hold the input selector ("daisy chain") values that select which pins |
| 40 | + // are connected to the LPI2C device, and should be defined where the I2C |
| 41 | + // instance is declared (e.g., in the board definition). see the godoc |
| 42 | + // comments on type muxSelect for more details. |
| 43 | + muxSDA, muxSCL muxSelect |
| 44 | + |
| 45 | + // these are copied from I2CConfig, during (*I2C).Configure(I2CConfig), and |
| 46 | + // should be considered read-only for internal reference (i.e., modifying them |
| 47 | + // will have no desirable effect). |
| 48 | + sda, scl Pin |
| 49 | + frequency uint32 |
| 50 | + |
| 51 | + // auxiliary state data used internally |
| 52 | + configured bool |
| 53 | +} |
| 54 | + |
| 55 | +// Configure is intended to setup an I2C interface for transmit/receive. |
| 56 | +func (i2c *I2C) Configure(config I2CConfig) { |
| 57 | + |
| 58 | + // init pins |
| 59 | + i2c.sda, i2c.scl = config.getPins() |
| 60 | + |
| 61 | + // configure the mux and pad control registers |
| 62 | + i2c.sda.Configure(PinConfig{Mode: PinModeI2CSDA}) |
| 63 | + i2c.scl.Configure(PinConfig{Mode: PinModeI2CSCL}) |
| 64 | + |
| 65 | + // configure the mux input selector |
| 66 | + i2c.muxSDA.connect() |
| 67 | + i2c.muxSCL.connect() |
| 68 | + |
| 69 | + i2c.frequency = config.Frequency |
| 70 | + if 0 == i2c.frequency { |
| 71 | + i2c.frequency = TWI_FREQ_DEFAULT |
| 72 | + } |
| 73 | + |
| 74 | + // reset clock and registers, and enable LPI2C module interface |
| 75 | + i2c.reset() |
| 76 | + |
| 77 | + i2c.configured = true |
| 78 | +} |
| 79 | + |
| 80 | +func (i2c *I2C) reset() { |
| 81 | + // software reset all interface registers |
| 82 | + i2c.Bus.MCR.Set(nxp.LPI2C_MCR_RST) |
| 83 | + // configure clock using receiver frequency |
| 84 | + i2c.setFrequency(i2c.frequency) |
| 85 | +} |
| 86 | + |
| 87 | +func (i2c *I2C) setFrequency(freq uint32) { |
| 88 | + const i2cClockStretchTimeout = 15000 // microseconds |
| 89 | + |
| 90 | + var ( |
| 91 | + mccr0 uint32 |
| 92 | + mcfgr1, mcfgr2, mcfgr3 uint32 |
| 93 | + mfcr uint32 |
| 94 | + // MCCR0 |
| 95 | + mccr0CLKLO = func(n uint32) uint32 { return (n << nxp.LPI2C_MCCR0_CLKLO_Pos) & nxp.LPI2C_MCCR0_CLKLO_Msk } |
| 96 | + mccr0CLKHI = func(n uint32) uint32 { return (n << nxp.LPI2C_MCCR0_CLKHI_Pos) & nxp.LPI2C_MCCR0_CLKHI_Msk } |
| 97 | + mccr0DATAVD = func(n uint32) uint32 { return (n << nxp.LPI2C_MCCR0_DATAVD_Pos) & nxp.LPI2C_MCCR0_DATAVD_Msk } |
| 98 | + mccr0SETHOLD = func(n uint32) uint32 { return (n << nxp.LPI2C_MCCR0_SETHOLD_Pos) & nxp.LPI2C_MCCR0_SETHOLD_Msk } |
| 99 | + // MCFGR1/MCFGR2/MCFGR3 |
| 100 | + mcfgr1PRESCALE = func(n uint32) uint32 { return (n << nxp.LPI2C_MCFGR1_PRESCALE_Pos) & nxp.LPI2C_MCFGR1_PRESCALE_Msk } |
| 101 | + mcfgr2FILTSDA = func(n uint32) uint32 { return (n << nxp.LPI2C_MCFGR2_FILTSDA_Pos) & nxp.LPI2C_MCFGR2_FILTSDA_Msk } |
| 102 | + mcfgr2FILTSCL = func(n uint32) uint32 { return (n << nxp.LPI2C_MCFGR2_FILTSCL_Pos) & nxp.LPI2C_MCFGR2_FILTSCL_Msk } |
| 103 | + mcfgr2BUSIDLE = func(n uint32) uint32 { return (n << nxp.LPI2C_MCFGR2_BUSIDLE_Pos) & nxp.LPI2C_MCFGR2_BUSIDLE_Msk } |
| 104 | + mcfgr3PINLOW = func(n uint32) uint32 { return (n << nxp.LPI2C_MCFGR3_PINLOW_Pos) & nxp.LPI2C_MCFGR3_PINLOW_Msk } |
| 105 | + // MFCR |
| 106 | + mfcrRXWATER = func(n uint32) uint32 { return (n << nxp.LPI2C_MFCR_RXWATER_Pos) & nxp.LPI2C_MFCR_RXWATER_Msk } |
| 107 | + mfcrTXWATER = func(n uint32) uint32 { return (n << nxp.LPI2C_MFCR_TXWATER_Pos) & nxp.LPI2C_MFCR_TXWATER_Msk } |
| 108 | + ) |
| 109 | + |
| 110 | + if freq >= TWI_FREQ_1MHZ { |
| 111 | + // I2C FastModePlus 1 MHz |
| 112 | + mccr0 = mccr0CLKHI(9) | mccr0CLKLO(10) | mccr0DATAVD(4) | mccr0SETHOLD(7) |
| 113 | + mcfgr1 = mcfgr1PRESCALE(0) |
| 114 | + mcfgr2 = mcfgr2FILTSDA(1) | mcfgr2FILTSCL(1) | mcfgr2BUSIDLE(2400) // 100us timeout |
| 115 | + mcfgr3 = mcfgr3PINLOW(i2cClockStretchTimeout*24/256 + 1) |
| 116 | + } else if freq >= TWI_FREQ_400KHZ { |
| 117 | + // I2C FastMode 400 kHz |
| 118 | + mccr0 = mccr0CLKHI(26) | mccr0CLKLO(28) | mccr0DATAVD(12) | mccr0SETHOLD(18) |
| 119 | + mcfgr1 = mcfgr1PRESCALE(0) |
| 120 | + mcfgr2 = mcfgr2FILTSDA(2) | mcfgr2FILTSCL(2) | mcfgr2BUSIDLE(3600) // 150us timeout |
| 121 | + mcfgr3 = mcfgr3PINLOW(i2cClockStretchTimeout*24/256 + 1) |
| 122 | + } else { |
| 123 | + // I2C StandardMode 100 kHz |
| 124 | + mccr0 = mccr0CLKHI(55) | mccr0CLKLO(59) | mccr0DATAVD(25) | mccr0SETHOLD(40) |
| 125 | + mcfgr1 = mcfgr1PRESCALE(1) |
| 126 | + mcfgr2 = mcfgr2FILTSDA(5) | mcfgr2FILTSCL(5) | mcfgr2BUSIDLE(3000) // 250us timeout |
| 127 | + mcfgr3 = mcfgr3PINLOW(i2cClockStretchTimeout*12/256 + 1) |
| 128 | + } |
| 129 | + mfcr = mfcrRXWATER(1) | mfcrTXWATER(1) // set FIFO watermarks |
| 130 | + |
| 131 | + i2c.Bus.MCR.Set(0) // disable interface |
| 132 | + i2c.Bus.MCCR0.Set(mccr0) |
| 133 | + i2c.Bus.MCFGR1.Set(mcfgr1) |
| 134 | + i2c.Bus.MCFGR2.Set(mcfgr2) |
| 135 | + i2c.Bus.MCFGR3.Set(mcfgr3) |
| 136 | + i2c.Bus.MCCR1.Set(i2c.Bus.MCCR0.Get()) |
| 137 | + i2c.Bus.MCFGR0.Set(0) |
| 138 | + i2c.Bus.MFCR.Set(mfcr) |
| 139 | + i2c.Bus.MCR.Set(nxp.LPI2C_MCR_MEN) // re-enable interface |
| 140 | +} |
| 141 | + |
| 142 | +func (i2c *I2C) setClock() bool { |
| 143 | + ret := false |
| 144 | + // take control of GPIO pins |
| 145 | + i2c.sda.Configure(PinConfig{Mode: PinOutput}) |
| 146 | + i2c.scl.Configure(PinConfig{Mode: PinOutput}) |
| 147 | + for i := 0; !ret && i <= 10; i++ { |
| 148 | + if ret = i2c.sda.Get() && i2c.scl.Get(); !ret { |
| 149 | + // both pins are not high, clear and re-set SCL |
| 150 | + i2c.scl.Set(false) |
| 151 | + i2c.scl.Set(true) |
| 152 | + } |
| 153 | + } |
| 154 | + // return control of pins to I2C |
| 155 | + i2c.sda.Configure(PinConfig{Mode: PinModeI2CSDA}) |
| 156 | + i2c.scl.Configure(PinConfig{Mode: PinModeI2CSCL}) |
| 157 | + return ret |
| 158 | +} |
0 commit comments