Electronics Btrieve Motorcycling Software If you find this site useful and want to support, click on few ads to throw some money into hat.

Using DL2416 intelligent 16-segment led displays with AVR

I have a project in mind where I need an old-school display, and got some of these awesome looking retro things (The actual part number is DL2416T, manufactured by Siemens):

You can still get them from ebay, most are probably extracted from old equipment. Be aware, that there is unit to unit variation in brightness, and if you need to stack more than one, then you want to get them with same brightness code if possible. Brightness code is a single letter printed on the side of the unit like this:

I ordered 4 displays, 3 were with 'E', one with 'D'. The D unit is just slightly less bright than E units.

Obviously I wanted to check these puppies out in action, and took a chance to also test out cheap AVR-s (or clones perhaps) that I ordered off ebay a while ago. So I wired up a simple test rig on solderless breadboard (click on the diagram to see it better):

As you can see, these displays are from pre-SPI era, and need a lot of I/O even in very minimal configuration without cursors and flashing display. Fortunately 8 of the 12 I/O lines can be reused for other functions when displays are not being written to. Capacitors are non-critical, 10n-100n are ok, and the setup may function without these. I used 8MHz ceramic resonator as clock source, just so that I dont have to figure out the new fuse settings.

In case you are wondering, I have a nonstandard programming connector that also passes on +5V from USB to device end, making in convenient for powering small temporary projects like this.

The displays cannot keep up with AVR running at 8MHz, I just added few delays into display write function, one of them may be unnecessary.


The sample code should be pretty clear, the Display class has a full display length buffer for easily creating scrolling displays. The refresh() method copies the entire buffer to display units, using write() to send each character out. The address manipulation in write() is to revert the address bits (because the display unit addresses characters from right to left) and to pull the CS of correct display low.

The putc() method displays a character, and if the display is already full then scrolls the display left to make room for the new character. putc() also converts any lowercase ASCII characters to upper case, because the DL2614 only has uppecase character set.

#include <avr/io.h>
#include <util/delay.h>
#include <string.h>

class Display
  uint8_t buf[8];
  uint8_t cp;    // next character address in buf

  void write(uint8_t adr,uint8_t data)
    if (adr>3)
      adr=(adr&3)|4; // two lowest bits are adr, next two are chipsels
    adr=adr^3;   // the display addresses characters from right to left
    PORTB=(PORTB&0xf0)|adr|0x0c; // apply address, both CS high
    PORTD=data|0x80;           // apply data, WR high
    PORTB=(PORTB&0xf0)|adr;    // appropriate CS low
    PORTD=data&0x7f;           // WR low
    PORTD=data|0x80;           // WR high
    PORTB|=0x0c;               // both CS high

  void refresh(void)
    for (uint8_t i=0;i<sizeof(buf);i++)



  void clear(void)
    memset(buf,' ',sizeof(buf));

  void putc(char c)
    uint8_t i;
    if (cp>=sizeof(buf))
      for (i=0;i<sizeof(buf)-1;i++)
    if (c>='a' && c<='z')

  void puts(const char* s)
    while (s && *s)


// creating a global instance like that works fine, except that
// when the constructor is called the I/O is not yet set up, so
// the display is not cleared
Display display;

I/O configuration
I/O pin                               direction     DDR  PORT
PC0 unused                            output        1    0
PC1 unused                            output        1    0
PC2 unused                            output        1    0
PC3 unused                            output        1    0
PC4 unused                            output        1    0
PC5 unused                            output        1    0

PD0 D0                                output        1    0
PD1 D1                                output        1    0
PD2 D2                                output        1    0
PD3 D3                                output        1    0
PD4 D4                                output        1    0
PD5 D5                                output        1    0
PD6 D6                                output        1    0
PD7 /WR                               output        1    1
PB0 A0                                output        1    0
PB1 A1                                output        1    0
PB2 /CE1                              output        1    1
PB3 /CE2                              output        1    1
PB4 unused                            input,pullup  0    1
PB5 unused                            input,pullup  0    1
int main(void)
  // I/O directions
  // initial state
  while (1);

Build and flash it

$ avr-g++ -I. -I.. -g -mmcu=atmega328 -Os -fpack-struct -fshort-enums -funsigned-bitfields -funsigned-char -Wall \
 -fno-exceptions -DF_CPU=8000000UL -I.. -c display_demo.cpp -o display_demo.o
$ avr-g++ -Wl,-Map,display_demo.map -mmcu=atmega328  -o display_demo.elf display_demo.o
   text	   data	    bss	    dec	    hex	filename
    508	     10	      9	    527	    20f	display_demo.elf
avr-objcopy: --change-section-lma .eeprom=0x00000000 never used
$ avrdude -P usb -B 10 -c usbtiny -p m328p -U lfuse:w:0xE6:m -U hfuse:w:0xDC:m \
 -U efuse:w:0x07:m -U lock:w:0x3F:m -U flash:w:display_demo.hex -U eeprom:w:display_demo.eep

And there you have it

If you did everything right, and the ancient silicon still chooces right, then you should be seeing this now:

Copyright © Madis Kaal 2000-