Zurück (C) Christof Ermer, Regensburg
Gästebuch 
(public. sonst besser Email)  ChristofErmer@gmail.com

07.01.2019


Charlie Plexing
ist eine Technik zur Ansteuerung von z.B. LEDs in einer Matrix.
Siehe dazu:
Mein WINAVR Programm für den Download:     ATM328-SparkArray_8x7_V2.zip
 http://www.elektronik-labor.de/AVR/Charlieplexing.html
Mir hat sich die Frage gestellt, wie man 8x7=56 LEDs nur mit 8 Steuerleitungen A-H darstellen/ansteuern will und Muss.





Der Trick heißt „Charlie-Plexing“.     




das How to ?




Ein Pin hat bis zu 3(~4) Zustände:
Neben 0= "0" Volt und 1="5" Volt,    gibt es nch den DRITTEN Zustand,  "NC=Not Connected" oder sinngleich Portpin=INPUT (ohne Pullup)"

1. Output: 0 (0V)
2. OutPut: 1 (~5V)
3. Input-mit Pullup: schwache_5V  (nicht relevant)
4. Input: Hochohmig=TRI-STATE
     Hier wird es Interessant!.. 


Betrachten wir die Methoden.
Die klassische Matrix !
Dies heisst:  5 +5  = 10 Leitungen  erforderlich für 25 LEDs                                    Charlie-Plexing Matrix   Schon deutlich  weniger Leitungen  --> 5x4 LEDs
                                                                                                                                5 Leitungen für 20 LEDS. Aber etwas verwirrend.
                        


Aber wie geht das ????  Wie ist die Ansteuerung ?
Aus den 4 möglichen Zuständen lassen sich neue Kombinationsmöglichkeiten ableiten.
Und diese lassen sich Kombinieren


P1:+, P2:-, P3:Tristate
P1:-, P2:+, P3:Tristate
P1:Tristate, P2:+, P3:-
P1:Tristate, P2:-, P3:+
P1:+, P2:Tristate, P3:-
P1:-, P2: Tristate, P3:+
 6 Kombinationen mit Tristate, mit 3 Leitungen. Dazu kommen noch „ohne Tri-state“.
Also müssten auch 6 LEDs mit 3 Leitungen steuerbar sein.
LEDs sind ja selbst Dioden, und somit in der Matrix definiert ansprechbar.




Die „1“ ist das Reihen Selektier Bit mit +3.3V..5V
1. Die Spalte hat 7 Bits bei 8 Reihen.
2. Das 8te Bit selektiert die waagerechte Linie mit +5V
3. Da das Reihen Selektier-Bit sich durchschiebt, ist das Datenbyte( 7Bit ) an dieser Stelle zu Splitten, und die rechte Hälfte um 1 nach rechts zu schieben .
4. Alles was NICHT leuchten soll, bekommt im Daten Direktion Register eine 0=Input auf dieses Bit. Das dazugehörige PORT Bit = 0 = „Pullup Off“
5. Alles was leuchten soll, bekommt im Daten Direction Register eine 1, Aber im PORT Register wir an diesem BIT eine 0=0V geschrieben. 

ERGO, Das Port bekommt immer eine 0 außer das Selektier Bit. --> PORTD = (1<<u8NN);


//Globals
uint8_t gu8ParkFun[FONT_HEIGHT];  //8    7 Teilen, 8 Spalten, 1 Bit=selector
volatile uint32_t gu32_Ticks;
volatile uint8_t gu8StatusFlags=0;
volatile uint8_t gu8Spark8x7Flags=0;

// 6 Bits auf D

#define A_F_DATA_MASK  0b00111111

#define A_F_DATA_SHIFT_NR  2 //<<

#define A_F_PORT  PORTD

#define A_F_DDR  DDRD

#define A_F_PORT_MASK  0b11111100

// 2 Bits auf B

#define G_H_DATA_MASK  0b11000000

#define G_H_DATA_SHIFT_NR  6   //>>

#define G_H_PORT  PORTB

#define G_H_DDR  DDRB

#define G_H_PORT_MASK  0b00000011




--
Beispiel der FONT7x8.h Datei

ifndef FONT7X8_H

  #define FONT7X8_H

#define FONT_CHAR_FIRST 32

#define FONT_CHAR_LAST  126

#define FONT_CHARS 95

#define FONT_WIDTH 7

#define FONT_HEIGHT 8

#define FONT_SPACING 1

#include <avr/pgmspace.h>

