PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Frage zu Software Uart mit TimerOverflow Interrupt



fugitivus
25.11.2011, 10:41
Hi !
Ich versuche Krampfhaft einen Software Uart zu Basteln...
Er soll erstmal nur Empfangen können.
Mein Ansatz ist nicht der beste, ich dachte irgendwie das müsste so funktionieren aber scheinbar habe ich einen mega falschen Ansatz....

ich verwende 16Mhz, habe meinen Timer auf 300Hz gestellt, da ich erst mal mit 300 Baud am testen binn...
wo ist hier der Fehler in Meinem Ansatz ?




#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#include "init.h"
#include "lcd.h"


//SoftUART PINS
#define RXPIN 2
#define RXINT INT0
#define UARTDDR DDRD
#define UARTPIN PIND
#define UARTPORT PORTD
#define T0REL 152

char iOut[20];

unsigned char bitCnt = 0;
unsigned char udr = 0;


void cfgInt0(void)
{
MCUCR |= (1<<ISC01); //INT0 falling edge
GICR |= (1<<INT0); //INT0 interrupt enable
}

void cfgTimer0(void)
{
TCNT0 = T0REL;
TCCR0 |= (1<<CS02);
//TIMSK |=(1<<TOIE0);
}

void cfgSoftUart(void)
{
UARTDDR &=~ (1<<RXPIN); //RX Pin als Input
UARTPORT &=~ (1<<RXPIN); //RX Pin auf 0

cfgInt0(); //Software Uart Interrupt
cfgTimer0(); //Software Uart Timer
}


int main(void)
{
cfgSoftUart();
cfgLcd(LCD_ON_CURSOR_OFF);
lcdCls();
enableInterrupts();

_delay_ms(200);
lcdPrint("Software Uart",1,1);

while(1)

{
itoa(udr,iOut,10);
lcdPrint(" ",1,3);
lcdPrint(iOut,1,3);
itoa(udr,iOut,2);
lcdPrint(iOut,5,3);

_delay_ms(1000);
}
return 0;
}


ISR(INT0_vect)
{
GICR &=~ (1<<INT0);
TCNT0 = T0REL;
TIMSK |=(1<<TOIE0);
}

ISR(TIMER0_OVF_vect)
{
TCNT0 = T0REL;

if (!(PIND & (1 << PD2)))
{
udr &=~ (1<<bitCnt++);
}else
{
udr |= (1<<bitCnt++);
}

if(bitCnt == 7)
{
bitCnt = 0;
TIMSK &=~(1<<TOIE0);
GIMSK |= (1<<INT0);
}
}


Mir ist auch bewusst das ich momentan das Startbit mit einlese, ich sende mir eine 85(weil binär 01010101) bekomme aber immer eine 255.

Ich währe hier für jede Hilfe Dankbar!

Mfg Fugitivus

PicNick
25.11.2011, 11:00
damit man die Uart 0-er u. 1-er schön oder halbwegs in der Mitte erwischt, muss man nach der Start-Bit Flanke 1 1/2 mal die Bitbreite warten.

guckstu da:
http://www.rn-wissen.de/index.php/Software-UART_mit_avr-gcc

fugitivus
25.11.2011, 11:25
habs mit nem _delay_ms(5) inner int0 isr versucht... ich weiß das das nicht genau ist, hab auch 4 und 6 ausprobiert... das funktioniert auch nicht...

sternst
25.11.2011, 17:41
Bei welchem AVR soll denn die gezeigte Konfiguration 300 Interrupts pro Sekunde ergeben?
Und übrigens: hat dieser AVR kein CTC?


habs mit nem _delay_ms(5) inner int0 isr versucht... ich weiß das das nicht genau ist, hab auch 4 und 6 ausprobiert... das funktioniert auch nicht... Keines dieser Delays entspricht einer halben Bitlänge. Das wäre _delay_ms(1.67). Warum überhaupt ein Delay? Warum nicht einfach dort den Timer mit einem anderen Wert vorladen?

