Hades logo    Hades applet banner

TAMS / Java / Hades / applets (print version): contents | previous | next

TinyMips - multiplexed seven-segment display

TinyMips - multiplexed seven-segment display screenshot

Description

This applet demonstrates a multiplexed seven-segment display controlled via the PIO8255 parallel input/output adapter chip.

Please check the introductory applet for the introduction and description of the TinyMips processor. The hardware structure consists of the processor, some glue logic, a single RAM component that stores both the program and the working set data, and the PIO8255 interface chip. See the PIO 8255 overview page for details about the parallel input/output adapter.

The multiplexed display

This applet demonstrates the typical way to connect peripheral devices to a microprocessor system via a configurable parallel input/output adapter. While the applet uses a simulation model based on the Intel 8255 chip, similar devices from other vendors could have been used as well. Also, some commercially available microcontrollers already include several programmable input/output lines on chip, but our TinyMips processor does not.

The 8255 PIO is based on a bidirectional 8-bit databus. Four memory addresses are reserved in order to access the four internal on-chip registers. To avoid extra complexity with byte-ordering issues during load-byte and store-byte operations, we connect the PIO with the full 32-bit width to the MIPS data-bus. Naturally, only the lower eight bits are actually used during actual transfers to and from the PIO. This also means that we can use word addressing, and the address inputs A1 and A0 of the PIO are therefore connected to bits A3 and A2 of the MIPS address bus.

The address-decoder is set up to activate the PIO for the virtual address range a0080000-a00800fc (hex). This is a typical address range for peripheral devices, because it lies in the "unmapped uncached" kernel segment (kuseg1) of the R3000-series processors. The static MMU of a R3000 processor would translate the virtual addresses in the above range to physical addresses in the range 00080000-000800c. However, we have disabled the MMU of the TinyMips processor in this applet, and the virtual addresses are directly used as physical addresses. Therefore, the control and data registers of the 8255 PIO-chip are accessed via the following addresses:

 MIPS virtual address    physical address    PIO register
                         (MMU disabled)
 ----------------------+-------------------+-------------
            a008.0000           a008.0000          port A
            a008.0004           a008.0004          port B
            a008.0008           a008.0008          port C
            a008.000c           a008.000c         control

The program (see below for the full sourcecode) defines a few simple utility functions to write data into the control and data registers, and to read the current values of input ports. At program start, we once call pioSetMode() to configure the PIO with ports A and B selected as outputs and port C as input. As you can see in the schematics, port A then drives the segment lines of the seven-segment displays, while bits PB4..PB0 are connected to inverting buffers (amplifiers) to drive the common cathodes of the displays.

The main part of the program then consists of an endless loop that increments the count variable, and then calls the displayVariable method to display the counter value. Each iteration of the display loop first disables all digit drivers. It then repeatly calculates the segment-data for to the 4-bit digit value to be displayed, loads this data into port A to drive the segment lines, and then activates one of the digit drivers via port B.

Usage

Wait until the applet is loaded, then watch the program. You can now open the memory-editor (via popup > edit) The memory editor highlights the last read operation in green color, and the last write operation in cyan color, which allows you to easily watch the program execution.

If you want to change the simulation speed, just select 'edit' on the clock-generator component, then edit the value of the clock-generator 'period' property, and select 'apply' (and 'ok' to close the clock-generator config dialog). The default clock rate is slow enough to allow you watching the memory accesses during the main loops of the program.

Similarly, open the TinyMips user-interface window (via popup > edit) to watch the current register values.

The binary program running on the processor was compiled and linked with the GNU gcc (2.7.2.3) and binutils cross-compiler toolchain on a Linux x86 host, with the final ELF loading and relocation done via the Hades Elf2rom tool. See:

The following listing shows the actual C source code of the program:

/* demonstrate a multiplexed display controlled via the PIO8255 */

// base address for the stack. This leaves very little space for
// the stack, but means that you can watch program-code, stack,
// and heap accesses at the same time in the memory editor.
// Remember that byte-address 0x800 is displayed at word-address 0x200
// in the RAM editor.
#define STACKBASE 0x00000800

// base address for some debugging output in the RAM.
// Note that byte-address 0x880 is displayed at word-address 0x220
// in the RAM editor.
#define BASE 0x00000880 

// axxx.xxxx is mapped to 0xxx.xxxx by the (static) R-3000 MMU
// but remains axxx.xxx in this demo because we disable the MMU.
#define PIO_BASE  0xa0080000

#define PIO_REGA  (PIO_BASE+0)
#define PIO_REGB  (PIO_BASE+4)
#define PIO_REGC  (PIO_BASE+8)
#define PIO_XREG  (PIO_BASE+12)

