AVR Embedded Tutorial - Multiplex

From Lazarus wiki
Revision as of 12:42, 24 January 2020 by Trev (talk | contribs) (English translation of German page)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

Deutsch (de) English (en)

Charlieplexing (Multiplexing) LEDs

Charlieplexing is a technique for driving a multiplexed display in which relatively few I/O pins of a microcontroller are used to drive an array of LEDs or LED segments.

Port access

For how the shift register commands access the ports, see:

Example

Each time the timer is run through, a different LED segment is activated. So the timer has to run 4 times until a frame is updated. For a constant repetition frequency, the timer is best suited because it is called up regularly. A multiplex could also be made in the main loop, but if there is a short pause there for other functions, the display will start to flicker.

The output takes place via two shift registers (74HC595). If the AVR has enough GPIO pins, you could also do this without the shift registers.

Digits constant for 7-segment display

The LED combinations of the digits are stored in the Digit[] constant. HEX characters are also saved.

 const
   Digit : packed array[0..15] of byte = (
   %00111111, // = 0
   %00000110, // = 1
   %01011011, // = 2
   %01001111, // = 3
   %01100110, // = 4
   %01101101, // = 5
   %01111101, // = 6
   %00000111, // = 7
   %01111111, // = 8
   %01100111, // = 9
   %01110111, // = a
   %01111100, // = b
   %00111001, // = c
   %01011110, // = d
   %01111001, // = e
   %01110001  // = f
   );

Values ​​of the individual segments

As an example, a 4-digit 7-segment display is used. If you want a larger multiplex, you have to increase the number of digits in the display.

 const
   anzDigit = 4;
 var
   Digit : array[0..anzDigit - 1] of byte;

Multiplex

That is the heart of the multiplex. Each time the timer is run through, the segment is changed. If you want to see this, you have to reduce the timer frequency so that you can see it. For this example, Timer0 is used.

procedure Timer0_Interrupt; public name 'TIM0_COMPA_ISR'; interrupt;
  const
    segPos : byte = 0;
begin
  segPos := segPos + 1;

  if(segPos > anzSegment - 1) then
    begin
      segPos := 0;
    end;

  WritePortA(latchPin, False);
  WritePortA(latchPin, True);

  shiftOut595(digits[Digit[segPos]]);  // Go to the current segment in the digit
  shiftOut595(1 shl segPos);           // Activate current segment
end;

Delay

A simple delay. This is required when changing the number in the main loop.

procedure delay;
var
  i : integer;
begin
  for i := 0 to 200 do
    asm 
      nop  // waste cycles 
    end;
end;

Initialization and main loop

Timer0 for the mutiplex is initialized here and the required ports are switched to output.

For more about timers, see: Timers.

 begin
   // Disable interrupts
   asm 
     cli
   end;

   // Switch ports to output
   DDRA := (1 shl dataOutPin) or (1 shl latchPin) or (1 shl clockPin);

   // Initialize the Timer0
   TCCR0A := (1 shl 1);
   TCCR0B := TCCR0B or %101;
   TIMSK0 := TIMSK0 or (1 shl OCIE0A);

   // Enable interrupts
   asm 
     sei
   end;

   // Main loop
   repeat
     Digit[0] := 1;
     Digit[1] := 2;
     Digit[2] := 3;
     Digit[3] := 4;
     delay;

     Digit[0] := 5;
     Digit[1] := 6;
     Digit[2] := 7;
     Digit[3] := 8;
     delay ;
   until 1 = 2;
 end;

See also