Und noch was: du ließt nur 7 Datenbits ein. Ist das Absicht?

fugitivus
25.11.2011, 18:48
Hi !
Du hast recht, das ergibt 600Hz war was das probiren angeht war ich schon nen stück weiter, habe es aber irgendwie übersehen das ich den vorladewert schon verändert habe zur halben bitlänge, 5ms sind 1,5 bitlängen um das startbit zu überspringen..... 0-7 ist doch 8 ^^ oder ? ^^

hab es ja jetzt mit 600Hz und nem flag versucht sprich bei der isr des timers wird nur jeder 2.overflow ausgewertet(was man in dieser ver des programms nicht sieht da ich irgendwie zu blöd binn in ner antwort code einzufügen) aber die idee mit 600Hz und dem flag führt immer noch zum selben Ergebniss.... Was ist denn ein CTC ?

sternst
25.11.2011, 19:14
5ms sind 1,5 bitlängen um das startbit zu überspringenDu musst ja aber nur ein halbes Bit überbrücken, denn mit dem Timer überbrückst du ja dann ein komplettes weiteres Bit. Aber wie gesagt, mit _delay_ms ist das sowieso Murks. Lass einfach den ersten Timer-Interrupt nach 1,5 Bits kommen.


0-7 ist doch 8 ^^ oder ?
udr |= (1<<bitCnt++);
}

if(bitCnt == 7)Beim Einlesen von Bit 6 wird bitCnt auf 7 erhöht, und dann greift sofort das "bitCnt == 7", also ließt du nur 0-6.


Was ist denn ein CTC ?Ein Timer-Mode, bei dem du nicht manuell den Timer vorladen musst, und damit sehr viel genauere Abstände zwischen den Interrupts hast.

fugitivus
26.11.2011, 09:54
Hi !
Erst mal Danke für deine Antworten....
Mist ich erhöhe ja wirklich nur bis 6 ^^(wo hab ich nur meinen Kopf)
Also Das mit dem CTC hört sich gut an, da muss ich mich gleich mal drüber schlau machen....
Hier Jetzt erst mal der versprochene Code, damit sollte es doch aber rein theoretisch bei 300Baud doch funktionieren oder ? (mit ausnahme davon das ich immer noch das startbit mit einlese....)
Also ich würde wirklich gerne erst mal versuchen dieses Beispiel irgendwie zum laufen zu Bringen, so rein für den Kopf und das Verständniss... Achso, der chip den ich verwende ist ein Mega8 bei 16MHz



#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#include "init.h"
#include "lcd.h"


//SoftUART PINS
#define RXPIN 2
#define RXINT INT0
#define UARTDDR DDRD
#define UARTPIN PIND
#define UARTPORT PORTD
#define T0REL 152

char iOut[20];

unsigned char bitFlag = 0;
unsigned char bitCnt = 0;
unsigned char udr = 0;


void cfgInt0(void)
{
MCUCR |= (1<<ISC01); //INT0 falling edge
GICR |= (1<<INT0); //INT0 interrupt enable
}

void cfgTimer0(void)
{
TCNT0 = T0REL;
TCCR0 |= (1<<CS02);
//TIMSK |=(1<<TOIE0);
}

void cfgSoftUart(void)
{
UARTDDR &=~ (1<<RXPIN); //RX Pin als Input
UARTPORT &=~ (1<<RXPIN); //RX Pin auf 0

cfgInt0(); //Software Uart Interrupt
cfgTimer0(); //Software Uart Timer
}


int main(void)
{
cfgPorts();
cfgSoftUart();
cfgLcd(LCD_ON_CURSOR_OFF);
lcdCls();
enableInterrupts();

_delay_ms(200);
lcdPrint("Software Uart",1,1);

while(1)

{
itoa(udr,iOut,10);
lcdPrint(" ",1,3);
lcdPrint(iOut,1,3);
itoa(udr,iOut,2);
lcdPrint(iOut,5,3);

_delay_ms(1000);
}
return 0;
}


