i2c_controller.c 18 KB

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