| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918 |
- /*
- 20.02.2025:
- Flowchart of the code:
- 1. MCU requests ADC data over I2C
- 2. ADC is connected to the MULTIPLEXER.
- 3. Interrupt triggers when data is available.
- 4. Interrupt moves RECEIVED bytes into RXFIFO Buffer
- 5. Interrupt marks reception complete RX_DONE
- 6. Read ADC_Data and retrieve the final ADc value
- 7. Convert ADC value to Voltage
- 24.02.2025:
- Working Code for Reading Analog Signals from ADC to MCU through Multiplexer
- - configured the registers with Channel, Resolution, Mode(Continuous or Single-mode) and Gain: Construct_Config_Byte()
- - SET configuration registers to ADC: SetConfiguration()
- - Read the Analog Output: Read_ADC_Data()
- - Convert Analog output to voltage in Volts: Convert_ADC_To_Voltage()
- - Commented out interrupts for the time being.
- - Adding code for Pi to READ ADC voltages in mV.
- -
- */
- #include "ti/devices/msp/peripherals/hw_dac12.h"
- #include "ti/driverlib/dl_gpio.h"
- #include "ti/driverlib/dl_i2c.h"
- #include "ti/driverlib/m0p/dl_core.h"
- #include "ti_msp_dl_config.h"
- #include "ti/comm_modules/i2c/controller/i2c_comm_controller.h"
- #include<stdint.h>
- #include<stdio.h>
- I2C_Instance gI2C;
- /*MULTIPLEXER SECTION:*/
- /*Multiplexer Address*/
- #define MULTIPLEXER_I2C_ADDR (0x70)
- /*Multiplexer channel*/
- #define ADC_I2C_CHANNEL (0x01)
- /*****************DAC*********************/
- /*Fast write command for DAC*/
- #define FAST_WRITE_COMMAND 0x00
- #define MULTI_WRITE 0x40
- #define SINGLE_WRITE 0x58
- #define SEQ_WRITE 0x50
- #define VREF_CMD 0x80
- #define GAIN_CMD 0xC0
- #define PWR_DWN_CMD 0xA0
- //DAC Channel Selection
- typedef enum{
- DAC_A = 0,
- DAC_B,
- DAC_C,
- DAC_D
- } DAC_CH;
- typedef enum{
- VREF_VDD= 0,
- VREF_INTERNAL= 1
- }VREF_SELECTION;
- typedef enum{
- GAIN_X1= 0,
- GAIN_X2= 1
- }GAIN_SELECTION;
- typedef enum{
- PD_NORMAL=0,
- PD_GND_1KOHM= 1,
- PD_GND_100KOHM= 2,
- PD_GND_500KOHM= 3
- }PWR_DOWN_SELECTION;
- // Structure to Hold DAC Register Data
- typedef struct {
- VREF_SELECTION vref;
- PWR_DOWN_SELECTION pd;
- GAIN_SELECTION gain;
- uint16_t data;
- } DACRegisterData;
- // Create Global Storage for DAC Values
- DACRegisterData read_reg_[4]; // Holds Active Register Values
- DACRegisterData read_eep_[4]; // Holds EEPROM Stored Values
- /***********************/
- /*
- ADC SETTINGS:
- Fixed Reference Voltage is 2.048V and PGA Gain setting default: x1
- Mode: can be 1: Continuous or 0: for One-Shot Conversion
- ADC_RESOLUTION: 16 bits -> A higher resolution means better precision but slower conversion time.
- Calculation for V(IN)= (ADC Value/2^ (Resolution-1))* V(REF)
- */
- /*Voltage is calculated in ADC in milliVolts:
- - Reference voltage in milliVolts to be read from Pi
- - Register address where ADC result would be stored
- */
- #define VREF 2.048
- // address register for ADC Voltage Read:
- #define ADC_REGISTER 0x10
- /*Register for the MCU*/
- #define REGISTER_SIZE 256
- #define I2C_BUFFER_SIZE (16)
- //#define DAC12_REF_VOLTAGE_mV (2500)
- uint8_t registers[REGISTER_SIZE]= {1, 2, 3, 4, 5};
- /* Buffers for data exchange with Pi*/
- uint16_t gRxBuffer[I2C_BUFFER_SIZE]= {0};
- uint16_t gTxBuffer[I2C_BUFFER_SIZE]={0};
- /*Counter for data length and bytes*/
- uint32_t gTxLen, gTxCount;
- uint32_t gRxLen, gRxCount;
- volatile I2C_CommandInfo gCommand;
- I2C_ResponseInfo gResponse;
- volatile bool gSendCommand = true;
- volatile bool newMeasurementAvailable = false;
- uint8_t gTxData[MAX_DATA_SIZE] = {0} ;
- /*
- Scans all the addresses of the peripherals:
- */
- void I2C_scanBus() {
- printf("Scanning I2C Bus...\n");
- // **Step 1: Reset I2C Controller if Busy**
- if (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS) {
- printf("I2C Bus Busy! Resetting I2C Controller...\n");
- DL_I2C_disableController(I2C_controller_INST); // Disable I2C
- delay_cycles(20000);
- DL_I2C_enableController(I2C_controller_INST); // Re-enable I2C
- delay_cycles(20000);
- }
- // **Step 2: Scan I2C Bus**
- for (uint8_t addr = 0x08; addr < 0x78; addr++) { // Valid I2C Address Range
- DL_I2C_startControllerTransfer(I2C_controller_INST, addr, DL_I2C_CONTROLLER_DIRECTION_RX, 1);
- delay_cycles(5000);
-
- if (!(DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_ERROR)) {
- printf("Device found at: 0x%02X\n", addr);
- }
- }
- printf("I2C Scan Complete!\n");
- }
- /*
- Multiplexer TCA9548A:
- Address:0x70
- The TCA9548A is example of a single-register device, which is controlled via I2C commands. Since it has 1 bit to enable or disable a channel, there is only 1 register
- needed, and the controller merely writes the register data after the target address, skipping the register number.
- ADC connected to channel 0
- Both SDA and SL lines of the multiplexer is connected to VIN through pull-up resistors
- The following is the general procedure for a controller to access a target device:
- 1. If a controller wants to send data to a target:
- • Controller-transmitter sends a START condition and addresses the target-receiver.
- • Controller-transmitter sends data to target-receiver.
- • Controller-transmitter terminates the transfer with a STOP condition.
- 2. If a controller wants to receive or read data from a target:
- • Controller-receiver sends a START condition and addresses the target-transmitter.
- • Controller-receiver sends the requested register to read to target-transmitter.
- • Controller-receiver receives data from the target-transmitter.
- • Controller-receiver terminates the transfer with a STOP condition.
- The TCA9548A is example of a single-register device, which is controlled via I2C commands. Since it has 1 bit to enable or disable a channel,
- there is only 1 register needed, and the controller merely writes the register data after the target address, skipping the register number.
- */
- /*Function for Multiplexer*/
- void Multiplexer_SelectChannel(uint8_t channel)
- {
- uint8_t data = channel;
- printf("Selecting Multiplexer Channel: 0x%02X\n", data);
- // Ensure bus is idle before starting communication
- while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
- // SEND Command to Multiplexer
- DL_I2C_startControllerTransfer(I2C_controller_INST, MULTIPLEXER_I2C_ADDR, DL_I2C_CONTROLLER_DIRECTION_TX, 1);
- DL_I2C_fillControllerTXFIFO(I2C_controller_INST, &data, 1);
- // **Ensure STOP condition is sent**
- while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
- // **Slightly Increase Delay for Multiplexer to Process Command**
- delay_cycles(30000);
- // Verify Multiplexer Response:
- uint8_t response= 0x00;
- DL_I2C_startControllerTransfer(I2C_controller_INST, MULTIPLEXER_I2C_ADDR, DL_I2C_CONTROLLER_DIRECTION_RX, 1);
- // Wait for a response from the multiplexer
- while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
- response = DL_I2C_receiveControllerData(I2C_controller_INST);
- // **Debug: Print Expected vs. Received Response**
- printf("Multiplexer Response: 0x%02X (Expected: 0x%02X)\n", response, data);
-
- // **CHECK FOR ADDRESS ACKNOWLEDGMENT**
- /*if (!(DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_ERROR)) {
- printf("Multiplexer detected at 0x70.\n");
- return;
- } else{
- printf("ERROR: Multiplexer (0x70) detected.\n");
- }*/
- // Wait for transaction completion
- //while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
- if(response != data){
- printf("ERROR: Multiplexer did not set the correct channel!\n");
- // **Retry Mechanism: Attempt to Set the Channel Again**
- delay_cycles(10000);
- Multiplexer_SelectChannel(channel);
- } else {
- printf("Multiplexer Active Channel: 0x%X\n", data);
- }
- }
- /*
- CONFIGURATION REGISTER:
- 8-bit wide configuration register to select for: input channel, conversion mode, conversion rate, and PGA gain.
- Device allows the user to changethe operating condition of the device:
- */
- uint8_t Construct_Config_Byte(uint8_t channel, uint8_t resolution, bool continuous, uint8_t gain) {
- uint8_t config = 0;
-
- config |= ((channel - 1) << 5); // Channel Selection (Bits 6-5)
- if (continuous) config |= (1 << 4); // Continuous Mode (Bit 4)
- switch (resolution) {
- case 12: config |= (0b00 << 2); break;
- case 14: config |= (0b01 << 2); break;
- case 16: config |= (0b10 << 2); break;
- case 18: config |= (0b11 << 2); break;
- default: printf("ERROR: Invalid Resolution!\n"); return 0;
- }
- switch (gain) {
- case 1: config |= (0b00); break;
- case 2: config |= (0b01); break;
- case 4: config |= (0b10); break;
- case 8: config |= (0b11); break;
- default: printf("ERROR: Invalid Gain!\n"); return 0;
- }
- return config;
- }
- /* Tansmit Data from MCU to ADC: Function to SET configuration to ADC over I2C*/
- void SetConfiguration(uint8_t channel, uint8_t resolution, bool mode, uint8_t gain) {
- // **Step 1: Construct Configuration Byte**
- uint8_t config_byte = Construct_Config_Byte(channel, resolution, mode, gain);
- printf("Writing Config: 0x%02X to ADC (0x%X)...\n", config_byte, DEF_TARGET_ADDR_ADC);
- // **Step 2: Wait for I2C Bus to be Free**
- while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
- // **Step 3: Start I2C Write Transaction**
- DL_I2C_startControllerTransfer(I2C_controller_INST, DEF_TARGET_ADDR_ADC, DL_I2C_CONTROLLER_DIRECTION_TX, 1);
- // **Step 4: Load Configuration Byte into TX FIFO**
- DL_I2C_fillControllerTXFIFO(I2C_controller_INST, &config_byte, 1);
- // **Step 5: Ensure STOP Condition is Sent**
- while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
- printf("Configuration Byte: 0x%02X\n", config_byte);
- printf("Configuration Sent Successfully!\n");
- }
- /*Function to get the RDY bit from ADC*/
- bool Check_ADC_Ready()
- {
- uint8_t config_byte = 0;
- /*
- READY BIT:
- This bit is the data ready flag. In read mode, this bit indicates if the output register has been updated
- with a latest conversion result. In One-Shot Conversion mode, writing this bit to “1” initiates a new
- conversion.
- 1= Output Register has not been updated
- 0= Output Register has been updated
- */
- DL_I2C_startControllerTransfer(I2C_controller_INST, DEF_TARGET_ADDR_ADC, DL_I2C_CONTROLLER_DIRECTION_RX, 1);
- config_byte = DL_I2C_receiveControllerData(I2C_controller_INST);
- return (config_byte & 0x80) == 0; // Check if RDY bit is cleared in bit 7
- }
- /*
- Function to read ADC DATA: This function simply retrieves the ADC raw digital output as 16-bit signed integer.
- * The value returned is not yet converted to VOLTAGE.
- * adc_data[2]: Buffer with an array of size 2, to store two bytes of ADC data.
- * Since, the ADC output consists of two 8-bit bytes:
- * - adc_data[0] (MSB)
- * - adc_data[1] (LSB)
- * Next, we verify if the the I2C bus is busy using the function in the driverlib: DL_I2C_get ControllerStatus
- * - Prevents collisions by ensuring no two I2C device is using the same bus before initializing.
- * BEGIN I2C READ operation to request 2 bytes from the ADC-> DL_I2C_startControllerTransfer()
- * Parameters:
- - I2C_controller_INST: Refererence to the I2C instance bring used.
- - DEF_TARGET_ADDR_ADC: Address of the ADC
- - DL_I2C_CONTROLLER_DIRECTION_RX: Indicates that we want to receive the data from ADC
- - 2: Specifies that we expect 2 bytes from the ADC
- * WAIT for the data to be received: (DL_I2C_getControllerStatus())
- - Waits until the ADC sends the data over I2C.
- - Ensures that the data transfer is complete before proceeding.
- * READ the two bytes of ADc Data:
- - adc_data[0] : MSB
- - adc_data[1] : LSB
- - adc_data[2] : config_bytes
- ADC sends its 16-bit value in two parts over 8-bit I2C data frames.
- * COMBINE the two bytes into 16-bit Integer:
- - Shifts the MSB(first byte) left by 8 bits to make space for LSB.
- - Performs a bitwise OR operation to combine MSB and LSB into a single 16-bit number.
- * PRINT HEXADEC and DECIMAL format for DEBUGGING.
- * Output code is in binary and is proportional to the Input Voltage and PGA settings.
- * From the datasheet: https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/22226a.pdf
- - Equation 4.4 is being used to convert the output codes to input voltage
- */
- int16_t Read_ADC_Data(uint8_t resolution)
- {
- uint8_t adc_data[3] = {0}; // Buffer for ADC data (MSB, LSB, Config Byte)
- int16_t raw_adc = 0;
-
- printf("Reading ADC Data from MCP3428...\n");
- while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
- // Determine bytes to read
- printf("Resolution for reading ADC is %d\n", resolution);
- delay_cycles(10000);
- // Request 3 Bytes from ADC**
- DL_I2C_startControllerTransfer(I2C_controller_INST, DEF_TARGET_ADDR_ADC, DL_I2C_CONTROLLER_DIRECTION_RX, 3);
- // Read Data into Buffer**
- int i = 0;
- while(i<3){
- while (DL_I2C_isControllerRXFIFOEmpty(I2C_controller_INST));
- printf("Loop no: %d\n",i);
- while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
- adc_data[i] = DL_I2C_receiveControllerData(I2C_controller_INST);
- printf("Received Byte[%d]: 0x%02X\n", i, adc_data[i]);
- i++;
- }
- newMeasurementAvailable = true;
- /*Combining MSB and LSB for ADC output*/
- raw_adc= adc_data[0] << 8 | adc_data[1];
- printf("Raw ADC Value: 0x%0X (%d)\n", raw_adc, raw_adc);
- //printf("Resolution: %d \n", resolution_bits );
- return raw_adc;
-
- }
- /* Function to Convert ADC Reading to Voltage */
- float Convert_ADC_To_Voltage(int16_t adc_value, uint8_t resolution, uint8_t gain) {
- if (!newMeasurementAvailable)
- { return -1; }
- float voltage= 0;
- float LSB= 0;
- uint32_t max_adc_value= 1;
- switch (resolution) {
- case 12: // 12-bit
- max_adc_value = 2048;
- break;
- case 14: // 14-bit
- max_adc_value = 8192;
- break;
- case 16: // 16-bit
- max_adc_value = 32768;
- break;
- case 18: // 18-bit
- max_adc_value = 131072;
- break;
- default:
- printf("Error: Unknown ADC Resolution!\n");
- return 0.0;
- }
- /*Considering that MSB is always 0*/
- printf("Max Resolution %d\n", max_adc_value);
- LSB= ((VREF)/max_adc_value);
- printf("LSB: %f\n", LSB);
- float inputVoltage= ((float)adc_value*(LSB/gain));
-
- /*Formula for ADC to Voltage Conversion*/
- /*voltage = ((float)adc_value / max_adc_value) * VREF;
-
- printf("Converted Voltage: %.2f V\n", voltage);*/
- printf("Input Voltage Measured: %.2f V\n", inputVoltage);
- /*To send the voltage to Pi in milliVolts*/
- uint16_t voltage_mV= inputVoltage * 1000;
- /*Storing ADC voltages in mVolts */
- registers[ADC_REGISTER] = (voltage_mV>>8) & 0xFF; //Highest Byte
- registers[ADC_REGISTER + 1] = (voltage_mV) & 0xFF; //Lowest Byte
- newMeasurementAvailable = false;
- printf("ADC Voltage Stored %d mV in MCU: MSB= 0x%02X, LSB= 0x%02X\n", voltage_mV, registers[ADC_REGISTER], registers[ADC_REGISTER + 1]);
- return voltage;
- }
- /*
- DAC Configuration function: Tx to DAC channel
- DAC expects 3 bytes from the channel:
- Byte 0: write command + channel_selection
- Byte 1: configuration + high byte of DAC
- Byte 2: low byte of DAC
- */
- /*bool SET_CHANNEL_VALUE(DAC_CH ch, uint16_t new_value, VREF_SELECTION new_vref, GAIN_SELECTION new_gain, PWR_DOWN_SELECTION new_pwr_dwn, bool udac){
- uint8_t output_buffer[8];
- //Build the setter header (Address) -> 01000 DAC0 DAC1 DAC2
- uint8_t channel_write_cmd = FAST_WRITE_COMMAND; //0x00; FAST write mode
- //channel_write_cmd |= (ch << 1); //SET DAC channel
- //channel_write_cmd|= udac; // if udac is SET to 0; VOUT is updated after the 4th byte's ACK is issued
- output_buffer[0]= channel_write_cmd;
- // Construct the 12bit DAC value with the configuration settings
- // For Fast write command it does not gives VREF:
- // PD1 PD2 Gx D11 D10 D9 D8 [Byte 1]
- // D7 D6 D5 D4 D3 D2 D1 D0 [Byte 2]
- //new_value &= 0x0FFF; //Ensure only 12 bits are set
- //new_value |= (new_vref << 15);
- new_value |= (new_pwr_dwn << 13);
- //new_value |= (new_gain << 12);
- new_value |= new_value<< 9;
- new_value |= new_value<< 8;
- output_buffer[1]= new_value >> 8; //Upper Byte
- output_buffer[2]= new_value & 0xFF; //Lower Byte
- //Send the data via I2C:
- if (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS){
- printf("I2C Bus is busy! Retry..\n");
- return false;
- }
- //start I2C transmission:
- DL_I2C_startControllerTransfer(I2C_controller_INST, DEF_TARGET_ADDR_DAC, DL_I2C_CONTROLLER_DIRECTION_TX, 3);
- DL_I2C_fillControllerTXFIFO(I2C_controller_INST, output_buffer, 3);
- while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
- if (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_ERROR) {
- printf("I2C Write Error: Failed to write to DAC!\n");
- return false; // Return failure if there was an error
- }
- return true;
- }*/
- //The device updates all DAC analog output(vout) at the same time
- void update_DAC_Output() {
- uint8_t general_call_command = 0x08; // General Call Update Command
- while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
- DL_I2C_fillControllerTXFIFO(I2C_controller_INST, &general_call_command, 1);
- // Start I2C transaction
- DL_I2C_startControllerTransfer(I2C_controller_INST, 0x00, DL_I2C_CONTROLLER_DIRECTION_TX, 1);
-
- while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
- printf("DAC Outputs Updated via General Call Software Update!\n");
- }
- /**Function for FAST write command, sending values over I2c to every channel of DAC**/
- bool fastWrite(uint16_t channel_a_value){
- /*DAC has a 12 bit resolution that ranges from 0 to 4095.
- 0x00: sets the Power Mode to NORMAL for Channel A
- (channel_a_value >> 8): shifts the value to 8 places right and gives upper 4 bits
- */
- uint8_t output_buffer[8];
- output_buffer[0]= (0x00)|(channel_a_value >> 8)&0x0F;
- //output_buffer[0] = channel_a_value >> 8;
- output_buffer[1]= channel_a_value & 0xFF;
- output_buffer[2]= 0x40; // Power down for the other channels
- output_buffer[3]= 0x00;
- output_buffer[4]= 0x40;
- output_buffer[5]= 0x00;
- output_buffer[6]= 0x40;
- output_buffer[7]= 0x00;
- if (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_ERROR) {
- printf("I2C Write Error: Failed to write to DAC channels for FAST write mode!\n");
- return false; // Return failure if there was an error
- }
- // **Wait for I2C Bus to be Free**
- while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
-
- // **Start I2C Write Transaction**
- DL_I2C_startControllerTransfer(I2C_controller_INST, DEF_TARGET_ADDR_DAC, DL_I2C_CONTROLLER_DIRECTION_TX, 8);
- // **Load Configuration Byte into TX FIFO**
- DL_I2C_fillControllerTXFIFO(I2C_controller_INST, (uint8_t*)&output_buffer, 8);
- // **Ensure STOP Condition is Sent**
- while (DL_I2C_getControllerStatus(I2C_controller_INST) & DL_I2C_CONTROLLER_STATUS_BUSY_BUS);
- printf("DAC Fast write Successful!\n");
- update_DAC_Output();
- return true;
- }
- /*void readRegisters(){
- uint8_t data[24];
- uint8_t channel;
- bool isEeprom;
- printf("Reading MCP4728 Registers...\n");
- // ** Request 24 Bytes from MCP4728 over I2C **
- DL_I2C_startControllerTransfer(I2C_controller_INST, DEF_TARGET_ADDR_DAC, DL_I2C_CONTROLLER_DIRECTION_RX, 24);
- for (int i = 0; i < 24; i++) {
- while (DL_I2C_isControllerRXFIFOEmpty(I2C_controller_INST));
- data[i] = DL_I2C_receiveControllerData(I2C_controller_INST);
- }
- // ** Process the Received Data (8 Iterations for 4 Channels) **
- for (uint8_t i = 0; i < 8; i++) {
- isEeprom = i % 2; // Even indices: Register, Odd indices: EEPROM
- //Extract channel:
- uint8_t ch= (data[0] & 0x30)>>4;
- if (isEeprom) {
- read_eep_[ch].vref = (VREF_SELECTION) ((data[i * 3 + 1] & 0b10000000) >> 7);
- read_eep_[ch].pd = (PWR_DOWN_SELECTION) ((data[i * 3 + 1] & 0b01100000) >> 5);
- read_eep_[ch].gain = (GAIN_SELECTION) ((data[i * 3 + 1] & 0b00010000) >> 4);
- read_eep_[ch].data = (uint16_t) ((data[i * 3 + 1] & 0b00001111) << 8 | data[i * 3 + 2]);
- } else {
- read_reg_[ch].vref = (VREF_SELECTION) ((data[i * 3 + 1] & 0b10000000) >> 7);
- read_reg_[ch].pd = (PWR_DOWN_SELECTION) ((data[i * 3 + 1] & 0b01100000) >> 5);
- read_reg_[ch].gain = (GAIN_SELECTION) ((data[i * 3 + 1] & 0b00010000) >> 4);
- read_reg_[ch].data = (uint16_t) ((data[i * 3 + 1] & 0b00001111) << 8 | data[i * 3 + 2]);
- }
- }
- printf("MCP4728 Registers Read Successfully!\n");
- }
- */
- /*
- DAC Function:
- -12 bit Voltage Output DAC with Four Buffered outputs.
- -Using Internal VREF (2.048V):
- - Output voltage range:
- - 0.000V to 2.048V with Gain setting= 1
- - 0.000V to 4.096V with Gain setting= 2
- - User can select internal reference or external voltage (VDD) for each channel
- - VOUTA: Buffered analog voltage of Channel A.
- - Channel Selection bits: 0x00 for Channel A
- */
- /*int16_t read_DAC_Output()
- {
- readRegisters(); //Read current register DAC values
- uint16_t dac_a = read_reg_[DAC_A].data;
- uint16_t dac_b = read_reg_[DAC_B].data;
- uint16_t dac_c = read_reg_[DAC_C].data;
- uint16_t dac_d = read_reg_[DAC_D].data;
- printf("DAC Output Digital Values: A=%d, B=%d, C=%d, D=%d\n",
- dac_a, dac_b, dac_c, dac_d);
- return dac_a;
- }
- float get_DAC_Output_Voltage(DAC_CH channel){
- if(channel< DAC_A || channel> DAC_D){
- printf("Error: Invalid DAC channel!\n");
- return -1.0;
- }
- uint16_t dac_value = read_reg_[channel].data;
- VREF_SELECTION vref = read_reg_[channel].vref;
- GAIN_SELECTION gain = read_reg_[channel].gain;
- //GET correct VREF value:
- float vref_value= (vref==VREF_INTERNAL)? 2.048: 3.3;
- uint8_t gain_factor= (gain==GAIN_X2)? 2: 1;
- // Calculate Output Voltage
- float vout = ((float)dac_value / 4095.0) * vref_value * gain_factor;
- printf("DAC Channel %d - Digital Value: %d, VOUT: %.3fV\n", channel, dac_value, vout);
- return vout;
- }*/
- /**Function to call DAC*/
- /*void get_Analog_Output(){
- bool success= fastWrite(2048, 0, 0, 0);
- if(!success){
- printf("Error: DAC Fast Write Failed.\n");
- return;
- }
- update_DAC_Output();
- read_DAC_Output();
- float vout_a= get_DAC_Output_Voltage(DAC_A);
- float vout_b= get_DAC_Output_Voltage(DAC_B);
- float vout_c= get_DAC_Output_Voltage(DAC_C);
- float vout_d= get_DAC_Output_Voltage(DAC_D);
- printf("\nMeasured DAC output Voltages:\n");
- printf("Channel A: %.2f V\n", vout_a);
- printf("Channel B: %.2f V\n", vout_b);
- printf("Channel C: %.2f V\n", vout_c);
- printf("Channel D: %.2f V\n", vout_d);
- }
- */
- /*
- Function to initialize MCU as target: Pi communicates as the Controller and MCU as the Target
- */
- void I2C_Target_Init(void) {
- // Set the target address
- DL_I2C_setTargetOwnAddress(I2C_target_INST, I2C_target_TARGET_OWN_ADDR);
- // Enable target mode
- DL_I2C_enableTarget(I2C_target_INST);
- /* Enable relevant I2C interrupts */
- DL_I2C_enableInterrupt(I2C_target_INST, DL_I2C_INTERRUPT_TARGET_START);
- DL_I2C_enableInterrupt(I2C_target_INST, DL_I2C_INTERRUPT_TARGET_RXFIFO_TRIGGER);
- DL_I2C_enableInterrupt(I2C_target_INST, DL_I2C_INTERRUPT_TARGET_TXFIFO_TRIGGER);
- DL_I2C_enableInterrupt(I2C_target_INST, DL_I2C_INTERRUPT_TARGET_STOP);
-
- }
- /*
- MAIN function:
- */
- int main(void)
- {
- // **Step 1: Initialize System and I2C**
- SYSCFG_DL_init();
- printf("............System Configuration Enabled...............\n");
- // **Step 2: Select ADC Channel via Multiplexer**
- printf("Selecting Multiplexer Channel...\n");
- Multiplexer_SelectChannel(ADC_I2C_CHANNEL);
- //I2C_scanBus();
- // **Step 3: Enable I2C Interrupts**
- //NVIC_EnableIRQ(I2C_controller_INST_INT_IRQN);
- //NVIC_EnableIRQ(I2C_target_INST_INT_IRQN);
- I2C_init(&gI2C);
- gTxCount = 0;
- gTxLen = REGISTER_SIZE;
- gRxCount = 0;
- gRxLen = REGISTER_SIZE;
- // **Step 4: Configure ADC (Example: CH1, 16-bit, Continuous Mode, Gain x1)**
- uint8_t channel= 4;
- uint8_t resolution= 16;
- bool continuous= 1;
- uint8_t gain= 1;
- printf("Configuring ADC...\n");
- //**Set Configuration Register for ADC**
- //SetConfiguration(channel, resolution, continuous, gain); // CH1, 16-bit, Continuous mode, Gain x1
- while (1)
- {
- // Read ADC Value and DAC values:
- //int16_t adc_value = Read_ADC_Data(resolution);
- //Convert_ADC_To_Voltage(adc_value, resolution, gain);
- //delay_cycles(10000);
- fastWrite(3300);
- //Add Delay for Next Conversion**
- delay_cycles(100000);
- }
- }
- /*Interrupt for MCU -> ADC
- * CASE: DL_I2C_IIDX_CONTROLLER_RX_DONE: ADC Reception Complete
- - ADC has finished sending data and it's fully received.
- - gI2C.rxMsg.len = gI2C.rxMsg.ptr:
- - Stores the received data length in the response buffer.
- - I2C_decodeResponse():
- - Decodes the received response.
- - gI2C.status = I2C_STATUS_RX_COMPLETE:
- - Marks reception is complete.
- * CASE: DL_I2C_IIDX_CONTROLLER_TX_DONE: Data Transmit to ADC complete
- - DL_I2C_disableInterrupt(..): Disables the TXFIFO interrupt since data is now sent
- * CASE: DL_I2C_IIDX_CONTROLLER_RXFIFO_TRIGGER: Receive Data in FIFO
- - The I2C Receive FIFO has data ready to be read.
- - while (DL_I2C_isControllerRXFIFOEmpty(...) != true): Loops until the RX FIFOis empty (READ all available bytes)
- - Inside the while loop:
- - If buffer has SPACE, store the received byte
- - Prints each received byte in HEXADECIMAL format for debugging
- - IF BUFFER is FULL, avoids OVERFLOW by discarding extra byte.
- * CASE: DL_I2C_IIDX_CONTROLLER_TXFIFO_TRIGGER: Transmit Data in FIFO
- - If there is still data to send:
- gI2C.txMsg.ptr += DL_I2C_fillControllerTXFIFO(I2C_controller_INST, &gI2C.txMsg.buffer[gI2C.txMsg.ptr], gI2C.txMsg.len - gI2C.txMsg.ptr);
- */
- void I2C_controller_INST_IRQHandler(void)
- { printf("I2C Interrupt Triggered to ADC!\n");
- switch (DL_I2C_getPendingInterrupt(I2C_controller_INST))
- {
- case DL_I2C_IIDX_CONTROLLER_RX_DONE:
- gI2C.rxMsg.len = gI2C.rxMsg.ptr;
- I2C_decodeResponse(&gI2C,&gResponse);
- gI2C.status = I2C_STATUS_RX_COMPLETE;
- printf("I2C RX COMPLETE!\n");
- break;
- case DL_I2C_IIDX_CONTROLLER_TX_DONE:
- DL_I2C_disableInterrupt(
- I2C_controller_INST, DL_I2C_INTERRUPT_CONTROLLER_TXFIFO_TRIGGER);
- gI2C.status = I2C_STATUS_TX_COMPLETE;
- printf("I2C TX COMPLETE!\n");
- break;
- case DL_I2C_IIDX_CONTROLLER_RXFIFO_TRIGGER:
- gI2C.status = I2C_STATUS_RX_INPROGRESS;
- /* Store bytes received from target in Rx Msg Buffer */
- while (DL_I2C_isControllerRXFIFOEmpty(I2C_controller_INST) != true) {
- if (gI2C.rxMsg.ptr < MAX_BUFFER_SIZE) {
- gI2C.rxMsg.buffer[gI2C.rxMsg.ptr++] =
- DL_I2C_receiveControllerData(I2C_controller_INST);
- if(gI2C.rxMsg.ptr>0){
- //printf("Received Byte[%d]: 0x%02X\n", gI2C.rxMsg.ptr, gI2C.rxMsg.buffer[gI2C.rxMsg.ptr - 1]);
- }
-
- } else {
- printf("ERROR: RX Buffer Overflow! ptr=%d MAX_BUFFER_SIZE=%d\n", gI2C.rxMsg.ptr, MAX_BUFFER_SIZE);
- /* Ignore and remove from FIFO if the buffer is full */
- DL_I2C_receiveControllerData(I2C_controller_INST);
- }
- }
- break;
- case DL_I2C_IIDX_CONTROLLER_TXFIFO_TRIGGER:
- printf("TX FIFO with data!\n");
- gI2C.status = I2C_STATUS_TX_INPROGRESS;
- /* Fill TX FIFO with bytes to send */
- if (gI2C.txMsg.ptr < gI2C.txMsg.len) {
- gI2C.txMsg.ptr += DL_I2C_fillControllerTXFIFO(
- I2C_controller_INST, &gI2C.txMsg.buffer[gI2C.txMsg.ptr], gI2C.txMsg.len - gI2C.txMsg.ptr);
- }
- break;
- case DL_I2C_IIDX_CONTROLLER_ARBITRATION_LOST:
- printf("Interrupt index for I2C controller Arbitration Lost!\n");
- break;
- case DL_I2C_IIDX_CONTROLLER_NACK:
- printf("I2C NACK Received\n");
- break;
- default:
- break;
- }
- }
- void I2C_target_INST_IRQHandler(void) {
- static bool DataRx= false;
- static uint16_t registerAddress=0;
- //printf("I2C Interrupt Triggered to MCU (TARGET)!\n");
- uint32_t status = DL_I2C_getPendingInterrupt(I2C_target_INST);
- switch (status) {
- /* START condition detected */
- case DL_I2C_IIDX_TARGET_START:
- //printf("START condition detected.\n");
- gTxCount= 0;
- gRxCount= 0;
- DataRx= false;
- DL_I2C_flushTargetTXFIFO(I2C_target_INST);
- break;
- /* STOP condition detected */
- case DL_I2C_IIDX_TARGET_STOP:
- //printf("STOP condition detected.\n");
- if (DataRx == true){
- //printf("Data received from Pi: ");
- for (uint8_t i = 0; i < gRxCount; i++) {
- //printf("0x%02X ", gRxBuffer[i]);
- }
- //printf("\n");
- DataRx= false;
- }
- DL_I2C_flushTargetTXFIFO(I2C_target_INST);
- break;
- /* RX FIFO trigger (Pi is writing data to MCU) */
- case DL_I2C_IIDX_TARGET_RXFIFO_TRIGGER:
- //printf("receiving data from Pi.\n");
- DataRx= true;
- while (!DL_I2C_isTargetRXFIFOEmpty(I2C_target_INST)) {
- if (gRxCount == 0) {
- // First byte received is the register address
- registerAddress = DL_I2C_receiveTargetData(I2C_target_INST);
- DL_I2C_flushTargetTXFIFO(I2C_target_INST);
- //printf("Register Address Set: 0x%02X\n", registerAddress);
- }
- else if (registerAddress < REGISTER_SIZE) {
- //Storing the received data from the controller correctly
- registers[registerAddress] = DL_I2C_receiveTargetData(I2C_target_INST);
- printf("Stored 0x%02X in Register 0x%02X\n", registers[registerAddress], registerAddress);
- //gRxBuffer[gRxCount++] = DL_I2C_receiveTargetData(I2C_0_INST);
- }
- else {
- printf("ERROR: RX Buffer Overflow!\n");
- break;
- }
- gRxCount++;
- }
- break;
- /* TX FIFO trigger (Pi is reading data from MCU) */
- case DL_I2C_IIDX_TARGET_TXFIFO_TRIGGER:
- //printf("transmitting data to Pi...\n");
- if(!DL_I2C_isTargetTXFIFOFull(I2C_target_INST)) {
- if (registerAddress < REGISTER_SIZE) {
- //DL_I2C_fillTargetTXFIFO(I2C_0_INST, &gTxBuffer[gTxCount], 1);
- // Retrieve stored value from the correct register
- uint8_t value = registers[registerAddress];
- //uint8_t value_test[2] = { 0x01, 0x03 };
- DL_I2C_fillTargetTXFIFO(I2C_target_INST, &value, 1);
- //printf("Sending to 0x%02X: 0x%02X\n", registerAddress, value); // Debugging
- } else {
- // **Fix: Avoid infinite while loop**
- printf("WARNING: TX Buffer Underflow! Sending default value.\n");
- DL_I2C_fillTargetTXFIFO(I2C_target_INST, (uint8_t[]){0x00}, 1);
- break;
- }
- }
- break;
- /* Arbitration lost or NACK */
- case DL_I2C_IIDX_TARGET_ARBITRATION_LOST:
- default:
- printf("Unknown Interrupt.\n");
- break;
- }
- }
|