Skip to content

Conversation

mjcross
Copy link
Contributor

@mjcross mjcross commented Oct 14, 2025

Add an example showing how to use the PIO to control a multiplexed 7-segment display.
@lurch this re-creates #375 as discussed, due to loss of the original repo

@lurch
Copy link
Contributor

lurch commented Oct 15, 2025

@mjcross I've not tested this, as I don't have a bagful of transistors handy, but does this work?

// Simple example of how to convert an integer between -999 and 9999
// into a 32-bit word representing up to four 7-segment digits.
//
uint32_t int_to_seven_segment (int num) {
    uint32_t word = 0;
    if (num < -999 || num > 9999) {
        // number out of range, display 'E' symbol
        //       EDBGACF.
        word = 0b11011010
    } else { 
        if (num == 0) {
            word = segments[0];
        } else {
            bool negative = num < 0;
            if (negative) {
                num *= -1;
            }
            int bitshift;
            for (bitshift = 0; bitshift < 32 && num > 0; bitshift += 8) {
                word |= segments[num % 10] << bitshift;
                num /= 10;
            }
            if (negative) {
                bitshift += 8;
                // display '-' symbol
                //        EDBGACF.
                word |= 0b00010000 << bitshift;
            }
        }
    }
    return word;
}

@mjcross
Copy link
Contributor Author

mjcross commented Oct 15, 2025

@lurch yes: tested a couple of years ago when I wrote it, with tiny 7-segment displays that didn't need driver transistors ;-) The project I wrote it for needed up using an OLED display instead so it never got used.
The only thing I changed was the ordering of the bit-patterns in the lookup tables, just to make the wiring look simpler in the circuit diagram.
I'll do a quick check on that function later today, just to be sure though.

@mjcross
Copy link
Contributor Author

mjcross commented Oct 15, 2025

TBH if there's any bit of the project likely to cause issues it's probably the CMakeLists.txt files: I see at some point several of the example builds were adjusted to use add_subdirectory_exclude_platforms() so I've copied that, but I must admit I have no idea what it does.

@lurch
Copy link
Contributor

lurch commented Oct 15, 2025

yes: tested a couple of years ago when I wrote it

Ahhh, sorry for the miscommunication - I wasn't asking "Does your function work?", I was asking "Does my suggested change to your function, to add support for displaying negative numbers, work?" 😂

to use add_subdirectory_exclude_platforms() so I've copied that, but I must admit I have no idea what it does.

It's so that if an example uses a hardware-feature that a particular board / chip / platform doesn't support, we don't try building that example for that chip. See e.g. https://github.com/raspberrypi/pico-examples/blob/master/rtc/CMakeLists.txt where we prevent the RTC examples from building for the RP2350 (as it doesn't have an RTC) or https://github.com/raspberrypi/pico-examples/blob/master/sha/CMakeLists.txt where we prevent the SHA examples from building for the RP2040 (as it doesn't have SHA256 hardware).

P.S. I've just realised that you also need to edit the top-level README.md in your PR, to add a link to your new example to https://github.com/raspberrypi/pico-examples?tab=readme-ov-file#pio 🙂

@mjcross
Copy link
Contributor Author

mjcross commented Oct 15, 2025

Ahh - sorry! Didn't read carefully enough. That code looks nice - I'll check it this afternoon: tks!

@mjcross
Copy link
Contributor Author

mjcross commented Oct 15, 2025

I like it! Turns out you don't need the bitshift += 8; otherwise the minus sign falls off the end LoL

@mjcross
Copy link
Contributor Author

mjcross commented Oct 15, 2025

You can simulate it with this slightly hacky code...

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

// segment patterns for digits 0-9
//
// By convention the segments are labelled as follows:
//
//  AAAA
// F    B
// F    B
//  GGGG
// E    C
// E    C
//  DDDD  .
//
// the bit ordering should match the way you connect up the GPIOs. Here it is
//    EDBGACF.
static const uint8_t segments[] = {
    0b11101110,     // 0
    0b00100100,     // 1
    0b11111000,     // 2
    0b01111100,     // 3
    0b00110110,     // 4
    0b01011110,     // 5
    0b11011110,     // 6
    0b00101100,     // 7
    0b11111110,     // 8
    0b01111110      // 9
};

// for display
static char rows[7][50];

// Simple example of how to convert an integer between -999 and 9999
// into a 32-bit word representing up to four 7-segment digits.
//
uint32_t int_to_seven_segment (int num) {
    uint32_t word = 0;
    if (num < -999 || num > 9999) {
        // number out of range, display 'E' symbol
        //       EDBGACF.
        word = 0b11011010;
    } else { 
        if (num == 0) {
            word = segments[0];
        } else {
            bool negative = num < 0;
            if (negative) {
                num *= -1;
            }
            int bitshift;
            for (bitshift = 0; bitshift < 32 && num > 0; bitshift += 8) {
                word |= segments[num % 10] << bitshift;
                num /= 10;
            }
            if (negative) {
                //bitshift += 8;
                // display '-' symbol
                //        EDBGACF.
                word |= 0b00010000 << bitshift;
            }
        }
    }
    return word;
}



void append_digit(uint8_t word) {
    static char temp_row[7][10];

    char e = word & 0b10000000 ? '|': ' ';
    char d = word & 0b01000000 ? '-': ' ';
    char b = word & 0b00100000 ? '|': ' ';
    char g = word & 0b00010000 ? '-': ' ';
    char a = word & 0b00001000 ? '-': ' ';
    char c = word & 0b00000100 ? '|': ' ';
    char f = word & 0b00000010 ? '|': ' ';

    snprintf(temp_row[0], sizeof(temp_row[0]), " %c%c%c%c   ", a, a, a, a);
    snprintf(temp_row[1], sizeof(temp_row[1]), "%c    %c  ", f, b);
    snprintf(temp_row[2], sizeof(temp_row[2]), "%c    %c  ", f, b);
    snprintf(temp_row[3], sizeof(temp_row[3]), " %c%c%c%c   ", g, g, g, g);
    snprintf(temp_row[4], sizeof(temp_row[4]), "%c    %c  ", e, c);
    snprintf(temp_row[5], sizeof(temp_row[5]), "%c    %c  ", e, c);
    snprintf(temp_row[6], sizeof(temp_row[6]), " %c%c%c%c   ", d, d, d, d);

    for (int i=0; i<7; i+=1) {
        strlcat(rows[i], temp_row[i], sizeof(rows[i]) - 1);
    }
}

void display_all(uint32_t word) {
    for (int i=0; i<7; i+=1) {
        rows[i][0] = '\0';
    }

    append_digit(word >> (3 * 8));
    append_digit(word >> (2 * 8));
    append_digit(word >> (1 * 8));
    append_digit(word);

    for (int i=0; i<7; i+=1) {
        puts(rows[i]);
    }
}


int main(int argc, char **argv) {

    display_all(int_to_seven_segment(-999));
    return (0);
}

@mjcross
Copy link
Contributor Author

mjcross commented Oct 15, 2025

The output is actually rather cute

                 ----    ----   
             |       |       |  
             |       |       |  
 ----            ----    ----   
             |  |            |  
             |  |            |  
                 ----    ----  

@mjcross
Copy link
Contributor Author

mjcross commented Oct 15, 2025

Hope the complex-looking circuit doesn't put people off: it's a really good use-case for the PIO.
You're right that using a driver chip would have been a better idea than discrete transistors...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants