|
-
- #include "Wire.h"
-
-
- #if defined(__IMXRT1052__) || defined(__IMXRT1062__)
-
- #include "debug/printf.h"
-
- void TwoWire::begin(void)
- {
- // use 24 MHz clock
- CCM_CSCDR2 = (CCM_CSCDR2 & ~CCM_CSCDR2_LPI2C_CLK_PODF(63)) | CCM_CSCDR2_LPI2C_CLK_SEL;
- hardware.clock_gate_register |= hardware.clock_gate_mask;
- port->MCR = LPI2C_MCR_RST;
- setClock(100000);
-
- // Setup SDA register
- *(portControlRegister(hardware.sda_pins[sda_pin_index_].pin)) |= IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3);
- *(portConfigRegister(hardware.sda_pins[sda_pin_index_].pin)) = hardware.sda_pins[sda_pin_index_].mux_val;
- if (hardware.sda_pins[sda_pin_index_].select_input_register) {
- *(hardware.sda_pins[sda_pin_index_].select_input_register) = hardware.sda_pins[sda_pin_index_].select_val;
- }
-
- // setup SCL register
- *(portControlRegister(hardware.scl_pins[scl_pin_index_].pin)) |= IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3);
- *(portConfigRegister(hardware.scl_pins[scl_pin_index_].pin)) = hardware.scl_pins[scl_pin_index_].mux_val;
- if (hardware.scl_pins[scl_pin_index_].select_input_register) {
- *(hardware.scl_pins[scl_pin_index_].select_input_register) = hardware.scl_pins[scl_pin_index_].select_val;
- }
- }
-
- void TwoWire::begin(uint8_t address)
- {
- // TODO: slave mode
- }
-
- void TwoWire::end()
- {
- }
-
- void TwoWire::setSDA(uint8_t pin) {
- if (pin == hardware.sda_pins[sda_pin_index_].pin) return;
- uint32_t newindex=0;
- while (1) {
- uint32_t sda_pin = hardware.sda_pins[newindex].pin;
- if (sda_pin == 255) return;
- if (sda_pin == pin) break;
- if (++newindex >= sizeof(hardware.sda_pins)) return;
- }
- if ((hardware.clock_gate_register & hardware.clock_gate_mask)) {
- *(portConfigRegister(hardware.sda_pins[sda_pin_index_].pin)) = 5; // hard to know what to go back to?
-
- // setup new one...
- *(portControlRegister(hardware.sda_pins[newindex].pin)) |= IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3);
- *(portConfigRegister(hardware.sda_pins[newindex].pin)) = hardware.sda_pins[newindex].mux_val;
- if (hardware.sda_pins[newindex].select_input_register) {
- *(hardware.sda_pins[newindex].select_input_register) = hardware.sda_pins[newindex].select_val;
- }
-
- }
- sda_pin_index_ = newindex;
-
- }
-
- void TwoWire::setSCL(uint8_t pin) {
- if (pin == hardware.scl_pins[scl_pin_index_].pin) return;
- uint32_t newindex=0;
- while (1) {
- uint32_t scl_pin = hardware.scl_pins[newindex].pin;
- if (scl_pin == 255) return;
- if (scl_pin == pin) break;
- if (++newindex >= sizeof(hardware.scl_pins)) return;
- }
- if ((hardware.clock_gate_register & hardware.clock_gate_mask)) {
- *(portConfigRegister(hardware.scl_pins[scl_pin_index_].pin)) = 5; // hard to know what to go back to?
-
- // setup new one...
- *(portControlRegister(hardware.scl_pins[newindex].pin)) |= IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3);
- *(portConfigRegister(hardware.scl_pins[newindex].pin)) = hardware.scl_pins[newindex].mux_val;
- if (hardware.scl_pins[newindex].select_input_register) {
- *(hardware.scl_pins[newindex].select_input_register) = hardware.scl_pins[newindex].select_val;
- }
-
- }
- scl_pin_index_ = newindex;
-
- }
-
- size_t TwoWire::write(uint8_t data)
- {
- if (transmitting || slave_mode) {
- if (txBufferLength >= BUFFER_LENGTH+1) {
- setWriteError();
- return 0;
- }
- txBuffer[txBufferLength++] = data;
- return 1;
- }
- return 0;
- }
-
- size_t TwoWire::write(const uint8_t *data, size_t quantity)
- {
- if (transmitting || slave_mode) {
- size_t avail = BUFFER_LENGTH+1 - txBufferLength;
- if (quantity > avail) {
- quantity = avail;
- setWriteError();
- }
- memcpy(txBuffer + txBufferLength, data, quantity);
- txBufferLength += quantity;
- return quantity;
- }
- return 0;
- }
-
- uint8_t TwoWire::endTransmission(uint8_t sendStop)
- {
- //printf("\nendTransmission %d (%x %x %x) %x\n", txBufferLength,txBuffer[0], txBuffer[1], txBuffer[2], sendStop);
- uint32_t i=0, len, status;
-
- len = txBufferLength;
- if (!len) return 4; // no data to transmit
-
- // wait while bus is busy
- while (1) {
- status = port->MSR; // pg 2899 & 2892
- if (!(status & LPI2C_MSR_BBF)) break; // bus is available
- if (status & LPI2C_MSR_MBF) break; // we already have bus control
- // TODO: timeout...
- }
- //printf("m=%x\n", status);
-
- // Wonder if MFSR we should maybe clear it?
- if ( port->MFSR & 0x7) {
- port->MCR = LPI2C_MCR_MEN | LPI2C_MCR_RTF; // clear the FIFO
- port->MSR = LPI2C_MSR_PLTF | LPI2C_MSR_ALF | LPI2C_MSR_NDF | LPI2C_MSR_SDF | LPI2C_MSR_FEF; // clear flags
- //printf("Clear TX Fifo %lx %lx\n", port->MSR, port->MFSR);
- }
- // TODO: is this correct if the prior use didn't send stop?
- //port->MSR = LPI2C_MSR_PLTF | LPI2C_MSR_ALF | LPI2C_MSR_NDF | LPI2C_MSR_SDF; // clear flags
- port->MSR = status;
-
- //printf("MSR=%lX, MFSR=%lX\n", status, port->MFSR);
- //elapsedMillis timeout=0;
-
- while (1) {
- // transmit stuff, if we haven't already
- if (i <= len) {
- uint32_t fifo_used = port->MFSR & 0x07; // pg 2914
- //if (fifo_used < 4) printf("t=%ld\n", fifo_used);
- while (fifo_used < 4) {
- if (i == 0) {
- //printf("start %x\n", txBuffer[0]);
- port->MTDR = LPI2C_MTDR_CMD_START | txBuffer[0];
- i = 1;
- } else if (i < len) {
- port->MTDR = LPI2C_MTDR_CMD_TRANSMIT | txBuffer[i++];
- } else {
- if (sendStop) port->MTDR = LPI2C_MTDR_CMD_STOP;
- i++;
- break;
- }
- fifo_used = fifo_used + 1;
- }
- }
- // monitor status
- status = port->MSR; // pg 2899 & 2892
- if (status & LPI2C_MSR_ALF) {
- //printf("arbitration lost\n");
- return 4; // we lost bus arbitration to another master
- }
- if (status & LPI2C_MSR_NDF) {
- //printf("NACK, f=%d, i=%d\n", port->MFSR & 0x07, i);
- // TODO: check that hardware really sends stop automatically
- port->MCR = LPI2C_MCR_MEN | LPI2C_MCR_RTF; // clear the FIFO
- // TODO: is always sending a stop the right way to recover?
- port->MTDR = LPI2C_MTDR_CMD_STOP;
- return 2; // NACK for address
- //return 3; // NACK for data TODO: how to discern addr from data?
- }
- //if (status & LPI2C_MSR_PLTF) {
- //printf("bus stuck - what to do?\n");
- //return 4;
- //}
-
- //if (timeout > 100) {
- //printf("status = %x\n", status);
- //timeout = 0;
- //}
-
- if (sendStop) {
- if (status & LPI2C_MSR_SDF) {
- // master automatically sends stop condition on some
- // types of errors, so this flag only means success
- // when all comments in fifo have been fully used
- uint32_t fifo = port->MFSR & 0x07;
- if (fifo == 0) return 0;
- }
- } else {
- uint32_t fifo_used = port->MFSR & 0x07; // pg 2914
- if (fifo_used == 0) {
- //digitalWriteFast(15, HIGH);
- //delayMicroseconds(2);
- //digitalWriteFast(15, LOW);
- // TODO: this returns before the last data transmits!
- // Should verify every byte ACKs, arbitration not lost
- //printf("fifo empty, msr=%x\n", status);
- return 0;
- }
- }
- }
- }
-
- uint8_t TwoWire::requestFrom(uint8_t address, uint8_t length, uint8_t sendStop)
- {
- uint32_t cmd=0, status, fifo;
-
- // wait while bus is busy
- //printf("\nrequestFrom %x %x %x\n", address, length, sendStop);
- while (1) {
- status = port->MSR; // pg 2899 & 2892
- if (!(status & LPI2C_MSR_BBF)) break; // bus is available
- if (status & LPI2C_MSR_MBF) break; // we already have bus control
- // TODO: timeout...
- }
- //printf("idle2, msr=%x\n", status);
-
- // TODO: is this correct if the prior use didn't send stop?
- port->MSR = LPI2C_MSR_PLTF | LPI2C_MSR_ALF | LPI2C_MSR_NDF | LPI2C_MSR_SDF | LPI2C_MSR_FEF; // clear flags
-
- //printf("MSR=%lX, MCR:%lx, MFSR=%lX\n", status, port->MCR, port->MFSR);
-
- // Wonder if MFSR we should maybe clear it?
- if ( port->MFSR & 0x7) {
- port->MCR = LPI2C_MCR_MEN | LPI2C_MCR_RTF; // clear the FIFO
- port->MSR = LPI2C_MSR_PLTF | LPI2C_MSR_ALF | LPI2C_MSR_NDF | LPI2C_MSR_SDF | LPI2C_MSR_FEF; // clear flags
- //printf("Clear TX Fifo %lx %lx\n", port->MSR, port->MFSR);
- }
- address = (address & 0x7F) << 1;
- if (length < 1) length = 1;
- if (length > 255) length = 255;
- rxBufferIndex = 0;
- rxBufferLength = 0;
-
- //elapsedMillis timeout=0;
-
- while (1) {
- // transmit stuff, if we haven't already
- if (cmd < 3) {
- fifo = port->MFSR & 0x07; // pg 2914
- //if (fifo < 4) printf("t=%ld\n", fifo);
- while (fifo < 4 && cmd < 3) {
- if (cmd == 0) {
- port->MTDR = LPI2C_MTDR_CMD_START | 1 | address;
- } else if (cmd == 1) {
- // causes bus stuck... need way to recover
- //port->MTDR = LPI2C_MTDR_CMD_START | (length - 1);
- port->MTDR = LPI2C_MTDR_CMD_RECEIVE | (length - 1);
- } else {
- if (sendStop) port->MTDR = LPI2C_MTDR_CMD_STOP;
- }
- cmd++;
- fifo = fifo + 1;
- }
- }
- // receive stuff
- if (rxBufferLength < sizeof(rxBuffer)) {
- fifo = (port->MFSR >> 16) & 0x07;
- //if (fifo > 0) printf("r=%ld\n", fifo);
- while (fifo > 0 && rxBufferLength < sizeof(rxBuffer)) {
- rxBuffer[rxBufferLength++] = port->MRDR;
- fifo = fifo - 1;
- }
- }
- // monitor status
- status = port->MSR; // pg 2899 & 2892
- if (status & LPI2C_MSR_ALF) {
- //printf("arbitration lost\n");
- break;
- }
- if (status & LPI2C_MSR_NDF) {
- //printf("got NACK\n");
- // TODO: how to make sure stop is sent?
- break;
- }
-
- //if (timeout > 250) {
- //printf("Status = %x\n", status);
- //timeout = 0;
- //}
-
- if (rxBufferLength >= length && cmd >= 3) break;
- }
- //digitalWriteFast(15, HIGH);
- //delayMicroseconds(2);
- //digitalWriteFast(15, LOW);
- return rxBufferLength;
- }
-
- uint8_t TwoWire::requestFrom(uint8_t addr, uint8_t qty, uint32_t iaddr, uint8_t n, uint8_t stop)
- {
- if (n > 0) {
- union { uint32_t ul; uint8_t b[4]; } iaddress;
- iaddress.ul = iaddr;
- beginTransmission(addr);
- if (n > 3) n = 3;
- do {
- n = n - 1;
- write(iaddress.b[n]);
- } while (n > 0);
- endTransmission(false);
- }
- if (qty > BUFFER_LENGTH) qty = BUFFER_LENGTH;
- return requestFrom(addr, qty, stop);
- }
-
-
-
- PROGMEM
- constexpr TwoWire::I2C_Hardware_t TwoWire::i2c1_hardware = {
- CCM_CCGR2, CCM_CCGR2_LPI2C1(CCM_CCGR_ON),
- {{18, 3 | 0x10, &IOMUXC_LPI2C1_SDA_SELECT_INPUT, 1}, {0xff, 0xff, nullptr, 0}},
- {{19, 3 | 0x10, &IOMUXC_LPI2C1_SCL_SELECT_INPUT, 1}, {0xff, 0xff, nullptr, 0}},
- IRQ_LPI2C1
- };
- TwoWire Wire(&IMXRT_LPI2C1, TwoWire::i2c1_hardware);
-
- PROGMEM
- constexpr TwoWire::I2C_Hardware_t TwoWire::i2c3_hardware = {
- CCM_CCGR2, CCM_CCGR2_LPI2C3(CCM_CCGR_ON),
- {{17, 1 | 0x10, &IOMUXC_LPI2C3_SDA_SELECT_INPUT, 2}, {36, 2 | 0x10, &IOMUXC_LPI2C3_SDA_SELECT_INPUT, 1}},
- {{16, 1 | 0x10, &IOMUXC_LPI2C3_SCL_SELECT_INPUT, 2}, {37, 2 | 0x10, &IOMUXC_LPI2C3_SCL_SELECT_INPUT, 1}},
- IRQ_LPI2C3
- };
- TwoWire Wire1(&IMXRT_LPI2C3, TwoWire::i2c3_hardware);
-
- PROGMEM
- constexpr TwoWire::I2C_Hardware_t TwoWire::i2c4_hardware = {
- CCM_CCGR6, CCM_CCGR6_LPI2C4_SERIAL(CCM_CCGR_ON),
- {{25, 0 | 0x10, &IOMUXC_LPI2C4_SDA_SELECT_INPUT, 1}, {0xff, 0xff, nullptr, 0}},
- {{24, 0 | 0x10, &IOMUXC_LPI2C4_SCL_SELECT_INPUT, 1}, {0xff, 0xff, nullptr, 0}},
- IRQ_LPI2C4
- };
- TwoWire Wire2(&IMXRT_LPI2C4, TwoWire::i2c4_hardware);
-
-
-
-
-
- void TwoWire::setClock(uint32_t frequency)
- {
- port->MCR = 0;
- if (frequency < 400000) {
- // 100 kHz
- port->MCCR0 = LPI2C_MCCR0_CLKHI(55) | LPI2C_MCCR0_CLKLO(59) |
- LPI2C_MCCR0_DATAVD(25) | LPI2C_MCCR0_SETHOLD(40);
- port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(1);
- port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(5) | LPI2C_MCFGR2_FILTSCL(5) |
- LPI2C_MCFGR2_BUSIDLE(3900);
- } else if (frequency < 1000000) {
- // 400 kHz
- port->MCCR0 = LPI2C_MCCR0_CLKHI(26) | LPI2C_MCCR0_CLKLO(28) |
- LPI2C_MCCR0_DATAVD(12) | LPI2C_MCCR0_SETHOLD(18);
- port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
- port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(2) | LPI2C_MCFGR2_FILTSCL(2) |
- LPI2C_MCFGR2_BUSIDLE(3900);
- } else {
- // 1 MHz
- port->MCCR0 = LPI2C_MCCR0_CLKHI(9) | LPI2C_MCCR0_CLKLO(10) |
- LPI2C_MCCR0_DATAVD(4) | LPI2C_MCCR0_SETHOLD(7);
- port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
- port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(1) | LPI2C_MCFGR2_FILTSCL(1) |
- LPI2C_MCFGR2_BUSIDLE(3900);
- }
- port->MCCR1 = port->MCCR0;
- port->MCFGR0 = 0;
- port->MCFGR3 = LPI2C_MCFGR3_PINLOW(3900);
- port->MFCR = LPI2C_MFCR_RXWATER(1) | LPI2C_MFCR_TXWATER(1);
- port->MCR = LPI2C_MCR_MEN;
- }
-
- #endif
|