ISR(INT0_vect)
{
GICR &=~ (1<<INT0);
TCNT0 = T0REL;
TIMSK |=(1<<TOIE0);
}

ISR(TIMER0_OVF_vect)
{
TCNT0 = T0REL;
if(bitFlag == 0)
{
bitFlag = 1;
}else
{
bitFlag = 0;
if(!(PIND & (1 << PD2)))
{
udr &=~ (1<<bitCnt++);
}else
{
udr |= (1<<bitCnt++);
}

if(bitCnt == 8)
{
bitCnt = 0;
TIMSK &=~(1<<TOIE0);
GIMSK |= (1<<INT0);
}
}
}



So sollte ich doch eigendlich die halbe bitzeit erwischen oder nicht ? Leider funktioniert das so immer noch nicht.... aber für 300 Baud sollte das mit dem timer overflow doch eigendlich genau genug sein oder nicht ?!?

Mfg Fugitivus

sternst
26.11.2011, 10:18
Du hast auch noch ein Problem mit deinen Interrupts. Auch wenn ein Interrupt gerade nicht enabled ist, setzt das entsprechende Ereignis doch das zugehörige Flag. Wenn der Interrupt dann enabled wird, kommt er sofort, weil das Flag ja schon gesetzt ist. Also musst du im INT0-Interrupt das Flag für den Overflow löschen, und im Overflow-Interrupt nach dem letzten Bit das Flag für INT0. Aber Achtung, schau erst im Datenblatt nach, wie man diese Flags löscht, ein "&=~" wäre da nämlich falsch.

PS: Benutze entweder die Bezeichnung GIMSK oder die Bezeichnung GICR. Beides zu benutzen verwirrt doch nur.

fugitivus
26.11.2011, 10:52
das mit den flags werde ich gleich mal kontrollieren ! hört sich irgendwie genau nach meinem problem an !
Danke !

Mfg Fugitivus

fugitivus
26.11.2011, 11:30
Das war also das was ich nicht wusste, wieder was dazu Gelernt !
Herzlichen Dank ! Dann werd ich mich jetzt mal mit CTC auseinandersetzen....

:p

Mfg Fugitivus

fugitivus
26.11.2011, 13:07
Habs mal mit 38400 Baud getestet funktioniert wunderbar....



#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#include "init.h"
#include "lcd.h"


//SoftUART PINS
#define RXPIN 2
#define RXINT INT0
#define UARTDDR DDRD
#define UARTPIN PIND
#define UARTPORT PORTD

#define T0REL 204
#define T0WAIT 48

char iOut[20];

unsigned char sFlag = 0;
unsigned char bitCnt = 0;
unsigned char udr = 0;


void cfgInt0(void)
{
MCUCR |= (1<<ISC01); //INT0 falling edge
GICR |= (1<<INT0); //INT0 interrupt enable
}

void cfgTimer0(void)
{
TCNT0 = T0WAIT;
TCCR0 = 1; //pre = 1
//TIMSK |=(1<<TOIE0);
}

void cfgSoftUart(void)
{
UARTDDR &=~ (1<<RXPIN); //RX Pin als Input
UARTPORT &=~ (1<<RXPIN); //RX Pin auf 0

cfgInt0(); //Software Uart Interrupt
cfgTimer0(); //Software Uart Timer
}


int main(void)
{
cfgPorts();
cfgSoftUart();
cfgLcd(LCD_ON_CURSOR_OFF);
lcdCls();
enableInterrupts();

_delay_ms(200);
lcdPrint("Software Uart",1,1);

while(1)

{
itoa(udr,iOut,10);
lcdPrint(" ",1,3);
lcdPrint(iOut,1,3);
itoa(udr,iOut,2);
lcdPrint(iOut,5,3);

_delay_ms(1000);
}
return 0;
}


ISR(INT0_vect)
{
GICR &=~ (1<<INT0);
TCNT0 = T0WAIT;
TCCR0 = 1;
TIFR |= (1<<TOV0);
TIMSK |=(1<<TOIE0);
}

