Hallo zusammen,
ich hoffe, es erbarmen sich ein paar von euch, um mir zu helfen.
Was ist mein Problem?
Ich habe einen PIC16F µC und möchte 8 LEDs separat ansteuern und damit
das kurze Aufleuchten nicht alzu ruppig erscheint, sollte es
ausglimmen/"faden".
Beim PIC werden 8 Ports als Input und entsprechend 8 als Output
verwendet. Die pull-up Widerstände sind ausgeschalten.
Versorgungsspannung des PIC ist 5V und die LEDs sind standard 5mm und
benötigen 20mA und 2,1V.
Googln hat mir zwar schon ne Menge Ideen gebracht, aber nicht wirklich
eine Lösung passend für mein Problem.
Was habe ich bisher probiert?
ELKOs in verschiedensten Dimensionen von 100µF bis 3300µF. Leider will
es mir nicht einleuchten, warum bei Reihenschaltung von LED+Widerstand
und dem ELKO direkt am Ausgang des PIC die LED permanent leuchtet. Es
scheint fast so, als würde der ELKO den Ausgang ständig offen halten.
Also danach mit einer Diode probiert. Die Diode an einen Ausgangspin und
daran ELKO und LED+Widerstand parallel in Richtung GND geschalten.
Klappt soweit, aber bsp. beim 1000er ELKO glimmt die LED gefühlt ne
Ewigkeit nach bis sie tatsächlich "erloschen" ist.
Also brauch ich ein Bauteil, was erst ab einer gewissen Stromspannung
aktiviert wird und sonst sperrt, also der ELKO nicht ewig die LED mit
minimalem Strom versorgen soll. Da dachte ich an einen Transistor. Da
scheitere ich allerdings etwas an einer vernünftigen Schaltung. Gibt es
vielleicht auch eine andere Diode, die mir bei dem Problem weiterhelfen
kann? Das Problem ist ja die doch sehr geringe Spannung die als
Aktivierung nur benötigt werden soll. Vielleicht 0,3 V o.ä..
Mit den Transistoren wären mir ehrlich gesagt auch zu viele Bauteile für
die 8 LEDs zu verbauen.
Gibt es eine Möglichkeit mit nur einem ELKO alle 8 LEDs separat zu
versorgen, um den gewünschten Nachleuchteffekt zu erhalten?
Ist die Idee mit der Diode grundsätzlich ok, oder hab ich da eventuell
Probleme mit unerwünschten Strömen?
Beim PIC gibt es fürs Dimmen ja auch das PWM. Analog zur Frage oben,
gibt es eine Schlatung, bei der ich mit nur einem PWMsignal alle 8 LEDs
separat ansteuern kann? D.h. wenn ich Inputpin1 aktiviere und Outpupin1
auf High schaltet, dass dann nur die LED an Outputpin1 nachglimmt?
Wäre für Hilfe wirklich dankbar, da ich langsam etwas verzweifele.
Besten Dank im Voraus für eure Mühen und konstruktiven Hinweise!
Moin,
soll dir der Elko die Spannung solange halten, dass die LED verzögert
bzw langsam ausgeht?
Wie sagst du denn, dass die LED ausgehen soll? Ich hoffe du schaltest
den Pin des PICs nicht auf GND? Du müsstest wenn zwischen HIGH und
Tristate umschalten.
Willst du immer nur eine LED an schalten, oder mehrere völlig unabhängig
voneinander? Ich empfehle eine Software-PWM mit einem Timer zu bauen.
Ist auch hier als Artikel vorhanden. Es reicht ja relativ langsam,
sodass du nix flackern siehst.
multimeter90 schrieb:> Moin,>> soll dir der Elko die Spannung solange halten, dass die LED verzögert> bzw langsam ausgeht?
Genau das ist das Ziel.
> Wie sagst du denn, dass die LED ausgehen soll? Ich hoffe du schaltest> den Pin des PICs nicht auf GND? Du müsstest wenn zwischen HIGH und> Tristate umschalten.
Also momentan sind an den Outputpins je ein Widerstand und eine LED
gegen GND geschalten. Wird Inputpin von High auf Low gesetzt, dann wird
das Outputpin auf High gesetzt und die Led leuchtet kurz auf und der
Outputpin wird wieder auf Low gesetzt.
> Willst du immer nur eine LED an schalten, oder mehrere völlig unabhängig> voneinander? Ich empfehle eine Software-PWM mit einem Timer zu bauen.> Ist auch hier als Artikel vorhanden. Es reicht ja relativ langsam,> sodass du nix flackern siehst.
Wenn du so fragst, dann wohl eher mehrere völlig unabhänig voneinander.
Da ist auch mein Problem, dass ich das mit einem PWMsignal und nem PIC
nicht wirklich gebastelt bekomm, oder anders- nicht weiß wie da eine
Schaltung aussehen könnte.
@Teo Derix:
Den Beitrag hatte ich auch entdeckt, aber brachte mich nicht wirklich
weiter, da ich wie oben beschrieben, leider nicht weiß, wie ich mit
einem PWM Signal meherere LEDs unabhängig ansteuern kann.
Dennoch besten Dank für den Link.
nighti schrieb:> Beim PIC gibt es fürs Dimmen ja auch das PWM. Analog zur Frage oben,> gibt es eine Schlatung, bei der ich mit nur einem PWMsignal alle 8 LEDs> separat ansteuern kann? D.h. wenn ich Inputpin1 aktiviere und Outpupin1> auf High schaltet, dass dann nur die LED an Outputpin1 nachglimmt?
Du bräuchtest 8 PWM Kanäle einzeln steuerbar und auch noch in dem
Bereich.
Das wird schwierig.
Soft PWM ist das Mittel der Wahl, Beispiele gibt es im Netz zu hauf.
Ähm. Ich hatte doch geschrieben, dass ich nur einen pwm-port habe. D.h.
dein Vorschlag klappt leider nicht.
Könnte mir jemand bzgl. Kondensatorschaltung helfen? (Siehe erster Post)
Markus schrieb:> nighti schrieb:>> Ich hatte doch geschrieben, dass ich nur einen pwm-port habe.>> Darum eben mit Soft-PWM. Da brauchst du keinen PWM-Port, dafür etwas> mehr an SW.> http://www.mikrocontroller.net/articles/AVR-Tutori...>> http://www.mikrocontroller.net/articles/Soft-PWM>> Das mit Kondensator zu lösen, wenn schon ein Controller da ist, ist doch> murks.
Ach herje. Jetzt begreif ich so langsam in welche Richtung es gehen soll
;-)
Danke für die nochmalige Verdeutlichung des SoftPWM. Das scheint auch
die beste Lösung des Problems zu sein. Nun bin ich allerdings Neuling
was die Programmierung von PICs betrifft und bin schon froh ein
Lauflicht programmiert zu haben. Ohne Vorkenntnisse habe ich da mit
Assembler begonnen.
Habt ihr da mal einen Link für mich wo jemand SoftPWM mit Assembler
programmiert hat? Optimalerweise für LEDs?
Habe bereits den Link entdeckt
Beitrag "LED Fading mir PIC"
Allerdings ist der Kommentar "Du musst nur die
Register belegen und ab geht die Post, in C geht das sogar noch
einfacher." für mich etwas unklar. Könnte mir jemand einen Ansatz
bringen, was ich tun muss, damit z.B. PORTB1 mit PWM ausfaded?
Entsprechend würd ich das ja dann für die andren Pins anpassen können.
Wäre demjenigen (derjeinigen? ;-) )hier echt dankbar :)
Na ja. So schwer ist das auch wieder nicht.
Kannst du bereits mit einem Timer und einer zugehörigen Interrupt
Service Routine (ISR) umgehen?
Das wäre dein erster Schritt.
Lass dich nicht irre machen von denen, die dir einreden, das ginge auch
ohne.
Ja es geht auch ohne. In Demonstrationsprogrammen. Aber wenn es etwas
vernünftiges sein soll, kommst du um Timer + ISR nicht herum.
Die ISR ist dann für dich einfach nur ein Stück Code, welches
(timergesteuert) regelmässig alle x µ-Sekunden ausgeführt wird. Das x
wird durch die Timereinstellungen gesteuert.
Eine Soft-PWM ist dann im Grunde ganz einfach. Zu jeder LED gehört ein
Zahlenwert, der eine Aussage darüber macht, wie hell sie leuchten soll.
Ich nehm jetzt der Einfachheit halber an, das wären Zahlen von 0 bis 3.
D.h. wenn die LED1 mit Helligkeit 2 und die LED2 mit Helligkeit 1
leuchten soll, dann bedeutet das einfach nur, dass LED1 von 4 Perioden 2
eingeschaltet und 2 ausgeschaltet sein soll. LED2 hingegen ist von den 4
Perioden nur 1 eingeschaltet und 3 ausgeschaltet
1
Zeitschritt LED1 (2) LED2 (1)
2
-------------------------------------
3
0 ein ein
4
1 ein aus
5
2 aus aus
6
3 aus aus
Das ist aber nicht weiter schwer.
Ein derartiger Zeitschritt, das ist nichts anders als der Aufruf deiner
ISR. Jeder Aufruf der ISR ist ein Zeitschritt.
Du brauchst dann noch einen unabhängigen Zähler, der die Schrittnummer
zählt.
Denn jetzt kommts. Wie kannst du leicht entscheiden, ob die LED ein oder
aus sein muss, in einem bestimmten Zeitschritt.
Schau dir die Tabelle an. Jede LED ist genau dann eingeschaltet, wenn
ihr vorgegebener Helligkeitswert kleiner als die SChrittnummer des
Zeitschritts ist.
Im SChritt 0 gilt:
LED1 soll einen Helligkeitswert von 2 haben. 0 ist kleiner als 2, daher
ist die LED in diesem Schritt eingeschaltet.
LED2 soll einen Helligkeitswert von 1 haben. 0 ist kleiner als 1, daher
ist die LED in diesem Schritt eingeschaltet.
Im Schritt 1 gilt:
LED1 soll einen Helligkeitswert von 2 haben. 1 ist kleiner als 2, daher
ist diese LED in diesem Schritt eingeschaltet.
LED2 soll einen Helligkeitswert von 1 haben. 1 ist aber nicht kleiner
als 1, daher ist die LED in diesem Zeitschritt ausgeschaltet.
Und so werden die LED eine nach der anderen in jedem Zeitschritt (bei
jedem ISR Aufruf) gegen die Schrittnummer geprüft und je nach Ergebnis
ein oder ausgeschaltet.
In Summe kommt dann eben raus, dass die LED über die Schritte gemittelt
mal mehr bzw. wenig oft eingeschaltet ist. Demenstprechend leuchtet sie
für uns Menschen heller oder weniger hell (tatsächlich blinken sie nur
unterschiedlich. Aber das merken wir nicht)
Das ist das Grundprinzip einer PWM. Das ist jetzt eine sehr einfache
Variante von PWM und es gibt algorithmisch bessere Versionen. Aber fürs
erste wirds auch das tun.
Aber: Alles beginnt mit einem Timer und seiner ISR, die du brauchst um
das Konzept der Zeitschritte zu implementieren.
Kein Mensch wird dir hier
ein fertiges Programm schreiben.
Da wirste dich schon selber dran machen müßen.
Beispiele gibt es genug im Netz, sogar hier im
Forum. Und wie es geht, wurde ja hier auch
schon beschrieben. Nun fang an zu Programmieren
und wenn du nicht weiter weist, dann zeig was
du bis jetzt gemacht hast, dann wird dir
auch weiter geholfen.
Karl Heinz schrieb:> Na ja. So schwer ist das auch wieder nicht.>
Hab ganz lieben Dank für die ausführliche Erklärung! Sehr verständlich-
auch für Anfänger ;)
Mein Problem ist allerdings, dass ich mit den Timerinterrupts so meine
Probleme bekommen werde.
Momentan ist die Struktur meines Programms wie folgt:
1
Variablendeklarationen
2
start
3
Anfangssetup
4
loop
5
;***********************************
6
;*** A4 wird gedrückt (Pinwatch0)
7
8
btfsc PINWatch,0 ;bleibt A4 gedrückt, so sollen die LEDs nicht nochmal ausgelöst werden
9
goto $+5
10
btfss PORTA,4
11
call Wait20 ;wenn Taste gedrückt warte 20ms um Prellen zu verhindern
12
btfss PORTA,4 ;wenn A4=0, dann löse entsprechende LED-Laufreihe aus
13
call A4LED
14
15
analog die anderen Pins von PortA
16
17
Pinwatch wird gecheckt, ob z.B. A4 gedrückt bleibt und somit in der nächsten Schleife nicht ausgelöst wird.
18
19
goto loop
20
21
LEDEffekte
22
A4LED
23
A4LED
24
bsf PORTB, 2
25
call Wait100
26
27
bsf PORTB, 3
28
bsf PORTB, 5
29
bsf PORTB, 1
30
call Wait100
31
32
bcf PORTB, 3
33
bcf PORTB, 5
34
bcf PORTB, 1
35
bsf PORTB, 4
36
bsf PORTB, 6
37
bsf PORTB, 0
38
call Wait100
39
40
bcf PORTB, 4
41
bcf PORTB, 6
42
bcf PORTB, 0
43
bsf PORTB, 7
44
call Wait100
45
46
bcf PORTB, 7
47
48
bsf PINWatch,0 ;wird Bit gesetzt, so wird die LED nicht nochmals ausgelöst durch gedrücktlassen von A4
49
50
bcf PORTB, 2
51
52
return
53
end.
--------------------------------
Da ist zwar noch ein wenig mehr an Basteleien drin enthalten, aber die
tun erstmal nix zur Sache.
Wenn ich nun mit nem Timer permanent Interrupts auslöse, dann wird im
blödesten Falle ja immer das LEDAnsteuern unterbunden.
Wie ihr vielleicht auch seht, möchte ich nicht nur eine LED mit einem
PORTApin auslösen, sondern eine Abfolge von LEDs. Die sollten dann aber
eben nachglimmen.
Eine Idee von mir wäre nun die (main) loop als ISR zu setzen und am Ende
irgendwie den Vorschlag von Karl Heinz aufzugreifen und die Leds via
Counter zu dimmen.
Allerdings müsste das Dimmen sich ja auf die LEDEffekte beziehen und die
zeitlich in ihrer Abfolge auch noch irgendwie berücksichtigen. Hat da
jeamdn einen Gedankenstoß für mich?
nighti schrieb:> Wenn ich nun mit nem Timer permanent Interrupts auslöse, dann wird im> blödesten Falle ja immer das LEDAnsteuern unterbunden.
Ähm.
Im Timerinterrupt werden die LED angesteuert. Und NUR im Timerinterrupt.
Dein Hauptprogramm steuert die LEDs insofern an, dass es die gewünschten
Helligkeitswerte setzt. Nicht mehr und nicht weniger. Umgesetzt, im
Sinne von 'durch die Art der Led Ansteuerung realisiert' werden die
Helligekeitswerte dann im Timer Interrupt.
Und ja. In deinem Programm bleibt kein Stein auf dem anderen. Wenn du
mit der Aufgabenstellung dann fertig bist, sieht es komplett anders aus
als jetzt.
Dann frag ich mal anders:
Würde jemand für eine Aufwandsentschädigung mir nach obigen Vorgaben ein
Programm schreiben?
Wenn ja, dann bitte hier posten, dann meld ich mich im Forum an und
schreib eine pn.
Würd mich freuen.
Karl Heinz schrieb:> Und ja. In deinem Programm bleibt kein Stein auf dem anderen.
Kompromißvorschlag: Das Hauptprogramm macht weiterhin seine LED-Effekte,
aber anstatt ins Portregister schreibt es an eine bestimmte
Speicherstelle. Und der Interrupt sieht an dieser Speicherstelle nach,
wie er seine PWM-Werte ändern soll. Wenn da steht LED an, dann setzt er
den entsprechenden PWM-Wert auf Maximum, wenn da steht LED aus, dann
zieht er 1 vom PWM-Wert ab, falls er noch nicht 0 ist. Dadurch erledigt
der Interrupt das Ausglimmen, und das Hauptprogramm bleibt im
wesentlichen unverändert.
pic0 hab echt vielen lieben Dank! Zumindest ist mir nun klar, wie das
ganze laufen kann.
Mit Interrupts hab ich bisher leider noch keine Erfahrungen gemacht,
aber sprut und co helfen mir da grad auf die Sprünge.
pic0 schrieb:> #define endPWM> startPWM macro> local done> decfsz pwm> goto done> decfsz pwm ;> clrf portb> done> endm
Hier verstehe ich nur nicht ganz den Block. Bezieht sich endPWM durch
das "define" nun auf den ganzen Macroblock?
Besten Dank mal wieder im voraus :)
> das "define" nun auf den ganzen Macroblock?
Nein, es ist nur eine Konvention von mir, dass es auch ein endPWM gibt.
z.B. folgender Code, auch pwm.
#define TICK flags,3
startPWM macro
bcf TICK
movlw 0x04 ; 64 pwm steps
addwf led_pwm
skpnc
bsf TICK
endm
doPWM macro bit
comf led_cnt+bit,w
addwf led_pwm,w
rlf led_tmp
endm
endPWM macro
movfw led_tmp
movwf portb
endm
hier braucht es ein endPWM. Da es im obigen Falle kein endPWM braucht,
wird einfach ein leeres define verwendet.
Define ist vom c preprozessor, und wird vor dem Macro angewendet.
Define kann nur eine Zeile gehen, ein macro kann mehrere Zeilen haben.
Ich kann z.B folgendes machen
#define noop goto $+1
dasselbe ist
noop macro
goto $+1
endm
auch kann man machen:
noop macro
clrwdt
nop
endm
dasselbe geht aber nicht mehr mit einem define.
Auch kann man folgendes mit einem Macro nicht nachbilden:
#define TICK flags,3
und z.B der Befehl bz ist ein macro wie folgt welches der assembler
kennt:
bz macro lbl
btfsc 3,2
goto lbl
endm
Besten Dank für die Erklärung.
Ich trau mich ja schon fast gar nicht mehr zu fragen und ich fühl mich,
je länger ich am Rechner sitze, immer mehr so nach dem Motto: "Gehe
zurück zur Badstrasse" weil mehr unklar wird als klar..
Verzeih mir die doofen Nachfragen aber folgende Probleme hab ich:
Wozu ein endPWM "leer" definieren, wenns doch gar nicht gebraucht wird?
Dann kann ich den Zugriff auf endPWM doch einfach als Befehl löschen,
oder hat es einen tieferen Sinn im Interrupt zu haben?
pic0 schrieb:> startPWM macro> local done> decfsz pwm> goto done> decfsz pwm ;> clrf portb> done> endm
Bei mir meckert der Assembler, dass "Using default destination of 1
(file)." in den Zeilen wo pwm geprüft wird, also hier Zeile 3 und 5.
Eine Ahnung woran das liegt? Ich habe das Macro in keine andere Datei
ausgelagert- liegt es daran?
pic0 schrieb:> doPWM macro bit> movlw 0xff> btfsc led,bit> decfsz leds+bit,w> movwf leds+bit> xorwf pwm,w> skpnz> bsf portb,bit> endm
Die Var "leds" soll a 8 byte groß sein, um vermutlich die PWM für jede
LED festzulegen, aber was bewirkt die Addition "leds+bit"? Ist mir
leider schleierhaft. Oder beziht sich die "PWMstate" leds auf die 8
Helligkeitsstufen der LEDs? Dummerweise ist mir auch da schleierhaft,
was die Addition bewirkt..
Ich hoffe, es nervt nicht zu sehr zu viele Anfängerfragen zu
beantworten.
Besten Dank!
nighti schrieb:> Besten Dank für die Erklärung.>> Ich trau mich ja schon fast gar nicht mehr zu fragen und ich fühl mich,
kein Problem
> Wozu ein endPWM "leer" definieren, wenns doch gar nicht gebraucht wird?
einfach als Konvention, weil ich es meistens dann doch noch brauch, z.B.
fuer ein Multiplexing, oder sonstwas.
> Dann kann ich den Zugriff auf endPWM doch einfach als Befehl löschen,> oder hat es einen tieferen Sinn im Interrupt zu haben?
klar hatte man dies auch loeschen konnen.
Der Grund ist vielleicht, dass die Macros bei mir vor dem Programm und
Variablen sind und deshalb die Definition und Implementierung getrennt
sind, ist aber keine wirkliche Erklaerung. Ist halt eine Konvention von
mir, bzw mache ich es so. Andere machen es anders.
>>> decfsz pwm>> goto done>> decfsz pwm ;>> Bei mir meckert der Assembler, dass "Using default destination of 1> (file)." in den Zeilen wo pwm geprüft wird, also hier Zeile 3 und 5.> Eine Ahnung woran das liegt? Ich habe das Macro in keine andere Datei> ausgelagert- liegt es daran?
Nein, es haengt von meiner schlechten Programmierung ab.
man muesste es so schreiben, dann meckert der Assembler nicht mehr
>> decfsz pwm,f>> goto done>> decfsz pwm,f ;
Am Anfang ist es gut, diese Warnings eingeschaltet zu haben.
Message[302] C:\MTC_3_T-336.ASM 115 : Register in operand not in bank 0.
Ensure that bank bits are correct.
Message[305] C:\MTC_3_T-336.ASM 151 : Using default destination of 1
(file).
Wenn man dann ASM beherrscht, dann schaltet man diese gerne aus mittels
errorlevel -302,-305
um jetzt, z.B. diese zwei Warnings auszuschalten.
Auch ein
radix dec
wird gerne verwendet. Man kann aber auch immer ,f schreiben, und auch
D'10' fuer dezimal 10 usw. Ich bevorzuge den Radix auf dezimal zu
stellen
und 0xff sowie 0b111 fuer hex sowie binary zu verwenden. Auf octal
muss man halt noch aufpassen, sprich keine Zahl mit 0 anzufangen.
> Die Var "leds" soll a 8 byte groß sein, um vermutlich die PWM für jede> LED festzulegen, aber was bewirkt die Addition "leds+bit"? Ist mir> leider schleierhaft.
Angenommen du hast eine 16bit Variable foo und dein Konvention ist,
zuerst low byte und dann high byte, sprich little endian und du willst
die Zahl 12345 laden, dann ist der Code folgender:
movlw high(12345)
movwf foo+1
movlw low(12345)
movwf foo
foo ist eine Variable mit adresse 0x20 als Beispiel
man kann eine Variable wie folgt definieren
; =
foo = 0x20
bar = 0x22
; equ
foo equ 0x20
bar equ 0x22
; set
foo set 0x20
bar set 0x22
; define
#define foo 0x20
; res
foo res 2
bar res 2
; cblock
foo :2
bar :2
Egal, welche Methode man benutzt, foo hat die Adresse 0x20 und folgender
code ist identisch:
movwf foo
movwf .32 ; .32 = D'32' , eine kurzschreibweise
und natuerlich foo + 1 ist dasselbe wie 32+1 = 33 , also Adresse 33,
das
high byte von foo, oder wenn foo ein Array von bytes ist, index 1.
Poste mal deinen Code, dann schaue ich darueber und korrigiere ich den.
Zunächst einmal sry für das sinnfreie doppelte Nachfragen bzgl endPWM.
Da ich aber noch nicht angemeldet war, konnte ich den Post nicht mehr
ändern.. Hattest ja schon vorher geschrieben, dass es eine reine
Konvention von dir ist. Da hatten sich eigentlich schon alle Fragen
erübrigt ;-)
Aber nun zurück..
Also ich verzweifel gerade an dem Setzen der Bank1. Keine Ahnung warum
das gerade nichtmehr klappt. Dachte, es liegt an der
Speicheraddressierung, aber die Variablen liegen ja nun deutlich weit
hinten..
bcf OPTION_REG, 7 ;keine pull up Widerstände an PortB
9
movlw B'00000000' ;Output: alle PortB
10
movwf TRISB
11
movlw B'11111111' ;Input: alle PortA
12
movwf TRISA
13
bcf STATUS, RP0 ;wieder Bank 0
Optionreg, trisb und trisa "Register in operand not in bank 0. Ensure
that bank bits are correct.".
Ich weiß gerade beim besten Willen nicht, warum der Assembler nicht das
Stausbit setzt und auf Bank 1 umschaltet. Laut Datenblatt muss RP0
gesetzt und RP1 gelöscht sein?
Der Compiler Meckert nur, dies heisst nicht, dass es nicht passt.
aber ich glaube, du hast ganz andere Fehler, wieso es nicht passt.
list p=16F628A
__CONFIG _INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _BODEN_OFF &
_MCLRE_OFF & _CP_OFF & _LVP_OFF
#include <P16f628A.INC>
_WDT_OFF usw wird erst in der Include definierte, schon deshalb duerft
dein
Programm nicht compilierbar sein und der assembler muesste ein unknown
symbol oder sonstwas ausspucken, mit einer grossen Error dran.
Um das Meckern abzustellen, wie bereits oben geschrieben kannst du dies
machen, aber wie gesagt, Anfaenger sollten es an lassen.
list p=16F628A
errorlevel -302,-305
radix dec
#include <P16f628A.INC>
__CONFIG _INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _BODEN_OFF &
_MCLRE_OFF & _CP_OFF & _LVP_OFF
Werde mir den Code ansehen. Hast du den jemals assembliert ?
Das ist ja keine Fehlermeldung sondern eine Warnung, das du auf die
Bankumschaltung achten solltest (weil nach einem Reset ist immer Bank 0
eingestellt ist). Diese Hinweise werden immer dann auftauchen wenn auf
ein Register zugegriffen wird, welches NICHT in Bank 0 liegt. Man kann
diese Warnung allerdings auch abschalten - wurde glaube ich schon oben
erwähnt).
Da du es ja gemacht hast, kannst du das Ignorieren.
Und wenn du dir das Disassembler Listing ansiehst, findest dort auch die
beiden "STATUS"-Befehle samt erzeugten Opcode. Also alles im grünen
Bereich.
Ich finde es ja gut, wenn heute noch jemand mit Assembler programmieren
kann, und es auch noch lernen will. Aber wieso machst du dir das Leben
unnötig schwer? Lerne doch gleich C, da bist du innerhalb von ~10h am
Ziel und hast ein funktionierendes Programm. Weiter hast du die
Vorteile, dass du auch gleich auf dem PC mit C programmieren kannst,
oder du gehts noch etwas weiter und lernst C# und C++ für den PC, was
auch nicht viel Aufwand ist (für kleine Erfolge). Der einzige Grund für
Assembler ist aus meiner Sicht die Optimierung bis zum geht-nicht-mehr.
Aber selbst da greift man lieber zum nächsten uC und kopiert den Code
mit ein paar kleinen Anpassungen und muss nicht wie in Assembler gleich
alles neu schreiben.
Hi Chris, schön, dass du regelmäßig hier reinschaust. Das freut mich
sehr. Danke für den Hinweis.
Bzgl. PWM ist da aber noch nix weiter passiert. Das muss ich noch
machen/vertshen. :)
Ich wollt den Auszug aus dem Quellcode nur posten, damit man das Gefühl
hat, ich mache auch tatsächlich was und fordere nicht nur Dinge :)
chris schrieb:> Werde mir den Code ansehen. Hast du den jemals assembliert ?
Jupp und der Assembler meckert da nicht im Ansatz ;P Den Code brauchst
dir glaube in dem Status noch nicht weiter anschauen, da ich wie gesagt
noch am PWM dran bin...
Chris B. schrieb:> Und wenn du dir das Disassembler Listing ansiehst, findest dort auch die> beiden "STATUS"-Befehle samt erzeugten Opcode. Also alles im grünen> Bereich.
Danke für den Hinweis :)
Patrick B. schrieb:> Aber wieso machst du dir das Leben> unnötig schwer?
Hi Patrick, gute Frage. Ehrlich gesagt bin ich da einer Empfehlung
gefolgt (ich glaube sprut?), zuerst Assembler zu lernen und dann eine
höhere Sprache, da man dann auch weiß, was man hardwareseitig zu
berücksichtigen hat. C soll danach Einfach zu verstehen sein. Also gibt
es nicht wirklich einen guten Grund dafür ;-) Außerdem gefiel mir die
doch sehr überschaubare Anzahl an möglichen Befehlen, die in Kombi eben
dennoch alles bewerkstelligen können.
So far..
Besten Dank bis dahin ;)
Zunächst einmal vielen lieben Dank Chris!
Leider spuckte der Assembler mir einen Batzen Fehler vor die Füße und
selbst nachdem ich alle Ungereimtheiten beseitigt hatte, kann ich als
Anfänger deinen Code nicht ganz nachvollziehen. Ohne Dokumentation kann
ich da leider wenig mit anfangen :(
Das if_pressed Macro z.B. bleibt mir völlig schleierhaft. Die
btfss/btfsc Befehle machen doch rein technisch gesehen gar nichts. Auch
die Sprungmarke 'done' mag der Assembler so nicht akzeptieren. Habe mal
spaßenshalber statt auf done+1 zu verweisen auf $+7 verwiesen.
Was die gesamte Logik des PWM betrifft, scheine ich allerdings auch noch
nicht wirklkich verstanden zu haben, was ich wann wie an welche Variable
übergeben muss. Muss wohl doch die Artikel nochmals lesen- vielleicht
diesmal mit etwas mehr Aha-Effekten..
> Ohne Dokumentation kann> ich da leider wenig mit anfangen :(
Einfach fragen, was du nicht verstehst.
> Das if_pressed Macro z.B. bleibt mir völlig schleierhaft. Die> btfss/btfsc Befehle machen doch rein technisch gesehen gar nichts.
doch die machen genau dies, was das Datasheet sagt.
#define pressed 0 ; Wann ist die Taste gedrueckt (pressed),
; wenn 0 oder 1 gelesen wird. Hier im Beispiel 0
Macro reduziert auf den Fall pressed = 0
if_pressed MACRO port,pin
local done
btfsc port, pin ; skip if pin is 0
goto done+1 ; jump to end of of
waitMS 20 ; 20 ms Warten
btfss port, pin ; skip if pin is 1
done
endm
also wenn man es so benutzt
if_pressed porta,0
goto button_pressed
dann wenn die Taste gedrueckt wird, wird button_pressed angesprungen,
ansonsten wird der nächste Befehl nach if_pressed einfach uebersprungen.
Je nach Qualität der Taster muss man ev. die 20ms raufsetzen.
;***** VARIABLE DEFINITIONS
cblock 0x70 ; variablen welche aus allen Banks erreichbar sind
cblock 0x20 ; variablen in Bank0
; init, kann auch mit #define gemacht werden, ich habe dies lieber,
; da es dann in der .lst aufgelistet wird.
PORTA_INIT = 00000000b ;
; isr code can go here or be located as a call subroutine elsewhere
btfsc intcon,t0ie ; if not T0 Interrupt enabled
btfss intcon,t0if ; or T0 Interrupt not fired
goto t0_done ; then goto to t0_done
; else :
movlw -(1000000/PWM_RATE/PWM_STEPS-2) ; PWM_STEPS = 1 tick, 256 Tick
= 1 sec
addwf tmr0 ; nächsten Interrupt nach 122 uS als Beispiel nach diesem
movlw leds ; lade Pointer auf Array leds[8]
movwf fsr ; in das INDF register
bsf TICK_FLAG ; setze Flag auf 1
movlw 256/PWM_STEPS ; lade Increment Wert in W
addwf pwm ; addiere es zu pwm , setzt Carry flag im
Overflow
skpc ; sprich nach jedem PWM Zyklus.
bcf TICK_FLAG ; lösche Flag wenn kein neuer Zyklus
; ab hier ist TICK_FLAG jedesmal wahr, wenn pwm den Wert 0 hat.
movfw led ; lade Led
movwf pwm_tmp ; in tmp register
movlw 8 ; for(pwm_cnt=0;pwm_cnt<8;pwm_cnt++) ...
movwf pwm_cnt
pwm_loop ; {
rrf pwm_tmp ; tmp>>=1 ; lsb von tmp geht ins Carry
skpnc ; wenn Led gesetzt
clrf 0 ; lösche leds[pwm_cnt] -- bit0 = leds[0] , ....
comf 0,w ; W = ~led[pwm_cnt] ; complementäre Logic
addwf pwm ; W = W + pwm , Carry gesetz wenn Überlauf
btfsc TICK_FLAG ; wenn neue PWM Periode
incfsz 0 ; decrementiere (incr) led PWM Wert,
skip ; limitiere led[pwm_cnt] auf 0 (0xff)
decfsz 0
incfsz 4 ; pwm_cnt++ , incrementiert Pointer auf leds
decfsz pwm_cnt ;
goto pwm_loop ; }
rrf pwm_tmp,w ; schiebt letztes Carry von PWM ein und läd W
movwf portb ; gibt PWM auf portb aus
btfss TICK_FLAG ; wenn neue PWM Periode
goto t0_done-1 ; ende von t0_int
incfsz ticks ; incrementiere Ticks
skip
incfsz ticks+1 ; 16bit increment
skip
incf ticks+2 ; 24bit increment
decfsz cnt1 ; 3,9ms timer
incfsz cnt1 ; welcher hochzählt
incfsz cnt1 ; und auf 0 stehenbleibt
bcf intcon,t0if ; Lösche Interrupt Flag
t0_done
; isr code can go here or be located as a call subroutine elsewhere
movfw isr_fsr
movwf fsr
movf isr_status,w ; retrieve copy of STATUS register
movwf STATUS ; restore pre-isr STATUS register contents
swapf isr_w,f
swapf isr_w,w ; restore pre-isr W register contents
retfie ; return from interrupt
init
; status löschen
; comparator ausschalten
; Portregister setzen
; Timer löschen
; Wdt löschen
; Option setzen
; Trisregister setzen
; status löschen
; Ram von 0x20 - 0x7f löschen
; Interrupt Register setzten
; return ich hab dies lieber,
; da ich ev. dann init im Programm aufrufen kann.
main
; remaining code goes here
; es gibt nur ein Timer, also gehen nicht mehrere Tasten gleichzeitig.
; taster 1-5 (bit0-4) wird auf led gesetzt
clrw
if_pressed porta,0
iorlw 1<<0
if_pressed porta,1
iorlw 1<<1
if_pressed porta,2
iorlw 1<<2
if_pressed porta,3
iorlw 1<<3
if_pressed porta,4
iorlw 1<<4
movwf led
waitMS 80
goto main ;loop forever, remove this instruction, for test only
Was versteht du da genau nicht ?
chris schrieb:> movlw leds ; lade Pointer auf Array leds[8]> movwf fsr ; in das INDF register>> bsf TICK_FLAG ; setze Flag auf 1> movlw 256/PWM_STEPS ; lade Increment Wert in W> addwf pwm ; addiere es zu pwm , setzt Carry flag im> Overflow> skpc ; sprich nach jedem PWM Zyklus.> bcf TICK_FLAG ; lösche Flag wenn kein neuer Zyklus> ; ab hier ist TICK_FLAG jedesmal wahr, wenn pwm den Wert 0 hat.> movfw led ; lade Led
Hi Chris, ich bin dir wirklich dankbar für deine Hilfe.
Im Prinzip fehlt mir die generelle Logik des Programmaufbaus. So alla
Flußdiagramm. Ich weiß einfach nicht, welche Variablen ich in der Main
ändern bzw. setzen muss, damit etwas in der ISR ausgelöst wird.
Würde mich vor allem gerade echt ärgern, weil du dir ja augenscheinlich
extra Zeit genommen hast, um das Programm anzupassen...
chris schrieb:> iorlw 1<<0
Auch diese Anweisung hab ich im Buch leider nicht gefunden. Was bewirkt
'<<'?
Die Variable LED wird verwendet, um die Leds an Portb anzuzeigen, mit
automatischen fading out in ca 1 Sekunde.
Sei es PWM_RATE sowie PWM_STEPS ändern diese Zeit.
PWM_RATE ist die PWM Frequenz, 256 HZ derzeit, und PWM_STEPS die Anzahl
der
PWM Schritte, derzeit 32. Bei beiden können auch krumme Werte verwendet
werden, wie z.b. 100, 60, .. .
http://www.microautomate.com/PIC/pic-timing-timers.php
erklärt dir die 2/3 Register, welche fuer den Timerinterrupt
zuständig sind. Im Datenblatt steht dasselbe, bzw steht es genauer.
Diesem Code generiert einen T0 Interrupt alle
4Mhz/4/PWM_RATE/PWM_STEPS = 1000000/256/32 = 122 µS .
Dies ist im OPTION_INIT und INTCON_INIT und durch setzen des TMR0
Register
im Interrupt definiert. Mehr gibt es da nicht.
Eigentlich, da ich dort einen kleinen Fehler gemacht habe, das
movlw -(1000000/256/PWM_STEPS-2) ; PWM_STEPS = 1 tick, 256 Tick = 1
sec
müsste so sein
movlw -(1000000/256/PWM_STEPS-2) ; PWM_STEPS = 1 tick, 256 Tick = 1
sec
Die CPU braucht 2 clk um den Timer update zu machen.
Also wenn die CPU ein addwf tmr0 macht, und in W -100 drinsteht, dann
bei prescaler 0 braucht es 100 clk Cyclen
1<<0 => 1 , oder bit 0 ist on
1<<3 => 8 , oder bit 3 ist on
INTCON = 1<<GIE|1<<T0IE ; -- INTCON = 0x84 -- oder 10000100b ,
also bit GIE und bit T0IE ist on, sprich die Zahl eins 7 mal nach links
geschoben und 1<<T0IE ist eins 3 mal nach links geschieben, | ist ein
verodern der bits, hier dasselbe wie ein + .
Eine Übersicht über die Operatoren:
http://www.peacesoftware.de/ckurs4.html
abgesehen von den Operatoren mit = werden alle unterstuetzt, die
Vergleichsoperatoren werden unterstuetzt.
Besten Dank für die Hinweise! Wieder was dazugelernt bzgl.
Programmierung.
Nun aber noch einmal zurück zu der Frage, was ich in der Main(loop)
ansteuern muss, damit eine LED auf high gesetzt wird und anschließend
ausfaded? Reicht ein einfaches bsf led,pin?
Alles was du in led reinschreibst, wird auf den leds rausgeschrieben,
ob mit direkter Zuweisung oder mit Bits setzen.
Das fading ist automatisch, nachdem das Bit gelöscht wurde.
Nur, das Register led wird alle 122µS gelesen, bzw aktualisiert.
Also wenn du ein
bsf led,0
machst, und dann ein
bcf led 0,
wird warscheinlich nichts passieren, aber wenn du z.B. den obigen
Code nimmst, mit if_pressed , oder inzwischen ein waitMS machst, dann
funktioniert es, da dann sichergestellt ist, daß ein Interrupt
inzwischen
aufgetreten ist.
Ahh... das klingt sehr elegant.
Nun habe ich auf meiner Testplatine den Pic damit ausprobiert, aber beim
Start leuchten alle LEDs, die einen mehr die andre weniger.
Die Eingänge sind bei mir alle auf High gesetzt und werden dann gegen
GND gezogen, sodass ein LOW die LED auslösen soll. Kann durch diese
Logikumkehrung dieses Problem entstehen?
Dies tut mir Leid.
Die Zeile addwf pwm war falsch, sollte addwf pwm,w sein.
Im Anhang habe ich dir noch was reinkopiert, schau es dir mal an,
EEprom read sowie Flash read und random
Die Obige Source sollte gelöscht werden, werde es flaggen.
Wenn was nicht funktioniert oder du was anderst haben willst, einfach
melden.
pic prog schrieb:> Dies tut mir Leid.> Die Zeile addwf pwm war falsch, sollte addwf pwm,w sein.>> Im Anhang habe ich dir noch was reinkopiert, schau es dir mal an,> EEprom read sowie Flash read und random>> Die Obige Source sollte gelöscht werden, werde es flaggen.>> Wenn was nicht funktioniert oder du was anderst haben willst, einfach> melden.
Wow. Bin beeindruckt wie hilfsbereit du bist!
Allerdings wird dein Programm immer komplexer und für mich immer weniger
nachvollziehbar. Muss ich wohl mal Zeile für Zeile durchgehen und für
mich sortieren.
Leider scheint auf PORTA nur ein High die Leds auszulösen. Bräuchte ja
genau den umgekehrten Fall, d.h. High ist Standardzustand und bei
Lowpegel sollte LED ausgelöst werden. Ehrlich gesagt, kann ich aber eine
Anpassung hier auch nichtmehr wirklich annehmen. Hast mir schon eine
Menge gezeigt und hab viel gelernt. Bin grad selbst dran für mich das
Fading von grundauf zu verstehen und zu programmieren. Es scheitert bei
mir schon an den Grundlagen, dass für mich unklar ist, was XORLW
tatsächlich bewirkt.. Brauche wohl mal einen Intensivkurs..
Also vielen lieben Dank für deine Hilfe!
M. Hahn schrieb:> Es scheitert bei mir schon an den Grundlagen, dass für mich unklar> ist, was XORLW tatsächlich bewirkt..
Hallo,
Ich mach' mal den Versuch - vielleicht hilft dies:
;
;Beispiel zum Vergleich zweier beliebiger Zahlenwerte 0-255
;----------------------------------------------------------
;---Initialisierung der zu vergleichende Zahlen
movlw d'101' ;Istwert (z.B. Zählerstand)
movwf Zahl_beliebig
movlw d'100' ;zu prüfender Sollwert
movwf Zahl_Sollwert
;
;---Zahlenwerte vergleichen (hier gehts los)
;
movf Zahl_beliebig,w ;1. Istwert ins WREG
xorwf Zahl_Sollwert,w ;2. WREG (Istwert) mit Sollwert vergleichen
; oder
; xorlw d'100' Istwert mit Festwert (Literal)
; vergleichen.
; Ergebnis steht in beiden Fällen im WREG
; oder
; xorwf Zahl_Sollwert,f
; Ergebnis steht im File, also im Register
; Zahl_Sollwert)
;---Ergebnis auswerten
btfss STATUS,Z ;Zeroflag abfragen
GOTO Zahlen_ungleich_100 ;verzweigen, wenn Zeroflag=0
GOTO Zahlen_gleich_100 ; bzw. wenn Z=1
;oder anderen Job erledigen z.B.
;clrf ... mov...
;bsc... usw.
;----------------------------------------------------------
;
;---Erklärung des Funktionsablaufes
;
1. Istwert in das WREG kopieren
2. Exclusive ODER-Verknüpfung der beiden Zahlen
2.1 Möglichkeit 1: Werte sind ungleich
Dann wird das Zeroflag in STATUS auf 0 gesetzt
Möglichkeit 2: Werte sind gleich
Dann wird das Zeroflag in STATUS auf 1 gesetzt
Exclusive ODER (XOR)
korrespondierende Bits in zwei Binärwerten werden verglichen.
Das Ergebnis kann nur dann 1 sein, wenn alle korrespondierenden
Bits den gleichen Wert haben. Beispiele:
WREG: binär 01100100 (dezimal 100)
Zahl: binär 01100100 (dezimal 100)
--------
Ergebnis: 11111111 = TRUE (1) alle Bits stimmen überein
TRUE = "1" wird in das Z-Flag übertragen
WREG: binär 01100101 (dezimal 101)
Zahl: binär 01100100 (dezimal 100)
--------
Ergebnis: 11111110 = FALSE (0) nicht alle Bits stimmen überein
FALSE ="0" wird in das Z-Flag übertragen.
mfg Ottmar
Schon fast ein Jahr ist es nun her, dass ich dieses Bastelprojekt in
Angriff genommen hatte. Leider hatte ich nun zwischenzeitlich andere
Sorgen, aber nun bin ich wieder motiviert, das endlich fertigzustellen.
Grundsätzlich habe ich mich auch wieder in die Materie eingearbeitet,
aber muss nach wie vor gestehen, dass ich das Programm leider nicht
nachvollziehen kann, da man z.t. über 3 Ecken schauen muss, was gemeint
ist (Makrozugriff, Verweis auf Funktion und dann erst die jeweilige
Operation.)
Erst einmal die Grundlegende Frage: ist das ohne externen Oszillator
lauffähig und es wird beschrieben, dass aufgrund nur des einen Timers
auch nur ein Taster abgefragt werden kann- heißt das, dass auch nur eine
LED ausfaden kann oder funktioniert es dennoch, alle 8 LEDS anzusteuern
und unterschiedliche Stufen des Fadings zu übergeben?
Besten Dank an der Stelle schon einmal vorab!
Hier ist alles din was du suchst, aber lesen und es versuchen zu
verstehen solltest du selbst
http://electromotiveforces.blogspot.de/2011/10/cascading-snowfall-led-lights.html
Hinweise: Dieses Programm nutzt keine Inputs sondern erzeugt nur
Ausgabemuster. Musst eben anpassen an was du erreichen möchstest.
Falls du das Prj neu beginnst, bitte verwende moderneren uC Pic16F1827
o.ä., mit ner vierstelligen Nummer.
Gruss
Ottmar K. schrieb:> Exclusive ODER (XOR)> korrespondierende Bits in zwei Binärwerten werden verglichen.> Das Ergebnis kann nur dann 1 sein, wenn alle korrespondierenden> Bits den gleichen Wert haben. Beispiele:
LOL.
Das ist mir aber neu.