Freitag, 12. Februar 2010

LCD mit HD44780

Nachdem ich mehrere HD44780kompatibele LCDs bekommen habe war es an der Zeit sich mal eingehender damit zu beschäftigen.
Die Leude Mikrocontroller.net haben da ganze Arbeit geleistet und mich viel weitergebracht...
Wie auch immer, das erste "Projekt" auf dem LCD war -natürlich- eine Uhr. Und natürlich wieder mit meinem "Standart"-AVR ATtiny2313.



Das sieht doch schon mal nach was aus.
Uhrzeit war ein Klacks und auch das Datum war kaum herausfordernd. Was wirklich "schwer" war, war die implementierung der Bedienelemente: Taster wollen abgefragt werden und es soll erkannt werden od der Taster gedrüückt wurde, fetgehalten wurde oder losgelassen ist. Das ganze in verschiedenen "kontexten", schliesslich will man die Uhr ja auch stellen könnnen...

Und da war sie nun, ein paar zahlen auf nem LCD.
Ist aus einigem Abstand recht schwer zu lesen...

Das muss sich ändern:



Also einen neuen Zeichensatz gebastelt, und aus den maximal 8 eigenen Zeichen ein paar "richtig Große" Ziffern zusammengestellt.

Das ganze Programm füllt den 2313 mittlerweile zu 80% aus und das Listing ist irgendwie viel zu lang um es einfach hier zu posten...
( und dabei wollt ich noch einen Alarm-timer und ein Calibrierungs-byte (um sekunden/woche auszugleichen) mit einbauen...)

Also erslmal einen Ausschnitt, und zwar den für die grossen Ziffern:


lcd_big_ziffer:
; wandelt eine BCD ziffer in
; eine ziffer in großschrift
; es ist erforderlich das vorher der Zeichensatz_big geladen ist.
; die zahl wird in temp übergeben
; der offset auf dem LCD in temp2
; aufgerufene Unterprogramme
push temp
push temp2
push temp3
push ZL
push ZH

; Z-register initialisieren
ldi ZL, low(big_font*2)
ldi ZH, high(big_font*2)

;offset in der tabelle aus der ziffer berechnen (ziffer=ziffer*4)
lsl temp
lsl temp
clr temp3

add ZL, temp
adc ZH, temp3

;cursor in der ersten zeile positionieren
mov temp, temp2
rcall lcd_pos

;symbole für die erste Zeile ausgeben
lpm temp, Z+
rcall lcd_data
lpm temp, Z+
rcall lcd_data

;cursor in der 2. zeile positionieren
mov temp, temp2
ori temp, 0x40
rcall lcd_pos

;symbole für die 2. zeile ausgeben
lpm temp, Z+
rcall lcd_data
lpm temp, Z
rcall lcd_data

pop ZH
pop ZL
pop temp3
pop temp2
pop temp

ret

big_font:
; hier ist abgelegt welche symbole wie zu grossen ziffern zusammengesetzt werden
.db 0x07, 0x01, 0x06, 0x05 ;0
.db 0x20, 0x01, 0x20, 0x00 ;1
.db 0x04, 0x02, 0x06, 0x04 ;2
.db 0x02, 0x04, 0x04, 0x05 ;3
.db 0x06, 0x06, 0x03, 0x07 ;4
.db 0x00, 0x03, 0x04, 0x02 ;5
.db 0x06, 0x04, 0x06, 0x05 ;6
.db 0x03, 0x02, 0x20, 0x00 ;7
.db 0x05, 0x02, 0x06, 0x05 ;8
.db 0x07, 0x01, 0x03, 0x02 ;9


und dazu noch den Zeichensatz fürs LCD:


zeichensatz_big:
.db 0b00000011, 0b00011111 ;...xx xxxxx
.db 0b00000011, 0b00011111 ;...xx xxxxx
.db 0b00000011, 0b00000011 ;...xx ...xx
.db 0b00000011, 0b00000011 ;...xx ...xx
.db 0b00000011, 0b00000011 ;...xx ...xx
.db 0b00000011, 0b00000011 ;...xx ...xx
.db 0b00000011, 0b00000011 ;...xx ...xx
.db 0b00000011, 0b00000011 ;...xx ...xx

.db 0b00011111, 0b00011111 ;xxxxx xxxxx
.db 0b00011111, 0b00011111 ;xxxxx xxxxx
.db 0b00000011, 0b00000000 ;...xx .....
.db 0b00000011, 0b00000000 ;...xx .....
.db 0b00000011, 0b00000000 ;...xx .....
.db 0b00000011, 0b00000000 ;...xx .....
.db 0b00011111, 0b00000000 ;xxxxx .....
.db 0b00011111, 0b00000000 ;xxxxx .....

