PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

WireIMXRT.cpp 12KB

3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. #include "Wire.h"
  2. #if defined(__IMXRT1062__)
  3. //#include "debug/printf.h"
  4. #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))
  5. void TwoWire::begin(void)
  6. {
  7. // use 24 MHz clock
  8. CCM_CSCDR2 = (CCM_CSCDR2 & ~CCM_CSCDR2_LPI2C_CLK_PODF(63)) | CCM_CSCDR2_LPI2C_CLK_SEL;
  9. hardware.clock_gate_register |= hardware.clock_gate_mask;
  10. port->MCR = LPI2C_MCR_RST;
  11. setClock(100000);
  12. // Setup SDA register
  13. *(portControlRegister(hardware.sda_pins[sda_pin_index_].pin)) = PINCONFIG;
  14. *(portConfigRegister(hardware.sda_pins[sda_pin_index_].pin)) = hardware.sda_pins[sda_pin_index_].mux_val;
  15. if (hardware.sda_pins[sda_pin_index_].select_input_register) {
  16. *(hardware.sda_pins[sda_pin_index_].select_input_register) = hardware.sda_pins[sda_pin_index_].select_val;
  17. }
  18. // setup SCL register
  19. *(portControlRegister(hardware.scl_pins[scl_pin_index_].pin)) = PINCONFIG;
  20. *(portConfigRegister(hardware.scl_pins[scl_pin_index_].pin)) = hardware.scl_pins[scl_pin_index_].mux_val;
  21. if (hardware.scl_pins[scl_pin_index_].select_input_register) {
  22. *(hardware.scl_pins[scl_pin_index_].select_input_register) = hardware.scl_pins[scl_pin_index_].select_val;
  23. }
  24. }
  25. void TwoWire::begin(uint8_t address)
  26. {
  27. // TODO: slave mode
  28. }
  29. void TwoWire::end()
  30. {
  31. }
  32. void TwoWire::setSDA(uint8_t pin) {
  33. if (pin == hardware.sda_pins[sda_pin_index_].pin) return;
  34. uint32_t newindex=0;
  35. while (1) {
  36. uint32_t sda_pin = hardware.sda_pins[newindex].pin;
  37. if (sda_pin == 255) return;
  38. if (sda_pin == pin) break;
  39. if (++newindex >= sizeof(hardware.sda_pins)) return;
  40. }
  41. if ((hardware.clock_gate_register & hardware.clock_gate_mask)) {
  42. *(portConfigRegister(hardware.sda_pins[sda_pin_index_].pin)) = 5; // hard to know what to go back to?
  43. // setup new one...
  44. *(portControlRegister(hardware.sda_pins[newindex].pin)) |= IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3);
  45. *(portConfigRegister(hardware.sda_pins[newindex].pin)) = hardware.sda_pins[newindex].mux_val;
  46. if (hardware.sda_pins[newindex].select_input_register) {
  47. *(hardware.sda_pins[newindex].select_input_register) = hardware.sda_pins[newindex].select_val;
  48. }
  49. }
  50. sda_pin_index_ = newindex;
  51. }
  52. void TwoWire::setSCL(uint8_t pin) {
  53. if (pin == hardware.scl_pins[scl_pin_index_].pin) return;
  54. uint32_t newindex=0;
  55. while (1) {
  56. uint32_t scl_pin = hardware.scl_pins[newindex].pin;
  57. if (scl_pin == 255) return;
  58. if (scl_pin == pin) break;
  59. if (++newindex >= sizeof(hardware.scl_pins)) return;
  60. }
  61. if ((hardware.clock_gate_register & hardware.clock_gate_mask)) {
  62. *(portConfigRegister(hardware.scl_pins[scl_pin_index_].pin)) = 5; // hard to know what to go back to?
  63. // setup new one...
  64. *(portControlRegister(hardware.scl_pins[newindex].pin)) |= IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3);
  65. *(portConfigRegister(hardware.scl_pins[newindex].pin)) = hardware.scl_pins[newindex].mux_val;
  66. if (hardware.scl_pins[newindex].select_input_register) {
  67. *(hardware.scl_pins[newindex].select_input_register) = hardware.scl_pins[newindex].select_val;
  68. }
  69. }
  70. scl_pin_index_ = newindex;
  71. }
  72. bool TwoWire::force_clock()
  73. {
  74. bool ret = false;
  75. uint32_t sda_pin = hardware.sda_pins[sda_pin_index_].pin;
  76. uint32_t scl_pin = hardware.scl_pins[scl_pin_index_].pin;
  77. uint32_t sda_mask = digitalPinToBitMask(sda_pin);
  78. uint32_t scl_mask = digitalPinToBitMask(scl_pin);
  79. // take control of pins with GPIO
  80. *portConfigRegister(sda_pin) = 5 | 0x10;
  81. *portSetRegister(sda_pin) = sda_mask;
  82. *portModeRegister(sda_pin) |= sda_mask;
  83. *portConfigRegister(scl_pin) = 5 | 0x10;
  84. *portSetRegister(scl_pin) = scl_mask;
  85. *portModeRegister(scl_pin) |= scl_mask;
  86. delayMicroseconds(10);
  87. for (int i=0; i < 9; i++) {
  88. if ((*portInputRegister(sda_pin) & sda_mask)
  89. && (*portInputRegister(scl_pin) & scl_mask)) {
  90. // success, both pins are high
  91. ret = true;
  92. break;
  93. }
  94. *portClearRegister(scl_pin) = scl_mask;
  95. delayMicroseconds(5);
  96. *portSetRegister(scl_pin) = scl_mask;
  97. delayMicroseconds(5);
  98. }
  99. // return control of pins to I2C
  100. *(portConfigRegister(sda_pin)) = hardware.sda_pins[sda_pin_index_].mux_val;
  101. *(portConfigRegister(scl_pin)) = hardware.scl_pins[scl_pin_index_].mux_val;
  102. return ret;
  103. }
  104. size_t TwoWire::write(uint8_t data)
  105. {
  106. if (transmitting || slave_mode) {
  107. if (txBufferLength >= BUFFER_LENGTH+1) {
  108. setWriteError();
  109. return 0;
  110. }
  111. txBuffer[txBufferLength++] = data;
  112. return 1;
  113. }
  114. return 0;
  115. }
  116. size_t TwoWire::write(const uint8_t *data, size_t quantity)
  117. {
  118. if (transmitting || slave_mode) {
  119. size_t avail = BUFFER_LENGTH+1 - txBufferLength;
  120. if (quantity > avail) {
  121. quantity = avail;
  122. setWriteError();
  123. }
  124. memcpy(txBuffer + txBufferLength, data, quantity);
  125. txBufferLength += quantity;
  126. return quantity;
  127. }
  128. return 0;
  129. }
  130. // 2 BBF = Bus Busy Flag
  131. // 1 MBF = Master Busy Flag
  132. // 40 DMF = Data Match Flag
  133. // 20 PLTF = Pin Low Timeout Flag
  134. // 10 FEF = FIFO Error Flag
  135. // 08 ALF = Arbitration Lost Flag
  136. // 04 NDF = NACK Detect Flag
  137. // 02 SDF = STOP Detect Flag
  138. // 01 EPF = End Packet Flag
  139. // 2 RDF = Receive Data Flag
  140. // 1 TDF = Transmit Data Flag
  141. bool TwoWire::wait_idle()
  142. {
  143. elapsedMillis timeout = 0;
  144. while (1) {
  145. uint32_t status = port->MSR; // pg 2899 & 2892
  146. if (!(status & LPI2C_MSR_BBF)) break; // bus is available
  147. if (status & LPI2C_MSR_MBF) break; // we already have bus control
  148. if (timeout > 16) {
  149. //Serial.printf("timeout waiting for idle, MSR = %x\n", status);
  150. if (force_clock()) break;
  151. //Serial.printf("unable to get control of I2C bus\n");
  152. return false;
  153. }
  154. }
  155. port->MSR = 0x00007F00; // clear all prior flags
  156. return true;
  157. }
  158. uint8_t TwoWire::endTransmission(uint8_t sendStop)
  159. {
  160. uint32_t tx_len = txBufferLength;
  161. if (!tx_len) return 4; // no address for transmit
  162. if (!wait_idle()) return 4;
  163. uint32_t tx_index = 0; // 0=start, 1=addr, 2-(N-1)=data, N=stop
  164. elapsedMillis timeout = 0;
  165. while (1) {
  166. // transmit stuff, if we haven't already
  167. if (tx_index <= tx_len) {
  168. uint32_t fifo_used = port->MFSR & 0x07; // pg 2914
  169. while (fifo_used < 4) {
  170. if (tx_index == 0) {
  171. port->MTDR = LPI2C_MTDR_CMD_START | txBuffer[0];
  172. tx_index = 1;
  173. } else if (tx_index < tx_len) {
  174. port->MTDR = LPI2C_MTDR_CMD_TRANSMIT | txBuffer[tx_index++];
  175. } else {
  176. if (sendStop) port->MTDR = LPI2C_MTDR_CMD_STOP;
  177. tx_index++;
  178. break;
  179. }
  180. fifo_used++;
  181. }
  182. }
  183. // monitor status
  184. uint32_t status = port->MSR; // pg 2884 & 2891
  185. if (status & LPI2C_MSR_ALF) {
  186. port->MCR |= LPI2C_MCR_RTF | LPI2C_MCR_RRF; // clear FIFOs
  187. return 4; // we lost bus arbitration to another master
  188. }
  189. if (status & LPI2C_MSR_NDF) {
  190. port->MCR |= LPI2C_MCR_RTF | LPI2C_MCR_RRF; // clear FIFOs
  191. port->MTDR = LPI2C_MTDR_CMD_STOP;
  192. return 2; // NACK (assume address, TODO: how to tell address from data)
  193. }
  194. if ((status & LPI2C_MSR_PLTF) || timeout > 50) {
  195. port->MCR |= LPI2C_MCR_RTF | LPI2C_MCR_RRF; // clear FIFOs
  196. port->MTDR = LPI2C_MTDR_CMD_STOP; // try to send a stop
  197. return 4; // clock stretched too long or generic timeout
  198. }
  199. // are we done yet?
  200. if (tx_index > tx_len) {
  201. uint32_t tx_fifo = port->MFSR & 0x07;
  202. if (tx_fifo == 0 && ((status & LPI2C_MSR_SDF) || !sendStop)) {
  203. return 0;
  204. }
  205. }
  206. yield();
  207. }
  208. }
  209. uint8_t TwoWire::requestFrom(uint8_t address, uint8_t length, uint8_t sendStop)
  210. {
  211. if (!wait_idle()) return 4;
  212. address = (address & 0x7F) << 1;
  213. if (length < 1) length = 1;
  214. if (length > 255) length = 255;
  215. rxBufferIndex = 0;
  216. rxBufferLength = 0;
  217. uint32_t tx_state = 0; // 0=begin, 1=start, 2=data, 3=stop
  218. elapsedMillis timeout = 0;
  219. while (1) {
  220. // transmit stuff, if we haven't already
  221. if (tx_state < 3) {
  222. uint32_t tx_fifo = port->MFSR & 0x07; // pg 2914
  223. while (tx_fifo < 4 && tx_state < 3) {
  224. if (tx_state == 0) {
  225. port->MTDR = LPI2C_MTDR_CMD_START | 1 | address;
  226. } else if (tx_state == 1) {
  227. port->MTDR = LPI2C_MTDR_CMD_RECEIVE | (length - 1);
  228. } else {
  229. if (sendStop) port->MTDR = LPI2C_MTDR_CMD_STOP;
  230. }
  231. tx_state++;
  232. tx_fifo--;
  233. }
  234. }
  235. // receive stuff
  236. if (rxBufferLength < sizeof(rxBuffer)) {
  237. uint32_t rx_fifo = (port->MFSR >> 16) & 0x07;
  238. while (rx_fifo > 0 && rxBufferLength < sizeof(rxBuffer)) {
  239. rxBuffer[rxBufferLength++] = port->MRDR;
  240. rx_fifo--;
  241. }
  242. }
  243. // monitor status, check for error conditions
  244. uint32_t status = port->MSR; // pg 2884 & 2891
  245. if (status & LPI2C_MSR_ALF) {
  246. port->MCR |= LPI2C_MCR_RTF | LPI2C_MCR_RRF; // clear FIFOs
  247. break;
  248. }
  249. if ((status & LPI2C_MSR_NDF) || (status & LPI2C_MSR_PLTF) || timeout > 50) {
  250. port->MCR |= LPI2C_MCR_RTF | LPI2C_MCR_RRF; // clear FIFOs
  251. port->MTDR = LPI2C_MTDR_CMD_STOP; // try to send a stop
  252. break;
  253. }
  254. // are we done yet?
  255. if (rxBufferLength >= length && tx_state >= 3) {
  256. uint32_t tx_fifo = port->MFSR & 0x07;
  257. if (tx_fifo == 0 && ((status & LPI2C_MSR_SDF) || !sendStop)) {
  258. break;
  259. }
  260. }
  261. yield();
  262. }
  263. uint32_t rx_fifo = (port->MFSR >> 16) & 0x07;
  264. if (rx_fifo > 0) port->MCR |= LPI2C_MCR_RRF;
  265. return rxBufferLength;
  266. }
  267. uint8_t TwoWire::requestFrom(uint8_t addr, uint8_t qty, uint32_t iaddr, uint8_t n, uint8_t stop)
  268. {
  269. if (n > 0) {
  270. union { uint32_t ul; uint8_t b[4]; } iaddress;
  271. iaddress.ul = iaddr;
  272. beginTransmission(addr);
  273. if (n > 3) n = 3;
  274. do {
  275. n = n - 1;
  276. write(iaddress.b[n]);
  277. } while (n > 0);
  278. endTransmission(false);
  279. }
  280. if (qty > BUFFER_LENGTH) qty = BUFFER_LENGTH;
  281. return requestFrom(addr, qty, stop);
  282. }
  283. PROGMEM
  284. constexpr TwoWire::I2C_Hardware_t TwoWire::i2c1_hardware = {
  285. CCM_CCGR2, CCM_CCGR2_LPI2C1(CCM_CCGR_ON),
  286. {{18, 3 | 0x10, &IOMUXC_LPI2C1_SDA_SELECT_INPUT, 1}, {0xff, 0xff, nullptr, 0}},
  287. {{19, 3 | 0x10, &IOMUXC_LPI2C1_SCL_SELECT_INPUT, 1}, {0xff, 0xff, nullptr, 0}},
  288. IRQ_LPI2C1
  289. };
  290. TwoWire Wire(&IMXRT_LPI2C1, TwoWire::i2c1_hardware);
  291. PROGMEM
  292. constexpr TwoWire::I2C_Hardware_t TwoWire::i2c3_hardware = {
  293. CCM_CCGR2, CCM_CCGR2_LPI2C3(CCM_CCGR_ON),
  294. {{17, 1 | 0x10, &IOMUXC_LPI2C3_SDA_SELECT_INPUT, 2}, {36, 2 | 0x10, &IOMUXC_LPI2C3_SDA_SELECT_INPUT, 1}},
  295. {{16, 1 | 0x10, &IOMUXC_LPI2C3_SCL_SELECT_INPUT, 2}, {37, 2 | 0x10, &IOMUXC_LPI2C3_SCL_SELECT_INPUT, 1}},
  296. IRQ_LPI2C3
  297. };
  298. TwoWire Wire1(&IMXRT_LPI2C3, TwoWire::i2c3_hardware);
  299. PROGMEM
  300. constexpr TwoWire::I2C_Hardware_t TwoWire::i2c4_hardware = {
  301. CCM_CCGR6, CCM_CCGR6_LPI2C4_SERIAL(CCM_CCGR_ON),
  302. {{25, 0 | 0x10, &IOMUXC_LPI2C4_SDA_SELECT_INPUT, 1}, {0xff, 0xff, nullptr, 0}},
  303. {{24, 0 | 0x10, &IOMUXC_LPI2C4_SCL_SELECT_INPUT, 1}, {0xff, 0xff, nullptr, 0}},
  304. IRQ_LPI2C4
  305. };
  306. TwoWire Wire2(&IMXRT_LPI2C4, TwoWire::i2c4_hardware);
  307. // Timeout if a device stretches SCL this long, in microseconds
  308. #define CLOCK_STRETCH_TIMEOUT 15000
  309. void TwoWire::setClock(uint32_t frequency)
  310. {
  311. port->MCR = 0;
  312. if (frequency < 400000) {
  313. // 100 kHz
  314. port->MCCR0 = LPI2C_MCCR0_CLKHI(55) | LPI2C_MCCR0_CLKLO(59) |
  315. LPI2C_MCCR0_DATAVD(25) | LPI2C_MCCR0_SETHOLD(40);
  316. port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(1);
  317. port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(5) | LPI2C_MCFGR2_FILTSCL(5) |
  318. LPI2C_MCFGR2_BUSIDLE(3000); // idle timeout 250 us
  319. port->MCFGR3 = LPI2C_MCFGR3_PINLOW(CLOCK_STRETCH_TIMEOUT * 12 / 256 + 1);
  320. } else if (frequency < 1000000) {
  321. // 400 kHz
  322. port->MCCR0 = LPI2C_MCCR0_CLKHI(26) | LPI2C_MCCR0_CLKLO(28) |
  323. LPI2C_MCCR0_DATAVD(12) | LPI2C_MCCR0_SETHOLD(18);
  324. port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
  325. port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(2) | LPI2C_MCFGR2_FILTSCL(2) |
  326. LPI2C_MCFGR2_BUSIDLE(3600); // idle timeout 150 us
  327. port->MCFGR3 = LPI2C_MCFGR3_PINLOW(CLOCK_STRETCH_TIMEOUT * 24 / 256 + 1);
  328. } else {
  329. // 1 MHz
  330. port->MCCR0 = LPI2C_MCCR0_CLKHI(9) | LPI2C_MCCR0_CLKLO(10) |
  331. LPI2C_MCCR0_DATAVD(4) | LPI2C_MCCR0_SETHOLD(7);
  332. port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
  333. port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(1) | LPI2C_MCFGR2_FILTSCL(1) |
  334. LPI2C_MCFGR2_BUSIDLE(2400); // idle timeout 100 us
  335. port->MCFGR3 = LPI2C_MCFGR3_PINLOW(CLOCK_STRETCH_TIMEOUT * 24 / 256 + 1);
  336. }
  337. port->MCCR1 = port->MCCR0;
  338. port->MCFGR0 = 0;
  339. port->MFCR = LPI2C_MFCR_RXWATER(1) | LPI2C_MFCR_TXWATER(1);
  340. port->MCR = LPI2C_MCR_MEN;
  341. }
  342. #endif