i2c_controller.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. /*
  2. 20.02.2025:
  3. Flowchart of the code:
  4. 1. MCU requests ADC data over I2C
  5. 2. ADC is connected to the MULTIPLEXER.
  6. 3. Interrupt triggers when data is available.
  7. 4. Interrupt moves RECEIVED bytes into RXFIFO Buffer
  8. 5. Interrupt marks reception complete RX_DONE
  9. 6. Read ADC_Data and retrieve the final ADc value
  10. 7. Convert ADC value to Voltage
  11. 24.02.2025:
  12. Working Code for Reading Analog Signals from ADC to MCU through Multiplexer
  13. - configured the registers with Channel, Resolution, Mode(Continuous or Single-mode) and Gain: Construct_Config_Byte()
  14. - SET configuration registers to ADC: SetConfiguration()
  15. - Read the Analog Output: Read_ADC_Data()
  16. - Convert Analog output to voltage in Volts: Convert_ADC_To_Voltage()
  17. - Commented out interrupts for the time being.
  18. - Adding code for Pi to READ ADC voltages in mV.
  19. 27.02.2025:
  20. DAC function to write to Channel A in Fast Mode and return the Analog output
  21. */
  22. #include "i2c_target.h"
  23. #include "ti/devices/msp/peripherals/hw_dac12.h"
  24. #include "ti/driverlib/dl_adc12.h"
  25. #include "ti/driverlib/dl_gpio.h"
  26. #include "ti/driverlib/dl_i2c.h"
  27. #include "ti/driverlib/m0p/dl_core.h"
  28. #include "ti_msp_dl_config.h"
  29. #include "ti/comm_modules/i2c/controller/i2c_comm_controller.h"
  30. #include <stdint.h>
  31. #include <stdio.h>
  32. #include "multiplexer.h"
  33. #include "adc.h"
  34. #include "dac.h"
  35. #include "battery.h"
  36. #include "i2c_target.h"
  37. I2C_Instance gI2C;
  38. I2C_ResponseInfo gResponse;
  39. BatteryData battery_data;
  40. /*****************/
  41. /*Register for the MCU*/
  42. #define REGISTER_SIZE 256
  43. uint8_t registers[REGISTER_SIZE]= {1, 2, 3, 4, 5};
  44. /*Counters for TX length and bytes sent*/
  45. uint32_t gTxLen, gTxCount;
  46. /* Counters for RX length and bytes sent*/
  47. uint32_t gRxLen, gRxCount;
  48. /* Boolean to know when a stop command was issued */
  49. bool gStopReceived = false;
  50. uint8_t gTxData[MAX_DATA_SIZE] = {0} ;
  51. /*
  52. Scans all the addresses of the peripherals:
  53. */
  54. void I2C_scanBus() {
  55. printf("Scanning I2C Bus...\n");
  56. // **Step 1: Reset I2C Controller if Busy**
  57. if (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS) {
  58. printf("I2C Bus Busy! Resetting I2C Controller...\n");
  59. DL_I2C_disableController(I2C_controller_INST); // Disable I2C
  60. delay_cycles(20000);
  61. DL_I2C_enableController(I2C_controller_INST); // Re-enable I2C
  62. delay_cycles(20000);
  63. }
  64. // **Step 2: Scan I2C Bus**
  65. for (uint8_t addr = 0x08; addr < 0x78; addr++) { // Valid I2C Address Range
  66. DL_I2C_startControllerTransfer(I2C_controller_INST, addr, DL_I2C_CONTROLLER_DIRECTION_RX, 1);
  67. delay_cycles(5000);
  68. if (!(DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_ERROR)) {
  69. printf("Device found at: 0x%02X\n", addr);
  70. }
  71. }
  72. printf("I2C Scan Complete!\n");
  73. }
  74. /*Function to get the RDY bit from ADC*/
  75. bool Check_ADC_Ready()
  76. {
  77. uint8_t config_byte = 0;
  78. /*
  79. READY BIT:
  80. This bit is the data ready flag. In read mode, this bit indicates if the output register has been updated
  81. with a latest conversion result. In One-Shot Conversion mode, writing this bit to “1” initiates a new
  82. conversion.
  83. 1= Output Register has not been updated
  84. 0= Output Register has been updated
  85. */
  86. DL_I2C_startControllerTransfer(I2C_controller_INST, ADC_TARGET_BASE_ADDRESS, DL_I2C_CONTROLLER_DIRECTION_RX, 1);
  87. config_byte = DL_I2C_receiveControllerData(I2C_controller_INST);
  88. return (config_byte & 0x80) == 0; // Check if RDY bit is cleared in bit 7
  89. }
  90. /*
  91. Function to initialize MCU as target: Pi communicates as the Controller and MCU as the Target
  92. */
  93. /*Interrupt for MCU -> ADC
  94. * CASE: DL_I2C_IIDX_CONTROLLER_RX_DONE: ADC Reception Complete
  95. - ADC has finished sending data and it's fully received.
  96. - gI2C.rxMsg.len = gI2C.rxMsg.ptr:
  97. - Stores the received data length in the response buffer.
  98. - I2C_decodeResponse():
  99. - Decodes the received response.
  100. - gI2C.status = I2C_STATUS_RX_COMPLETE:
  101. - Marks reception is complete.
  102. * CASE: DL_I2C_IIDX_CONTROLLER_TX_DONE: Data Transmit to ADC complete
  103. - DL_I2C_disableInterrupt(..): Disables the TXFIFO interrupt since data is now sent
  104. * CASE: DL_I2C_IIDX_CONTROLLER_RXFIFO_TRIGGER: Receive Data in FIFO
  105. - The I2C Receive FIFO has data ready to be read.
  106. - while (DL_I2C_isControllerRXFIFOEmpty(...) != true): Loops until the RX FIFOis empty (READ all available bytes)
  107. - Inside the while loop:
  108. - If buffer has SPACE, store the received byte
  109. - Prints each received byte in HEXADECIMAL format for debugging
  110. - IF BUFFER is FULL, avoids OVERFLOW by discarding extra byte.
  111. * CASE: DL_I2C_IIDX_CONTROLLER_TXFIFO_TRIGGER: Transmit Data in FIFO
  112. - If there is still data to send:
  113. gI2C.txMsg.ptr += DL_I2C_fillControllerTXFIFO(I2C_controller_INST, &gI2C.txMsg.buffer[gI2C.txMsg.ptr], gI2C.txMsg.len - gI2C.txMsg.ptr);
  114. */
  115. void I2C_controller_INST_IRQHandler(void)
  116. {
  117. //printf("I2C Interrupt Triggered to ADC!\n");
  118. switch (DL_I2C_getPendingInterrupt(I2C_controller_INST))
  119. { /*START Condition*/
  120. case DL_I2C_IIDX_CONTROLLER_START:
  121. //gTxADCcount= 0;
  122. gRxADCcount= 0;
  123. DL_I2C_flushControllerTXFIFO(I2C_controller_INST);
  124. break;
  125. case DL_I2C_IIDX_CONTROLLER_RXFIFO_TRIGGER:
  126. gI2C.status= I2C_STATUS_RX_INPROGRESS;
  127. /* Store bytes received from target in Rx Msg Buffer */
  128. while (DL_I2C_isControllerRXFIFOEmpty(I2C_controller_INST) != true) {
  129. if (gRxADCcount< gRxADClen) {
  130. gRxPacket[gRxADCcount] = DL_I2C_receiveControllerData(I2C_controller_INST);
  131. printf("Received Byte[%d]: 0x%02X\n", gRxADCcount, gRxPacket[gRxADCcount]); // Debug print
  132. gRxADCcount++;
  133. } else {
  134. //printf("ERROR: RX Buffer Overflow! ptr=%d MAX_BUFFER_SIZE=%d\n", gI2C.rxMsg.ptr, MAX_BUFFER_SIZE);
  135. /* Ignore and remove from FIFO if the buffer is full */
  136. DL_I2C_receiveControllerData(I2C_controller_INST);
  137. }
  138. }
  139. if (gRxADCcount == gRxADClen){
  140. //printf("ADC Bytes Received!\n");
  141. DL_I2C_enableInterrupt(I2C_controller_INST, DL_I2C_INTERRUPT_CONTROLLER_STOP);
  142. //gRxComplete = true;
  143. }
  144. break;
  145. /*TRANSMIT data to ADC*/
  146. case DL_I2C_IIDX_CONTROLLER_TXFIFO_TRIGGER:
  147. printf("TX FIFO with data!\n");
  148. gI2C.status= I2C_STATUS_TX_INPROGRESS;
  149. if(gTxADCcount<gTxADClen){
  150. gTxADCcount+= DL_I2C_fillControllerTXFIFO(I2C_controller_INST, &gTxPacket[gTxADCcount], (gTxADClen-gTxADCcount));
  151. } else{
  152. /*Prevent overflow and just ignore data*/
  153. DL_I2C_fillTargetTXFIFO(I2C_controller_INST, (uint8_t[]){0x00}, 1);
  154. }
  155. break;
  156. /*STOP condition*/
  157. case DL_I2C_IIDX_CONTROLLER_STOP:
  158. gTxComplete= true;
  159. gRxComplete = true;
  160. printf("I2C Stop Detected- RX Complete");
  161. case DL_I2C_IIDX_CONTROLLER_ARBITRATION_LOST:
  162. //printf("Interrupt index for I2C controller Arbitration Lost!\n");
  163. break;
  164. case DL_I2C_IIDX_CONTROLLER_NACK:
  165. //printf("I2C NACK Received\n");
  166. if((gI2C.status== I2C_STATUS_RX_STARTED)|| (gI2C.status= I2C_STATUS_TX_STARTED)){
  167. gI2C.status= I2C_STATUS_ERROR;
  168. }
  169. break;
  170. default:
  171. break;
  172. }
  173. }
  174. //void I2C_target_INST_IRQHandler(void) {
  175. // static bool DataRx= false;
  176. // static uint16_t registerAddress=0;
  177. //printf("I2C Interrupt Triggered to MCU (TARGET)!\n");
  178. // uint32_t status = DL_I2C_getPendingInterrupt(I2C_target_INST);
  179. // switch (status) {
  180. /* START condition detected */
  181. // case DL_I2C_IIDX_TARGET_START:
  182. //printf("START condition detected.\n");
  183. // gTxCount= 0;
  184. // gRxCount= 0;
  185. // DataRx= false;
  186. // DL_I2C_flushTargetTXFIFO(I2C_target_INST);
  187. // break;
  188. /* STOP condition detected */
  189. // case DL_I2C_IIDX_TARGET_STOP:
  190. //printf("STOP condition detected.\n");
  191. // if (DataRx == true){
  192. //printf("Data received from Pi: ");
  193. // for (uint8_t i = 0; i < gRxCount; i++) {
  194. // //printf("0x%02X ", gRxBuffer[i]);
  195. // }
  196. //printf("\n");
  197. // DataRx= false;
  198. // }
  199. // DL_I2C_flushTargetTXFIFO(I2C_target_INST);
  200. // break;
  201. /* RX FIFO trigger (Pi is writing data to MCU) */
  202. // case DL_I2C_IIDX_TARGET_RXFIFO_TRIGGER:
  203. //printf("receiving data from Pi.\n");
  204. // DataRx= true;
  205. // while (!DL_I2C_isTargetRXFIFOEmpty(I2C_target_INST)) {
  206. // if (gRxCount == 0) {
  207. // // First byte received is the register address
  208. // registerAddress = DL_I2C_receiveTargetData(I2C_target_INST);
  209. // DL_I2C_flushTargetTXFIFO(I2C_target_INST);
  210. //printf("Register Address Set: 0x%02X\n", registerAddress);
  211. // }
  212. // else if (registerAddress < REGISTER_SIZE) {
  213. //Storing the received data from the controller correctly
  214. // registers[registerAddress] = DL_I2C_receiveTargetData(I2C_target_INST);
  215. //printf("Stored 0x%02X in Register 0x%02X\n", registers[registerAddress], registerAddress);
  216. //gRxBuffer[gRxCount++] = DL_I2C_receiveTargetData(I2C_0_INST);
  217. // }
  218. // else {
  219. // printf("ERROR: RX Buffer Overflow!\n");
  220. // break;
  221. // }
  222. // gRxCount++;
  223. // }
  224. // break;
  225. /* TX FIFO trigger (Pi is reading data from MCU) */
  226. // case DL_I2C_IIDX_TARGET_TXFIFO_TRIGGER:
  227. //printf("transmitting data to Pi...\n");
  228. // if(!DL_I2C_isTargetTXFIFOFull(I2C_target_INST)) {
  229. // if(registerAddress == 0x01){
  230. //DL_I2C_fillTargetTXFIFO(I2C_target_INST, battery_status, NUM_BATTERY_SLOT);
  231. // }
  232. // else if(registerAddress== 0x02){
  233. //DL_I2C_fillTargetTXFIFO(I2C_target_INST, &value , 1);
  234. // }
  235. // else {
  236. // **Fix: Avoid infinite while loop**
  237. // printf("WARNING: TX Buffer Underflow! Sending default value.\n");
  238. // DL_I2C_fillTargetTXFIFO(I2C_target_INST, (uint8_t[]){0x00}, 1);
  239. // break;
  240. // }
  241. // }
  242. // break;
  243. /* Arbitration lost or NACK */
  244. // case DL_I2C_IIDX_TARGET_ARBITRATION_LOST:
  245. // printf("Arbitration Lost.\n");
  246. // case DL_I2C_IIDX_CONTROLLER_NACK:
  247. // printf("I2C NACK Received! Possible communication failure.\n");
  248. //DL_I2C_stopControllerTransfer(I2C_controller_INST); // Force STOP
  249. // break;
  250. // default:
  251. // printf("Unknown Interrupt.\n");
  252. // break;
  253. // }
  254. //}
  255. void I2C_Target_Init(void) {
  256. // Set the target address
  257. DL_I2C_setTargetOwnAddress(I2C_target_INST, I2C_target_TARGET_OWN_ADDR);
  258. // Enable target mode
  259. DL_I2C_enableTarget(I2C_target_INST);
  260. /* Enable relevant I2C interrupts */
  261. DL_I2C_enableInterrupt(I2C_target_INST, DL_I2C_INTERRUPT_TARGET_START);
  262. DL_I2C_enableInterrupt(I2C_target_INST, DL_I2C_INTERRUPT_TARGET_RXFIFO_TRIGGER);
  263. DL_I2C_enableInterrupt(I2C_target_INST, DL_I2C_INTERRUPT_TARGET_TXFIFO_TRIGGER);
  264. DL_I2C_enableInterrupt(I2C_target_INST, DL_I2C_INTERRUPT_TARGET_STOP);
  265. }
  266. void I2C_target_INST_IRQHandler(void) {
  267. //printf("I2C Interrupt Triggered to MCU (TARGET)!\n");
  268. static uint8_t receivedCommand= 0;
  269. uint32_t status = DL_I2C_getPendingInterrupt(I2C_target_INST);
  270. ADC_PARAMS params;
  271. switch (status) {
  272. /* START condition detected */
  273. case DL_I2C_IIDX_TARGET_START:
  274. piTxCount= 0;
  275. piRxCount= 0;
  276. piTxComplete= false;
  277. DL_I2C_flushTargetTXFIFO(I2C_target_INST);
  278. break;
  279. /* STOP condition detected */
  280. case DL_I2C_IIDX_TARGET_STOP:
  281. piTxComplete= true;
  282. piRxComplete = true;
  283. printf("I2C Stop Detected- Rx/Tx Complete");
  284. break;
  285. /* TX FIFO trigger (Pi is reading data from MCU) */
  286. case DL_I2C_IIDX_TARGET_TXFIFO_TRIGGER:
  287. switch(receivedCommand){
  288. case CMD_GET_BATTERY_STATUS:
  289. for(uint8_t slot=0; slot< NUM_SLOTS; slot++){
  290. Battery_StateUpdate(slot);
  291. uint8_t battery_status= batteries[slot].state;
  292. DL_I2C_fillTargetTXFIFO(I2C_target_INST, &battery_status, NUM_SLOTS);
  293. while (DL_I2C_getTargetStatus(I2C_target_INST) & DL_I2C_TARGET_STATUS_BUS_BUSY);
  294. }
  295. break;
  296. case CMD_GET_BATTERY_DATA:
  297. piTxCount= 0;
  298. piTxComplete= false;
  299. // Create a data message and transmit it
  300. for(uint8_t slot_id; slot_id<NUM_SLOTS; slot_id++){
  301. battery_data.slot_id= slot_id;
  302. battery_data.voltage= batteries[slot_id].voltage;
  303. battery_data.current= 0;
  304. battery_data.temperature=0;
  305. }
  306. //printf("Sending Battery Data: Slot %d | Voltage %dmV| Current %d| Temperature %d.\n", slot_id, battery_data.voltage, battery_data.current, battery_data.temperature);
  307. while (DL_I2C_getTargetStatus(I2C_target_INST) & DL_I2C_TARGET_STATUS_BUS_BUSY);
  308. //Casting the struct pointer to byte array: (uint8_t*)
  309. DL_I2C_fillTargetTXFIFO(I2C_target_INST, (uint8_t*)&battery_data, sizeof(BatteryData));
  310. while(!piTxComplete);
  311. while (DL_I2C_getTargetStatus(I2C_target_INST) & DL_I2C_TARGET_STATUS_BUS_BUSY);
  312. // Check if full data has been sent
  313. if (piTxCount >= sizeof(BatteryData)) {
  314. piTxComplete = true;
  315. piTxCount = 0; // Reset counter for next request
  316. printf("Sent Full Battery Data\n");
  317. }
  318. break;
  319. }
  320. /* TARGET_Rx FIFO trigger (Pi is writing data to MCU) */
  321. case DL_I2C_IIDX_TARGET_RXFIFO_TRIGGER:
  322. break;
  323. /* Arbitration lost or NACK */
  324. case DL_I2C_IIDX_TARGET_ARBITRATION_LOST:
  325. printf("Arbitration Lost.\n");
  326. break;
  327. default:
  328. printf("Unknown Interrupt.\n");
  329. break;
  330. }
  331. }
  332. void Reset_I2C_Bus() {
  333. printf("I2C Bus is stuck! Resetting...\n");
  334. // Disable I2C Controller
  335. DL_I2C_disableController(I2C_controller_INST);
  336. delay_cycles(50000);
  337. // Re-enable I2C Controller
  338. DL_I2C_enableController(I2C_controller_INST);
  339. delay_cycles(50000);
  340. // Check if bus is free now
  341. uint32_t status = DL_I2C_getControllerStatus(I2C_controller_INST);
  342. printf("I2C Bus Status After Reset: 0x%08X\n", status);
  343. }
  344. /********MAIN function*************/
  345. int main(void)
  346. {
  347. // Initialize System and I2C
  348. SYSCFG_DL_init();
  349. //Reset_I2C_Bus();
  350. //NVIC_EnableIRQ(I2C_target_INST_INT_IRQN);
  351. NVIC_EnableIRQ(I2C_controller_INST_INT_IRQN);
  352. printf("............System Configuration Enabled...............\n");
  353. Multiplexer_SelectChannel(I2C_CHANNEL);
  354. //I2C_scanBus();
  355. I2C_init(&gI2C);
  356. // *Configure ADC*
  357. ADC_PARAMS adc_params={
  358. .channel= 1,
  359. .resolution= 12,
  360. .continuous= 1,
  361. .gain= 1
  362. };
  363. printf("Configuring ADC....\n");
  364. //**Set Configuration Register for ADC**
  365. ADC_SetConfigurationBytes(adc_params);
  366. uint16_t channel_a_value= 3300; // in mVolts
  367. while (1)
  368. {
  369. Battery_UpdateCurrentVoltage(adc_params);
  370. //delay_cycles(5000);
  371. //DAC_fastWrite(channel_a_value);
  372. delay_cycles(100000);
  373. }
  374. }