.db 0b00000000, 0b00000011 ;..... ...xx
.db 0b00000000, 0b00000011 ;..... ...xx
.db 0b00000000, 0b00000011 ;..... ...xx
.db 0b00000000, 0b00000011 ;..... ...xx
.db 0b00000000, 0b00000011 ;..... ...xx
.db 0b00000000, 0b00000011 ;..... ...xx
.db 0b00011111, 0b00011111 ;xxxxx xxxxx
.db 0b00011111, 0b00011111 ;xxxxx xxxxx

.db 0b00011000, 0b00011111 ;xx... xxxxx
.db 0b00011000, 0b00011111 ;xx... xxxxx
.db 0b00011000, 0b00011000 ;xx... xx...
.db 0b00011000, 0b00011000 ;xx... xx...
.db 0b00011000, 0b00011000 ;xx... xx...
.db 0b00011000, 0b00011000 ;xx... xx...
.db 0b00011111, 0b00011000 ;xxxxx xx...
.db 0b00011111, 0b00011000 ;xxxxx xx...

;tabellenende
.db 0xff, 0xff


Wenn ich mal eine schöne Möglichkeit gefunden habe Dateien zum download annzubieten gibt es den kompletten code.

Und ja, auch hiervon hab ich keine vorzeigbare schaltung. Aber vlt kann man sowas ja bald einfach mir "Fritzing" erstellen...

Montag, 31. August 2009

Meine erste selbsgebaute Uhr

So, nach mehreren (z.T.) erfolgreichen Vorversionen ist meine erste Uhr(enschaltung) nun fertig.
Ich, als Microkontroler-Anfänger bin über dieses Projekt gestolpert und habe wirklich viel davon gelernt. Z.B. wie man einen schönen BCD-7Segment-Decoder programmiert und wie man überhaupt ein Programm (in AVR-asm) struckturiert usw...

Also erstmal das Steckbrett:


Typisch Breadboard - mit Drahtverhau...

Man beachte die Anzeige:


Wie ich dieses klassische Rot und diese winzigen LEDs mag.
Einfach mal hier vorbeischauen.
(und JA die Siemens DL340M sind um Größenordnungen besser. aber für den Steckbrettaufbau irgendwie zu Schade)

Die Bediehnung der Uhr hab ich bei der aus "meinem" Auto abgeschaut: einfach aber nicht ausbaufähig. Mit einem Taster kann ein "reset" ausgelöst werden: dann zeigt die Uhr wieder 01:00 an. Mit einem weiteren werden die Stunden gestellt: jeder Tastendruck lässt dei Anteige eine Stunde weiterschalten. Und mit einem dritten Taster wird das gleiche mit der Minutenanzeige gemacht.

Die Schaltung werde ich nochmal schön mit Eagle aufhübschen, bis dahin muss der Code reichen.


; einfache Uhr mit 4stelliger LED Anzeige
; Anzeige ist DL340M - 7 Segement mit gem. Kathode
; by DH8SZ nach GPL

; Version 0.0.1 - Zählanzeige mit int.RC als Taktgeber und keiner Stellmöglichkeit
; Version 0.0.1a - statt Zeit werden Festwerte Angezeigt - Test
; Version 0.0.1b - Anzeige wieder normal - timer beschleunigt
; Version 0.0.1c - reti für interrupt-return geändert
; Version 0.0.2 - zählt nun bis 24:00 und der Dezimalpunkt der 2. Stundenstelle blinkt
; Version 0.0.3 - Tasten zum stellen: reset, PD2, PD3
; Version 0.0.3a - alte zählung + bugfixes in ledoff
; Version 0.0.3b - neue zählung (i.o.) + dezimalpunkt blinkt nun wirklich
; Version 0.0.4 - Sekunden 1:1 für Testaufbau
; Version 0.0.4a - LED-Multiplexfrequenz verdoppelt

; Display:
; -----
; I g I
; e I I f
; I I
; -----
; I d I
; b I I c
; I I
; -----
; a

; Ports (Display DL340M)
; PB0 - Pin4 - c
; PB1 - Pin9 - f
; PB2 - Pin3 - a
; PB3 - Pin6 - d
; PB4 - Pin13 - g
; PB5 - Pin2 - b
; PB6 - Pin11 - e
; PB7 - Pin5 - 5
;
; PD0 - Pin14 - kat.System1
; PD1 - Pin12 - kat.system2
; PD4 - Pin8 - kat.System3
; PD5 - Pin9 - kat.System4

