5

I was trying to display 7-segment LED on MDA-8086 kit, but I am stuck at calculating the hexadecimal values for respective digits. I have the code with me, but I don't understand how it actually works. For example, 0 is represented by hexadecimal value 0xc0 [I guess]. I am wondering, how the values have been calculated here?

C Code for 7-segment LED display:

#include"mde8086.h"

int data[11] = { 0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x00 };

void wait(long del)
{
    while( del-- );
}

void main(void)
{
    int *data1;

    /* 8255 -1 Initialization */
    outportb( PPI1_CR, 0x80 );
    outportb( PPI1_B, 0xf0 );
    outportb( PPI1_C, 0x00 );

    //main loop
    do {
        data1 = data;
        while( *data1 != 0x00 )
        {
            outportb( PPI1_A, *data1 );
            wait(30000);
            data1++;
        }
    } while(1);
}

The output has been generated from here:

7-Segment display

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Apprentice
  • 65
  • 3
  • https://stackoverflow.com/questions/2670639/why-are-hexadecimal-numbers-prefixed-with-0x check this stackoverflow question. Also check this out https://stackoverflow.com/questions/8186965/what-do-numbers-using-0x-notation-mean/8186974 – iwrestledthebeartwice Aug 26 '20 at 05:18
  • 1
    My first thought was that `1` is displayed by lighting only 2 segments total, so we can tell that must be `0xc0` (which only has 2 bits set in its binary representation, so segment b = 0x80, c = 0x40 or vice versa).So looping over this array counts up from 1 to 9 then wraps to 0, I guess. But that doesn't work if you look at the rest; there are other patterns with only 1 or a couple bits set, so it must be that the masks are inverted, and clear bits light up the corresponding segment? Then leaving segment G and the decimal-point unlit could be the top 2 bits. – Peter Cordes Aug 26 '20 at 05:19

2 Answers2

6

This is about how your HW hook-up is, i.e. how the pins of the display is connected to the port and whether it requires a 0 or 1 on the port to turn on a segment.

From your numbers it seems:

  1. The port is connected like g, f, e, d, c, b, a That is: a is LSB.

  2. The "missing" 8th bit shall always be programmed to 1, i.e. as 8 bit it will be port = 1gfedcba

  3. It takes a 0 to turn on a segment in the display.

So

0xc0 -> 1100.000 -> 1    1    0    0    0    0    0    0
                    ^    ^    ^    ^    ^    ^    ^    ^
                    |    |    |    |    |    |    |    |
                unused   g    f    e    d    c    b    a
                        off  on   on   on   on   on   on

which results in a zero on the display.

Now for 0xf9

0xf9 -> 1111.1001

so only segment b and c will turn on and the display will display 1

Check the rest yourself.

EDIT Looking at the sigment picture, it could be that the 8th bit (that I called "unused") is actually controlling the "DP" part of the segment. This is just a guess but maybe if you write 0x40 to the port, you'll see 0. on the display.

When you only display numbers, there are many unused combinations. Some of these look like letters, e.g. H. So for fun (aka an exercise) you can make the display spell words like "HELLO", "CACAO", "BEEF" and many more.

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
2

I am stuck at calculating the hexadecimal values for respective digits.

Sometime a little macro art is fun and illustrative. It provides a graphical way to define the hex values for data[] rather than doing so by hand.

seven() takes the 3 rows (strings) and looks for -, |, . to form the hex value. When the corresponding segment is off, the value ors in another bit.

#include <stdio.h>

#define seven(r1,r2,r3) (\
/* Seg A */ (r1[1] == '_' ? 0 : 0x01) |  \
/* Seg B */ (r2[2] == '|' ? 0 : 0x02) |  \
/* Seg C */ (r3[2] == '|' ? 0 : 0x04) |  \
/* Seg D */ (r3[1] == '_' ? 0 : 0x08) |  \
/* Seg E */ (r3[0] == '|' ? 0 : 0x10) |  \
/* Seg F */ (r2[0] == '|' ? 0 : 0x20) |  \
/* Seg G */ (r2[1] == '_' ? 0 : 0x40) |  \
/* Seg DP*/ (r3[3] == '.' ? 0 : 0x80) \
)

int datax[11] = { //
    seven( // Zero
    " _ ",//
    "| |",//
    "|_| "),//
    seven( // One
    "   ",//
    "  |",//
    "  | "),//
    seven( // Two
    " _ ",//
    " _|",//
    "|_  "),//
    };

// Remaining digits left for OP


int main(/*int argc, char *argv[]*/) {
  for (int i = 0; i < 3; i++)
    printf("%02X\n", data[i]);
  return 0;
}

Output

C0
F9
A4
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 2
    I might have compacted my representations to 3 rows instead of 5, e.g. top row of zero as `"|-|"`. That looks nice for 0 but maybe a lot worse for 2. Still, it keeps the source more compact and is still pretty visually intuitive. (Overall really neat idea, +1) – Peter Cordes Aug 26 '20 at 09:38
  • 1
    @PeterCordes Good idea. Maybe `"|_|"` to push the horizontal segment visually down. What I like about the graphical approach is that it is easy to extend to other 7-segment characters [figure 2](https://www.engineersgarage.com/electronic-projects/how-to-display-numbers-and-alphabet-on-7-segment-display/). – chux - Reinstate Monica Aug 26 '20 at 09:41
  • 1
    @PeterCordes Re-coded with a slight variation on you idea. Thanks for the tip. – chux - Reinstate Monica Aug 26 '20 at 09:52
  • 1
    Nice, that's even better. The macros could also be `r1[1] != ' '` so it's the same character every time, without having to match up the right shape to the right position. It would even be possible to write `(r1[1] != ' ') << 0` and so on, but the ternary is probably better for readability than shifting a boolean->int result. – Peter Cordes Aug 26 '20 at 09:59
  • @PeterCordes Re: `r1[1] != ' '`, I thought of that too, yet with 2 coding choices: with/without negation, I have found the negation-less code easier to maintain. and [understand](https://www.youtube.com/watch?v=DBITZw5-HWY) – chux - Reinstate Monica Aug 26 '20 at 10:02
  • Interesting point, that's probably worth the pain of having to get the right symbol for each position. – Peter Cordes Aug 26 '20 at 10:05