No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

322 líneas
8.8KB

  1. #include "Wire.h"
  2. #if defined(__IMXRT1052__) || defined(__IMXRT1062__)
  3. #include "debug/printf.h"
  4. void TwoWire::begin(void)
  5. {
  6. // use 24 MHz clock
  7. CCM_CSCDR2 = (CCM_CSCDR2 & ~CCM_CSCDR2_LPI2C_CLK_PODF(63)) | CCM_CSCDR2_LPI2C_CLK_SEL;
  8. hardware.clock_gate_register |= hardware.clock_gate_mask;
  9. port->MCR = LPI2C_MCR_RST;
  10. setClock(100000);
  11. hardware.sda_mux_register = hardware.sda_mux_value;
  12. hardware.scl_mux_register = hardware.scl_mux_value;
  13. hardware.sda_input_register = hardware.sda_input_value;
  14. hardware.scl_input_register = hardware.scl_input_value;
  15. hardware.sda_pad_register |= IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3);
  16. hardware.scl_pad_register |= IOMUXC_PAD_PKE | IOMUXC_PAD_PUE | IOMUXC_PAD_PUS(3);
  17. }
  18. void TwoWire::begin(uint8_t address)
  19. {
  20. // TODO: slave mode
  21. }
  22. void TwoWire::end()
  23. {
  24. }
  25. size_t TwoWire::write(uint8_t data)
  26. {
  27. if (transmitting || slave_mode) {
  28. if (txBufferLength >= BUFFER_LENGTH+1) {
  29. setWriteError();
  30. return 0;
  31. }
  32. txBuffer[txBufferLength++] = data;
  33. return 1;
  34. }
  35. return 0;
  36. }
  37. size_t TwoWire::write(const uint8_t *data, size_t quantity)
  38. {
  39. if (transmitting || slave_mode) {
  40. size_t avail = BUFFER_LENGTH+1 - txBufferLength;
  41. if (quantity > avail) {
  42. quantity = avail;
  43. setWriteError();
  44. }
  45. memcpy(txBuffer + txBufferLength, data, quantity);
  46. txBufferLength += quantity;
  47. return quantity;
  48. }
  49. return 0;
  50. }
  51. uint8_t TwoWire::endTransmission(uint8_t sendStop)
  52. {
  53. uint32_t i=0, len, status;
  54. len = txBufferLength;
  55. if (!len) return 4; // no data to transmit
  56. // wait while bus is busy
  57. while (1) {
  58. status = port->MSR; // pg 2899 & 2892
  59. if (!(status & LPI2C_MSR_BBF)) break; // bus is available
  60. if (status & LPI2C_MSR_MBF) break; // we already have bus control
  61. // TODO: timeout...
  62. }
  63. //printf("m=%x\n", status);
  64. // TODO: is this correct if the prior use didn't send stop?
  65. //port->MSR = LPI2C_MSR_PLTF | LPI2C_MSR_ALF | LPI2C_MSR_NDF | LPI2C_MSR_SDF; // clear flags
  66. port->MSR = status;
  67. //printf("MSR=%lX, MFSR=%lX\n", status, port->MFSR);
  68. //elapsedMillis timeout=0;
  69. while (1) {
  70. // transmit stuff, if we haven't already
  71. if (i <= len) {
  72. uint32_t fifo_used = port->MFSR & 0x07; // pg 2914
  73. //if (fifo_used < 4) printf("t=%ld\n", fifo_used);
  74. while (fifo_used < 4) {
  75. if (i == 0) {
  76. //printf("start %x\n", txBuffer[0]);
  77. port->MTDR = LPI2C_MTDR_CMD_START | txBuffer[0];
  78. i = 1;
  79. } else if (i < len) {
  80. port->MTDR = LPI2C_MTDR_CMD_TRANSMIT | txBuffer[i++];
  81. } else {
  82. if (sendStop) port->MTDR = LPI2C_MTDR_CMD_STOP;
  83. i++;
  84. break;
  85. }
  86. fifo_used = fifo_used + 1;
  87. }
  88. }
  89. // monitor status
  90. status = port->MSR; // pg 2899 & 2892
  91. if (status & LPI2C_MSR_ALF) {
  92. //printf("arbitration lost\n");
  93. return 4; // we lost bus arbitration to another master
  94. }
  95. if (status & LPI2C_MSR_NDF) {
  96. //printf("NACK, f=%d, i=%d\n", port->MFSR & 0x07, i);
  97. // TODO: check that hardware really sends stop automatically
  98. port->MCR = LPI2C_MCR_MEN | LPI2C_MCR_RTF; // clear the FIFO
  99. // TODO: is always sending a stop the right way to recover?
  100. port->MTDR = LPI2C_MTDR_CMD_STOP;
  101. return 2; // NACK for address
  102. //return 3; // NACK for data TODO: how to discern addr from data?
  103. }
  104. //if (status & LPI2C_MSR_PLTF) {
  105. //printf("bus stuck - what to do?\n");
  106. //return 4;
  107. //}
  108. //if (timeout > 100) {
  109. //printf("status = %x\n", status);
  110. //timeout = 0;
  111. //}
  112. if (sendStop) {
  113. if (status & LPI2C_MSR_SDF) {
  114. // master automatically sends stop condition on some
  115. // types of errors, so this flag only means success
  116. // when all comments in fifo have been fully used
  117. uint32_t fifo = port->MFSR & 0x07;
  118. if (fifo == 0) return 0;
  119. }
  120. } else {
  121. uint32_t fifo_used = port->MFSR & 0x07; // pg 2914
  122. if (fifo_used == 0) {
  123. //digitalWriteFast(15, HIGH);
  124. //delayMicroseconds(2);
  125. //digitalWriteFast(15, LOW);
  126. // TODO: this returns before the last data transmits!
  127. // Should verify every byte ACKs, arbitration not lost
  128. //printf("fifo empty, msr=%x\n", status);
  129. return 0;
  130. }
  131. }
  132. }
  133. }
  134. uint8_t TwoWire::requestFrom(uint8_t address, uint8_t length, uint8_t sendStop)
  135. {
  136. uint32_t cmd=0, status, fifo;
  137. // wait while bus is busy
  138. while (1) {
  139. status = port->MSR; // pg 2899 & 2892
  140. if (!(status & LPI2C_MSR_BBF)) break; // bus is available
  141. if (status & LPI2C_MSR_MBF) break; // we already have bus control
  142. // TODO: timeout...
  143. }
  144. //printf("idle2, msr=%x\n", status);
  145. // TODO: is this correct if the prior use didn't send stop?
  146. port->MSR = LPI2C_MSR_PLTF | LPI2C_MSR_ALF | LPI2C_MSR_NDF | LPI2C_MSR_SDF; // clear flags
  147. //printf("MSR=%lX, MFSR=%lX\n", status, port->MFSR);
  148. address = (address & 0x7F) << 1;
  149. if (length < 1) length = 1;
  150. if (length > 255) length = 255;
  151. rxBufferIndex = 0;
  152. rxBufferLength = 0;
  153. //elapsedMillis timeout=0;
  154. while (1) {
  155. // transmit stuff, if we haven't already
  156. if (cmd < 3) {
  157. fifo = port->MFSR & 0x07; // pg 2914
  158. //if (fifo < 4) printf("t=%ld\n", fifo);
  159. while (fifo < 4 && cmd < 3) {
  160. if (cmd == 0) {
  161. port->MTDR = LPI2C_MTDR_CMD_START | 1 | address;
  162. } else if (cmd == 1) {
  163. // causes bus stuck... need way to recover
  164. //port->MTDR = LPI2C_MTDR_CMD_START | (length - 1);
  165. port->MTDR = LPI2C_MTDR_CMD_RECEIVE | (length - 1);
  166. } else {
  167. if (sendStop) port->MTDR = LPI2C_MTDR_CMD_STOP;
  168. }
  169. cmd++;
  170. fifo = fifo + 1;
  171. }
  172. }
  173. // receive stuff
  174. if (rxBufferLength < sizeof(rxBuffer)) {
  175. fifo = (port->MFSR >> 16) & 0x07;
  176. //if (fifo > 0) printf("r=%ld\n", fifo);
  177. while (fifo > 0 && rxBufferLength < sizeof(rxBuffer)) {
  178. rxBuffer[rxBufferLength++] = port->MRDR;
  179. fifo = fifo - 1;
  180. }
  181. }
  182. // monitor status
  183. status = port->MSR; // pg 2899 & 2892
  184. if (status & LPI2C_MSR_ALF) {
  185. //printf("arbitration lost\n");
  186. break;
  187. }
  188. if (status & LPI2C_MSR_NDF) {
  189. //printf("got NACK\n");
  190. // TODO: how to make sure stop is sent?
  191. break;
  192. }
  193. //if (timeout > 250) {
  194. //printf("Status = %x\n", status);
  195. //timeout = 0;
  196. //}
  197. if (rxBufferLength >= length && cmd >= 3) break;
  198. }
  199. //digitalWriteFast(15, HIGH);
  200. //delayMicroseconds(2);
  201. //digitalWriteFast(15, LOW);
  202. return rxBufferLength;
  203. }
  204. PROGMEM
  205. constexpr TwoWire::I2C_Hardware_t TwoWire::i2c1_hardware = {
  206. CCM_CCGR2, CCM_CCGR2_LPI2C1(CCM_CCGR_ON),
  207. IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_01, // 18/A4 AD_B1_01 GPIO1.17 I2C1_SDA
  208. IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_00, // 19/A5 AD_B1_00 GPIO1.16 I2C1_SCL
  209. IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_01,
  210. IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_00,
  211. IOMUXC_LPI2C1_SDA_SELECT_INPUT,
  212. IOMUXC_LPI2C1_SCL_SELECT_INPUT,
  213. 3 | 0x10,
  214. 3 | 0x10,
  215. 1,
  216. 1,
  217. IRQ_LPI2C1
  218. };
  219. TwoWire Wire(&IMXRT_LPI2C1, TwoWire::i2c1_hardware);
  220. PROGMEM
  221. constexpr TwoWire::I2C_Hardware_t TwoWire::i2c3_hardware = {
  222. CCM_CCGR2, CCM_CCGR2_LPI2C3(CCM_CCGR_ON),
  223. IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_06, // 17/A3 AD_B1_06 GPIO1.22 I2C3_SDA
  224. IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_07, // 16/A2 AD_B1_07 GPIO1.23 I2C3_SCL
  225. IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_06,
  226. IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_07,
  227. IOMUXC_LPI2C3_SDA_SELECT_INPUT,
  228. IOMUXC_LPI2C3_SCL_SELECT_INPUT,
  229. 1 | 0x10,
  230. 1 | 0x10,
  231. 2,
  232. 2,
  233. IRQ_LPI2C3
  234. };
  235. TwoWire Wire1(&IMXRT_LPI2C3, TwoWire::i2c3_hardware);
  236. PROGMEM
  237. constexpr TwoWire::I2C_Hardware_t TwoWire::i2c4_hardware = {
  238. CCM_CCGR6, CCM_CCGR6_LPI2C4_SERIAL(CCM_CCGR_ON),
  239. IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_13, // 25/A11 AD_B0_13 GPIO1.13 I2C4_SDA
  240. IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_12, // 24/A10 AD_B1_12 GPIO1.12 I2C4_SCL
  241. IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_13,
  242. IOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_12,
  243. IOMUXC_LPI2C4_SDA_SELECT_INPUT,
  244. IOMUXC_LPI2C4_SCL_SELECT_INPUT,
  245. 0 | 0x10,
  246. 0 | 0x10,
  247. 1,
  248. 1,
  249. IRQ_LPI2C4
  250. };
  251. TwoWire Wire2(&IMXRT_LPI2C4, TwoWire::i2c4_hardware);
  252. void TwoWire::setClock(uint32_t frequency)
  253. {
  254. port->MCR = 0;
  255. if (frequency < 400000) {
  256. // 100 kHz
  257. port->MCCR0 = LPI2C_MCCR0_CLKHI(55) | LPI2C_MCCR0_CLKLO(59) |
  258. LPI2C_MCCR0_DATAVD(25) | LPI2C_MCCR0_SETHOLD(40);
  259. port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(1);
  260. port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(5) | LPI2C_MCFGR2_FILTSCL(5) |
  261. LPI2C_MCFGR2_BUSIDLE(3900);
  262. } else if (frequency < 1000000) {
  263. // 400 kHz
  264. port->MCCR0 = LPI2C_MCCR0_CLKHI(26) | LPI2C_MCCR0_CLKLO(28) |
  265. LPI2C_MCCR0_DATAVD(12) | LPI2C_MCCR0_SETHOLD(18);
  266. port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
  267. port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(2) | LPI2C_MCFGR2_FILTSCL(2) |
  268. LPI2C_MCFGR2_BUSIDLE(3900);
  269. } else {
  270. // 1 MHz
  271. port->MCCR0 = LPI2C_MCCR0_CLKHI(9) | LPI2C_MCCR0_CLKLO(10) |
  272. LPI2C_MCCR0_DATAVD(4) | LPI2C_MCCR0_SETHOLD(7);
  273. port->MCFGR1 = LPI2C_MCFGR1_PRESCALE(0);
  274. port->MCFGR2 = LPI2C_MCFGR2_FILTSDA(1) | LPI2C_MCFGR2_FILTSCL(1) |
  275. LPI2C_MCFGR2_BUSIDLE(3900);
  276. }
  277. port->MCCR1 = port->MCCR0;
  278. port->MCFGR0 = 0;
  279. port->MCFGR3 = LPI2C_MCFGR3_PINLOW(3900);
  280. port->MFCR = LPI2C_MFCR_RXWATER(1) | LPI2C_MFCR_TXWATER(1);
  281. port->MCR = LPI2C_MCR_MEN;
  282. }
  283. #endif