.include "tn2313def.inc"

.def w1=R14
.def W2=R15
.def temp=R16
.def temp2=R17
.def std10=R18
.def std01=R19
.def min10=R20
.def min01=R21
.def sek10=R22
.def sek01=R23

;===========================================================================

.dseg
;alles leer

;===========================================================================

.cseg
.org 0x0000
rjmp reset ; Der Start des Programmes
.org 0x0001
rjmp stunden_stellen ;Interruptroutine zum Stunden erhöhen
.org 0x0002
rjmp minuten_stellen ;Interruptroutine zum Minuten erhöhen
.org 0x0004
rjmp takt ; Interruptroutine zum Sekunden zählen


charset:
; hier kommt die Tabelle für die BCD/7segment Umsetzung hin
.include "charset.asm"

;===========================================================================
; Initianisierung
;===========================================================================
reset:
;initialisieren des Stackpointers
ldi temp, RAMEND
out SPL, temp

;Analogkomparatoren abschalten
sbi ACSR,7

;Voreinstellungen für Uhrzeit
ldi std10, 0x00
ldi std01, 0x01
ldi min10, 0x00
ldi min01, 0x00

;Ausgabeports initianisieren PB7:0 = Ausgabe und 0
ldi temp, 0xff
out DDRB, temp
ldi temp, 0x00
out PORTB, temp

;Ports PD0, PB1, PB4, PB5 = Ausgabe und 1 / PB2, PB3 als Eingänge und pullup an
ldi temp, 0b00110011
out DDRD, temp
ldi temp, 0b00111111
out PORTD, temp

;INT0 und INT1 initialisieren
ldi temp, 0b00001010 ;fallende Flanke an INT0 und INT1 ist Auslöser
out mcucr, temp
ldi temp, 0b11000000 ;INT0 und INT1 freischalten
out gimsk, temp

;Timer/Counter1 (16Bit) initianisieren
;TCCR1B = 0 0 0 0 1 1 0 1 = no InputNoiseCanceler, InputCaptureEdgeSelect, Mode=CTC, Prescaler=1024
ldi temp, 0x0d
;ldi temp, 0x0c ;prescaler auf /256 statt /1024
out TCCR1B, temp
;OCR1A - Top für Counter setzen = 3200
ldi temp, 0x0c
ldi temp2, 0x80
out OCR1AH, temp
out OCR1AL, temp2
;TIMSK --> enabele OCIE1A output Compare Interrupt
ldi temp, 0x40
out TIMSK, temp
;TCNT1 auf 0 reseten
clr temp
out TCNT1H, temp
out TCNT1L, temp

;Interrupt erlauben
sei

;===========================================================================
; Hauptprogramm
;===========================================================================

main:
;Stunden darstellen - 10er Stelle
mov temp, std10
rcall convert
out PORTB, temp2
cbi PORTD, 5
rcall wait2ms
rcall ledoff

;Stunden darstellen - 1er Stelle - Dezimalpunkt blinkt im sekundentakt
mov temp, std01
rcall convert
sbrc sek01, 0
ori temp2, 0b10000000
out PORTB, temp2
cbi PORTD, 4
rcall wait2ms
rcall ledoff

;Minuten darstellen - 10er Stelle
mov temp, min10
rcall convert
out PORTB, temp2
cbi PORTD, 1
rcall wait2ms
rcall ledoff

;Minuten darstellen - 1er Stelle
mov temp, min01
rcall convert
out PORTB, temp2
cbi PORTD, 0
rcall wait2ms
rcall ledoff

;zurück zum Mainloop
rjmp main


;======================================================================
; Unterprogramme
;======================================================================

takt:
;dieses Unterprogramm wird ein mal pro Sekunde angesprungen
cli
inc sek01
cpi sek01, 0x0a
brlo takt_ende
clr sek01
inc sek10
cpi sek10, 0x06
brlo takt_ende
clr sek10
inc min01
cpi min01, 0x0a
brlo takt_ende
clr min01
inc min10
cpi min10, 0x06
brlo takt_ende
clr min10
inc std01
cpi std01, 0x04
brlo takt_ende
cpi std10, 0x02
brlo takt_01
clr std01
clr std10
rjmp takt_ende
takt_01:
cpi std01, 0x0a
brlo takt_ende
clr std01
inc std10
cpi std10, 0x0a
brlo takt_ende
clr std10
takt_ende:
sei
reti