{

PROGMEM static const prog_char cmaFont7x8[] = {

  0,    0,   0,   0,   0,   0,   0, // ' '  32

  0,    6,  95,  95,   6,   0,   0, // '!'  33

  0,    7,   7,   0,   7,   7,   0, // '"'  34

  20, 127, 127,  20, 127, 127,  20, // '#'  35

  36,  46, 107, 107,  58,  18,   0, // '$'  36

  70, 102,  48,  24,  12, 102,  98, // '%'  37

  48, 122,  79,  93,  55, 122,  72, // '&'  38

  4,    7,   3,   0,   0,   0,   0, // '''  39

  0,   28,  62,  99,  65,   0,   0, // '('  40

  0,   65,  99,  62,  28,   0,   0, // ')'  41

...................................................... usw..




Noch sehr wichtig. Zur Ansteuerung  benötigt man eine genaue wie gleichmäßige Zeitscheibe.
Das geht nur über den 8 Bit Timer.
Flackerfrei geht das nur über eine Geschwindigkeit von ~30*/Zeile/Sekunde

30*8 = 240 ist mindestens 240*/Sek. Oder schneller ! Ich wählte 977/Sek.

// In Main Intitialsierung
OCR0A = 127; // PWM=Halbe Helligkeit
TCCR0A = 0;
TIMSK0 = (1<<TOIE0) | (1<<OCIE0A);
TCCR0B = (1<<CS01)| (1<<CS00); // F_CPU/256/64= 977

ISR( TIMER0_OVF_vect ) 
 {
ByteToMatrix(gu8RowNN);
gu8RowNN++;       
gu8RowNN %= 8;
};     (dazu kommt noch ein  PWM Abschaltinterrupt, der die Helligkeit regelt.


ISR( TIMER0_COMPA_vect )
{
A_F_DDR &= ~A_F_PORT_MASK;    
G_H_DDR &= ~G_H_PORT_MASK;
A_F_PORT &= ~A_F_PORT_MASK;    
G_H_PORT &= ~G_H_PORT_MASK;
};


// ********************************
void ByteToMatrix( uint8_t u8Row )
// ********************************
{
uint8_t u8Split_Low_Data;
uint8_t u8Split_High_Data;
uint8_t u8SParkFun8x7Data;
uint8_t u8Low_0_Maske;
uint8_t u8High_0_Maske;

//Data sind schon "left flush"
u8Low_0_Maske  = (1<<u8Row) - 1;  //bsp3: 1000-"1"=00000111
  // 11110111  & ~00000111   = 11110000

u8High_0_Maske = ~(1<<u8Row) & ~u8Low_0_Maske;
// +5V Selektiert Reihe,      0V = LED ON
u8Split_Low_Data = ((gu8ParkFun[u8Row]) >> 1) & u8Low_0_Maske;
u8Split_High_Data = gu8ParkFun[u8Row]  &  u8High_0_Maske;
u8SParkFun8x7Data = (u8Split_High_Data | u8Split_Low_Data) | (1<<u8Row);
   

//lösche Port   
A_F_DDR &= ~A_F_PORT_MASK;
G_H_DDR &= ~G_H_PORT_MASK;
A_F_PORT &= ~A_F_PORT_MASK;
G_H_PORT &= ~G_H_PORT_MASK;

if( u8Row < 6)
{
A_F_DDR |= (((u8SParkFun8x7Data & A_F_DATA_MASK) | (1<<u8Row) ) << A_F_DATA_SHIFT_NR) & A_F_PORT_MASK ;

G_H_DDR |= ((u8SParkFun8x7Data & G_H_DATA_MASK) >> G_H_DATA_SHIFT_NR) & G_H_PORT_MASK;

A_F_PORT |= ((~A_F_DDR) | ((1<<u8Row) << A_F_DATA_SHIFT_NR))  & A_F_PORT_MASK;

G_H_PORT |= (~G_H_DDR) & G_H_PORT_MASK;
}

else  // Row>=6
{
A_F_DDR |= ((u8SParkFun8x7Data & A_F_DATA_MASK) << A_F_DATA_SHIFT_NR) & A_F_PORT_MASK ;

G_H_DDR |= (((u8SParkFun8x7Data & G_H_DATA_MASK) | (1<<u8Row)) >> G_H_DATA_SHIFT_NR) & G_H_PORT_MASK;

A_F_PORT |= (~A_F_DDR) & A_F_PORT_MASK;

G_H_PORT |= ((~G_H_DDR) | ((1<<u8Row) >> G_H_DATA_SHIFT_NR)) & G_H_PORT_MASK;
};



// ********************************
void FetchZeichen(uint8_t u8Char)
// ********************************
{
uint8_t u8NN;
uint8_t u8aOrgField[FONT_WIDTH]; //7

for(u8NN=0; u8NN < 8; u8NN++)
    {
    //'A'=65  ('A'-32)*7
    u8aOrgField[u8NN] = pgm_read_byte_near(cmaFont7x8 + ((u8Char - FONT_CHAR_FIRST) * FONT_WIDTH) + u8NN);
    };
//lösche BIFfeld
for(u8NN=0;u8NN < FONT_HEIGHT; u8NN++)//8
    {       
    gu8ParkFun[u8NN] = 0;
    };
//Drehe Bitfeld nach Links und Spiegele
//Linksbündig
for(u8NN=0; u8NN < FONT_WIDTH; u8NN++) //7
    {       
    if(u8aOrgField[u8NN] & 0x01) {gu8ParkFun[0] |= (1<<(7-u8NN));};
    if(u8aOrgField[u8NN] & 0x02) {gu8ParkFun[1] |= (1<<(7-u8NN));};
    if(u8aOrgField[u8NN] & 0x04) {gu8ParkFun[2] |= (1<<(7-u8NN));};
    if(u8aOrgField[u8NN] & 0x08) {gu8ParkFun[3] |= (1<<(7-u8NN));};
    if(u8aOrgField[u8NN] & 0x10) {gu8ParkFun[4] |= (1<<(7-u8NN));};
    if(u8aOrgField[u8NN] & 0x20) {gu8ParkFun[5] |= (1<<(7-u8NN));};
    if(u8aOrgField[u8NN] & 0x40) {gu8ParkFun[6] |= (1<<(7-u8NN));};
    };
gu8ParkFun[7] = 0;
};

//im header
#define TICK_EVENT        0x01
#define ZEICHEN_EVENT    0x02
#define ORDER_FLAG        0x04
#define TOGGLE_FLAG        0x08
//gu8StatusFlags


In Main gibt es diese Sequenz:
    if( gu8StatusFlags & ZEICHEN_EVENT )
        {                
        gu8StatusFlags &= ~ZEICHEN_EVENT;   
        FetchZeichen( 32 + u8XX );   
            u8XX++;
            u8XX %= 95;       
        };