i2c_controller.c 18 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 <string.h>
  33. #include "multiplexer.h"
  34. #include "adc.h"
  35. #include "dac.h"
  36. #include "battery.h"
  37. #include "i2c_target.h"
  38. #include "cc_cv_charging.h"
  39. I2C_Instance gI2C;
  40. I2C_ResponseInfo gResponse;
  41. BatteryData battery_data;
  42. /*****************/
  43. /*Register for the MCU*/
  44. #define REGISTER_SIZE 256
  45. uint8_t registers[REGISTER_SIZE]= {1, 2, 3, 4, 5};
  46. /*Counters for TX length and bytes sent*/
  47. uint32_t gTxLen, gTxCount;
  48. /* Counters for RX length and bytes sent*/
  49. uint32_t gRxLen, gRxCount;
  50. /* Boolean to know when a stop command was issued */
  51. bool gStopReceived = false;
  52. uint8_t gTxData[MAX_DATA_SIZE] = {0} ;
  53. /*
  54. Scans all the addresses of the peripherals:
  55. */
  56. void I2C_scanBus() {
  57. printf("Scanning I2C Bus...\n");
  58. // **Step 1: Reset I2C Controller if Busy**
  59. if (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS) {
  60. printf("I2C Bus Busy! Resetting I2C Controller...\n");
  61. DL_I2C_disableController(I2C_controller_INST); // Disable I2C
  62. delay_cycles(20000);
  63. DL_I2C_enableController(I2C_controller_INST); // Re-enable I2C
  64. delay_cycles(20000);
  65. }
  66. // **Step 2: Scan I2C Bus**
  67. for (uint8_t addr = 0x08; addr < 0x78; addr++) { // Valid I2C Address Range
  68. DL_I2C_startControllerTransfer(I2C_controller_INST, addr, DL_I2C_CONTROLLER_DIRECTION_RX, 1);
  69. delay_cycles(5000);
  70. if (!(DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_ERROR)) {
  71. printf("Device found at: 0x%02X\n", addr);
  72. }
  73. }
  74. printf("I2C Scan Complete!\n");
  75. }
  76. /*Function to get the RDY bit from ADC*/
  77. bool Check_ADC_Ready()
  78. {
  79. uint8_t config_byte = 0;
  80. /*
  81. READY BIT:
  82. This bit is the data ready flag. In read mode, this bit indicates if the output register has been updated
  83. with a latest conversion result. In One-Shot Conversion mode, writing this bit to “1” initiates a new
  84. conversion.
  85. 1= Output Register has not been updated
  86. 0= Output Register has been updated
  87. */
  88. DL_I2C_startControllerTransfer(I2C_controller_INST, ADC_TARGET_BASE_ADDRESS, DL_I2C_CONTROLLER_DIRECTION_RX, 1);
  89. config_byte = DL_I2C_receiveControllerData(I2C_controller_INST);
  90. return (config_byte & 0x80) == 0; // Check if RDY bit is cleared in bit 7
  91. }
  92. /*
  93. Function to initialize MCU as target: Pi communicates as the Controller and MCU as the Target
  94. */
  95. /*Interrupt for MCU -> ADC
  96. * CASE: DL_I2C_IIDX_CONTROLLER_RX_DONE: ADC Reception Complete
  97. - ADC has finished sending data and it's fully received.
  98. - gI2C.rxMsg.len = gI2C.rxMsg.ptr:
  99. - Stores the received data length in the response buffer.
  100. - I2C_decodeResponse():
  101. - Decodes the received response.
  102. - gI2C.status = I2C_STATUS_RX_COMPLETE:
  103. - Marks reception is complete.
  104. * CASE: DL_I2C_IIDX_CONTROLLER_TX_DONE: Data Transmit to ADC complete
  105. - DL_I2C_disableInterrupt(..): Disables the TXFIFO interrupt since data is now sent
  106. * CASE: DL_I2C_IIDX_CONTROLLER_RXFIFO_TRIGGER: Receive Data in FIFO
  107. - The I2C Receive FIFO has data ready to be read.
  108. - while (DL_I2C_isControllerRXFIFOEmpty(...) != true): Loops until the RX FIFOis empty (READ all available bytes)
  109. - Inside the while loop:
  110. - If buffer has SPACE, store the received byte
  111. - Prints each received byte in HEXADECIMAL format for debugging
  112. - IF BUFFER is FULL, avoids OVERFLOW by discarding extra byte.
  113. * CASE: DL_I2C_IIDX_CONTROLLER_TXFIFO_TRIGGER: Transmit Data in FIFO
  114. - If there is still data to send:
  115. gI2C.txMsg.ptr += DL_I2C_fillControllerTXFIFO(I2C_controller_INST, &gI2C.txMsg.buffer[gI2C.txMsg.ptr], gI2C.txMsg.len - gI2C.txMsg.ptr);
  116. */
  117. void I2C_controller_INST_IRQHandler(void)
  118. {
  119. //printf("I2C Interrupt Triggered to ADC!\n");
  120. switch (DL_I2C_getPendingInterrupt(I2C_controller_INST))
  121. { /*START Condition*/
  122. case DL_I2C_IIDX_CONTROLLER_START:
  123. //gTxADCcount= 0;
  124. gRxADCcount= 0;
  125. DL_I2C_flushControllerTXFIFO(I2C_controller_INST);
  126. break;
  127. case DL_I2C_IIDX_CONTROLLER_RXFIFO_TRIGGER:
  128. gI2C.status= I2C_STATUS_RX_INPROGRESS;
  129. /* Store bytes received from target in Rx Msg Buffer */
  130. while (DL_I2C_isControllerRXFIFOEmpty(I2C_controller_INST) != true) {
  131. if (gRxADCcount< gRxADClen) {
  132. gRxPacket[gRxADCcount] = DL_I2C_receiveControllerData(I2C_controller_INST);
  133. printf("Received Byte[%d]: 0x%02X\n", gRxADCcount, gRxPacket[gRxADCcount]); // Debug print
  134. gRxADCcount++;
  135. } else {
  136. //printf("ERROR: RX Buffer Overflow! ptr=%d MAX_BUFFER_SIZE=%d\n", gI2C.rxMsg.ptr, MAX_BUFFER_SIZE);
  137. /* Ignore and remove from FIFO if the buffer is full */
  138. DL_I2C_receiveControllerData(I2C_controller_INST);
  139. }
  140. }
  141. if (gRxADCcount == gRxADClen){
  142. //printf("ADC Bytes Received!\n");
  143. DL_I2C_enableInterrupt(I2C_controller_INST, DL_I2C_INTERRUPT_CONTROLLER_STOP);
  144. //gRxComplete = true;
  145. }
  146. break;
  147. /*TRANSMIT data to ADC*/
  148. case DL_I2C_IIDX_CONTROLLER_TXFIFO_TRIGGER:
  149. printf("TX FIFO with data!\n");
  150. gI2C.status= I2C_STATUS_TX_INPROGRESS;
  151. if(gTxADCcount<gTxADClen){
  152. gTxADCcount+= DL_I2C_fillControllerTXFIFO(I2C_controller_INST, &gTxPacket[gTxADCcount], (gTxADClen-gTxADCcount));
  153. } else{
  154. /*Prevent overflow and just ignore data*/
  155. DL_I2C_fillTargetTXFIFO(I2C_controller_INST, (uint8_t[]){0x00}, 1);
  156. }
  157. break;
  158. /*STOP condition*/
  159. case DL_I2C_IIDX_CONTROLLER_STOP:
  160. gTxComplete= true;
  161. gRxComplete = true;
  162. printf("I2C Stop Detected- RX Complete");
  163. case DL_I2C_IIDX_CONTROLLER_ARBITRATION_LOST:
  164. //printf("Interrupt index for I2C controller Arbitration Lost!\n");
  165. break;
  166. case DL_I2C_IIDX_CONTROLLER_NACK:
  167. //printf("I2C NACK Received\n");
  168. if((gI2C.status== I2C_STATUS_RX_STARTED)|| (gI2C.status= I2C_STATUS_TX_STARTED)){
  169. gI2C.status= I2C_STATUS_ERROR;
  170. }
  171. break;
  172. default:
  173. break;
  174. }
  175. }
  176. /**** Interrupt for Pi to MCU ****/
  177. void I2C_target_INST_IRQHandler(void) {
  178. //printf("I2C Interrupt Triggered to MCU (TARGET)!\n");
  179. uint8_t receivedCommand= 0;
  180. uint32_t status = DL_I2C_getPendingInterrupt(I2C_target_INST);
  181. //ADC_PARAMS params;
  182. switch (status) {
  183. /* START condition detected */
  184. case DL_I2C_IIDX_TARGET_START:
  185. piTxCount= 0;
  186. piRxCount= 0;
  187. piTxComplete= false;
  188. DL_I2C_flushTargetTXFIFO(I2C_target_INST);
  189. break;
  190. /* STOP condition detected */
  191. case DL_I2C_IIDX_TARGET_STOP:
  192. piTxComplete= true;
  193. piRxComplete = true;
  194. break;
  195. /* TX FIFO trigger (Pi is reading data from MCU) */
  196. /*TXFIFO to GET battery status is triggered when command is 0x01
  197. - Pi on request of 0x01 will get a response of the battery status for all the slots
  198. - Battery_StateUpdate function is called, which in turn calls the Battery_ReadState funtion to set the state of the batteries
  199. -Pi on command of [0x02, slot_id] will GET the 'Battery Data' which is voltage, current and temperature for a given slot.
  200. - MCU reads the slot_id from Pi using DL_I2C_receiveTargetData()
  201. - piTxCount is set to 0
  202. - piTxLen is the sizeof BatteryData struct which is 7 bytes
  203. - If the requested slot is correct then:
  204. - battery pointer variable points to the memory of the requested slot
  205. - the values of voltage, current and temperature are then stored in battery_data struct
  206. - Once the values are in BatteryData struct we wait for the bus to be free
  207. - Next we send the BatteryData to Pi using DL_I2C_fillTargetTXFIFO()
  208. - Reset the TX counter for the next data.
  209. */
  210. case DL_I2C_IIDX_TARGET_TXFIFO_TRIGGER:
  211. printf("TX to PI triggered!\n");
  212. if(!DL_I2C_isTargetTXFIFOEmpty(I2C_target_INST)){
  213. receivedCommand= DL_I2C_receiveTargetData(I2C_target_INST);
  214. printf("Received Command: 0x%02X\n", receivedCommand);
  215. if (receivedCommand == CMD_GET_BATTERY_STATUS){
  216. printf("Battery status received.\n");
  217. Battery_StateUpdate();
  218. if(piTxCount < piTxLen){
  219. while (DL_I2C_getTargetStatus(I2C_target_INST) & DL_I2C_TARGET_STATUS_BUS_BUSY);
  220. DL_I2C_fillTargetTXFIFO(I2C_target_INST, &piTxPacket[piTxCount], 1);
  221. piTxCount++;
  222. }
  223. else {
  224. /*
  225. * Fill FIFO with 0x00 if more data is requested than expected piTxLen
  226. */
  227. while (
  228. DL_I2C_transmitTargetDataCheck(I2C_target_INST, 0x00) != false);
  229. }
  230. }
  231. else if (receivedCommand == CMD_GET_BATTERY_DATA){
  232. uint8_t requestedSlot= DL_I2C_receiveTargetData(I2C_target_INST);
  233. while (DL_I2C_getTargetStatus(I2C_target_INST) & DL_I2C_TARGET_STATUS_BUS_BUSY);
  234. printf("Battery Data!\n");
  235. piTxCount= 0;
  236. piTxLen= sizeof(BatteryData);
  237. BatteryData battery_data;
  238. if(requestedSlot < NUM_SLOTS){
  239. Battery *battery= &batteries[requestedSlot];
  240. battery_data.slot_id = battery-> slot_id;
  241. battery_data.voltage= battery-> voltage;
  242. battery_data.current= battery-> current;
  243. battery_data.temperature= battery-> temperature;
  244. while (DL_I2C_getTargetStatus(I2C_target_INST) & DL_I2C_TARGET_STATUS_BUS_BUSY);
  245. piTxCount += DL_I2C_fillTargetTXFIFO(I2C_target_INST, (uint8_t*)&battery_data, piTxLen);
  246. while (DL_I2C_getTargetStatus(I2C_target_INST) & DL_I2C_TARGET_STATUS_BUS_BUSY);
  247. if(piTxCount >= piTxLen){
  248. piTxComplete= true;
  249. piTxCount=0;
  250. }
  251. } else{
  252. printf("Invalid Slot ID: %d\n.", requestedSlot);
  253. }
  254. }
  255. }
  256. break;
  257. /* TARGET_Rx FIFO trigger (Pi is writing data to MCU) */
  258. /*Pi SET battery data limits for each slot, where:
  259. - RXFIFO buffer is filled if the command from Pi is 0x03
  260. - Creating a temporary buffer named ´rxbuffer´
  261. - sizeof(BatteryLimitMsg): 11 bytes (1 byte: slot_id, 2 bytes: min_voltage; max_voltage; cut_off_current; capacitance; charge_fraction)
  262. - rx_buffer stores the data from Pi.
  263. - if all the expected bytes are received from Pi then,
  264. - memcpy() to copy the block of address from the temporary buffer to the BatteryLimitMsg structure
  265. - Why?, A: It copies the specified number of bytes from one memory location to another regardless of the type of the data stored.
  266. - verify if the received slot_id is less than NUM_SLOTS, where slot_id count starts from 0 then:
  267. - create a pointer variable for 'Battery'
  268. - battery_limits.slot_id: index of the battery slot to be updated
  269. - &batteries[battery_limits.slot_id]: gets the memory address of the battery in that slot
  270. - Accessing the structure members of Battery using -> operator. This allows efficient access to the structure's members
  271. without directly using the structure variable.
  272. */
  273. case DL_I2C_IIDX_TARGET_RXFIFO_TRIGGER:
  274. printf("RX FIFO triggered from PI.\n");
  275. if(!DL_I2C_isTargetRXFIFOEmpty(I2C_target_INST)){
  276. receivedCommand= DL_I2C_receiveTargetData(I2C_target_INST);
  277. printf("Received Command: 0x%02X\n", receivedCommand);
  278. if(receivedCommand == CMD_SET_BATTERY_LIMIT){
  279. uint8_t rx_buffer[sizeof(BatteryLimitMsg)];
  280. uint8_t index= 0;
  281. while (!DL_I2C_isTargetRXFIFOEmpty(I2C_target_INST)){
  282. if(index < sizeof(BatteryLimitMsg)){
  283. rx_buffer[index]= DL_I2C_receiveTargetData(I2C_target_INST);
  284. printf("Received Byte[%d]: 0x%02X\n", index, rx_buffer[index]);
  285. index++;
  286. }
  287. else{
  288. DL_I2C_receiveTargetData(I2C_target_INST);
  289. }
  290. }
  291. printf("Total Bytes Received: %d (Expected: %d)\n", index, sizeof(BatteryLimitMsg));
  292. if(index == sizeof(BatteryLimitMsg)){
  293. printf("Received Battery Limits.\n");
  294. BatteryLimitMsg battery_limits;
  295. memcpy(&battery_limits, rx_buffer, sizeof(BatteryLimitMsg));
  296. if(battery_limits.slot_id < NUM_SLOTS){
  297. Battery *battery = &batteries[battery_limits.slot_id];
  298. battery -> min_voltage = battery_limits.min_voltage;
  299. battery -> max_voltage = battery_limits.max_voltage;
  300. battery -> cut_off_current = battery_limits.cut_off_current;
  301. battery -> capacitance = battery_limits.capacitance;
  302. battery -> charge_fraction = battery_limits.charge_fraction;
  303. printf("\n Received Battery Limits for slot %d: \n", battery_limits.slot_id);
  304. printf(" Min Voltage: %d mV (0x%04X)\n", battery_limits.min_voltage, battery_limits.min_voltage);
  305. printf(" Max Voltage: %d mV (0x%04X)\n", battery_limits.max_voltage, battery_limits.max_voltage);
  306. printf(" Cutoff Current: %d mA (0x%04X)\n", battery_limits.cut_off_current, battery_limits.cut_off_current);
  307. printf(" Capacitance: %d µF (0x%04X)\n", battery_limits.capacitance, battery_limits.capacitance);
  308. printf(" Charge Fraction: %d%% (0x%02X)\n", battery_limits.charge_fraction, battery_limits.charge_fraction);
  309. }
  310. }
  311. }
  312. }
  313. break;
  314. /* Arbitration lost or NACK */
  315. case DL_I2C_IIDX_TARGET_ARBITRATION_LOST:
  316. printf("Arbitration Lost.\n");
  317. break;
  318. default:
  319. printf("Unknown Interrupt.\n");
  320. break;
  321. }
  322. }
  323. void Reset_I2C_Bus() {
  324. printf("I2C Bus is stuck! Resetting...\n");
  325. // Disable I2C Controller
  326. DL_I2C_disableController(I2C_controller_INST);
  327. delay_cycles(50000);
  328. // Re-enable I2C Controller
  329. DL_I2C_enableController(I2C_controller_INST);
  330. delay_cycles(50000);
  331. // Check if bus is free now
  332. uint32_t status = DL_I2C_getControllerStatus(I2C_controller_INST);
  333. printf("I2C Bus Status After Reset: 0x%08X\n", status);
  334. }
  335. /********MAIN function*************/
  336. int main(void)
  337. {
  338. // Initialize System and I2C
  339. SYSCFG_DL_init();
  340. // Initialize battery array and default params
  341. Battery_Init();
  342. //Reset_I2C_Bus();
  343. NVIC_EnableIRQ(I2C_target_INST_INT_IRQN);
  344. //NVIC_EnableIRQ(I2C_controller_INST_INT_IRQN);
  345. printf("............System Configuration Enabled...............\n");
  346. //Multiplexer
  347. Multiplexer_SelectChannel(I2C_CHANNEL);
  348. //I2C_scanBus();
  349. I2C_init(&gI2C);
  350. // *Configure ADC*
  351. ADC_PARAMS adc_params={
  352. .channel= 1,
  353. .resolution= 12,
  354. .continuous= 1,
  355. .gain= 1
  356. };
  357. //printf("Configuring ADC....\n");
  358. //**Set Configuration Register for ADC**
  359. //ADC_SetConfigurationBytes(adc_params);
  360. //uint16_t channel_a_value= 3300; // in mVolts
  361. while (1)
  362. {
  363. //Battery_UpdateCurrentVoltage(adc_params);
  364. //for(uint8_t slot_id= 0; slot_id < NUM_SLOTS; slot_id++){
  365. // CC_CV_ControlCharging(slot_id);
  366. //}
  367. //delay_cycles(5000);
  368. //DAC_fastWrite(channel_a_value);
  369. delay_cycles(100000);
  370. }
  371. }