;--------------------------------------------------------------------------

stunden_stellen: ;dieser Teil ist auch zum Stundn hochzählen
inc std01
cpi std01, 0x04
brlo stunden_ende
cpi std10, 0x02
brlo stunden_01
clr std01
clr std10
rjmp stunden_ende
stunden_01:
cpi std01, 0x0a
brlo stunden_ende
clr std01
inc std10
cpi std10, 0x0a
brlo stunden_ende
clr std10
stunden_ende:
reti


;---------------------------------------------------------------------------

minuten_stellen: ;minuten bei tastendruck hochzählen
inc min01
cpi min01, 0x0a
brlo minuten_ende
clr min01
inc min10
cpi min10, 0x06
brlo minuten_ende
clr min10
minuten_ende:
reti


;---------------------------------------------------------------------------

convert:
;convertiert BCD (temp) zu 7segment (temp2)
ldi ZL, LOW(charset<<1)
ldi ZH, HIGH(charset<<1)
add ZL, temp
lpm temp2, Z+
ret

;---------------------------------------------------------------------------

ledoff:
;Anzeige ausschalten
ldi temp, 0x00
out PORTB, temp
ldi temp, 0b00111111
out PORTD, temp
ret

;---------------------------------------------------------------------------

wait2ms:
;verzögrung für 1 ms at 3,2768MHz
ldi temp, 0x0d
mov w1, temp
clr w2
wait2ms_loop:
dec w2
brne wait2ms_loop
dec w1
brne wait2ms_loop
ret


Man, da hab ich mir ja ne Naht zusammengetippt: da wird tatsächlich jede Stelle der Anzeige in einem eigenen Register hochgezählt. Und bei einem Überlauf natürlich kompliziert verglichen und gesprungen...
mal sehn ob das noch einfacher zu lösen ist...

Nun hab ich keinen Pin mehr frei. Das Bedienkonzept ist auch eine Sackgasse und die Genauigkeit ist noch weiter zu prüfen.


Für meine nächse Uhr werde ich nen ATMega48 nehmen, der hat ein paar Beinchen mehr und es lässt sich dann auch noch eine Weckfunktion mit einbauen.

p.s. natürlich hab ich auch noch eine Zeichentabelle für den BCD-7Segment-Umsetzer:

; Carsetfile für BCD - zu - 7segment Umsetung
.db 0b01110111, 0b00000011 ;0 ,1
.db 0b00111110, 0b00011111 ;2, 3
.db 0b01001011, 0b01011101 ;4, 5
.db 0b01111101, 0b01010011 ;6, 7
.db 0b01111111, 0b01011111 ;8, 9
.db 0b01111110, 0b01101101 ;A, b
.db 0b00101100, 0b00101111 ;c, d
.db 0b01111100, 0b01111000 ;E, F

Freitag, 21. August 2009

80m Fuchsjagdsender nach DH1DA

Beim stöbern nach interessanten Projekten bin ich auf den Fuchsjagesender von DH1DA gestoßen.

( Für alle die eine Fuchsjagt für einen blutigen Sport halten. )

Hier ein Bild von meinem Aufbau (ohne Gehäuse):



Die Schaltung hab ich minimal abgewandelt: R1 und C2 weggelassen. Der Attiny2313 hat schon einen gute Power-On-Reset eingebaut so das ich eine externe R/C-Kombo für überflüssig halte.
Damit konnte das Layout der Platine vereinfacht werden, so das die (in meinen Augen) unsägliche Leiterbahnenkreuzung der Originalplatine wegfallen kann.

(ok, ich bin faul, desshalb hier erstmal nur ein Q&D Bild der Leiterseite):



Ein paar Freilufttests in leicht bebautem Gelände (also bei mir zu Hause) haben eine Reichweite von 200m ergeben.
Der (frische) 9V-Block hat über einen Tag ohne zu schwächeln durchgehalten und tut nun wo anders seinen Dienst.

Dieses Projet werde ich bei Gelegenheit mal "modernisieren": Platine erstellen, SMD, usw. ...

Montag, 3. August 2009

Leuchtturm oder Froschaugen

Alles fing mit den Glasaugen eines Garten-Blechfrosches an: Diese sollte gelegentlich blinken/blitzen.
Der Blechfrosch ist inzwischen weggerostet, aber die Schaltung (und die FW) für die Augen sind fertig...
...naja, jetzt wo ich keine 2 Augen mehr brauche hab ich nur noch eine LED genommen.

