Archiv verlassen und diese Seite im Standarddesign anzeigen : Sei() Cli() Atmega 16 Timer starten/stoppen
Hallo Leute,
da ich vorhabe eine Stoppuhr zu programmieren habe ich mir zum Testen folgendes Programm ueberlegt:
[//define all external dependencies
#include <avr/io.h>
#include <avr/interrupt.h>
//declare all used functions
void Sensor_init(void);
void LED_SW_init(void);
//define variables
volatile unsigned int z;
void TIMER2_interrupt_init(void)
{
TCCR2 = (1<<WGM21)|(0<<WGM20)|(1<<CS22)| (1<<CS21) |(0<<CS20); //CTC mode und Prescaler auf 256
TCNT2=0;
OCR2=249; //alle 0.008 s Interrupt
TIMSK = (1<<OCIE2);
}
ISR(TIMER2_COMP_vect) //LED soll alle 1 leuchten
{
z++;
if(z==125)
{
PORTB ^= (1<<PB0);
z=0;
}
}
int main(void)
{
Sensor_init();
LED_SW_init();
TIMER2_interrupt_init();
while(1)
{
if(!(PIND & (1<<PD0))) sei(); //Mit Pin 0 soll Timer gestartet werden
if(!(PIND & (1<<PD7))) cli(); //Mit Pin 7 soll Timer stoppen
}
}
void Sensor_init(){
DDRA=0x00; // Port A has to be used as a digital input
PORTA=0xff; // activate internal pull-up resistors for PORTA
}
void LED_SW_init(){
DDRB=0xFF; // PORTB is output for LEDs
PORTB=0xFF; // all LEDs off
}
]
Wenn ich den Taster 0 druecke soll der Timer aktiviert werden und wenn ich den andren Taster Pin 7 druecke soll er deaktiviert werden und nicht mehr zaehlen bzw die LED hier nicht mehr im sekundentakt blinken. Dieses Programm habe ich nur geschrieben um das Grundprinzip meines Programms zu testen...
Jetzt meine Probleme:
1) Die LED blinkt immer und reagiert auf keinen Taster:
2) Wenn ich von Port D die Bruecke zu RXD TDX drin habe, blinkt zu Beginn gar nichts. Erst wenn ich sie rausnehm fängt die LED an zu blinken.
Später will ich mit meinem Programm die Zeit stoppen, d.h. wenn der Taster 0 gedrueckt ist, soll der Timer starten und wenn der Taster 7 gedrueckt ist soll der Timer stoppen. Wie krieg ich das hin, dass meine LED nach gedruecktem Taster 0 blinkt und wenn ich Taster 7 einmal druecke aufhoert ?
Vielen Dank und viele Gruesse,
Jimmy[/code]
Hubert.G
04.05.2010, 13:53
Mit cli sperrst du alle Interrupts und mit sei gibst du alle wieder frei.
Der Timer läuft aber trotzdem weiter. Der Timer startet mit setzen der CSxx Bit im TCCR2 Register und wird mit löschen dieser Bit wieder gestoppt.
Auf PortD sind keine PullUp aktiv.
sei und cli bewirken nur das schalten der interrupts und deren vektoren, der timer läuft trotzdem weiter nur wird das interrupt nicht mehr ausgeführt
hm am besten postet du noch wie die Tasten angeschlossen sind .
desweiteren, Timer kannst auch anhalten indem du die CS02 01 00 bits im TCCRx register auf null setzt, und wieder aktivierst in dem du sie so setzt wie oben.
edit: da war wohl jemand schneller :)
Hubert.G
04.05.2010, 14:09
So wie es jetzt ist, ist es natürlich nicht schön, da der Timer bei jedem Durchlauf wieder gestartet wird solange die Taste gedrückt ist.
Also mein Ziel ist es einen Timer mit einem Taster zu starten bzw. genau dann eine Variable hochzaehlen lassen. Wenn ich einen anderen Taster druecke moechte ich mir per UART den Zaehlerstand ausgeben lassen. Wie kann man denn so etwas machen ? Es geht mir jetzt nur darum wie ich den Timer fuer so was per Taste starte.
Aber was mit dem PortD los is hab ich jetzt nicht verstanden...warum beeinflusst dieser die Funktion meines Programmes obwohl ich ihn nirgends drin hab!
Vielen Dank und viele Gruesse
Hubert.G
04.05.2010, 16:58
Soweit sollte es schon passen. Du hast nur die Tasten im Programm auf PIND.
Also mein Ziel ist es einen Timer mit einem Taster zu starten bzw. genau dann eine Variable hochzaehlen lassen. Wenn ich einen anderen Taster druecke moechte ich mir per UART den Zaehlerstand ausgeben lassen. Wie kann man denn so etwas machen ?
Das geht am einfachsten über die Manipulation des TCCR1B Registers für Timer 1.
Zuerst setzt du bei einem Tastendruck der Starttaste das TCNT1 Register auf 0
TCNT1=0;
Danach Stellst du das TCCR1B auf den gewünschten Teilerfaktor ein.
TCCR1B=0b00000100; //für Prescaler :256
Der Timer 1 zählt nun hoch.
Bei Druck auf die Taste Stopp holst Du Dir den Wert aus dem TCNT1 Register
ui_zeit=TCNT1;
und stoppst den Counter 1
TCCR1B=0b00000000;
Eventuell brauchst Du noch eine Überlaufvariable, weil der TCNT1 nur 16 Bit breit ist.
Dann muß natürlich der TIMER1 Overflow Interrupt aktiviert werden.
Die Tastenabfrage kannst Du jeweils in einen Interrupt packen.
Start z.B. in den INT0 und Stopp in den ICP1 Interrupt, weil damit das ICR Register des Timers 1 mit dem aktuellen Zählerstand gefüllt wird, den man dann nur noch auszulesen braucht.
Den ermittelten Wert kannst Du dann mit itoa bzw. ltoa in einen String verwandeln und über die serielle Schnittstelle ausgeben.
Ich würd bei der Stopp Taste ( Interrupt ) ein Flag setzen , der Timerwert speichern und dann erst in der Hauptroutine den Sendestring generieren und an den USART ausgeben.
Dadurch wird die Interruptroutine auch sehr kurz.
Du hast natürlich bei der Aktivierung des TIMER 1 ein paar Prozessortakte verloren, weil natürlich zuerst die Interruptroutine aufgerufen und das TCCR1B Register beschrieben wird, bevor der Timer 1 zu zählen beginnt.
Kommt drauf an, wie genau die ermittelte Zeit sein muß und welchen Prescaler Du verwendest.
Ne andere Methode:
Ich persönlich verwende auch eine relative Zeitmessung mit freilaufendem Timer.
Der Zählerstand wird bei Druck auf die Starttaste ausgelesen und abgespeichert.
Bei Druck auf die Stopptaste wird der Zählerstand erneut ausgelesen und der vorher ermittelte Wert angezogen.
Wenn Du unsigned Variablen verwendest funktioniert das auch bei einem Überlauf des Timers, oder der Überlaufvariable.
Soweit so gut :-) Super!!! Vielen vielen Dank !!!!Ich kann die LED die im sekundentakt blinkt ueber meine schalter aktivieren und deaktivieren :-) jaaaaaaaaaaaaaaaaaaaa :-)
wenn ich jetzt meine Uhr da rein haue...wie speicher ich denn beim ausschalten meinen Zaehlstand in einer Variable ab ? Ich möchte sie mir nämlich per UART ausgeben lassen...so sieht mein Programm dann aus:
//define all external dependencies
#include <avr/io.h>
#include <avr/interrupt.h>
//declare all used functions
void Sensor_init(void);
void LED_SW_init(void);
//define variables
volatile unsigned int z;
volatile unsigned int sekunde;
volatile unsigned int minute;
volatile unsigned int stunde;
void TIMER2_interrupt_init(void)
{
TCCR2 = (1<<WGM21)|(0<<WGM20)|(1<<CS22)| (1<<CS21) |(0<<CS20); //CTC mode und Prescaler auf 256
TCNT2=0;
OCR2=249; //alle 0.008 s Interrupt
TIMSK = (1<<OCIE2);
}
ISR(TIMER2_COMP_vect) //Stoppuhr
{
z++;
if(z==125)
{
sekunde++;
z=0;
}
if(sekunde==60)
{
minute++;
sekunde=0;
}
if(minute==60)
{
stunde++;
minute = 0;
}
}
int main(void)
{
Sensor_init();
LED_SW_init();
TIMER2_interrupt_init();
while(1)
{
if(!(PINA & (1<<PINA0))) sei(); //Mit Pin 0 soll Timer gestartet werden
if(!(PINA & (1<<PINA1))) cli(); //Mit Pin 1 soll Timer stoppen
}
}
void Sensor_init(){
DDRA=0x00; // For using the floating sensor the Port A has to be used as a digital input
PORTA=0xff; // activate internal pull-up resistors for PORTA
}
void LED_SW_init(){
DDRB=0xFF; // PORTB is output for LEDs
PORTB=0xFF; // all LEDs off
}
Ich hab ja hier die Variablen: sekunde, minute, stunde...ich wuerd sie gern in einer Variablen umgerechnet zusammenfassen und dann an genau einmal an der PC per UART schicken. Und das wäre dann noch genau dann der Fall wenn ich Taster 1 betätige...irgendjemand ne Ahnung ?
Viele Gruesse und nochmal vielen Dank!!
:-) Das mit dem freilaufendem Timer is natuerlich ne feine sache :-) Wuerde es dir was ausmachen den Quellcode hier reinzustellen !? Hab noch nicht so die grosse Erfahrung damit...vielen Dank und viele Gruesse
Es geht mir dann speziell darum wie ich die Zaehlwerte abspreichere...also bei Druck von Start taste speichere Zaehlstand....bei Druck von Stopptaste speichere diesen Zaehlstand...und dann die Subtraktion...vom prinzip hab ich das verstanden und es gefällt mir auch besser als mein Weg...
Vielen Dank
Also ich hab jetzt mal versucht etwas zusammenzuschreiben...könntet ihr mir vielleicht nen Tip geben ob das prinzipiell so funktioniert ?
Mein Code lautet:
//define all external dependencies
#include <avr/io.h>
#include <avr/interrupt.h>
//declare all used functions
void Sensor_init(void);
void LED_SW_init(void);
//define variables
volatile unsigned int z;
volatile unsigned int sekunde;
volatile unsigned int T1;
volatile unsigned int T2;
volatile unsigned int TD;
void TIMER2_interrupt_init(void)
{
TCCR2 = (1<<WGM21)|(0<<WGM20)|(1<<CS22)| (1<<CS21) |(0<<CS20); //CTC mode und Prescaler auf 256
TCNT2=0;
OCR2=249; //alle 0.008 s Interrupt
TIMSK = (1<<OCIE2);
}
ISR(TIMER2_COMP_vect) /Sekunde hochzählen
{
z++;
if(z==125)
{
sekunde++;
z=0;
}
}
int main(void)
{
Sensor_init();
LED_SW_init();
TIMER2_interrupt_init();
while(1)
{
if(!(PINA & (1<<PINA0))) //Mit Pin 0 soll T1 gespeichert werden
{
T1 = sekunde;
}
if(!(PINA & (1<<PINA1))) //Mit Pin 1 soll T2 gespeichert werden und die Differenz berechnet werden
{
T2 = sekunde;
TD = T2-T1;
}
}
Jetzt stellt sich natürlich noch die Frage wie ich TD über UART auslesen kann...würde mich sehr über eine Antwort oder nen Code freuen.
Vielen Dank und viele Gruesse,
Jimmy
Zuerst solltest Du die Variable TD in einen String umwandeln
unsigned char uc_string[6]; //Stringvariable anlegen.
......
itoa(TC,uc_string);
Dann wird dieser String per USART gesendet.
Wie das geht ist von Compiler zu Compiler und der Art des Versendens abhängig.
Beispiel:
print(uc_string); //Senden - CR = Carriage Return und LF Line Feed anfügen
printf("\r\n");
sprint(uc_string); //Senden - CR und LF anfügen
sprintf("\r\n");
for(i=0;i<strlen(uc_string);i++)
{
putchar(uc_string[i]) //Senden
}
putchar('\r'); //CR und LF anfügen
putchar('\n);
Guck aber in deiner Library für die serielle Schnittstelle nach, wie das bei Dir gehandelt wird, welche Syntax verwendet wird und welche Befehle auf diese umgeroutet werden.
Anbei noch ein kurzes Codeschnipsel, das für Deinen Anwendungsfall nicht optimal ist, aber gut zeigt wie das Ganze funktioniert.
In diesem Beispiel wird bei einer steigenden Flanke am ICP Pin der alte Wert des Timers ausgelesen und das Sensing auf fallende Flanke umgestellt.
Fällt die Flanke dann wird der zweite Teil der Routine angesprochen,der aktuelle Wert ermittelt und der vorher ermittelte alte Wert abgezogen.
Dann wird das Sensing wieder auf steigende Flanke für den nächsten Impuls umgestellt.
Es wird also die High Time des Impulses ermittelt.
Das Ganze gehört zu einer Servoimpulsauswertung, bei der eigentlich nur die High Impulslänge wichtig ist.
Für deine anwendung kannst Du die beiden Codeteile in deinen beiden Tastenabfrage unterbringen.
// Timer 1 input capture interrupt service routine
interrupt [TIM1_CAPT] void timer1_capt_isr(void)
{
if((TCCR1B&0b01000000)>0) //Interrupt wurde durch steigende Flanke ausgelöst
{
ui_oldrise=ICR1; //Messwert der steigenden Flanke ablegen
TCCR1B&=0b10111111; //Interupt Sensing auf fallende Flanke umstellen
}
else //Interrupt wurde durch fallende Flanke ausgelöst
{
ui_pulsebuffer=ICR1-ui_oldrise; //Pulslänge berechnen
TCCR1B|=0b01000000; //Interrupt Sensing auf steigende Flanke Umstellen
}
}
Ich würde die Tastenabfragen in Deiner Anwendung über Interrupts abwickeln.
Wenn Du z.B. ein Display an deinem System hast, verbringt der Controller oft sehr lange Zeiten in den Displayroutinen.
Während dieser Zeiten werden die Taster nicht abgefragt und somit die Auslösezeit nicht exakt ermittelt.
Machst Du die Tastenabfrage im z.B. INT 0 wird die Displayroutine unterbrochen und diese Tastenabfrage sofort ausgeführt.
Die Zeiten werden dadurch nicht 100% genau, aber wesentlich genauer, als bei Deinem Verfahren, das man übrigens POLLING nennt.
Noch was solltest Du beachten - Tasten prellen.
Das bedeutet eine Taste schließt und offnet bei Betätigung mehrmals, bevor der Kontakt letztlich offen bzw. geschlossen ist.
Bei Deinem 2 Tasten System ist das eigentlich kein Problem, trotzdem sollte Dein Code damit zuecht kommen.
Also erstmal vielen Dank fuer deine Antwort...sehr viel Informationen :-)
Kann ich denn 2 ISR mit dem gleichen Timer laufen lassen...hab ja schon eine ISR fuer meine Zählvariable...und mit dem itoa() das versteh ich irgendwie nicht so recht...hab mein Programm mal mit Hilfe des Tutotrials so weit verändert wie es ging...
//define all external dependencies
#include <avr/io.h>
#include <avr/interrupt.h>
//declare all used functions
void Sensor_init(void);
void LED_SW_init(void);
void uart_init(void);
void TIMER2_interrupt_init(void);
//define variables
volatile unsigned int z;
volatile unsigned int sekunde;
volatile unsigned int T1;
volatile unsigned int T2;
volatile unsigned int TD;
void uart_init(void)
{
UCSRB |= (1<<TXEN); // UART TX einschalten
UCSRC |= (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); // Asynchron 8N1
UBRRH = UBRR_VAL >> 8;
UBRRL = UBRR_VAL & 0xFF;
}
void TIMER2_interrupt_init(void)
{
TCCR2 = (1<<WGM21)|(0<<WGM20)|(1<<CS22)| (1<<CS21) |(0<<CS20); //CTC mode und Prescaler auf 256
TCNT2=0;
OCR2=199; //alle 0.008 s Interrupt
TIMSK = (1<<OCIE2);
}
ISR(TIMER2_COMP_vect) //LED soll alle 1s leuchten
{
z++;
if(z==72)
{
PORTB ^= (1<<PB0);
z=0;
}
}
int main(void)
{
Sensor_init();
LED_SW_init();
uart_init();
TIMER2_interrupt_init();
while(1)
{
if(!(PINA & (1<<PINA0))) //Mit Pin 0 soll T1 gespeichert werden
{
T1 = sekunde;
}
if(!(PINA & (1<<PINA1))) //Mit Pin 1 soll T2 gespeichert werden und die Differenz berechnet werden
{
T2 = sekunde;
TD = T2-T1;
}
}
void Sensor_init(){
DDRA=0x00; // For using the floating sensor the Port A has to be used as a digital input
PORTA=0xff; // activate internal pull-up resistors for PORTA
}
void LED_SW_init(){
DDRB=0xFF; // PORTB is output for LEDs
PORTB=0xFF; // all LEDs off
}
//-------------------------------------------------------------------------------------------------//
/* ATmega16 */
int uart_putc(unsigned char c)
{
while (!(UCSRA & (1<<UDRE))) /* warten bis Senden moeglich */
{
}
UDR = c; /* sende Zeichen */
return 0;
}
/* puts ist unabhaengig vom Controllertyp */
void uart_puts (char *s)
{
while (*s)
{ /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
uart_putc(*s);
s++;
}
}
//-------------------------------------------------------------------------------------------------//
Weiss nicht so recht wie ich da jetzt meine Variable TD unterkriegen soll !? Un dass er mir die auch noch sendet...also als Terminalprogramm verwende ich hterm und als Entwicklungsumgebung avrstudio4. Mein Board ist ein stk500 und ich habe einen Atmega16 drauf. Mein groesstes Problem ist wie gesagt das Senden eines Variableninhalts und wo ich das unterbringen muss...macht man so etwas in main() in der Schleife oder irgendwie anders ?????
Viele Gruesse,
Jimmy
Also ich hab jetzt nochmal die Randbedingungen fuer die UART-Kommunikation eingefuegt...das Senden eines Strings hab ich schon mal in einem anderen Programm hingekriegt...jetzt fehlt mir noch wie ich die Variable auslese...ich weiss nicht wie das funktioniert...von der Programmiersprache her...da habe ich wohl noch viel zu Lernen:-)
Vorerst möchte ich auch wenn es ziemlich ungenau ist ersteinmal die Variable senden und am PC sehen können...erst dann wuerde ich mich gerne mit der Genauigkeit beschäftigen und ggfs. umprogrammieren...Hier hab ich nochmal meinen aktuellen Code...es wuerde mir sehr viel helfen wenn ihr vielleicht da drin den Änderungen vornehmt...Die neuen Teile hab ich so abgegrenzt : //----
Vielen Dank und viele Gruesse
//define all external dependencies
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <stdint.h>
#include <avr/interrupt.h>
//-------------------------------------------------------------------------------------------------//
#ifndef F_CPU
#warning "F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000"
#define F_CPU 3686400 // Systemtakt in Hz
#endif
#define BAUD 9600UL // Baudrate
// Berechnungen
#define UBRR_VAL (((F_CPU)/(BAUD*16))-1) // clever runden
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1))) // Reale Baudrate
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) // Fehler in Promille
//#if ((BAUD_ERROR>10) || (BAUD_ERROR<=10))
//#error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch!
//#endif
//-------------------------------------------------------------------------------------------------//
//declare all used functions
void Sensor_init(void);
void LED_SW_init(void);
void uart_init(void);
void TIMER2_interrupt_init(void);
//define variables
volatile unsigned int z;
volatile unsigned int sekunde;
volatile unsigned int T1;
volatile unsigned int T2;
volatile unsigned int TD;
void uart_init(void)
{
UCSRB |= (1<<TXEN); // UART TX einschalten
UCSRC |= (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); // Asynchron 8N1
UBRRH = UBRR_VAL >> 8;
UBRRL = UBRR_VAL & 0xFF;
}
void TIMER2_interrupt_init(void)
{
TCCR2 = (1<<WGM21)|(0<<WGM20)|(1<<CS22)| (1<<CS21) |(0<<CS20); //CTC mode und Prescaler auf 256
TCNT2=0;
OCR2=199; //alle 0.008 s Interrupt
TIMSK = (1<<OCIE2);
}
ISR(TIMER2_COMP_vect) //LED soll alle 1s leuchten
{
z++;
if(z==72)
{
PORTB ^= (1<<PB0);
z=0;
}
}
int main(void)
{
Sensor_init();
LED_SW_init();
uart_init();
TIMER2_interrupt_init();
while(1)
{
if(!(PINA & (1<<PINA0))) //Mit Pin 0 soll T1 gespeichert werden
{
T1 = sekunde;
}
if(!(PINA & (1<<PINA1))) //Mit Pin 1 soll T2 gespeichert werden und die Differenz berechnet werden
{
T2 = sekunde;
TD = T2-T1;
}
}
void Sensor_init(){
DDRA=0x00; // For using the floating sensor the Port A has to be used as a digital input
PORTA=0xff; // activate internal pull-up resistors for PORTA
}
void LED_SW_init(){
DDRB=0xFF; // PORTB is output for LEDs
PORTB=0xFF; // all LEDs off
}
//-------------------------------------------------------------------------------------------------//
/* ATmega16 */
int uart_putc(unsigned char c)
{
while (!(UCSRA & (1<<UDRE))) /* warten bis Senden moeglich */
{
}
UDR = c; /* sende Zeichen */
return 0;
}
/* puts ist unabhaengig vom Controllertyp */
void uart_puts (char *s)
{
while (*s)
{ /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
uart_putc(*s);
s++;
}
}
//-------------------------------------------------------------------------------------------------//
jetzt fehlt mir noch wie ich die Variable auslese
Die Variable wird in einen String umgewandelt und dann gesendet!
itoa(variable,string)
sprint(string)
Wenns ne long Variable ist, dann wirds halt ltoa(variable,string).
Hatte Ich Dir aber in diesem Threat schon 2x geschrieben;-)
mhhh ja gut....trotzdem danke :-)
Ich bin noch ziemlich unerfahren...hab also net wirklich ahnung...wuerds aber trotzdem gern verstehen :-)
Wenn ich jetzt die Variable umwandele...wo lass ich sie mir denn dann schicken...da wo ich die Differenz bilde...und den Rest in meinem Programm kann ich dann so lassen !? Wuerde es dir was ausmachen die Umwandlung und das Senden vielleicht in "meinen Code" reinzuschreiben !?
Sorry, das sich dir so viel Muehe bereite :-)
MFG
Wie wärs hier!
if(!(PINA & (1<<PINA1))) //Mit Pin 1 soll T2 gespeichert werden und die Differenz berechnet werden
{
unsigned char uc_string[6]; //Eine String Variable mit 6Stellen wird eingerichtet
unsigned char i=0; //Eine Schleifenvariable wird eingerichtet
T2 = sekunde;
TD = T2-T1;
itoa(TD,uc_string);
//nun musst Du nur noch uc_string über UART senden und <CR><LF> anhängen- fertig
for(i=0;i<strlen(uc_string);i++);
{
uart_putc(uc_string[i]);
}
uart_putc('\r'); //<CR> und <LF> anfügen
uart_putc('\n');
}
Übrigens, der Befehl itoa ist bei mir (CodeVision AVR) in der stdlib.h, die man natürlich includen muß.
#include <stdlib.h>
Wo der Befehl bei AVRGCC ist weiß ich nicht, sollte aber in der Hilfe zu finden sein.
Vielen Dank für deine Antwort...also hab jetzt mal reingeschrieben...kanns leider heut net mehr ausprobieren...erst morgen früh wieder...muss ich an dem Code jetzt noch etwas ändern !? Oder meinst du das funktioniert so mit dem Senden !? Krieg ich dann pro Tastendruck den Zählstand angezeigt!? Und was macht eigentlich uart_puts..im Tutorial versteh ich das nicht....
Vielen Dank nochmal...
Jimmy
//define all external dependencies
#include <avr/io.h>
#include <avr/interrupt.h>
//declare all used functions
void Sensor_init(void);
void LED_SW_init(void);
//define variables
volatile unsigned int z;
volatile unsigned int sekunde;
volatile unsigned int T1;
volatile unsigned int T2;
volatile unsigned int TD;
void TIMER2_interrupt_init(void)
{
TCCR2 = (1<<WGM21)|(0<<WGM20)|(1<<CS22)| (1<<CS21) |(0<<CS20); //CTC mode und Prescaler auf 256
TCNT2=0;
OCR2=249; //alle 0.008 s Interrupt
TIMSK = (1<<OCIE2);
}
ISR(TIMER2_COMP_vect) /Sekunde hochzählen
{
z++;
if(z==125)
{
sekunde++;
z=0;
}
}
int main(void)
{
Sensor_init();
LED_SW_init();
TIMER2_interrupt_init();
while(1)
{
if(!(PINA & (1<<PINA0))) //Mit Pin 0 soll T1 gespeichert werden
{
T1 = sekunde;
}
if(!(PINA & (1<<PINA1))) //Mit Pin 1 soll T2 gespeichert werden und die Differenz berechnet werden
{
unsigned char uc_string[6]; //Eine String Variable mit 6Stellen wird eingerichtet
unsigned char i=0; //Eine Schleifenvariable wird eingerichtet
T2 = sekunde;
TD = T2-T1;
itoa(TD,uc_string);
//nun musst Du nur noch uc_string über UART senden und <CR><LF> anhängen- fertig
for(i=0;i<strlen(uc_string);i++);
{
uart_putc(uc_string[i]);
}
uart_putc('\r'); //<CR> und <LF> anfügen
uart_putc('\n');
}
}
void Sensor_init(){
DDRA=0x00; // For using the floating sensor the Port A has to be used as a digital input
PORTA=0xff; // activate internal pull-up resistors for PORTA
}
void LED_SW_init(){
DDRB=0xFF; // PORTB is output for LEDs
PORTB=0xFF; // all LEDs off
}
//-------------------------------------------------------------------------------------------------//
/* ATmega16 */
int uart_putc(unsigned char c)
{
while (!(UCSRA & (1<<UDRE))) /* warten bis Senden moeglich */
{
}
UDR = c; /* sende Zeichen */
return 0;
}
/* puts ist unabhaengig vom Controllertyp */
void uart_puts (char *s)
{
while (*s)
{ /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
uart_putc(*s);
s++;
}
}
//-------------------------------------------------------------------------------------------------//
/* ATmega16 */
int uart_putc(unsigned char c)
{
while (!(UCSRA & (1<<UDRE))) /* warten bis Senden moeglich */
{
}
UDR = c; /* sende Zeichen */
return 0;
}
Was tut das!?
Es wird gewartet bis das UDRE Bit im UCSRA Register 1 ist.
Wenn ich mich recht entsinne bedeutet das, das das Senderegister leer ist.
while(!(UCSRA&(1<<UDRE)));
Dann wird das UDR mit dem gewünschten Zeichen beschrieben: UDR=c;
Die Routine stammt aus Deinem Code! - Eigentlich solltest Du dann schon wissen, was die Routine tut!
Die Routine gibt noch einen int Wert zurück, damit die aufrufende Routine weiß, das sie ausgeführt wurde.
Das wurde in meinem Codeschnipsel nicht berücksichtigt und macht hier auch keinen Sinn, weil ja keine anderen Werte als 0 zurück gegeben werden können.
Es würde Sinn machen, wenn da noch ein Timeout oder sonst eine Fehlererkennung drin wäre, die andere return Werte als 0 generieren kann.
Der aufruf müsste dann lauten:
status=uart_putc(uc_string[i]);
Der Wert 0, den die Routine dann zurück gibt wäre dann in der int Variable status zu finden.
Da dort aber nur der Wert 0 auftauchen kann, kann man sich die rückgabe des wertes gleich schenken also aus:
int uart_putc(unsigned char c)
wird
void uart_putc(unsigned char c)
das
return 0;
entfällt ersatzlos.
Diese Senderoutine verschwendet aber eine Menge Rechenzeit - das ist Dir hoffentlich klar!
Der Controller wartet ja so lange, bis 8Bit + Start und Stoppbit, also 10 Bits, eines Zeichens komplett über die TxD Leitung versendet ist.
Das dauert bei 9600bit/sek ca. 1ms. In dieser Zeit hätte der Controller bei 8MHz 8000Befehle abarbeiten können!
Das ist auch der Grund, warum man solche Funktionen über Interrupts erledigt.
Auch der USART kann Interruptgetriggert arbeiten!
Man schreibt dazu die zu sendenden Daten in einen Ringpuffer und eine Sendeinteruptroutine holt sich diese Daten dann ab und versentet sie dann ohne weiteres zutun des Hauptprogrammes im Transmit Interrupt des USART.
Aber arbeite erstmal mit Deiner Methode weiter.
Das kann man dann bei mehr Programmiererfahrung immer noch mal umbasteln.
Also das mit dem Umwandeln hab ich soweit hingekriegt...mein Programm sendet mir auch immer meinen Wert an den PC wenn ich den 2ten Taster druecke...leider funktioniert mein Timer in dem Programm nicht mehr...hab in der ISR die LED eingebaut die doch eigentlich im sekundentakt blinken muesste...warum läuft denn mein Timer nicht mehr...hier mein Programm:
Vielen Dank und viele Gruesse
//define all external dependencies
#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <stdint.h>
#include <avr/interrupt.h>
//-------------------------------------------------------------------------------------------------//
#ifndef F_CPU
#warning "F_CPU war noch nicht definiert, wird nun nachgeholt mit 4000000"
#define F_CPU 3686400 // Systemtakt in Hz - Definition als unsigned long beachten
#endif
#define BAUD 9600UL // Baudrate, das L am Ende ist wichtig, NICHT UL verwenden!
// Berechnungen
#define UBRR_VAL (((F_CPU)/(BAUD*16))-1) // clever runden
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1))) // Reale Baudrate
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD-1000) // Fehler in Promille
//#if ((BAUD_ERROR>10) || (BAUD_ERROR<=10))
//#error Systematischer Fehler der Baudrate grösser 1% und damit zu hoch!
//#endif
//-------------------------------------------------------------------------------------------------//
//declare all used functions
void Sensor_init(void);
void LED_SW_init(void);
void uart_init(void);
void TIMER2_interrupt_init(void);
//define variables
volatile unsigned int z = 0;
volatile unsigned int sekunde =0;
void sendString(char* string, int length) {
for (int c=0; c<length; c++) {
while (!(UCSRA & (1<<UDRE))) // warten bis Senden moeglich
{
}
UDR = string[c];
}
}
void uart_init(void)
{
UCSRB |= (1<<TXEN); // UART TX einschalten
UCSRC |= (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); // Asynchron 8N1
UBRRH = UBRR_VAL >> 8;
UBRRL = UBRR_VAL & 0xFF;
}
void TIMER2_interrupt_init(void)
{
TCCR2 = (1<<WGM21)|(0<<WGM20)|(1<<CS22)| (1<<CS21) |(0<<CS20); //CTC mode und Prescaler auf 256
TCNT2=0;
OCR2=199; //alle 0.008 s Interrupt
TIMSK = (1<<OCIE2);
}
ISR(TIMER2_COMP_vect) //LED soll alle 1s leuchten
{
z++;
if(z==72)
{
PORTB ^= (1<<PB0);
sekunde++;
z=0;
}
}
int main(void)
{
Sensor_init();
LED_SW_init();
uart_init();
TIMER2_interrupt_init();
int T1=0;
int T2=0;
int TD=0;
char myTestString[20]; // char-Array deklarieren
while(1)
{
if(!(PINA & (1<<PINA0))) //Mit Pin 0 soll T1 gespeichert werden
{
T1 = sekunde;
}
if(!(PINA & (1<<PINA1))) //Mit Pin 1 soll T2 gespeichert werden und die Differenz berechnet werden
{
T2 = sekunde;
TD = T2-T1;
sprintf(myTestString, "Zeit %d ", TD); // String formatieren
sendString(myTestString, 10); // String schicken
}
}
}
void Sensor_init(){
DDRA=0x00; // For using the floating sensor the Port A has to be used as a digital input
PORTA=0xff; // activate internal pull-up resistors for PORTA
}
void LED_SW_init(){
DDRB=0xFF; // PORTB is output for LEDs
PORTB=0xFF; // all LEDs off
}
Ich kann Zeit stoppen :-) Hab sei(); vergessen :-)
Und jetzt wird sie mir angezeigt....meeeeega cool :-) Vielen vielen Dank fuer deine Hilfe!!!!!!
Weisst du wie genau meine Zeitmessung hier ist...
Viele Gruesse Jimmy
Weisst du wie genau meine Zeitmessung hier ist...
Super wenns läuft.
Wie genau das Ganze ist lässt sich so einfach gar nicht beantworten.
Ich würd den Code mal mit dem Simulator von AVR Studio austesten und dabei mit der Stoppuhr die Zeit für einen Schleifendurchlauf der Hauptschleife messen.
Das wär schon mal ein Anhaltspunkt.
Da ja im Hauptprogramm verschiedene Programmteile und Funktionsaufrufe durchlaufen werden können ändert sich dies Zeit natürlich.
Mhhh...die Zeit is ziemlich genau messbar..kommt bis auf eine Sekunde eigentlich immer der gleiche Wert raus...und das hängt auch davon ab wie genau ich beide Taster zeitgleich drücke...also fuer meine Anwendung reicht die Genauigkeit auf jeden Fall!! Jetzt wird erstmal gestoppt was das Zeug hält :-) Ich werd mal schaun ob ch irgendwie was über diese Interruptausgabe da find...hast du da vielleicht nen Anhaltspunkt bzw. nen guten Link? Danke nochmal fuer deine Hilfe...hast mir sehr geholfen !!!
Gruss Jimmy
Ich versuch das mal mit einem Codeschnipsel für Codevision AVR.
Wie das dann bei AVR GCC auszusehen hat musst Du selber testen.
//Paremeter definitionen
#define RXB8 1
#define TXB8 0
#define UPE 2
#define OVR 3
#define FE 4
#define UDRE 5
#define RXC 7
#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<OVR)
#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)
//USART Puffer einrichten = Maximale Zeichenanzahl
// USART Transmitter buffer
#define TX_BUFFER_SIZE 64
char tx_buffer[TX_BUFFER_SIZE];
#if TX_BUFFER_SIZE<256
unsigned char tx_wr_index,tx_rd_index,tx_counter;
#else
unsigned int tx_wr_index,tx_rd_index,tx_counter;
#endif
//Sende Interrupt Routine
// USART Transmitter interrupt service routine
interrupt [USART_TXC] void usart_tx_isr(void)
{
if (tx_counter)
{
--tx_counter;
UDR=tx_buffer[tx_rd_index];
if (++tx_rd_index == TX_BUFFER_SIZE) tx_rd_index=0;
};
}
//Neudefinition des putchar Befehles
#ifndef _DEBUG_TERMINAL_IO_
// Write a character to the USART Transmitter buffer
#define _ALTERNATE_PUTCHAR_
#pragma used+
void putchar(char c)
{
while (tx_counter == TX_BUFFER_SIZE);
#asm("cli")
if (tx_counter || ((UCSRA & DATA_REGISTER_EMPTY)==0))
{
tx_buffer[tx_wr_index]=c;
if (++tx_wr_index == TX_BUFFER_SIZE) tx_wr_index=0;
++tx_counter;
}
else
UDR=c;
#asm("sei")
}
#pragma used-
#endif
...
//Initialisierung des USART
// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: Off
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud Rate: 38400
UCSRA=0x00;
UCSRB=0x48;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x0C;
...
//Ausgabe des Strings uc_textbuffer mittels eines Schleifenbefehls
//Die Zeichen werden im Puffer abgelegt und durch die Interruptroutine
//selbstständig versendet
for(uc_i=0;uc_i<strlen(uc_textbuffer);uc_i++)
{
putchar(uc_textbuffer[uc_i]);
}
putchar('\r');
putchar('\n');
Bis auf die Stringausgabe wurde das Programm durch den Compiler erzeugt.
Der USART Sendepuffer geht natürlich auf Kosten des RAM Speichers, da der Bereich ja ständig freigehalten werden muß.
Man sollte ihn also nur so groß wählen wie man ihn zur Ausgabe der gewünschten Zeichen wirklich braucht.
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.