Sechs LEDs an drei Ports    


Elektronik-Labor   Projekte  AVR 

 

Multiplexen kennt jeder, aber Charlieplexen ist mehr. Das Verfahren habe ich gerade erst von Gerd Sinning und seiner LED-Uhr gelernt. Und dann habe ich es noch  in einem neuen Arduino-Buch entdeckt. Der Name stammt vom Erfinder Charlie Allen. Und ich dachte auch, ich hätte das Verfahren  schon verstanden. Aber dann wollte ich es jemand erklären und musste feststellen, da ist noch ein Trick dabei, den ich zuerst noch übersehen hatte. Inzwischen habe ich es kapiert, der Trick ist, dass die gerade nicht benutzten Leitungen in den hochohmigen Zustand geschaltet werden.

Jetzt wollte ich das Prinzip einmal in Bascom mit drei Portleitungen eines Tiny13  ausprobieren. Beim Charlieplexing kann man mit N Leitungen bis zu N * (N - 1) LEDs ansteuern. Das macht sechs LEDs für drei Leitungen, 12 LEDs für vier Leitungen, 20 LEDs für fünf Leitungen usw. Im Prinzip legt man jeweils zwei LEDs antiparallel zwischen zwei Leitungen. Alle gegen alle außer sich selbst ergibt die obige Formel. Zum Vergleich: Beim Multiplexen bekommt man mit 8 Leitungen nur 4 * 4 = 16 LEDs, beim Chalieplexen sind es 8 * 7 = 56 LEDs.
Allerdings kann beim Charlieplexen zu einer Zeit nur eine LED leuchten, beim Multiplexen können mehrere gleichzeitig eingeschaltet sein.



Das Schaltbild zeigt den Anschluss der sechs LEDs mit ihren drei Widerständen. Die momentanen Spannungen zeigen einen von sechs möglichen Zuständen. PB2 ist high, PB4 ist low, die markierte LED leuchtet gerade. Damit keine andere LED mitleuchten kann muss PB3 als hochohmiger Eingang geschaltet werden. Hier stellt sich eine mittlere Spannung ein. Im Prinzip sind zwar zwei weitere LEDs in Durchlassrichtung angeschlossen, aber jede von ihnen bekommt nur 1 V, weil die aktive LED die Spannung auf 2 V begrenzt. 1 V liegt aber noch deutlich unter der Diodenschwelle, und deshalb leuchtet tatsächlich nur eine LED.

Das folgende Programm lässt die sechs LED einzeln nacheinander aufleuchten. Das Datenrichtungsregister Ddrb legt dabei fest, welche beiden Leitungen gerade Ausgänge sein sollen, das Portb-Register welche einzelne Leitung gerade high sein soll.

 'ATtiny13 6 LEDs an 3 Ports
$regfile = "attiny13.dat"
$crystal = 1200000
$hwstack = 8
$swstack = 4
$framesize = 4

Config Portb = Input
Portb = 0

Do
Ddrb = 00001100
Portb = 00001000
Waitms 500

Ddrb = 00001100
Portb = 00000100
Waitms 500

Ddrb = 00011000
Portb = 00010000
Waitms 500

Ddrb = 00011000
Portb = 00001000
Waitms 500

Ddrb = 00010100
Portb = 00000100
Waitms 500

Ddrb = 00010100
Portb = 00010000
Waitms 500
Loop
End

Hier ein kleiner Youtube-Film. Wenn man ganz genau hinsieht, erkennt man eine LED, die mit geringerer Leuchtkraft zur falschen Zeit mitleuchtet. Das liegt daran, dass der Port B2 auf dieser Platine aus dem Lernpaket Mikrocontroller nicht völlig frei liegt, sondern über einen Widerstand an der Schnittstelle hängt.




http://www.youtube.com/user/bkelektronik


Schaltungsvariante von Arne Rossius

Wie beim Multiplexing auch kann das Display in "Zeilen" und "Spalten" eingeteilt werden, so dass immer eine ganze Zeile (oder Spalte, je nach Ansteuerung) zugleich leuchten kann. Jede Zeile und jede Spalte hat bei Charlieplexing mit n Pins dann genau eine LED weniger als bei Multiplexing mit 2*n LEDs (quadratische Matrix). Ich habe Charlieplexing in diesem Projekt verwendet:

https://wiki.blinkenarea.org/index.php/LED-23

