github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_mimxrt1062_i2c.go (about) 1 //go:build mimxrt1062 2 3 package machine 4 5 // I2C peripheral abstraction layer for the MIMXRT1062 6 7 import ( 8 "device/nxp" 9 ) 10 11 // I2CConfig is used to store config info for I2C. 12 type I2CConfig struct { 13 Frequency uint32 14 SDA Pin 15 SCL Pin 16 } 17 18 type I2C struct { 19 Bus *nxp.LPI2C_Type 20 21 // these pins are initialized by each global I2C variable declared in the 22 // board_teensy4x.go file according to the board manufacturer's default pin 23 // mapping. they can be overridden with the I2CConfig argument given to 24 // (*I2C) Configure(I2CConfig). 25 sda, scl Pin 26 27 // these hold the input selector ("daisy chain") values that select which pins 28 // are connected to the LPI2C device, and should be defined where the I2C 29 // instance is declared (e.g., in the board definition). see the godoc 30 // comments on type muxSelect for more details. 31 muxSDA, muxSCL muxSelect 32 } 33 34 type i2cDirection bool 35 36 const ( 37 directionWrite i2cDirection = false 38 directionRead i2cDirection = true 39 ) 40 41 func (dir i2cDirection) shift(addr uint16) uint32 { 42 if addr <<= 1; dir == directionRead { 43 addr |= 1 44 } 45 return uint32(addr) & 0xFF 46 } 47 48 // I2C enumerated types 49 type ( 50 resultFlag uint32 51 statusFlag uint32 52 transferFlag uint32 53 commandFlag uint32 54 stateFlag uint32 55 ) 56 57 const ( 58 // general purpose results 59 resultSuccess resultFlag = 0x0 // success 60 resultFail resultFlag = 0x1 // fail 61 resultReadOnly resultFlag = 0x2 // read only failure 62 resultOutOfRange resultFlag = 0x3 // out of range access 63 resultInvalidArgument resultFlag = 0x4 // invalid argument check 64 // I2C-specific results 65 resultBusy resultFlag = 0x0384 + 0x0 // the controller is already performing a transfer 66 resultIdle resultFlag = 0x0384 + 0x1 // the peripheral driver is idle 67 resultNak resultFlag = 0x0384 + 0x2 // the peripheral device sent a NAK in response to a byte 68 resultFifoError resultFlag = 0x0384 + 0x3 // FIFO under run or overrun 69 resultBitError resultFlag = 0x0384 + 0x4 // transferred bit was not seen on the bus 70 resultArbitrationLost resultFlag = 0x0384 + 0x5 // arbitration lost error 71 resultPinLowTimeout resultFlag = 0x0384 + 0x6 // SCL or SDA were held low longer than the timeout 72 resultNoTransferInProgress resultFlag = 0x0384 + 0x7 // attempt to abort a transfer when one is not in progress 73 resultDmaRequestFail resultFlag = 0x0384 + 0x8 // DMA request failed 74 resultTimeout resultFlag = 0x0384 + 0x9 // timeout polling status flags 75 ) 76 77 const ( 78 statusTxReady statusFlag = nxp.LPI2C_MSR_TDF // transmit data flag 79 statusRxReady statusFlag = nxp.LPI2C_MSR_RDF // receive data flag 80 statusEndOfPacket statusFlag = nxp.LPI2C_MSR_EPF // end Packet flag 81 statusStopDetect statusFlag = nxp.LPI2C_MSR_SDF // stop detect flag 82 statusNackDetect statusFlag = nxp.LPI2C_MSR_NDF // NACK detect flag 83 statusArbitrationLost statusFlag = nxp.LPI2C_MSR_ALF // arbitration lost flag 84 statusFifoErr statusFlag = nxp.LPI2C_MSR_FEF // FIFO error flag 85 statusPinLowTimeout statusFlag = nxp.LPI2C_MSR_PLTF // pin low timeout flag 86 statusI2CDataMatch statusFlag = nxp.LPI2C_MSR_DMF // data match flag 87 statusBusy statusFlag = nxp.LPI2C_MSR_MBF // busy flag 88 statusBusBusy statusFlag = nxp.LPI2C_MSR_BBF // bus busy flag 89 90 // all flags which are cleared by the driver upon starting a transfer 91 statusClear statusFlag = statusEndOfPacket | statusStopDetect | statusNackDetect | 92 statusArbitrationLost | statusFifoErr | statusPinLowTimeout | statusI2CDataMatch 93 94 // IRQ sources enabled by the non-blocking transactional API 95 statusIrq statusFlag = statusArbitrationLost | statusTxReady | statusRxReady | 96 statusStopDetect | statusNackDetect | statusPinLowTimeout | statusFifoErr 97 98 // errors to check for 99 statusError statusFlag = statusNackDetect | statusArbitrationLost | statusFifoErr | 100 statusPinLowTimeout 101 ) 102 103 // LPI2C transfer modes 104 const ( 105 transferDefault transferFlag = 0x0 // transfer starts with a start signal, stops with a stop signal 106 transferNoStart transferFlag = 0x1 // don't send a start condition, address, and sub address 107 transferRepeatedStart transferFlag = 0x2 // send a repeated start condition 108 transferNoStop transferFlag = 0x4 // don't send a stop condition 109 ) 110 111 // LPI2C FIFO commands 112 const ( 113 commandTxData commandFlag = (0x0 << nxp.LPI2C_MTDR_CMD_Pos) & nxp.LPI2C_MTDR_CMD_Msk // transmit 114 commandRxData commandFlag = (0x1 << nxp.LPI2C_MTDR_CMD_Pos) & nxp.LPI2C_MTDR_CMD_Msk // receive 115 commandStop commandFlag = (0x2 << nxp.LPI2C_MTDR_CMD_Pos) & nxp.LPI2C_MTDR_CMD_Msk // generate STOP condition 116 commandStart commandFlag = (0x4 << nxp.LPI2C_MTDR_CMD_Pos) & nxp.LPI2C_MTDR_CMD_Msk // generate (REPEATED)START and transmit 117 ) 118 119 // LPI2C transactional states 120 const ( 121 stateIdle stateFlag = 0x0 122 stateSendCommand stateFlag = 0x1 123 stateIssueReadCommand stateFlag = 0x2 124 stateTransferData stateFlag = 0x3 125 stateStop stateFlag = 0x4 126 stateWaitForCompletion stateFlag = 0x5 127 ) 128 129 func (i2c *I2C) setPins(c I2CConfig) (sda, scl Pin) { 130 // if both given pins are defined, or either receiver pin is undefined. 131 if 0 != c.SDA && 0 != c.SCL || 0 == i2c.sda || 0 == i2c.scl { 132 // override the receiver's pins. 133 i2c.sda, i2c.scl = c.SDA, c.SCL 134 } 135 // return the selected pins. 136 return i2c.sda, i2c.scl 137 } 138 139 // Configure is intended to setup an I2C interface for transmit/receive. 140 func (i2c *I2C) Configure(config I2CConfig) error { 141 // init pins 142 sda, scl := i2c.setPins(config) 143 144 // configure the mux and pad control registers 145 sda.Configure(PinConfig{Mode: PinModeI2CSDA}) 146 scl.Configure(PinConfig{Mode: PinModeI2CSCL}) 147 148 // configure the mux input selector 149 i2c.muxSDA.connect() 150 i2c.muxSCL.connect() 151 152 freq := config.Frequency 153 if 0 == freq { 154 freq = 100 * KHz 155 } 156 157 // reset clock and registers, and enable LPI2C module interface 158 i2c.reset(freq) 159 160 return nil 161 } 162 163 // SetBaudRate sets the communication speed for I2C. 164 func (i2c I2C) SetBaudRate(br uint32) error { 165 // TODO: implement 166 return errI2CNotImplemented 167 } 168 169 func (i2c I2C) Tx(addr uint16, w, r []byte) error { 170 // perform transmit transfer 171 if nil != w { 172 // generate start condition on bus 173 if result := i2c.start(addr, directionWrite); resultSuccess != result { 174 return errI2CSignalStartTimeout 175 } 176 // ensure TX FIFO is empty 177 if result := i2c.waitForTxEmpty(); resultSuccess != result { 178 return errI2CBusReadyTimeout 179 } 180 // check if communication was successful 181 if status := statusFlag(i2c.Bus.MSR.Get()); 0 != (status & statusNackDetect) { 182 return errI2CAckExpected 183 } 184 // send transmit data 185 if result := i2c.controllerTransmit(w); resultSuccess != result { 186 return errI2CWriteTimeout 187 } 188 } 189 190 // perform receive transfer 191 if nil != r { 192 // generate (repeated-)start condition on bus 193 if result := i2c.start(addr, directionRead); resultSuccess != result { 194 return errI2CSignalStartTimeout 195 } 196 // read received data 197 if result := i2c.controllerReceive(r); resultSuccess != result { 198 return errI2CReadTimeout 199 } 200 } 201 202 // generate stop condition on bus 203 if result := i2c.stop(); resultSuccess != result { 204 return errI2CSignalStopTimeout 205 } 206 207 return nil 208 } 209 210 // WriteRegisterEx transmits first the register and then the data to the 211 // peripheral device. 212 // 213 // Many I2C-compatible devices are organized in terms of registers. This method 214 // is a shortcut to easily write to such registers. Also, it only works for 215 // devices with 7-bit addresses, which is the vast majority. 216 func (i2c I2C) WriteRegisterEx(address uint8, register uint8, data []byte) error { 217 option := transferOption{ 218 flags: transferDefault, // transfer options bit mask (0 = normal transfer) 219 peripheral: uint16(address), // 7-bit peripheral address 220 direction: directionWrite, // directionRead or directionWrite 221 subaddress: uint16(register), // peripheral sub-address (transferred MSB first) 222 subaddressSize: 1, // byte length of sub-address (maximum = 4 bytes) 223 } 224 if result := i2c.controllerTransferPoll(option, data); resultSuccess != result { 225 return errI2CWriteTimeout 226 } 227 return nil 228 } 229 230 // ReadRegisterEx transmits the register, restarts the connection as a read 231 // operation, and reads the response. 232 // 233 // Many I2C-compatible devices are organized in terms of registers. This method 234 // is a shortcut to easily read such registers. Also, it only works for devices 235 // with 7-bit addresses, which is the vast majority. 236 func (i2c I2C) ReadRegisterEx(address uint8, register uint8, data []byte) error { 237 option := transferOption{ 238 flags: transferDefault, // transfer options bit mask (0 = normal transfer) 239 peripheral: uint16(address), // 7-bit peripheral address 240 direction: directionRead, // directionRead or directionWrite 241 subaddress: uint16(register), // peripheral sub-address (transferred MSB first) 242 subaddressSize: 1, // byte length of sub-address (maximum = 4 bytes) 243 } 244 if result := i2c.controllerTransferPoll(option, data); resultSuccess != result { 245 return errI2CWriteTimeout 246 } 247 return nil 248 } 249 250 func (i2c *I2C) reset(freq uint32) { 251 // disable interface 252 i2c.Bus.MCR.ClearBits(nxp.LPI2C_MCR_MEN) 253 254 // software reset all interface registers 255 i2c.Bus.MCR.Set(nxp.LPI2C_MCR_RST) 256 257 // RST remains set until manually cleared! 258 i2c.Bus.MCR.ClearBits(nxp.LPI2C_MCR_RST) 259 260 // disable host request 261 i2c.Bus.MCFGR0.Set(0) 262 263 // enable ACK, use I2C 2-pin open drain mode 264 i2c.Bus.MCFGR1.Set(0) 265 266 // set FIFO watermarks (RX=1, TX=1) 267 mfcr := (uint32(0x1) << nxp.LPI2C_MFCR_RXWATER_Pos) & nxp.LPI2C_MFCR_RXWATER_Msk 268 mfcr |= (uint32(0x1) << nxp.LPI2C_MFCR_TXWATER_Pos) & nxp.LPI2C_MFCR_TXWATER_Msk 269 i2c.Bus.MFCR.Set(mfcr) 270 271 // configure clock using receiver frequency 272 i2c.setFrequency(freq) 273 274 // clear reset, and enable the interface 275 i2c.Bus.MCR.Set(nxp.LPI2C_MCR_MEN) 276 277 // wait for the I2C bus to idle 278 for i2c.Bus.MSR.Get()&nxp.LPI2C_MSR_BBF != 0 { 279 } 280 } 281 282 func (i2c *I2C) setFrequency(freq uint32) { 283 var ( 284 bestPre uint32 = 0 285 bestClkHi uint32 = 0 286 bestError uint32 = 0xFFFFFFFF 287 ) 288 289 // disable interface 290 wasEnabled := i2c.Bus.MCR.HasBits(nxp.LPI2C_MCR_MEN) 291 i2c.Bus.MCR.ClearBits(nxp.LPI2C_MCR_MEN) 292 293 // baud rate = (24MHz/(2^pre))/(CLKLO+1 + CLKHI+1 + FLOOR((2+FILTSCL)/(2^pre))) 294 // assume: CLKLO=2*CLKHI, SETHOLD=CLKHI, DATAVD=CLKHI/2 295 for pre := uint32(1); pre <= 128; pre *= 2 { 296 if bestError == 0 { 297 break 298 } 299 for clkHi := uint32(1); clkHi < 32; clkHi++ { 300 var absError, rate uint32 301 if clkHi == 1 { 302 rate = (24 * MHz / pre) / (1 + 3 + 2 + 2/pre) 303 } else { 304 rate = (24 * MHz / pre) / (3*clkHi + 2 + 2/pre) 305 } 306 if freq > rate { 307 absError = freq - rate 308 } else { 309 absError = rate - freq 310 } 311 if absError < bestError { 312 bestPre = pre 313 bestClkHi = clkHi 314 bestError = absError 315 // if the error is 0, then we can stop searching because we won't find a 316 // better match 317 if absError == 0 { 318 break 319 } 320 } 321 } 322 } 323 324 var ( 325 clklo = func(n uint32) uint32 { return (n << nxp.LPI2C_MCCR0_CLKLO_Pos) & nxp.LPI2C_MCCR0_CLKLO_Msk } 326 clkhi = func(n uint32) uint32 { return (n << nxp.LPI2C_MCCR0_CLKHI_Pos) & nxp.LPI2C_MCCR0_CLKHI_Msk } 327 datavd = func(n uint32) uint32 { return (n << nxp.LPI2C_MCCR0_DATAVD_Pos) & nxp.LPI2C_MCCR0_DATAVD_Msk } 328 sethold = func(n uint32) uint32 { return (n << nxp.LPI2C_MCCR0_SETHOLD_Pos) & nxp.LPI2C_MCCR0_SETHOLD_Msk } 329 ) 330 // StandardMode, FastMode, FastModePlus, and UltraFastMode 331 mccr0 := clkhi(bestClkHi) 332 if bestClkHi < 2 { 333 mccr0 |= (clklo(3) | sethold(2) | datavd(1)) 334 } else { 335 mccr0 |= clklo(2*bestClkHi) | sethold(bestClkHi) | datavd(bestClkHi/2) 336 } 337 i2c.Bus.MCCR0.Set(mccr0) 338 i2c.Bus.MCCR1.Set(i2c.Bus.MCCR0.Get()) 339 340 for i := uint32(0); i < 8; i++ { 341 if bestPre == (1 << i) { 342 bestPre = i 343 break 344 } 345 } 346 preMask := (bestPre << nxp.LPI2C_MCFGR1_PRESCALE_Pos) & nxp.LPI2C_MCFGR1_PRESCALE_Msk 347 i2c.Bus.MCFGR1.Set((i2c.Bus.MCFGR1.Get() & ^uint32(nxp.LPI2C_MCFGR1_PRESCALE_Msk)) | preMask) 348 349 var ( 350 filtsda = func(n uint32) uint32 { return (n << nxp.LPI2C_MCFGR2_FILTSDA_Pos) & nxp.LPI2C_MCFGR2_FILTSDA_Msk } 351 filtscl = func(n uint32) uint32 { return (n << nxp.LPI2C_MCFGR2_FILTSCL_Pos) & nxp.LPI2C_MCFGR2_FILTSCL_Msk } 352 busidle = func(n uint32) uint32 { return (n << nxp.LPI2C_MCFGR2_BUSIDLE_Pos) & nxp.LPI2C_MCFGR2_BUSIDLE_Msk } 353 pinlow = func(n uint32) uint32 { return (n << nxp.LPI2C_MCFGR3_PINLOW_Pos) & nxp.LPI2C_MCFGR3_PINLOW_Msk } 354 355 mcfgr2, mcfgr3 uint32 356 ) 357 const i2cClockStretchTimeout = 15000 // microseconds 358 if freq >= 5*MHz { 359 // I2C UltraFastMode 5 MHz 360 mcfgr2 = 0 // disable glitch filters and timeout for UltraFastMode 361 mcfgr3 = 0 // 362 } else if freq >= 1*MHz { 363 // I2C FastModePlus 1 MHz 364 mcfgr2 = filtsda(1) | filtscl(1) | busidle(2400) // 100us timeout 365 mcfgr3 = pinlow(i2cClockStretchTimeout*24/256 + 1) 366 } else if freq >= 400*KHz { 367 // I2C FastMode 400 kHz 368 mcfgr2 = filtsda(2) | filtscl(2) | busidle(3600) // 150us timeout 369 mcfgr3 = pinlow(i2cClockStretchTimeout*24/256 + 1) 370 } else { 371 // I2C StandardMode 100 kHz 372 mcfgr2 = filtsda(5) | filtscl(5) | busidle(3000) // 250us timeout 373 mcfgr3 = pinlow(i2cClockStretchTimeout*12/256 + 1) 374 } 375 i2c.Bus.MCFGR2.Set(mcfgr2) 376 i2c.Bus.MCFGR3.Set(mcfgr3) 377 378 // restore controller mode if it was enabled when called 379 if wasEnabled { 380 i2c.Bus.MCR.SetBits(nxp.LPI2C_MCR_MEN) 381 } 382 } 383 384 // checkStatus converts the status register to a resultFlag for return, and 385 // clears any errors if present. 386 func (i2c *I2C) checkStatus(status statusFlag) resultFlag { 387 result := resultSuccess 388 // check for error. these errors cause a stop to be sent automatically. 389 // we must clear the errors before a new transfer can start. 390 if status &= statusError; 0 != status { 391 // select the correct error code ordered by severity, bus issues first. 392 if 0 != (status & statusPinLowTimeout) { 393 result = resultPinLowTimeout 394 } else if 0 != (status & statusArbitrationLost) { 395 result = resultArbitrationLost 396 } else if 0 != (status & statusNackDetect) { 397 result = resultNak 398 } else if 0 != (status & statusFifoErr) { 399 result = resultFifoError 400 } 401 // clear the flags 402 i2c.Bus.MSR.Set(uint32(status)) 403 // reset fifos. these flags clear automatically. 404 i2c.Bus.MCR.SetBits(nxp.LPI2C_MCR_RRF | nxp.LPI2C_MCR_RTF) 405 } 406 return result 407 } 408 409 func (i2c *I2C) getFIFOSize() (rx, tx uint32) { return 4, 4 } 410 func (i2c *I2C) getFIFOCount() (rx, tx uint32) { 411 mfsr := i2c.Bus.MFSR.Get() 412 return (mfsr & nxp.LPI2C_MFSR_RXCOUNT_Msk) >> nxp.LPI2C_MFSR_RXCOUNT_Pos, 413 (mfsr & nxp.LPI2C_MFSR_TXCOUNT_Msk) >> nxp.LPI2C_MFSR_TXCOUNT_Pos 414 } 415 416 func (i2c *I2C) waitForTxReady() resultFlag { 417 result := resultSuccess 418 _, txSize := i2c.getFIFOSize() 419 for { 420 _, txCount := i2c.getFIFOCount() 421 status := statusFlag(i2c.Bus.MSR.Get()) 422 if result = i2c.checkStatus(status); resultSuccess != result { 423 break 424 } 425 if txSize-txCount > 0 { 426 break 427 } 428 } 429 return result 430 } 431 432 func (i2c *I2C) waitForTxEmpty() resultFlag { 433 result := resultSuccess 434 for { 435 _, txCount := i2c.getFIFOCount() 436 status := statusFlag(i2c.Bus.MSR.Get()) 437 if result = i2c.checkStatus(status); resultSuccess != result { 438 break 439 } 440 if 0 == txCount { 441 break 442 } 443 } 444 return result 445 } 446 447 // isBusBusy checks if the I2C bus is busy, returning true if it is busy and we 448 // are not the ones driving it, otherwise false. 449 func (i2c *I2C) isBusBusy() bool { 450 status := statusFlag(i2c.Bus.MSR.Get()) 451 return (0 != (status & statusBusBusy)) && (0 == (status & statusBusy)) 452 } 453 454 // start sends a START signal and peripheral address on the I2C bus. 455 // 456 // This function is used to initiate a new controller mode transfer. First, the 457 // bus state is checked to ensure that another controller is not occupying the 458 // bus. Then a START signal is transmitted, followed by the 7-bit peripheral 459 // address. Note that this function does not actually wait until the START and 460 // address are successfully sent on the bus before returning. 461 func (i2c *I2C) start(address uint16, dir i2cDirection) resultFlag { 462 // return an error if the bus is already in use by another controller 463 if i2c.isBusBusy() { 464 return resultBusy 465 } 466 // clear all flags 467 i2c.Bus.MSR.Set(uint32(statusClear)) 468 // turn off auto-stop 469 i2c.Bus.MCFGR1.ClearBits(nxp.LPI2C_MCFGR1_AUTOSTOP) 470 // wait until there is room in the FIFO 471 if result := i2c.waitForTxReady(); resultSuccess != result { 472 return result 473 } 474 475 // issue start command 476 i2c.Bus.MTDR.Set(uint32(commandStart) | dir.shift(address)) 477 return resultSuccess 478 } 479 480 // stop sends a STOP signal on the I2C bus. 481 // 482 // This function does not return until the STOP signal is seen on the bus, or 483 // an error occurs. 484 func (i2c *I2C) stop() resultFlag { 485 const tryMax = 0 // keep waiting forever 486 // wait until there is room in the FIFO 487 result := i2c.waitForTxReady() 488 if resultSuccess != result { 489 return result 490 } 491 // send the STOP signal 492 i2c.Bus.MTDR.Set(uint32(commandStop)) 493 // wait for the stop detected flag to set, indicating the transfer has 494 // completed on the bus. also check for errors while waiting. 495 try := 0 496 for resultSuccess == result && (0 == tryMax || try < tryMax) { 497 status := statusFlag(i2c.Bus.MSR.Get()) 498 result = i2c.checkStatus(status) 499 if (0 != (status & statusStopDetect)) && (0 != (status & statusTxReady)) { 500 i2c.Bus.MSR.Set(uint32(statusStopDetect)) 501 break 502 } 503 try++ 504 } 505 if 0 != tryMax && try >= tryMax { 506 return resultTimeout 507 } 508 return result 509 } 510 511 // controllerReceive performs a polling receive transfer on the I2C bus. 512 func (i2c *I2C) controllerReceive(rxBuffer []byte) resultFlag { 513 const tryMax = 0 // keep trying forever 514 rxSize := len(rxBuffer) 515 if rxSize == 0 { 516 return resultSuccess 517 } 518 // wait until there is room in the FIFO 519 result := i2c.waitForTxReady() 520 if resultSuccess != result { 521 return result 522 } 523 sizeMask := (uint32(rxSize-1) << nxp.LPI2C_MTDR_DATA_Pos) & nxp.LPI2C_MTDR_DATA_Msk 524 i2c.Bus.MTDR.Set(uint32(commandRxData) | sizeMask) 525 526 // receive data 527 for rxSize > 0 { 528 // read LPI2C receive FIFO register. the register includes a flag to 529 // indicate whether the FIFO is empty, so we can both get the data and check 530 // if we need to keep reading using a single register read. 531 var data uint32 532 try := 0 533 for 0 == tryMax || try < tryMax { 534 // check for errors on the bus 535 status := statusFlag(i2c.Bus.MSR.Get()) 536 result = i2c.checkStatus(status) 537 if resultSuccess != result { 538 return result 539 } 540 // read received data, break if FIFO was non-empty 541 data = i2c.Bus.MRDR.Get() 542 if 0 == (data & nxp.LPI2C_MRDR_RXEMPTY_Msk) { 543 break 544 } 545 try++ 546 } 547 // ensure we didn't timeout waiting for data 548 if 0 != tryMax && try >= tryMax { 549 return resultTimeout 550 } 551 // copy data to RX buffer 552 rxBuffer[len(rxBuffer)-rxSize] = byte(data & nxp.LPI2C_MRDR_DATA_Msk) 553 rxSize-- 554 } 555 return result 556 } 557 558 // controllerTransmit performs a polling transmit transfer on the I2C bus. 559 func (i2c *I2C) controllerTransmit(txBuffer []byte) resultFlag { 560 txSize := len(txBuffer) 561 for txSize > 0 { 562 // wait until there is room in the FIFO 563 result := i2c.waitForTxReady() 564 if resultSuccess != result { 565 return result 566 } 567 // write byte into LPI2C data register 568 i2c.Bus.MTDR.Set(uint32(txBuffer[len(txBuffer)-txSize] & nxp.LPI2C_MTDR_DATA_Msk)) 569 txSize-- 570 } 571 return resultSuccess 572 } 573 574 type transferOption struct { 575 flags transferFlag // transfer options bit mask (0 = normal transfer) 576 peripheral uint16 // 7-bit peripheral address 577 direction i2cDirection // directionRead or directionWrite 578 subaddress uint16 // peripheral sub-address (transferred MSB first) 579 subaddressSize uint16 // byte length of sub-address (maximum = 4 bytes) 580 } 581 582 func (i2c *I2C) controllerTransferPoll(option transferOption, data []byte) resultFlag { 583 // return an error if the bus is already in use by another controller 584 if i2c.isBusBusy() { 585 return resultBusy 586 } 587 // clear all flags 588 i2c.Bus.MSR.Set(uint32(statusClear)) 589 // turn off auto-stop 590 i2c.Bus.MCFGR1.ClearBits(nxp.LPI2C_MCFGR1_AUTOSTOP) 591 592 cmd := make([]uint16, 0, 7) 593 size := len(data) 594 595 direction := option.direction 596 if option.subaddressSize > 0 { 597 direction = directionWrite 598 } 599 // peripheral address 600 if 0 == (option.flags & transferNoStart) { 601 addr := direction.shift(option.peripheral) 602 cmd = append(cmd, uint16(uint32(commandStart)|addr)) 603 } 604 // sub-address (MSB-first) 605 rem := option.subaddressSize 606 for rem > 0 { 607 rem-- 608 cmd = append(cmd, (option.subaddress>>(8*rem))&0xFF) 609 } 610 // need to send repeated start if switching directions to read 611 if (0 != size) && (directionRead == option.direction) { 612 if directionWrite == direction { 613 addr := directionRead.shift(option.peripheral) 614 cmd = append(cmd, uint16(uint32(commandStart)|addr)) 615 } 616 } 617 // send command buffer 618 result := resultSuccess 619 for _, c := range cmd { 620 // wait until there is room in the FIFO 621 if result = i2c.waitForTxReady(); resultSuccess != result { 622 return result 623 } 624 // write byte into LPI2C controller data register 625 i2c.Bus.MTDR.Set(uint32(c)) 626 } 627 // send data 628 if option.direction == directionWrite && size > 0 { 629 result = i2c.controllerTransmit(data) 630 } 631 // receive data 632 if option.direction == directionRead && size > 0 { 633 result = i2c.controllerReceive(data) 634 } 635 if resultSuccess != result { 636 return result 637 } 638 if 0 == (option.flags & transferNoStop) { 639 result = i2c.stop() 640 } 641 return result 642 }