Liebe Leute,
Ich hoffe, jemand von Euch kann mir bei meinem Problem helfen
Ich programmiere das ArduinoUno Board (ATmega328p-µC) in Atmel. Bei der
seriellen Kommunikation hänge ich jedoch.
Code:
Nach dem Builden und danach Draufspielen aufs Arduino öffne ich das Tera
Term, welches mir ein Signal ans Board schicken soll.
Die RX-LED am Board blinkt kurz, was bedeutet, dass das Signal
angekommen ist. Die an PORTB3 angeschlossene LED leuchtet aber nicht.
Woran kann das Problem liegen? Tipps?
MfG
Uwe S. schrieb:> Hallo,>> bei deinen globalen Variable, mit ISR Zugriff, muss noch das Wort> "volatile" ergänzt werden.
habs ergänzt, funktioniert leider noch immer nicht :(
Darko Jen schrieb:> Uwe S. schrieb:>> Hallo,>>>> bei deinen globalen Variable, mit ISR Zugriff, muss noch das Wort>> "volatile" ergänzt werden.>> habs ergänzt, funktioniert leider noch immer nicht :(
Eine recht gute Strategie ist es, zuerst mal die Kommunikation vom Board
zum Terminal in Betrieb zu nehmen, damit du dein Programm in die Lage
versetzt dir bei der Fehleranalyse zu helfen.
Also vergiss erst mal die Übertragung vom PC zum AVR, und kümmere dich
um die umgekehrte Richtung.
Du willst, dass ein
1
....
2
intmain(void)
3
{
4
UBRR0H=(BRC>>8);
5
UBRR0L=BRC;
6
7
UCSR0B=(1<<TXEN0);
8
UCSR0C=(1<<UCSZ01)|(1<<UCSZ00);
9
10
while(1)
11
{
12
putChar('x');
13
}
14
}
dir lauter x aufs Terminal zaubert.
Die nächste Funktion, die du haben willst, ist die putString.
1
voidputString(constchar*s)
2
{
3
while(*s)
4
putChar(*s++);
5
}
denn damit kannst du dann
1
intmain()
2
{
3
...
4
while(1)
5
{
6
putString("Hallo\n\r");
7
}
8
}
schon Texte aufs Terminal ausgeben.
Und mit diesen beiden Hilfsmitteln bist du dann schon in der Lage,
deinen Empfangscode so zu pimpen, dass er dir mitteilt, was über die
UART reinkommt
1
ISR(USART_RX_vect)
2
{
3
rxBuffer[rxWritePos]=UDR0;
4
putChar(rxBuffer[rxWritePos]);
5
6
rxWritePos++;
und du kannst zum ersten Mal deinem Programm am Terminal dabei zusehen,
welche Zeichen es empfängt.
Wenn die stimmen, dann kann man natürlich auch in der main mal eine
Testausgabe machen lassen.
1
intmain()
2
{
3
...
4
5
while(1)
6
{
7
charc=getChar();
8
9
putString("Empfangen: ");
10
putChar(c);
11
12
if(c=='a')
13
....
Also: Prioritäten ändern!
Zuerst musst du dein Programm in die Lage versetzen sich bemerkbar zu
machen. Und dann überträgst du vom PC zum AVR. Denn in dieser
Reihenfolge kann dir der AVR helfen. Anders rum eher nicht, weil du
keine Möglichkeit hast, eindeutig festzustellen, was aus der UART
rauskommt. Wenn überhaupt.
>Die an PORTB3 angeschlossene LED leuchtet aber nicht.>Woran kann das Problem liegen? Tipps?
Falsche Baudrate.
F_CPU im Code zu definieren heisst noch lange nicht
das die CPU auch mit diesem Takt läuft.
Falsch gesetze Fuses machen dir schnell einen
Strich durch die Rechnung.
Karl Heinz schrieb:> Also vergiss erst mal die Übertragung vom PC zum AVR, und kümmere dich> um die umgekehrte Richtung.
Und wenn das Senden vom AVR funktioniert, kannst Du Dich wieder ums
Empfangen kümmern.
Darko Jen schrieb:> uint8_t rxReadPos = 0;> uint8_t rxWritePos = 0;
volatile wurde schon erwähnt. Außerderm kannst Du Dir die
Initialisierung mit 0 sparen, da sie sowieso Default ist.
> char peekChar(void)> {> char ret = '\0';>> if(rxReadPos != rxWritePos)> {> ret = rxBuffer[rxReadPos];> }> return ret;> }
Die Funktion sollte so funktionieren. Da rxReadPos volatile ist, muß der
Compiler die Variable 2 mal aus dem RAM laden. Mit einer Hilfsvariablen
kann ein RAM-Zugriff wegoptimiert werden. Bei dieser einfachen Funktion
bringt das wenig, aber das Prinzip ist nützlich für komplexere
Funktionen.
[/c]
char peekChar(void)
{
char ret = '\0';
uint8_t ri = rxReadPos;
if(ri != rxWritePos)
{
ret = rxBuffer[ri];
}
return ret;
}
[/c]
> char getChar(void)> {> char ret = '\0';>> if(rxReadPos != rxWritePos)> {> ret = rxBuffer[rxReadPos];> rxReadPos++;> if(rxReadPos >= RX_BUFFER_SIZE)> {> rxReadPos = 0;> }> }> return ret;> }
Hier bringt die Optimierung schon mehr, da rxReadPos sonst 4 mal gelesen
und 2 mal geschrieben wird.
1
chargetChar(void)
2
{
3
charret='\0';
4
uint8_tri=rxReadPos;
5
6
if(ri!=rxWritePos)
7
{
8
ret=rxBuffer[ri];
9
ri++;
10
if(ri>=RX_BUFFER_SIZE)
11
{
12
ri=0;
13
}
14
rxReadPos=ri;
15
}
16
returnret;
17
}
> ISR(USART_RX_vect)> {> rxBuffer[rxWritePos] = UDR0;> rxWritePos++;> if(rxWritePos >= RX_BUFFER_SIZE)> {> rxWritePos = 0;> }> }
Hier fehlt die Überprüfung ob der Puffer voll ist.
Da Du '\0' als "Puffer leer" wertest, würde ich dieses Zeichen auch
nicht in den Puffer schreiben.
1
ISR(USART_RX_vect)
2
{
3
uint8_twi;
4
charc=UDR0;
5
6
if(c!='\0')
7
{
8
wi=rxWritePos;
9
/* Eine Position im Puffer ist garantiert immer frei. */
10
rxBuffer[wi]=c;
11
wi++;
12
if(wi>=RX_BUFFER_SIZE)
13
{
14
wi=0;
15
}
16
/* Der Write-Index darf den Read-Index nicht überholen */
17
/* Deshalb WritePos nur speichern, wenn ReadPos noch nicht eingeholt wurde */
...
...
> Hier fehlt die Überprüfung ob der Puffer voll ist.> Da Du '\0' als "Puffer leer" wertest, würde ich dieses Zeichen auch> nicht in den Puffer schreiben.>
1
>ISR(USART_RX_vect)
2
>{
3
>uint8_twi;
4
>charc=UDR0;
5
>
6
>if(c!='\0')
7
>{
8
>wi=rxWritePos;
9
>/* Eine Position im Puffer ist garantiert immer frei. */
10
>rxBuffer[wi]=c;
11
>wi++;
12
>if(wi>=RX_BUFFER_SIZE)
13
>{
14
>wi=0;
15
>}
16
>/* Der Write-Index darf den Read-Index nicht überholen */
17
>/* Deshalb WritePos nur speichern, wenn ReadPos noch nicht eingeholt
18
> wurde */
19
>if(wi!=rxReadPos)
20
>{
21
>rxWritePos=wi;
22
>}
23
>}
24
>}
25
>
Danke Leo für deine Optimierung vom Code.
Leider ändert das nichts an der Funktion. Die LED leuchtet nicht.
Lediglich die RX-LED am Board selber leuchtet jedes Mal beim Tippen
eines Characters, sonst nichts. Hast noch einen Vorschlag?
Karl Heinz schrieb:> Darko Jen schrieb:>> Uwe S. schrieb:> ....> int main( void )> {> UBRR0H = (BRC >> 8);> UBRR0L = BRC;>> UCSR0B = (1 << TXEN0);> UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);>> while(1)> {> putChar( 'x' );> }> }> [/c]>> dir lauter x aufs Terminal zaubert.> Die nächste Funktion, die du haben willst, ist die putString.>>
1
>voidputString(constchar*s)
2
>{
3
>while(*s)
4
>putChar(*s++);
5
>}
6
>
> denn damit kannst du dann>
1
>intmain()
2
>{
3
>...
4
>while(1)
5
>{
6
>putString("Hallo\n\r");
7
>}
8
>}
9
>
> schon Texte aufs Terminal ausgeben.>> Und mit diesen beiden Hilfsmitteln bist du dann schon in der Lage,> deinen Empfangscode so zu pimpen, dass er dir mitteilt, was über die> UART reinkommt>
1
>ISR(USART_RX_vect)
2
>{
3
>rxBuffer[rxWritePos]=UDR0;
4
>putChar(rxBuffer[rxWritePos]);
5
>
6
>rxWritePos++;
7
>
>> und du kannst zum ersten Mal deinem Programm am Terminal dabei zusehen,> welche Zeichen es empfängt.>> Wenn die stimmen, dann kann man natürlich auch in der main mal eine> Testausgabe machen lassen.>>
1
>intmain()
2
>{
3
>...
4
>
5
>while(1)
6
>{
7
>charc=getChar();
8
>
9
>putString("Empfangen: ");
10
>putChar(c);
11
>
12
>if(c=='a')
13
>....
14
>
>> Also: Prioritäten ändern!> Zuerst musst du dein Programm in die Lage versetzen sich bemerkbar zu> machen. Und dann überträgst du vom PC zum AVR. Denn in dieser> Reihenfolge kann dir der AVR helfen. Anders rum eher nicht, weil du> keine Möglichkeit hast, eindeutig festzustellen, was aus der UART> rauskommt. Wenn überhaupt.
Danke Karl Heinz für deine Hilfe,
den 1. Schritt (Character-Ausgabe) habe ich somit geschafft.
Bei der Stringausgabe hänge ich leider - anstatt im Sekundentakt "Hallo"
auszugeben, werden jeweils 2xDs ausgegeben ("DD" "DD"...)
Wie kommt es dazu?
LG
> Bei der Stringausgabe hänge ich leider - anstatt im Sekundentakt "Hallo"> auszugeben, werden jeweils 2xDs ausgegeben ("DD" "DD"...)> Wie kommt es dazu?
Die Frage müßte lauten: "Wieso werden nur jeweils zwei 'D'
ausgegeben?"
"Hallo\n\r" hat schließlich 7 Zeichen.
Hint: UDRE0
Bernhard Schröcker schrieb:> Na dann schau mal was Deine putchar Funktion macht, dann weißt auch> warum das so ist.>> Grüße Bernhard...
Ich weiß schon, was die putchar Funktion macht, leider weiß ich aber
nicht, wie ich das Problem behebe. Daher bitte ich auch um Hilfe,
wodurch ich dann einiges dazulernen würde
Leo C. schrieb:>> Bei der Stringausgabe hänge ich leider - anstatt im Sekundentakt "Hallo">> auszugeben, werden jeweils 2xDs ausgegeben ("DD" "DD"...)>> Wie kommt es dazu?>> Die Frage müßte lauten: "Wieso werden nur jeweils zwei 'D'> ausgegeben?"> "Hallo\n\r" hat schließlich 7 Zeichen.>> Hint: UDRE0
Richtig übersetzt
Ins UDR0 wird 'D' reingeschrieben. Durch die putString Funktion (s++)
wird dann 2x 'D' ausgegeben.
> "Hallo\n\r" hat schließlich 7 Zeichen.
schon klar, wie soll ich die nun ausgeben?
1. Schau mal nach, wie putString() funktioniert. Welche Funktion ruft
sie auf?
> Ins UDR0 wird 'D' reingeschrieben. Durch die putString Funktion (s++)> wird dann 2x 'D' ausgegeben.
Nein, nicht 2x, es wird 7x 'D' ins UDR0 geschrieben. Daß Du nur 2 davon
zu sehen bekommst, liegt daran, daß Du die Zeichen schneller in UDR0
schreibst, als sie gesendet werden können.
2. UDRE0 != UDR0
Liebe Leute,
Ich bedanke mich für eure Hilfe. Der Code funktioniert nun
Was ich eigenartig finde ist, dass die LED nur dann leuchtet, wenn die
UART Initialisierung in einer eigenen Funktion steht.
Nun die Frage: Was muss hier am Code noch genau verändert werden, um
eine Bluetooth Kommunikation bereitzustellen statt dem Terminal?
LED soll quasi per Smartphone ein und ausgeschaltet werden
http://www.seeedstudio.com/wiki/index.php?title=Bluetooth_Shield
Verwendeter Bluetooth-Chip: BC417
TX des Moduls wird mit PORTD7 des Boards verbunden
RX mit PORTD6
wie geht es nun weiter? Konfigurationen?
Jeder Vorschlag ist willkommen!!
Code:
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
#include<stdint.h>
4
#define F_CPU 16000000UL
5
#include<util/delay.h>
6
7
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // clear bit
8
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // set bit
9
10
#define FOSC 16000000 // 16MHz
11
#define BAUD 9600 // Baudrate 9600
12
#define BRC FOSC/16/BAUD-1 // Prescaler
13
14
#define RX_BUFFER_SIZE 128 // buffer 128 bits
15
charrxBuffer[RX_BUFFER_SIZE];
16
17
volatileuint8_trxReadPos;
18
volatileuint8_trxWritePos;
19
20
chargetChar(void);
21
22
voidUSART_Init(unsignedintubrr)// wenn direkt in Main, funktioniert nicht
23
{
24
//Set baud rate
25
UBRR0H=(ubrr>>8);
26
UBRR0L=ubrr;
27
28
UCSR0B=(1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);// Enable receiver and transmitter
29
UCSR0A=(1<<UDRE0);//richtiger Empfang ansonsten nur mit BAUD 19200 bei Terminal möglich
30
UCSR0C=(1<<UCSZ01)|(1<<UCSZ00);// Set frame: 8 bit data
Warum die Init in main() nicht gehen sollte, ist mir ein Rätsel.
Ansonsten:
> //Set baud rate> UBRR0H = (ubrr>>8);> UBRR0L = ubrr;
Der Compiler kennt auch UBRR0 als 16 bit Register.
1
//Set baud rate
2
UBRR0=ubrr;
Der generierte Code ist der Gleiche. Aber: weniger Sourcecode == weniger
(Tipp-)Fehler-(möglichkeiten).
> UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1<<RXCIE0); // Enable
Besser den USART erst enablen, wenn er komplett initialisiert ist. Diese
Zeile also ans Ende setzen.
> UCSR0A = (1 << UDRE0); //richtiger Empfang ansonsten nur mit BAUD
Was soll das?
> UDR0 = 'D';> _delay_ms(500);
Also, das willt Du wirklich nicht!
sondern:
1
while((UCSR0A&(1<<UDRE0))==0)
2
;/* warte bis TX Data Register empty */
3
UDR0='D';
Hätte nicht gedacht, daß man den Hinweis auf UDRE0 so mißverstehen kann.
Leo C. schrieb:> Was soll das?>> UDR0 = 'D';> _delay_ms(500);>> Also, das willt Du wirklich nicht!> sondern:>
1
>while((UCSR0A&(1<<UDRE0))==0)
2
>;/* warte bis TX Data Register empty */
3
>UDR0='D';
4
>
>> Hätte nicht gedacht, daß man den Hinweis auf UDRE0 so mißverstehen kann.
der Abschnitt des Codes wurde Anfangs verwendet, um ein 'D' ans Terminal
zu schicken. Jetzt wurde er nicht rausgelöscht, sondern auskommentiert.
Danke trotzdem