Dort wird immer eine Spalte auf Masse gelegt, die restlichen Ausgänge des AVR sind dann entweder hochohmig (LED aus) oder high (LED an). Damit sich keine Helligkeitsunterschiede je nach Anzahl der eingeschalteten LEDs ergeben müssen die Kathoden aber direkt (ohne Vorwiderstand) an die Portpins des Controllers angeschlossen sein, also anders als bei dir im Artikel gezeigt.




Bei der Gelegenheit habe ich gleich noch zwei Skizzen erstellt, wie aus einer Multiplexing-Matrix eine Charlieplexing-Matrix wird.




Charlieplexing-Verstärker, von Jens Grabner  



Angeschlossen ist das bei mir an einem Arduino-mini-pro mit dem LOL-Shield. Softwaretechnisch ist es so, das es nicht genügt den Matrixtreiber von Output-High auf Eingang (Tri-State) zu schalten, weil .... durch den Vorwiderstand (R3) geht die Schaltung in Selbsthaltung.  Wichtig: Den Matrixtreiber von Output-High auf Output-Low schaltem (immer niederohmig) -- umgekehrt den Matrixtreiber ohne Last einschalten, weil es sein kann, das der Ausgang vom µC nicht die Last aller Led treiben kann (das soll ja auch der Mosfet machen ;-) ).



Nachlesen kann man es hier: http://www.mikrocontroller.net/topic/201582 Ob der Aufwand gerechtfertigt ist muss jeder selbst entscheiden -- ELV verwendet im "Info-Display ID100" einen ähnlichen Aufwand für die Matrixtreiber. http://www.elv.at/id100-info-display-tischanzeige-bausatz.html



Charlieplexing reloaded  von Gerd Sinning



Ein etwas älteres Projekt war mal ein Thermometer mit 12 Leds in einer Reihe, mit dem AT15 und Charlieplexing. Das habe ich nochmal gebaut mit dem AT13, aber das Programm sollte jetzt Basic sein. Das erste war Cplex0.bas, zum Test der Verdrahtung empfehlenswert. Das funktioniert. Dann kam Cplex3.bas mit dem ADC2 an PB4. Das funktioniert nicht, der ADC liefert keine eindeutigen Daten. Merkwürdig. Nun hatte ich schon das Assembler Programm für den AT15, das habe ich dann für den AT13 umgeschrieben. Siehe da, das funktioniert wie es soll, die Spannung am ADC steuert eine Led an. Die Verdrahtung ist wie bei der Cplex Clock, nur in einer Reihe. Die thresholds für die Spannung liegen in einer Tabelle, das ist flexibel, hier sind sie linear angeordnet, aber für ein VU-meter geht es auch logarithmisch. Warum geht's mit Basic nicht, mein Programm ist V 1.11.9.8, also schon älter, vielleicht liegt es daran.



Die Bilder zeigen den Aufbau für Cplex1T.asm. Mit einem LDR an + und 68 k an Gnd und die Mitte an PB4 hat man ein Miniluxmeter. Der LDR ist ja schon logarithmisch, das passt dann. Kann man an die Wand hängen.

Download: Cplexe.zip


'***************************************************************************
'
'ATTiny13 12 Charlieplexed Leds on PB 0 1 2 3
'
'
' input on ADC channel 2
'
'***************************************************************************
' This program is free software; you can redistribute it and/or
' modify it under the terms of the GNU General Public License.
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY;
'
'***************************************************************************
'
' Pinout ATtiny13/ATtiny13V 8-PDIP/SOIC
'
' (PCINT5/RESET/ADC0/dW) PB5 VCC
' (PCINT3/CLKI/ADC3) PB3 PB2 (SCK/ADC1/T0/PCINT2)
' (PCINT4/ADC2) PB4 PB1 (MISO/AIN1/OC0B/INT0/PCINT1)
' GND PB0 (MOSI/AIN0/OC0A/PCINT0)
'
'***************************************************************************
' PB0 output Led
' PB1 output
' PB2 output
' PB3 output
' PB4 input ADC channel 2
'***************************************************************************

$regfile = "attiny13.dat"
$crystal = 1200000
$hwstack = 8
$swstack = 8
$framesize = 4

Const C_value = 187 - 1 ' Compare Match IRQ 10 ms
Declare Sub Mydelay


Dim Voltage As Word , Avgv As Word
Dim Icnt0 As Byte
Dim N As Byte , Xp As Byte , Xd As Byte
'Dim Pp As Byte , Dd As Byte

Portb = &B00000000
Ddrb = &B00000000

Acsr.acd = 1 ' switch off analog comparator

Config Adc = Single , Prescaler = Auto , Reference = Avcc
Didr0.adc2d = 1
Start Adc