#define PIO_MODE_CHANGE    0x80
#define PIO_GROUPA_MODE0   0x00
#define PIO_GROUPA_MODE1   0x20
#define PIO_GROUPA_MODE2   0x60
#define PIO_GROUPB_MODE0   0x00
#define PIO_GROUPB_MODE1   0x04

#define PIO_PORTA_INPUT    0x10
#define PIO_PORTA_OUTPUT   0x00
#define PIO_UPPERC_INPUT   0x08
#define PIO_UPPERC_OUTPUT  0x00
#define PIO_PORTB_INPUT    0x02
#define PIO_PORTB_OUTPUT   0x00
#define PIO_LOWERC_INPUT   0x01
#define PIO_LOWERC_OUTPUT  0x00


void pioSetMode( int value ) {
  int *xreg   = (int*) PIO_XREG;
  int command = PIO_MODE_CHANGE | (value&0x000000ff);
  *xreg = command;
}

void pioWritePortA( int value ) {
  int *reg = (int*) PIO_REGA;
  int data = value & 0x000000ff;
  *reg = data;
}

void pioWritePortB( int value ) {
  int *reg = (int*) PIO_REGB;
  int data = value & 0x000000ff;
  *reg = data;
}

void pioWritePortC( int value ) {
  int *reg = (int*) PIO_REGC;
  int data = value & 0x000000ff;
  *reg = data;
}

void pioWriteUpperC( int value ) {
  int *reg = (int*) PIO_REGC;
  int data = (*reg & 0x0000000f) | value & 0x000000f0;
  *reg = data;
}

void pioWriteLowerC( int value ) {
  int *reg = (int*) PIO_REGC;
  int data = (*reg & 0x000000f0) | value & 0x0000000f;
  *reg = data;
}

int pioReadPortA() {
  int *reg = (int*) PIO_REGA;
  int data = *reg;
  return data;
}

int pioReadPortB() {
  int *reg = (int*) PIO_REGB;
  int data = *reg;
  return data;
}

int pioReadPortC() {
  int *reg = (int*) PIO_REGC;
  int data = *reg;
  return data;
}


/*
   +-A-+
   F   B
   +-G-+
   E   C
   +-D-+ (P)

   encoding is 0000....00 PGFEDCBA

   p is not driven by this routine. If necessary,   
   OR the return value with mask 0x80.
 */
int sevenSegment( int value ) {
  switch( value ) {
    case   0: return  0x0000003f; // 00111111
    case   1: return  0x00000006; // 00000110
    case   2: return  0x0000005b; // 01011011
    case   3: return  0x0000004f; // 01001111
    case   4: return  0x00000066; // 01100110
    case   5: return  0x0000006d; // 01101101
    case   6: return  0x0000007d; // 01111101
    case   7: return  0x00000007; // 00000111
    case   8: return  0x0000007f; // 01111111
    case   9: return  0x0000006f; // 01101111

    case 0xa: return  0x00000077; // 01110111
    case 0xb: return  0x0000007c; // 01111100
    case 0xc: return  0x00000039; // 00111001
    case 0xd: return  0x0000005e; // 01011110
    case 0xe: return  0x00000079; // 01111001
    case 0xf: return  0x00000071; // 01110001

    default:  // three horizontal bars as error indicator
              return  0x00000049; // 01001001  
  }
}


void waitLoop() {
  int i, limit;
  int *ptr;
  ptr = (int*) BASE;
  limit = 0x0015;
  for( i=0; i < limit; i++ ) {
    *ptr = i; 
  }
}


void displayValue( int value ) {
  int tmp, mask, hex, segs, i;
  int ndigits = 5;

  int *ptr = (int*) BASE;
  *(ptr-1) = 0xdeadbeef;
  *(ptr-2) = value;

  mask = 0xf;
  for( i=0; i < ndigits; i++ ) {
    pioWritePortB( 0x00 ); // all digits off

    hex  = value & mask;  
    segs = sevenSegment( hex >> (i << 2) ); 

    pioWritePortA( segs );
    pioWritePortB( 1 << i ); // one digit on
    waitLoop();

    mask = mask << 4;
  }

  pioWritePortB( 0 ); // disable all digits again
}




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


  pioSetMode(   PIO_MODE_CHANGE 
              | PIO_GROUPA_MODE0 
              | PIO_GROUPB_MODE0 
              | PIO_PORTA_OUTPUT 
              | PIO_PORTB_OUTPUT 
              | PIO_UPPERC_INPUT 
              | PIO_LOWERC_INPUT 
            );

  for( count=0; ; count++ ) {
    displayValue( count );
  }
   
}

Run the applet | Run the editor (via Webstart)


Impressum | 24.11.06
http://tams.informatik.uni-hamburg.de/applets/hades/webdemos/76-mips/16-led/led_print.html