ISR(TIMER0_OVF_vect)
{
TCNT0 = T0REL;

if(sFlag == 0)
{
sFlag = 1;
TCCR0 = 2;
}else
{
if(!(PIND & (1 << PD2)))
{
udr &=~ (1<<bitCnt++);
}else
{
udr |= (1<<bitCnt++);
}
}
if(bitCnt == 8)
{
bitCnt = 0;
sFlag = 0;
TIMSK &=~(1<<TOIE0);
GIFR |= (1<<INTF0);
GICR |= (1<<INT0);
}

}


mal gucken wie sowas mit ctc aussieht.....
noch mal super herzlichen dank !
mfg Fugitivus

sternst
26.11.2011, 16:34
TIFR |= (1<<TOV0);
GIFR |= (1<<INTF0);Das ist nicht korrekt. Damit löscht du gleich alle gesetzten Flags in dem jeweiligen Register. Bei deinem aktuellen Code ist es egal, ob nur ein bestimmtes Flag gelöscht wird, oder gleich alle, aber wenn du deinen Code dann mal erweiterst und mehr Interrupts benutzt, dann wird dich das irgendwann in den Hintern beißen.

fugitivus
27.11.2011, 11:18
Erst mal Danke für den Hinweis !

verstehe ich nicht so ganz.... weil ich es rein shifte ?

also im Datenblatt steht doch clear durch schreiben einer 1 für das entsprechende flag....

also statt



GIFR |= (1<<INTF0);


besser



GIFR |= 64; //???


bitte um genauere Erklährung :)

Hier noch mal Datenblattauszug:

• Bit 6 – INTF0: External Interrupt Flag 0
When an event on the INT0 pin triggers an interrupt request, INTF0 becomes set (one). If the Ibit
in SREG and the INT0 bit in GICR are set (one), the MCU will jump to the corresponding
Interrupt Vector. The flag is cleared when the interrupt routine is executed. Alternatively, the flag
can be cleared by writing a logical one to it. This flag is always cleared when INT0 is configured
as a level interrupt.

würde mich sehr freuen wenn du mir das genau erklährst...

Mfg Fugitivus

radbruch
27.11.2011, 11:33
Ausgeschrieben bedeutet es

GIFR = GIFR | 64;

und bewirkt, daß alle beim Auslesen von GIFR gesetzten Bits auch wieder geschrieben und deshalb mitgelöscht werden.

"...the flag can be cleared by writing a logical one to it." mit |= betrifft alle zu diesem Zeitpunkt gesetzten Flags!

fugitivus
27.11.2011, 11:51
also nen logisches & ?

fugitivus
27.11.2011, 12:04
sprich (wenn ich das richtig verstanden habe):

1100 0000
0100 0000 AND
0100 0000 =

radbruch
27.11.2011, 12:27
Ein schlichtes GIFR = 64; genügt. Dann wird nur dieses Bit mit "1" beschrieben und alle anderen Bits mit einer auf Flags wirkungslosen "0".

fugitivus
27.11.2011, 12:35
aber, jetzt mal rein um mir das brett wieder vom kopf zu nehmen währe das und auch ok ?!?
und wo wir schon mal dabei sind, ich habe nie den unterschied zwischen or und xor verstanden....
lust mir den mal gerade nahezulegen ? :p:confused:

das das mit dem or nicht klappt ist ja irgendwie logisch, manchmal sollte man mal nachdenken bevor man drauf los proggt ^^

Mfg Fugitivus

radbruch
27.11.2011, 14:17
Das "UND" wäre hier schon richtig. Allerdings unnötig und letzlich auch verwirrend, denn der ursprüngliche Inhalt des Registers interessiert ja gar nicht. Es soll lediglich die "1" ausgegeben werden um das Flag zu löschen.

Hier muss man sich von den gewohnten Formulierungen mit |= oder &= lösen, denn es geht nicht um einen Pin in einem Port dessen Pegel einzeln gewechselt werden soll.