Hier erstmal die Schaltung:


Was macht das ganze ?
Wenn die Umgebungshelligkeit ien bestimmtes Level unterschreitet fängt die LED an alle 2 sec zu blitzen.

Sehr einfach und vlt auch effektiv: Der AVR braucht 4µa im Standby und die LED wird auch mit als Lichtsensor benutzt.

hier nun die FW:
; Version 0.0.5a by DH8SZ
; * stomsparversion
; * Helligkeitssteuerung über LED
; zum blinken von 1 LED - zwischen PB0(Anode) & PB1(Katode)
; Helligkeitserkennug über die LED zum ausschalten bei Tag


.include "tn13def.inc"

.def temp = R16

.def w1 = R19
.def w2 = R20


;===============================================================================
; alles leer ...

.dseg


.eseg

; noch ...
;===============================================================================

.cseg
.org 0x000
rjmp start


;Waitloop - angepasst für 128kHz
wait:
;wait for 12 ms
ldi w1, 2
clr w2
wait_loop:
dec w2
brne wait_loop
dec w1
brne wait_loop
ret



start:

; Watchdogtimer setzen: systemreset alle 2 sek.
ldi temp, 0x0f
out WDTCR, temp


; testen hell/dunkel
; DDRB (Datenrichrungsregister) alle auf Eingänge setzen, bis auf pb0
ldi temp, 0b00000001
out ddrb, temp
; LED über int. Pullup "laden" --> pb1
ldi temp, 0b00000010
out portb, temp
rcall wait
; Pullup wieder abschalten und 12 ms warten
clr temp
out portb, temp
rcall wait
; testen ob LED noch "geladen" -->ja=dunkel / nein=hell
sbis pinb,1
rjmp ende


; DDRB (Datenrichtungsregister) auf pb0,pb1 Ausgänge setzen
ldi temp, 0b00000011
out ddrb, temp
; PB0 und PB1 kurz anschalten (pb0=1 und PB1=0)
ldi temp, 0b00000001
out portb, temp
rcall wait
; PB0 und PB1 wieder aus
clr temp
out portb, temp

ende:

; sleepmode klarmachen
ldi temp, 0x30
out mcucr,temp
wdr ; Watchdog zurücksetzen (damit der timer auch 2 sek. braucht...)
sleep ; enter sleepmode


(ich hoffe das kann man auch lesen...)
Mir ist auch nach mehrmaligem überfliegen keine wirkliche Optimierung mehr eingefallen.
Sicher, das eine oder andere ginge bestimt. Aber wozu ? Die meiste Zeit ist der MC im Standby und wartet auf den Watchdog...

Um eine 2. LED anzuschliessen müsste man eine separaten Port einrichten. LED1 wird halt "besonders" angesteuert.

Für das ganze, eigentlich um einen DAuertest mit der Batterie zu fahren, hab ich auf die Schnelle einen PETling als Gehäuse genommen, und ein bisschen verziert.



Das Ganze nochmal in Einzelteilen:


Dinge die man noch verändern könnte: ALLES! XD
Wie gesagt, dieser Aufbau ist in erster Linie zum testen der Batterielebendsdauer. Ich schätze 0,5 bis 1 Jahr, aber genau werde ich das erst in etwa einem Jahr wissen...
Die Hell/Dunkel Erkennung könnte optimiert werden - noch geht das ganze schon bei recht hoher Umgebungshelligkeit an.
Statt eines kurzen Aufblitzens könnte ein Morsezeichen ausgegeben werden - das ginge aber zu Lasten der Batterielebendsdauer.
Die ausgegebene Blinksequenz könnte programmierbar sein - ähnlich wie in Alex's Programmable LED
(aber auch das wieder zu lasten der Haltbarkeit...)

Und wozu ist das ganze gut ?:
Ja eigentlich...
Ursprünglich sollte das die Augensteuerung für den Blechfrosch werden, lässt sich aber bestimmt auch für andere GArtenDecoDInge verwenden. Ist bestimmt auch für Geocaching nützlich, oder für Nachtwanderungen oder für was man auch immer einen LED-Flasher mit Helligkeitssteuerung brauchen kann.
(vlt Alarmanlagendummie oder Klingelknopfbeleuchtung oder ...)

Dienstag, 31. März 2009

Blog erstellt !

So, nachdem ich nach meinen Projekten gefragt wurde, fange ich mal hier an sie online zu stellen. -naja erstmal werde ich mich mit dem System anfreunden, mit dem Blog-System...