How to build a C code for avr atmega32 microcontroller to measure power using ZMPT101B voltage sensor and SCT013 current sensor?

148 Views Asked by At

I'm constructing a device to measure power and display in a 16x2 LCD in a given power source. I'm using the above mentioned sensors to read current and voltage through ADC in ATMega32. Voltage sensor is connected to PA0 and current sensor to PA1.

I did a calibration and found a relationship between bit value and actual current/voltage. From these actual values, my aim is to do a discrete Fourier transformation as the power gets alternate with the time if I only display the actual values. I have constructed a C code as follows:

#define F_CPU 8000000UL     // CPU Frequency to 8MHz
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <math.h>
#include <string.h>

#define LCD_Dir  DDRC
#define LCD_Port PORTC
#define RS       PC0
#define EN       PC1

#define VOLTAGE_SENSOR_CHANNEL 0
#define CURRENT_SENSOR_CHANNEL 1
#define N 256  // Number of data points 

// Sampled voltage and current data 
double voltage_data[N];
double current_data[N];
double power_data[N];

// Function to perform DFT on a single frequency component
void dft(double data[], int n, double* result_real, double* result_imag, double frequency) {
    *result_real = 0;
    *result_imag = 0;
    for (int k = 0; k < n; k++) {
        double angle = 2.0 * M_PI * frequency * k / n;
        *result_real += data[k] * cos(angle);
        *result_imag -= data[k] * sin(angle); // Use subtraction here for the imaginary part
    }
}

// Function to initialize ADC
void ADC_init(void) {
    DDRA = 0x00;
    ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
    ADMUX |= (1 << REFS0);
}

// Function to read ADC value from a specified channel
uint16_t ADC_read(uint8_t channel) {
    ADMUX = (ADMUX & 0xF8) | (channel & 0x07);
    ADCSRA |= (1 << ADSC);
    while (!(ADCSRA & (1 << ADIF)));
    ADCSRA |= (1 << ADIF);
    _delay_ms(1);
    return ADCW;
}

// Function to convert ADC reading to voltage
double ADC_to_Voltage(uint16_t adcValue) {
    double voltage = (adcValue * 2.0457) - 1305.2;
    return voltage;
}

// Function to convert ADC reading to current
double ADC_to_Current(uint16_t adcValue) {
    double current = (adcValue * 0.056311) - 35.617; 
    return current;
}

// Function to calculate power using DFT
double calculatePower(double voltage[], double current[], int n, double frequency) {
    double voltage_real, voltage_imag;
    double current_real, current_imag;
    dft(voltage_data, n, &voltage_real, &voltage_imag, frequency);
    dft(current_data, n, &current_real, &current_imag, frequency);
    
    // Calculate real power as the product of the real parts of voltage and current
    double real_power = voltage_real * current_real + voltage_imag * current_imag;
    
    return real_power;
}

// Function to send LCD command
void LCD_Command(unsigned char cmnd) {
    LCD_Port = (LCD_Port & 0x0F) | (cmnd & 0xF0);
    LCD_Port &= ~(1 << RS);
    LCD_Port |= (1 << EN);
    _delay_us(1);
    LCD_Port &= ~(1 << EN);
    _delay_us(200);
    LCD_Port = (LCD_Port & 0x0F) | (cmnd << 4);
    LCD_Port |= (1 << EN);
    _delay_us(1);
    LCD_Port &= ~(1 << EN);
    _delay_ms(2);
}

// LCD data writing function
void LCD_Char(unsigned char data) {
    LCD_Port = (LCD_Port & 0x0F) | (data & 0xF0);                 
    LCD_Port |= (1 << RS);                                      
    LCD_Port |= (1 << EN);
    _delay_us(1);
    LCD_Port &= ~(1 << EN);
    _delay_us(200);                                               
    LCD_Port = (LCD_Port & 0x0F) | (data << 4);                  
    LCD_Port |= (1 << EN);
    _delay_us(1);
    LCD_Port &= ~(1 << EN);
    _delay_ms(2);
}

// LCD Initialization function
void LCD_Init (void) {
    LCD_Dir = 0xFF;                                              
    _delay_ms(20);                                               
    LCD_Command(0x02);                                          
    LCD_Command(0x28);                                          
    LCD_Command(0x0C);                                          
    LCD_Command(0x06);                                          
    LCD_Command(0x01);                                          
    _delay_ms(2);
}

// Function to display a string on the LCD
void LCD_String(char *str) {
    int i;
    for (i = 0; str[i] != 0; i++) {
        LCD_Char(str[i]);
    }
}

// Function to display a string on the LCD with specified position
void LCD_String_xy(char row, char pos, char *str) {
    if (row == 0 && pos < 16)
        LCD_Command((pos & 0x0F) | 0x80);                      
    else if (row == 1 && pos < 16)
        LCD_Command((pos & 0x0F) | 0xC0);                      
    LCD_String(str);                                        
}

void displayPower(double power_val) {
    char powerStr[16];
    sprintf(powerStr, "Power: %.2f W", power_val);

    // Implement a scrolling mechanism here
    int len = strlen(powerStr);
    int i;
    for (i = 0; i <= len; i++) {
        LCD_Command(0x01); // Clear display screen
        _delay_ms(2);
        LCD_String_xy(0, 0, powerStr + i);
        _delay_ms(500); // Delay for scrolling speed
    }
}


int main(void) {
    LCD_Init();
    ADC_init();
 
    int data_index = 0;     

    while (1) {
        // Read ADC values from voltage and current sensors
        uint16_t voltage_adc = ADC_read(VOLTAGE_SENSOR_CHANNEL);
        uint16_t current_adc = ADC_read(CURRENT_SENSOR_CHANNEL);

        // Convert ADC readings to voltage and current with calibration
        double current = ADC_to_Current(current_adc);
        double voltage = ADC_to_Voltage(voltage_adc);

        // Store the latest voltage and current values in the data arrays
        voltage_data[data_index] = voltage;
        current_data[data_index] = current;

        // Increment the data index (use a circular buffer implementation if needed)
        data_index++;
        if (data_index >= N) {
            data_index = 0; 
        }

        // Calculate power using DFT
        double power = calculatePower(voltage_data, current_data, N, 60.0); 

        // Store the calculated power value
        power_data[data_index] = power;

        // Display real power on LCD and scroll through values
        displayPower(power_data[data_index]);
     
        _delay_ms(1); 
    }

    return 0;
}

However this does not display the real output. Can someone help me to figure out where I have gone wrong and to do a Discrete Fourier Transformation and display the real values?

0

There are 0 best solutions below