Config Timer0 = Timer , Prescale = 64
Ocr0a = C_value ' value for 1.2 MHz
Tccr0a = &B00000010 ' ctc
Timsk0.ocie0a = 1
On Oc0a Tim0_compa
Start Timer0



For N = 0 To 11 'test leds
Xd = Lookup(n , Led12)
Xp = Xd
Shift Xd , Right , 4 ' upper is DDR
Xd = Xd And &H0F
Xp = Xp And &H0F ' lower is Port
Portb = Xp
Ddrb = Xd
Call Mydelay
Next N

Enable Interrupts

Timer0 = 0

Do ' main loop

Loop


'***************************************************************************
'Tim0_compa Interrupt 10 ms
'***************************************************************************

Tim0_compa: '

'Incr Icnt0
Voltage = Getadc(2)
Avgv = Getadc(2)
Voltage = Avgv + Voltage
N = Voltage / 170
If N = 12 Then
N = 11
End If

Xd = Lookup(n , Led12)
Xp = Xd
Shift Xd , Right , 4 ' upper is DDR
Xd = Xd And &H0F
Xp = Xp And &H0F ' lower is Port
Portb = Xp
Ddrb = Xd


'Voltage = Getadc(2)
'Avgv = Avgv + Voltage
'Shift Voltage , Right , 2 '8 bit

Return

End

Mydelay: 'delay
$asm
PUSH R27
Ldi R27 , 200 'Load R27
Mdec:
Dec R27
BRNE Mdec
POP R27
$end Asm

Return


Led12: ' Led0..11 Cplex ' 12 = 0 upper 4 bit DDRB lower 4 bit PORTB


Data &B00110001 , &B11001000 , &B11000100 , &B10101000 , &B10100010 , &B01100100 , &B01100010 , &B01010100 , &B01010001 , &B10011000 , &B10010001 , &B00110010 , 0 , 0





Assembler-Version:


;***************************************************************************
; ATtiny13 Charlieplex 12 Leds on PB 0 1 2 3, use threshold and Led table
;
; input on ADC2, PB4, 8 bit
; simple Voltmeter, 0,..,5 V, single Led is on
; bsed on ATtiny15 GS 2009
; The timing is adapted for 1.2 MHz
;
;***************************************************************************
; This program is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License.
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY;
;
;***************************************************************************
; Pinout ATtiny13/ATtiny13V 8-PDIP/SOIC
;
; (PCINT5/RESET/ADC0/dW) PB5 VCC
; (PCINT3/CLKI/ADC3) PB3 PB2 (SCK/ADC1/T0/PCINT2)
; (PCINT4/ADC2) PB4 PB1 (MISO/AIN1/OC0B/INT0/PCINT1)
; GND PB0 (MOSI/AIN0/OC0A/PCINT0)
;***************************************************************************
; PB4 input ADC2
; PB3 output Leds
; PB2 output
; PB1 output
; PB0 output
;***************************************************************************

.DEVICE ATtiny13 ;for gavrasm

.equ avgcnt = 16
.equ AD_resolution = 256 ; A/D converter resolution

; r0 used
.def S = r4
.def temp = r16
.def count = r17
.def lcnt = r18
.def next_step = r19
.def av_l = r20 ; Average Low Byte (ADC Measurement)
.def av_h = r21 ; Average High Byte (ADC Measurement)
.def LedPD = R22 ; Led port and DDR
.def Ledcnt = R23 ; Led 0,..,11
.def ZL = r30
.def ZH = r31

.cseg
.org 0

rjmp reset ; 0x0000 Reset-vector
reti ; 0x0001 External Interrupt 0
reti ; 0x0002 PCIrq0
rjmp TIM0_OVF ; 0x0003 Timer/Counter0 Overflow
reti

;********************************************************************
;* "TIM0_OVF" - Timer/counter overflow interrupt handler
;*
;* The overflow interrupt fires every 1.7ms
;*
;********************************************************************
TIM0_OVF: in S,sreg ; Updated every 1.7 ms, calibrate OSCCAL

inc count
inc next_step
out sreg,S
reti
;***************************************************************************
reset:

; writing calibration byte to OSCCAL Register.
;ldi temp,0x7F ; test
;out OSCCAL,temp ; for the clock
nop
nop
; Port B
ldi temp,0 ; set
out PORTB, temp ; 1 = pull-up , 0 = float
ldi temp,0 ; 1 = output , 0 = input
out DDRB, temp ; to data direction register

