SlMN099
SlMN099

Reputation: 21

How can I make the multiplexing to the 4-digit 7-segment display to work?

The code bellow is meant to read the voltage value from the ADC channels present in GPIO 26 and 27. It changes from ADC channel 0 to ADC channel 1 when the pushbutton in GPIO 15 is pressed.

The problem lies on how the common-cathode 4-digit 7-segment display works. The value when the voltage varies (it varies thanks to a potentiometer) is printed to the terminal the right way. Nevertheless, the display shows the integer value in all three digits (its only using three digits, because it wants to represent an integer value with two decimal places for precision).

The value only changes in all three digits when the voltage varies to the first significant figure level.

I'm aware the multiplexing should work so that one digit is displayed at a time and, with a proper timeframe, give the illusion that the right number is being displayed. I just don't see a way to convey this idea onto the code.

I think there should be a middle function between display_digit and display_voltage to keep a memory of the values before so that the values are all shown in sequence and only updated when the value in the given position changes, turning on only one digit at a time and with the proper segment configuration.

code:

#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/adc.h"

// Pin assignments for 7-segment display (adjust these as needed)
#define SEG_A  2
#define SEG_B  3
#define SEG_C  4
#define SEG_D  5
#define SEG_E  6
#define SEG_F  7
#define SEG_G  8
#define SEG_DP 14 // Decimal point
#define DIGIT_1  9  // Digit 1
#define DIGIT_2  10 // Digit 2
#define DIGIT_3  11 // Digit 3
#define DIGIT_4  12 // Digit 4

// Button for switching ADC input
#define BUTTON_PIN 15

// Define the 7-segment display segments for digits 0-9
const uint8_t digit_segments[] = {
    0b00111111, // 0
    0b00000110, // 1
    0b01011011, // 2
    0b01001111, // 3
    0b01100110, // 4
    0b01101101, // 5
    0b01111101, // 6
    0b00000111, // 7
    0b01111111, // 8
    0b01101111, // 9
};

// Function to initialize the GPIO pins for the 7-segment display
void init_7segment_display() {
    gpio_init(SEG_A); gpio_set_dir(SEG_A, GPIO_OUT);
    gpio_init(SEG_B); gpio_set_dir(SEG_B, GPIO_OUT);
    gpio_init(SEG_C); gpio_set_dir(SEG_C, GPIO_OUT);
    gpio_init(SEG_D); gpio_set_dir(SEG_D, GPIO_OUT);
    gpio_init(SEG_E); gpio_set_dir(SEG_E, GPIO_OUT);
    gpio_init(SEG_F); gpio_set_dir(SEG_F, GPIO_OUT);
    gpio_init(SEG_G); gpio_set_dir(SEG_G, GPIO_OUT);
    gpio_init(SEG_DP); gpio_set_dir(SEG_DP, GPIO_OUT); // Initialize decimal point

    gpio_init(DIGIT_1); gpio_set_dir(DIGIT_1, GPIO_OUT);
    gpio_init(DIGIT_2); gpio_set_dir(DIGIT_2, GPIO_OUT);
    gpio_init(DIGIT_3); gpio_set_dir(DIGIT_3, GPIO_OUT);
    gpio_init(DIGIT_4); gpio_set_dir(DIGIT_4, GPIO_OUT);

    gpio_put(DIGIT_1, 0);
    gpio_put(DIGIT_2, 0);
    gpio_put(DIGIT_3, 0);
    gpio_put(DIGIT_4, 0);
}

// Function to display a digit on the 7-segment display
void display_digit(int digit, int value, bool dp) {
    uint8_t segments = digit_segments[value];
    
    gpio_put(SEG_A, (segments & 0x01) ? 1 : 0);
    gpio_put(SEG_B, (segments & 0x02) ? 1 : 0);
    gpio_put(SEG_C, (segments & 0x04) ? 1 : 0);
    gpio_put(SEG_D, (segments & 0x08) ? 1 : 0);
    gpio_put(SEG_E, (segments & 0x10) ? 1 : 0);
    gpio_put(SEG_F, (segments & 0x20) ? 1 : 0);
    gpio_put(SEG_G, (segments & 0x40) ? 1 : 0);
    gpio_put(SEG_DP, dp ? 1 : 0); // Control the decimal point
    
    gpio_put(DIGIT_1, digit == 1);
    gpio_put(DIGIT_2, digit == 2);
    gpio_put(DIGIT_3, digit == 3);
    gpio_put(DIGIT_4, digit == 4);
}

// Function to display a floating-point number (2 decimal places)
void display_voltage(float voltage) {
    int integer_part = (int)voltage;
    int decimal_part = (int)((voltage - integer_part) * 100);

    int digits[4];
    digits[0] = integer_part / 1000;
    digits[1] = (integer_part / 100) % 10;
    digits[2] = (integer_part / 10) % 10;
    digits[3] = integer_part % 10;

    for (int i = 0; i < 4; i++) {
        // Ensure decimal point is only ON for digit 1
        bool dp = (i == 0);
        display_digit(i + 1, digits[i], dp);
    }
}

// Function to read the ADC value and convert it to voltage
float read_adc_voltage(int channel) {
    adc_select_input(channel);
    uint16_t result = adc_read();
    const float conversion_factor = 3.3f / 4095.0f;
    return result * conversion_factor;
}

// Global variables for active channel and button state
int active_channel = 0;

// Interrupt Service Routine (ISR) for button press
void button_isr(uint gpio, uint32_t events) {
    // Toggle between ADC channels on button press
    active_channel = (active_channel + 1) % 2;
}

// Timer callback function
bool timer_callback(struct repeating_timer *t) {
    // Read voltage from the active ADC channel
    float voltage = read_adc_voltage(active_channel);

    // Print the voltage for debugging
    printf("Voltage: %.2f\n", voltage);

    // Display the voltage only if it has changed
    static float last_voltage = -1;
    if (voltage != last_voltage) {
        last_voltage = voltage;
        display_voltage(voltage);
    }

    return true;  // Keep the timer repeating
}

int main() {
    stdio_init_all();

    // Initialize the ADC
    adc_init();
    adc_gpio_init(26);  // GPIO26 for ADC input (channel 0)
    adc_gpio_init(27);  // GPIO27 for ADC input (channel 1)

    // Initialize the button
    gpio_init(BUTTON_PIN);
    gpio_set_dir(BUTTON_PIN, GPIO_IN);
    gpio_pull_up(BUTTON_PIN);

    // Initialize 7-segment display
    init_7segment_display();

    // Set up the button interrupt
    gpio_set_irq_enabled_with_callback(BUTTON_PIN, GPIO_IRQ_EDGE_FALL, true, &button_isr);

    // Create a repeating timer
    struct repeating_timer timer;
    add_repeating_timer_ms(16, timer_callback, NULL, &timer);

    // Main loop does not need to do anything, everything is handled by the timer and interrupts
    while (1) {
        tight_loop_contents();
    }

    return 0;
}

I tried to debug the code by printing the value that is being passed to the display digit function. The result was that the digits and value are being passed correctly to the display_digit function. Yet, the number on each on the first three digits (left-to-right) where all the whole number.

Based on this logic, the digits do change but only for the whole number. Indicating there's an issue with how the digits are multiplexed.

I also did the whole circuitry on a breadboard thinking there should be an issue with the WOKWI simulation that I'm using. Only to find the same results as the simulation.

The desired result should be any number from 0.00 to 3.30 (since the potentiometers are both connected to the 3V3 PIN of the raspberry pi pico w that I'm using.

Upvotes: 2

Views: 71

Answers (0)

Related Questions