Hallo, ich habe aktuell als kleines Projekt ein per i2c steuerbares
Display inklusive Tastenfeld. Das Tastenfeld wird mittels
Widerstandsmatrix und ADC eingelesen. Mein Problem ist allerdings dass
der ADC zu schnell zu sein scheint, wenn ich nämlich nicht 50ms delay in
meine Hauptschleife integriere, habe ich keinen Zugriff mehr per I2C.
Und wenn ich den ADC ohne Interrupt einlese also das Bit (1<<ADIE) nicht
setze, dann stürzt er mir ab sobald ich eine Spannung am ADC anlegen
habe. Wisst ihr weiter?
Vielen Dank für eure Zeit
Mega8 hat afair kein Hardware-I2C also dürfte das per Software
(timingkritisch) gemacht werden und da der ADC-Interrupt
höchstwahrscheinlich viel zu lange braucht (itoa + LCD-Routinen,
letztere womöglich noch mit Delays) wird das die I2C-Übertragung aus dem
Takt bringen.
Lösung: Im ADC-Interrupt nur ein (volatile) Flag (adcReady o.ä.) setzen
und das ADC-Ergebnis kopieren und die Ausgabe und Umwandlung in der Main
machen, wenn das Flag gesetzt ist.
8 Mhz / 800 ergibt eine Interrupt-Rate von 10KHz oder 1/10usec.
Die Ausgabe eines LCD Zeichens dauert typischerweise (grob
geschätzt) 1..10usec.
Das bedeuted dass der Prozessor aus den Interrupts (wo er auf
das LCD schreibt) kaum mehr herauskommt um was anderes zu tun.
Harry_13 schrieb:> aber wieso wird genau dann, mit Interrupt disabled, immer mit Spannung> am ADC mein Controller resetted?
Da musst du schon den ganzen Code zeigen der dieses Verhalten
provoziert. Am Lesen des ADCs wird es wohl nicht liegen ....
.... vielleicht auch noch die Schaltung (zeigen) ....
Zugegeben ich bin etwas provokativ.
Weißt Du, was Du ausgibst?
Wenn ich Deine lcd_update()-Funktion richtig verstehe, so schreibst Du
immer das ganze Display voll.
Ist das würglich nötig?
Mit der Geschwindigkeit?
Ist die Ausgabe schneller als die Zufuhr von neuen Werten, so ist
definitiv irgendwas in die Hose gegangen.
Es gibt immer die Möglichkeit, Teile oder, wenn Du keine Ahnung hast,
ganze Zeilen zu aktualisieren.
Hast Du Dich mal mit der maximalen "Hinguckfrequenz" beschäftigt? Damit
meine ich die maximale Frequenz, mit der das Auge Änderungen erkennt und
das in 4 Zeilen...
Harry_13 schrieb:> aber wieso wird genau dann, mit Interrupt disabled, immer mit Spannung> am ADC mein Controller resetted?
Mal so ins Blaue geraten:
Du konfigurierst den ADC so dass beim Lesen ein Interrupt ausgelöst
wird den du nicht als ISR auch bedienst. Der unbediente Interrupt
lässt den Prozessor in den Reset springen.
Bei genau diesem Code passiert folgendes, solange der Eingang vom ADC
auf GND ist ist alles gut, sobald aber irgendeine Spannung anliegt, wird
das Display geleert, ( zeigt nichts mehr an) und per I2C ist er auch
nicht mehr Ansprechbar, nach einem RESET funktionierts dann wieder.
Fehler in der Beschaltung könnte man doch ausschliessen, da es ja mit
Interrupts einliest? AVCC und AREF liegen halt auf 5V GND wurde mit GND
verbunden. Das LCD hängt mit 4 Bit an PortD, An OC1A hängt per Pwm und
Transistor die Hintergundbeleuchtung an OC1B hängt die Kontrastspannung
mit 100uF Kondensator an Masse. Die Betriebsspannung ist stabilisiert
und Zusätzlich mit einem Glättungskondensator (330uF) versehen. der I2C
Bus ist mit PullUps versehen und wird mit einem Level Shifter auf 3.3 V
für einen Raspi geshiftet. Ich zeichne morgen aber gerne noch nen
Schaltplan :)
Amateur schrieb:> Wenn ich Deine lcd_update()-Funktion richtig verstehe, so schreibst Du> immer das ganze Display voll.>> Ist das würglich nötig?>> Mit der Geschwindigkeit?
Nein, die LCD_Update wird nur aufgerufen wenn mit I2C alle Daten
angekommen sind und in Rx_buffer[0] eine 1 steht, die wird nach dem
Update gelöscht. somit kann ich in aller ruhe Daten in den Speicher
laden und diese dann auf einen Rutsch aufs LCD schreiben lassen.
Amateur schrieb:> Wenn ich Deine lcd_update()-Funktion richtig verstehe, so schreibst Du> immer das ganze Display voll.
Und dann das ganze auch noch unter Interrupt Sperre
1
voidlcd_update(void){
2
cli();
3
for(into=1;o<=4;o++)
4
for(inti=1;i<=20;i++){
5
lcd_setcursor(i-1,o);
6
lcd_data(rxbuffer[i+((o-1)*20)]);
7
}
8
sei();
9
}
Den Cursor nach jedem Zeichen immer neu zu setzen braucht auch kein
Mensch. Jede einigermassen vernünftige LCD Library wird nach der Ausgabe
eines Zeichens den Cursor ganz für sich alleine um 1 Zeichen weiter
setzen.
Zum Thema Interrupt Sperre:
Da die I2C Routinen offensichtlich über Interrupts funktionieren
(funktionieren müssen), ist es ganz fies, wenn du für eine derart lange
Zeit die Interrupts abstellst. In dieser Zeit wird dein TWI Interface
nämlich lahm gelegt.
Ganz abgesehen davon, dass es vollkommmen ausreichen würde, wenn in der
Hauptschleife immer nur ein einziges Zeichen aus deinem
Bildschirmspeicher aufs LCD übertragen wird. Nur dann eben reihum immer
das jeweils nächste, so dass sich dein LCD dann eben über einen Zeitraum
von 20*4 = 80 Durchläufen durch die Hauptschleife aktualisiert. Das ist
immer noch schnell genug, so dass kein Mensch bemerken wird, dass das
nicht in einem Rutsch sondern nach und nach geschieht.
Alles in allem: das ist mal wieder alles mit der heissen Nadel
zusammengestrickt und dann noch dazu mit wenig Sachkentniss.
unsigned int adc_value=0; // Variable to hold ADC result
char text[4];
while(1)
{
ADCSRA |= (1<<ADSC); // Start conversion
while (ADCSRA & (1<<ADSC)); // wait for conversion to complete
adc_value = ADCW; //Store ADC value
itoa (adc_value,text,16);
itoa ist die falsche Funktion für einen unsigned int.
das i in itoa steht für int. Den hast du aber nicht. Du hast einen
unsigned int. Die richtige Funktion dafür ist utoa.
Und mach dir um Himmels willen Hilfsfunktionen!
Einen int oder unsigned int auf einem LCD ausgeben braucht man alle Nase
lang. Das ist wirklich keine Raketentechnik sich dafür eine lcd_puti
bzw. lcd_putu Funktion zu schreiben.
1
voidlcd_puti(intwert)
2
{
3
chartxt[7];
4
5
itoa(wert,txt,10);
6
lcd_string(txt);
7
}
8
9
voidlcd_putu(unsignedintwert)
10
{
11
chartxt[7];
12
13
utoa(wert,txt,10);
14
lcd_string(txt);
15
}
fertig. Die Funktionen kommen zu deinen LCD Funktionen mit dazu und in
Zukunft passiert dir so was nicht mehr, dass dein Ausgabearray zu klein
ist.
>Nein, die LCD_Update wird nur aufgerufen wenn mit I2C alle Daten...
Meine Frage bezog sich hauptsächlich darauf, ob es sinnvoll ist immer
das GANZE Display zu aktualisieren. Da gibt’s nämlich - versteckt - jede
Menge an delay.
Nicht vergessen: Egal ob Du nur alle paar Sekunden, oder im
Mikrosekundenrhythmus, einen Funktionsblock aufrufst. Ist dieser,
zeitlich, zu lang ist er zeitlich zu lang.
Mitlesa schrieb:> Karl H. schrieb:>> Da der größte Wert, den du vom ADC kriegen kannst, 1024 ist>> 1023? (-->0x3FF)
Ja ok. Danke
Das Array ist trotzdem zu klein
Amateur schrieb:> Meine Frage bezog sich hauptsächlich darauf, ob es sinnvoll ist immer> das GANZE Display zu aktualisieren. Da gibt’s nämlich - versteckt - jede> Menge an delay.
Da gebe ich dir recht, das muss ich noch ändern. Ein größerer Buffer
hilft auf nichts, trotzdem hängt er wieder... Die Interrupt Sperre war
aktuell nur testweise drinnen, da mir zuvor der ADC interrupt immer die
Display Aktualisierung unterbrochen hat, und somit zum teil nur halb
geschrieben wurde.
Harry_13 schrieb:> Okay ja das klingt logisch :), kann ich irgendwie den ADC ohne Interrupt> auslesen? Das wäre mir das liebste, so oft benötige ich den ja nicht...
Und warum nicht umgekehrt? ADC im Interrupt auslesen, aber mit dem LCD
in der Hauptschleife kommunizieren?
asdf schrieb:> Harry_13 schrieb:>> Okay ja das klingt logisch :), kann ich irgendwie den ADC ohne Interrupt>> auslesen? Das wäre mir das liebste, so oft benötige ich den ja nicht...>> Und warum nicht umgekehrt? ADC im Interrupt auslesen, aber mit dem LCD> in der Hauptschleife kommunizieren?
Im Prinzip muss es egal sein.
Ob mit oder ohne Interrupt, da darf nichts abschmieren.
Noch zwei Fragen:
Woher kommt rxbuffer. Immerhin 80 Zeichen.
Meckern Deine Include-Dateien nicht, wenn Du die Frequenz nachher
festlegst?
Soweit mir bekannt benötigt zumindest delay.h diese Angabe...
Harry_13 schrieb:> aktuell nur testweise drinnen, da mir zuvor der ADC interrupt immer die> Display Aktualisierung unterbrochen hat, und somit zum teil nur halb> geschrieben wurde.
Na ja gut.
Das war sowieso Quatsch.
Dein LCD ist nun mal eine Shared Resource und damit gibt es nur eine
Stelle, die darauf zugreifen darf. Entweder die Hauptschleife oder 1
ISR. Aber nicht beide gleichzeitig, denn sonst kommen sich die ins
Gehege.
Karl H. schrieb:> Hast du schon mal probiert, was pasiert wenn du nur den ADC am laufen> hast
Gerade getestet, nur der ADC läuft... Sobald ich jetzt aber I2C
aktiviere ist dieses dann nicht ansprechbar
Amateur schrieb:> Woher kommt rxbuffer. Immerhin 80 Zeichen.>> Meckern Deine Include-Dateien nicht, wenn Du die Frequenz nachher> festlegst?>> Soweit mir bekannt benötigt zumindest delay.h diese Angabe...
RXBuffer kommt aus der I2C Lib. von Jtronics
http://www.jtronics.de/avr-projekte/library-i2c-twi-slave.html
Die Frequenz übergebe ich zusätzlich im Makefile.
Harry_13 schrieb:> Karl H. schrieb:>> Hast du schon mal probiert, was pasiert wenn du nur den ADC am laufen>> hast> Gerade getestet, nur der ADC läuft... Sobald ich jetzt aber I2C> aktiviere ist dieses dann nicht ansprechbar
Dann würde ich mir an deiner Stelle die I2C Implementierung mal näher
ansehen
oder gleich mittels memcpy.
Solche doppelt gemoppelt Dinge solltest du dir abgewöhnen. Es gibt nur
eine einzige Stelle, an der die Buffergröße als Zahlenwert bekannt ist.
Und das ist hier in twislave.h
1
//#################################### von Benutzer konfigurierbare Einstellung
2
3
#define buffer_size 20 //Größe der Buffer in Byte (2..254)
wenn dort bei dir nicht 85 steht, dann liegt hier der Fehler. Aber
selbst wenn bei dir da 85 eingetragen sein sollte, dann solltest du
trotzdem überall dieses #define benutzen und nicht direkt in deinem Code
85 stehen haben. Das sind alles nur Stolpersteine.
> Die Frequenz übergebe ich zusätzlich im Makefile.
Dann nimm sie aus dem Code raus.
Doppelte Angaben verwirren nur.
if(buffer_adr==0xFF)// erster Zugriff, Bufferposition setzen
4
{
5
if(data<=buffer_size)// Kontrolle ob gewünschte Adresse im erlaubten bereich
das kann hier nicht <= heissen. Das muss < sein!
(Wenn immer im Zusammenhang mit Array Indizierung irgendwo ein <=
auftaucht, dann muss man hellhörig werden)