|
|
@@ -2,33 +2,33 @@ |
|
|
|
#include "Wire.h" |
|
|
|
|
|
|
|
|
|
|
|
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) |
|
|
|
#if defined(__IMXRT1062__) |
|
|
|
|
|
|
|
#include "debug/printf.h" |
|
|
|
//#include "debug/printf.h" |
|
|
|
|
|
|
|
#define PINCONFIG (IOMUXC_PAD_ODE | IOMUXC_PAD_SRE | IOMUXC_PAD_DSE(4) | IOMUXC_PAD_SPEED(1) | IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3)) |
|
|
|
|
|
|
|
void TwoWire::begin(void) |
|
|
|
{ |
|
|
|
// use 24 MHz clock |
|
|
|
CCM_CSCDR2 = (CCM_CSCDR2 & ~CCM_CSCDR2_LPI2C_CLK_PODF(63)) | CCM_CSCDR2_LPI2C_CLK_SEL; |
|
|
|
// 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); |
|
|
|
port->MCR = LPI2C_MCR_RST; |
|
|
|
setClock(100000); |
|
|
|
|
|
|
|
// Setup SDA register |
|
|
|
// Setup SDA register |
|
|
|
*(portControlRegister(hardware.sda_pins[sda_pin_index_].pin)) = PINCONFIG; |
|
|
|
*(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; |
|
|
|
} |
|
|
|
*(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)) = PINCONFIG; |
|
|
|
*(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; |
|
|
|
} |
|
|
|
*(hardware.scl_pins[scl_pin_index_].select_input_register) = hardware.scl_pins[scl_pin_index_].select_val; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void TwoWire::begin(uint8_t address) |
|
|
@@ -56,12 +56,10 @@ void TwoWire::setSDA(uint8_t pin) { |
|
|
|
*(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; |
|
|
|
} |
|
|
|
|
|
|
|
*(hardware.sda_pins[newindex].select_input_register) = hardware.sda_pins[newindex].select_val; |
|
|
|
} |
|
|
|
} |
|
|
|
sda_pin_index_ = newindex; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
void TwoWire::setSCL(uint8_t pin) { |
|
|
@@ -80,12 +78,43 @@ void TwoWire::setSCL(uint8_t pin) { |
|
|
|
*(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; |
|
|
|
} |
|
|
|
|
|
|
|
*(hardware.scl_pins[newindex].select_input_register) = hardware.scl_pins[newindex].select_val; |
|
|
|
} |
|
|
|
} |
|
|
|
scl_pin_index_ = newindex; |
|
|
|
} |
|
|
|
|
|
|
|
bool TwoWire::force_clock() |
|
|
|
{ |
|
|
|
bool ret = false; |
|
|
|
uint32_t sda_pin = hardware.sda_pins[sda_pin_index_].pin; |
|
|
|
uint32_t scl_pin = hardware.scl_pins[scl_pin_index_].pin; |
|
|
|
uint32_t sda_mask = digitalPinToBitMask(sda_pin); |
|
|
|
uint32_t scl_mask = digitalPinToBitMask(scl_pin); |
|
|
|
// take control of pins with GPIO |
|
|
|
*portConfigRegister(sda_pin) = 5 | 0x10; |
|
|
|
*portSetRegister(sda_pin) = sda_mask; |
|
|
|
*portModeRegister(sda_pin) |= sda_mask; |
|
|
|
*portConfigRegister(scl_pin) = 5 | 0x10; |
|
|
|
*portSetRegister(scl_pin) = scl_mask; |
|
|
|
*portModeRegister(scl_pin) |= scl_mask; |
|
|
|
delayMicroseconds(10); |
|
|
|
for (int i=0; i < 9; i++) { |
|
|
|
if ((*portInputRegister(sda_pin) & sda_mask) |
|
|
|
&& (*portInputRegister(scl_pin) & scl_mask)) { |
|
|
|
// success, both pins are high |
|
|
|
ret = true; |
|
|
|
break; |
|
|
|
} |
|
|
|
*portClearRegister(scl_pin) = scl_mask; |
|
|
|
delayMicroseconds(5); |
|
|
|
*portSetRegister(scl_pin) = scl_mask; |
|
|
|
delayMicroseconds(5); |
|
|
|
} |
|
|
|
// return control of pins to I2C |
|
|
|
*(portConfigRegister(sda_pin)) = hardware.sda_pins[sda_pin_index_].mux_val; |
|
|
|
*(portConfigRegister(scl_pin)) = hardware.scl_pins[scl_pin_index_].mux_val; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
size_t TwoWire::write(uint8_t data) |
|
|
@@ -116,198 +145,148 @@ size_t TwoWire::write(const uint8_t *data, size_t 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 |
|
|
|
|
|
|
|
// 2 BBF = Bus Busy Flag |
|
|
|
// 1 MBF = Master Busy Flag |
|
|
|
// 40 DMF = Data Match Flag |
|
|
|
// 20 PLTF = Pin Low Timeout Flag |
|
|
|
// 10 FEF = FIFO Error Flag |
|
|
|
// 08 ALF = Arbitration Lost Flag |
|
|
|
// 04 NDF = NACK Detect Flag |
|
|
|
// 02 SDF = STOP Detect Flag |
|
|
|
// 01 EPF = End Packet Flag |
|
|
|
// 2 RDF = Receive Data Flag |
|
|
|
// 1 TDF = Transmit Data Flag |
|
|
|
|
|
|
|
bool TwoWire::wait_idle() |
|
|
|
{ |
|
|
|
elapsedMillis timeout = 0; |
|
|
|
while (1) { |
|
|
|
status = port->MSR; // pg 2899 & 2892 |
|
|
|
uint32_t 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->MSR & (LPI2C_MSR_BBF|LPI2C_MSR_MBF)) != (LPI2C_MSR_BBF|LPI2C_MSR_MBF))) { |
|
|
|
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); |
|
|
|
if (timeout > 16) { |
|
|
|
//Serial.printf("timeout waiting for idle, MSR = %x\n", status); |
|
|
|
if (force_clock()) break; |
|
|
|
//Serial.printf("unable to get control of I2C bus\n"); |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
// 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; |
|
|
|
port->MSR = 0x00007F00; // clear all prior flags |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
//printf("MSR=%lX, MFSR=%lX\n", status, port->MFSR); |
|
|
|
//elapsedMillis timeout=0; |
|
|
|
|
|
|
|
uint8_t TwoWire::endTransmission(uint8_t sendStop) |
|
|
|
{ |
|
|
|
uint32_t tx_len = txBufferLength; |
|
|
|
if (!tx_len) return 4; // no address for transmit |
|
|
|
if (!wait_idle()) return 4; |
|
|
|
uint32_t tx_index = 0; // 0=start, 1=addr, 2-(N-1)=data, N=stop |
|
|
|
elapsedMillis timeout = 0; |
|
|
|
while (1) { |
|
|
|
// transmit stuff, if we haven't already |
|
|
|
if (i <= len) { |
|
|
|
if (tx_index <= tx_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]); |
|
|
|
if (tx_index == 0) { |
|
|
|
port->MTDR = LPI2C_MTDR_CMD_START | txBuffer[0]; |
|
|
|
i = 1; |
|
|
|
} else if (i < len) { |
|
|
|
port->MTDR = LPI2C_MTDR_CMD_TRANSMIT | txBuffer[i++]; |
|
|
|
tx_index = 1; |
|
|
|
} else if (tx_index < tx_len) { |
|
|
|
port->MTDR = LPI2C_MTDR_CMD_TRANSMIT | txBuffer[tx_index++]; |
|
|
|
} else { |
|
|
|
if (sendStop) port->MTDR = LPI2C_MTDR_CMD_STOP; |
|
|
|
i++; |
|
|
|
tx_index++; |
|
|
|
break; |
|
|
|
} |
|
|
|
fifo_used = fifo_used + 1; |
|
|
|
fifo_used++; |
|
|
|
} |
|
|
|
} |
|
|
|
// monitor status |
|
|
|
status = port->MSR; // pg 2899 & 2892 |
|
|
|
uint32_t status = port->MSR; // pg 2884 & 2891 |
|
|
|
if (status & LPI2C_MSR_ALF) { |
|
|
|
//printf("arbitration lost\n"); |
|
|
|
port->MCR |= LPI2C_MCR_RTF | LPI2C_MCR_RRF; // clear FIFOs |
|
|
|
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->MCR |= LPI2C_MCR_RTF | LPI2C_MCR_RRF; // clear FIFOs |
|
|
|
port->MTDR = LPI2C_MTDR_CMD_STOP; |
|
|
|
return 2; // NACK for address |
|
|
|
//return 3; // NACK for data TODO: how to discern addr from data? |
|
|
|
return 2; // NACK (assume address, TODO: how to tell address 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); |
|
|
|
if ((status & LPI2C_MSR_PLTF) || timeout > 50) { |
|
|
|
port->MCR |= LPI2C_MCR_RTF | LPI2C_MCR_RRF; // clear FIFOs |
|
|
|
port->MTDR = LPI2C_MTDR_CMD_STOP; // try to send a stop |
|
|
|
return 4; // clock stretched too long or generic timeout |
|
|
|
} |
|
|
|
// are we done yet? |
|
|
|
if (tx_index > tx_len) { |
|
|
|
uint32_t tx_fifo = port->MFSR & 0x07; |
|
|
|
if (tx_fifo == 0 && ((status & LPI2C_MSR_SDF) || !sendStop)) { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
yield(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
// these delays are an ugly kludge for BNO080 |
|
|
|
// https://forum.pjrc.com/threads/58268 |
|
|
|
// TODO: need more robust error recovery!! |
|
|
|
if (status & (LPI2C_MSR_ALF | LPI2C_MSR_NDF)) { |
|
|
|
delayMicroseconds(300); // min=120 |
|
|
|
} else if (status & (LPI2C_MSR_PLTF | LPI2C_MSR_FEF)) { |
|
|
|
delayMicroseconds(60); // min=30 |
|
|
|
} |
|
|
|
|
|
|
|
//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->MSR & (LPI2C_MSR_BBF|LPI2C_MSR_MBF)) != (LPI2C_MSR_BBF|LPI2C_MSR_MBF))) { |
|
|
|
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); |
|
|
|
} |
|
|
|
if (!wait_idle()) return 4; |
|
|
|
address = (address & 0x7F) << 1; |
|
|
|
if (length < 1) length = 1; |
|
|
|
if (length > 255) length = 255; |
|
|
|
rxBufferIndex = 0; |
|
|
|
rxBufferLength = 0; |
|
|
|
|
|
|
|
//elapsedMillis timeout=0; |
|
|
|
|
|
|
|
uint32_t tx_state = 0; // 0=begin, 1=start, 2=data, 3=stop |
|
|
|
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) { |
|
|
|
if (tx_state < 3) { |
|
|
|
uint32_t tx_fifo = port->MFSR & 0x07; // pg 2914 |
|
|
|
while (tx_fifo < 4 && tx_state < 3) { |
|
|
|
if (tx_state == 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); |
|
|
|
} else if (tx_state == 1) { |
|
|
|
port->MTDR = LPI2C_MTDR_CMD_RECEIVE | (length - 1); |
|
|
|
} else { |
|
|
|
if (sendStop) port->MTDR = LPI2C_MTDR_CMD_STOP; |
|
|
|
} |
|
|
|
cmd++; |
|
|
|
fifo = fifo + 1; |
|
|
|
tx_state++; |
|
|
|
tx_fifo--; |
|
|
|
} |
|
|
|
} |
|
|
|
// 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)) { |
|
|
|
uint32_t rx_fifo = (port->MFSR >> 16) & 0x07; |
|
|
|
while (rx_fifo > 0 && rxBufferLength < sizeof(rxBuffer)) { |
|
|
|
rxBuffer[rxBufferLength++] = port->MRDR; |
|
|
|
fifo = fifo - 1; |
|
|
|
rx_fifo--; |
|
|
|
} |
|
|
|
} |
|
|
|
// monitor status |
|
|
|
status = port->MSR; // pg 2899 & 2892 |
|
|
|
// monitor status, check for error conditions |
|
|
|
uint32_t status = port->MSR; // pg 2884 & 2891 |
|
|
|
if (status & LPI2C_MSR_ALF) { |
|
|
|
//printf("arbitration lost\n"); |
|
|
|
port->MCR |= LPI2C_MCR_RTF | LPI2C_MCR_RRF; // clear FIFOs |
|
|
|
break; |
|
|
|
} |
|
|
|
if (status & LPI2C_MSR_NDF) { |
|
|
|
//printf("got NACK\n"); |
|
|
|
// TODO: how to make sure stop is sent? |
|
|
|
if ((status & LPI2C_MSR_NDF) || (status & LPI2C_MSR_PLTF) || timeout > 50) { |
|
|
|
port->MCR |= LPI2C_MCR_RTF | LPI2C_MCR_RRF; // clear FIFOs |
|
|
|
port->MTDR = LPI2C_MTDR_CMD_STOP; // try to send a stop |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
//if (timeout > 250) { |
|
|
|
//printf("Status = %x\n", status); |
|
|
|
//timeout = 0; |
|
|
|
//} |
|
|
|
|
|
|
|
if (rxBufferLength >= length && cmd >= 3) break; |
|
|
|
// are we done yet? |
|
|
|
if (rxBufferLength >= length && tx_state >= 3) { |
|
|
|
uint32_t tx_fifo = port->MFSR & 0x07; |
|
|
|
if (tx_fifo == 0 && ((status & LPI2C_MSR_SDF) || !sendStop)) { |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
yield(); |
|
|
|
} |
|
|
|
//digitalWriteFast(15, HIGH); |
|
|
|
//delayMicroseconds(2); |
|
|
|
//digitalWriteFast(15, LOW); |
|
|
|
uint32_t rx_fifo = (port->MFSR >> 16) & 0x07; |
|
|
|
if (rx_fifo > 0) port->MCR |= LPI2C_MCR_RRF; |
|
|
|
return rxBufferLength; |
|
|
|
} |
|
|
|
|
|
|
@@ -332,65 +311,70 @@ uint8_t TwoWire::requestFrom(uint8_t addr, uint8_t qty, uint32_t iaddr, uint8_t |
|
|
|
|
|
|
|
PROGMEM |
|
|
|
constexpr TwoWire::I2C_Hardware_t TwoWire::i2c1_hardware = { |
|
|
|
CCM_CCGR2, CCM_CCGR2_LPI2C1(CCM_CCGR_ON), |
|
|
|
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 |
|
|
|
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), |
|
|
|
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 |
|
|
|
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), |
|
|
|
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 |
|
|
|
IRQ_LPI2C4 |
|
|
|
}; |
|
|
|
TwoWire Wire2(&IMXRT_LPI2C4, TwoWire::i2c4_hardware); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Timeout if a device stretches SCL this long, in microseconds |
|
|
|
#define CLOCK_STRETCH_TIMEOUT 15000 |
|
|
|
|
|
|
|
|
|
|
|
void TwoWire::setClock(uint32_t frequency) |
|
|
|
{ |
|
|
|
port->MCR = 0; |
|
|
|
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); |
|
|
|
LPI2C_MCFGR2_BUSIDLE(3000); // idle timeout 250 us |
|
|
|
port->MCFGR3 = LPI2C_MCFGR3_PINLOW(CLOCK_STRETCH_TIMEOUT * 12 / 256 + 1); |
|
|
|
} 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); |
|
|
|
LPI2C_MCFGR2_BUSIDLE(3600); // idle timeout 150 us |
|
|
|
port->MCFGR3 = LPI2C_MCFGR3_PINLOW(CLOCK_STRETCH_TIMEOUT * 24 / 256 + 1); |
|
|
|
} 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); |
|
|
|
LPI2C_MCFGR2_BUSIDLE(2400); // idle timeout 100 us |
|
|
|
port->MCFGR3 = LPI2C_MCFGR3_PINLOW(CLOCK_STRETCH_TIMEOUT * 24 / 256 + 1); |
|
|
|
} |
|
|
|
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; |
|
|
|
port->MCCR1 = port->MCCR0; |
|
|
|
port->MCFGR0 = 0; |
|
|
|
port->MFCR = LPI2C_MFCR_RXWATER(1) | LPI2C_MFCR_TXWATER(1); |
|
|
|
port->MCR = LPI2C_MCR_MEN; |
|
|
|
} |
|
|
|
|
|
|
|
#endif |