;***************************************************************************
; AD-Converter setup:
; If ADLAR is set, the result is left-adjusted.
ldi temp, 0b00100010 ; Ch 2 input=PB4, REFS0,1 =00 Vcc is RefVoltage
out ADMUX, temp
; ADC init single conversion prescaler 8:
ldi temp, 0b11010011 ;
out ADCSRA, temp

;***************************************************************************
; timer setup:
ldi temp,0b00000010 ; Timer/Counter 0 clocked at CK/8
out TCCR0B,temp ; 1,2 Mhz
;Bit 1 – TOIE0: Timer/Counter0 Overflow Interrupt Enable
ldi temp,0b00000010 ; set Bit 1
out TIMSK0,temp ; in the Timer Interupt Mask Register
out TIFR0,temp ; in the Timer Interupt Flag Register
ldi temp,0 ; Timer/Counter 0 clear
out TCNT0,temp ;

clr av_l
clr av_h ; Clear Average Registers
clr next_step
sei ; Enable gobal iterrupt

;***************************************************************************

clr Ledcnt

ldi ZH,high(2*Led12) ; setup Z pointer hi
testleds:
ldi ZL,low(2*Led12) ; Leds upper 4 bit DDRB lower 4 bit PORTB

add ZL, Ledcnt ; table offset
lpm
tst r0 ; 0 is end of table
breq ma1

rcall Ledset ; set PORTB and DDRB
inc Ledcnt
clr next_step
testwait: cpi next_step, 250 ; wait time
brlo testwait
rjmp testleds


ma1: clr next_step
main: cpi next_step, 8 ; reaction time
brlo main
clr next_step

rcall convert_average ; Measure Volt 0..255

mc0:
ldi ZH, high(2*Thresh) ; setup Threshold
ldi ZL, low(2*Thresh) ; setup Z pointer lo
ser Ledcnt ; to -1
mc1:
lpm
tst r0
breq mc2
inc ZL
inc Ledcnt ; 0..11
cp av_l, r0 ; av higher than threshold
brsh mc1 ; else led on

mc2:
ldi ZH,high(2*Led12) ; setup Z pointer hi to Leds
ldi ZL,low(2*Led12) ; Leds upper 4 bit DDRB lower 4 bit PORTB

add ZL, Ledcnt ; table offset
lpm
rcall Ledset ; set PORTB and DDRB
rjmp main

;***************************************************************************

Ledset:
mov LedPD, r0 ; subroutine to set Leds, input r0
andi LedPD, 0x0F ; lower 4 bit PORTB
out PORTB, LedPD ; first Port
mov LedPD, r0
swap LedPD ; swap
andi LedPD, 0x0F ; upper 4 bit DDRB
out DDRB, LedPD ; to data direction register
ret

;*************************[ ADC Handler ]*********************************
; This function measures the ADC channel, and returns the
; converted value in [av_l] 8 bit resolution
convert_average:
ldi temp,0b00100010
out ADMUX, temp ; Set ADC Channel PB4
clr av_l
clr av_h ; Clear Average Registers
ldi lcnt,avgcnt ; Set loop counter
convert_start:
ldi temp,0b11010011 ;

out ADCSRA,temp ; Start A/D Conversions
convert_wait:
sbis ADCSRA, ADIF
rjmp convert_wait ; Wait for Conversion to finish
in temp, ADCH
add av_l, temp
ldi temp, 0
adc av_h, temp ; Add measured value to average
dec lcnt
brne convert_start ; Repeat lcnt times

ldi lcnt,avgcnt/4 ; Set shift counter
convert_avg:
lsr av_h
ror av_l ; av_h:av_l = av_h:av_l/2
dec lcnt
brne convert_avg ; Repeat (avg_loop_cnt) times
;cbi ADCSRA,ADEN
ret ; return to measure

;***************************************************************************

; force table to begin at 256 byte boundary

.org 0x100


; Charlieplex Leds on PB 0 1 2 3
; in 1 byte: upper 4 bit DDRB lower 4 bit PORTB

Led12: ;led0..11 Cplex

.db 0b00110001, 0b00110010,
.db 0b10010001, 0b10011000,
.db 0b01010001, 0b01010100,
.db 0b01100010, 0b01100100,
.db 0b10100010, 0b10101000,
.db 0b11000100, 0b11001000,
.db 0,0

; linear voltage threshold for Leds
Thresh:
.db 21, 42, 63, 85, 106, 127, 148, 170, 191, 210, 233, 255, 0, 0

Led6:
.db 0b10110001, 0b10110010,
.db 0b11100010, 0b11100100,
.db 0b11010100, 0b11010001,
.db 0,0


Elektronik-Labor   Projekte  AVR