Hoi,
Voor een projectje ben ik bezig met de atmega16820
Er komt data binnen op de uart in het formaat: 123.4
Dit gebeurd met een frequentie van 8Hz.
Hoe kan ik nu de uit de uart buffer lezen?
Kan iemand me een voorbeeldje geven?
De programmeertaal is C
Bij voorbaat dank,
JvdB
plantrekker
True story bro!
Voorbeeldcode staat in de datasheet. Ze maken daar gebruik van polling maar als je wil, kan het uiteraard ook onder interrupts.
Heb je mischien een linkje naar die datasheet?
Ik kan er geen vinden waar voorbeeldcode in staat.
JvdB
Ik heb de data sheet gevonden maar het geeft nog geen compleet antwoord op mijn vraag.
de avr heeft een buffer van 64kb FIFO
Hoe kan ik nu een willekeurige byte, bijv. byte 5, uitlezen?
JvdB
pros
Prosper, yop la boum, c'est le roi du macadam (aldus Maurice Chevalier)
Voorbeeldje:
#define F_cpu 16000000UL
#define UART_RX_BUFFER_SIZE 8
static char uart_outbuf[UART_RX_BUFFER_SIZE + 1];
static char uart_rxbuf[UART_RX_BUFFER_SIZE + 1];
volatile static unsigned char uart_rx_pos; // end of string in rx
volatile static unsigned char uart_rx_linecomplete; // one complete line in buffer
// Deze functie wordt aangeroepen, telkens er een karakter ontvangen werd
SIGNAL(SIG_USART_RECV)
{
if (uart_rx_pos > UART_RX_BUFFER_SIZE) { // prevent overflow
uart_rx_pos = 0;
}
uart_rxbuf[uart_rx_pos] = UDR0;
if ((uart_rxbuf[uart_rx_pos] == '\n')
|| (uart_rx_pos > UART_RX_BUFFER_SIZE - 1)) {
uart_rxbuf[uart_rx_pos] = 0;
strcpy(uart_outbuf, uart_rxbuf);
uart_rx_linecomplete = 1;
uart_rx_pos = 0;
} else {
uart_rx_pos++;
}
}
void uart_init(void)
{
uart_rx_pos = 0;
uart_rx_linecomplete = 0;
UCSR0B = _BV(TXEN0) | _BV(RXEN0) | _BV(RXCIE0); // enable tx/rx and interrupt on rx
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); // 8N1
UBRR0H = 0;
UBRR0L = (F_CPU / (16 * 38400UL)) - 1; // set baudrate - 38400bd
}
// Wait until tx holding register is empty, then send character c down the UART Tx.
void uart_sendchar(unsigned char c)
{
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = c;
}
// send string to the rs232
void uart_sendstr(char *s)
{
while (*s) {
uart_sendchar(*s);
s++;
}
}
Je past F_CPU aan naar wens, de grootte van UART_RX_BUFFER_SIZE, en ook de baudrate in uart_init(). Die functie roep je bij het initialiseren aan. Vervolgens hoeft main() enkel te kijken naar uart_rx_linecomplete. Als dit '1' is, betekent dit dat er wat bruikbaars is binnengekomen. main() moet wel uart_rx_linecomplete terug op 0 zetten.
De code gaat er van uit, dat elke string met een newline (\n) wordt afgesloten.
Kleine opmerking op je code pros:
De SIGNAL-macro is 'Deprecated'. Zie http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html (onderaan). Het is dus eigenlijk 'beter' om ISR samen met USART_RX_vect te gebruiken.
plantrekker
True story bro!
En nog een kleine opmerking
Op 9 juni 2010 21:36:50 schreef JvdB:
de avr heeft een buffer van 64kb FIFO
De USART module is maar enkelvoudig gebufferd, wil je een FIFO buffer dan moet je die zelf softwarematig maken, zoals pros het doet.
Hoe kan ik nu een willekeurige byte, bijv. byte 5, uitlezen?
Dat is niet de opzet van een FIFO buffer. Bij een FIFO buffer hoort altijd een functie die de eerstvolgende byte teruggeeft.
Is dat niet wat je wil, dan wil je geen FIFO buffer.
Hoi,
Bedankt voor alle reacties tot zo ver.
Kan iemand dit stukje een beetje toelichten?
Ik ben nog niet zo lang bezig met programmeren maar ik wil wel graag weten waar ik mee bezig ben.
SIGNAL(SIG_USART_RECV)
{
if (uart_rx_pos > UART_RX_BUFFER_SIZE) { // prevent overflow
uart_rx_pos = 0;
}
uart_rxbuf[uart_rx_pos] = UDR0;
if ((uart_rxbuf[uart_rx_pos] == '\n')
|| (uart_rx_pos > UART_RX_BUFFER_SIZE - 1)) {
uart_rxbuf[uart_rx_pos] = 0;
strcpy(uart_outbuf, uart_rxbuf);
uart_rx_linecomplete = 1;
uart_rx_pos = 0;
} else {
uart_rx_pos++;
}
MVG,
JvdB
pros
Prosper, yop la boum, c'est le roi du macadam (aldus Maurice Chevalier)
Je hebt twee string-buffers, uart_outbuf en uart_rxbuf.
De ISR leest de ontvangen byte uit UDR0 en plaatst het in uart_rxbuf[uart_rx_pos] (uart_rx_pos wordt op 0 gezet tijdens het initialiseren).
Dan gaat de ISR na, of die byte soms een newline ('\n') was. Zo ja, dan wordt de inhoud van uart_rxbuf naar uart_outbuf gecopieerd en daar afgesloten met een 0. uart_rx_pos wordt terug op 0 gezet, en uart_rx_linecomplete wordt 1 gemaakt.
main() hoeft niets anders te doen dan tussendoor uart_rx_linecomplete te checken. Is die 1, dan staat er een nieuwe boodschap klaar in uart_outbuf. Die kan verwerkt worden, waarna main() terug een 0 in uart_rx_linecomplete plaatst.
Ondertussen kan de ISR gewoon verder werken als er data binnenkomt - die werkt immers met zijn eigen buffer.
Is het ontvangen karakter geen newline, dan wordt uart_rx_pos met 1 verhoogd.
Oh ja, de ISR controleert ook op een mogelijke buffer-overflow (eigenlijk mag die eerste controle weg). In dat geval zet hij uart_rx_pos terug op 0. Dat betekent wel, dat main() slechts een deel van de string te zien krijgt. Zorg er dus voor, dat je UART-communicatie beknopt is, of maak de buffers groter.
Bedankt voor de informatie tot zover want het is inmiddels gelukt.
Nu een ander vraagje:
Nu heb ik een string met bijvoorbeeld het formaat 123.4
hoe kan ik hiermee rekenen?
Ik wil het vergelijken en aan de hand daarvan twee uitgangen aansturen.
MVG,
JvdB
pros
Prosper, yop la boum, c'est le roi du macadam (aldus Maurice Chevalier)
Probeer hem om te zetten naar een unsigned int of een unsigned long, en reken daar mee. Omzetten zou je zo kunnen doen:
// String -> UL
unsigned long get_UL(char *ptr)
{
unsigned long result = 0;
while ((*ptr > '/') && (*ptr < ':')) { // eenheden
result *= 10;
result += (*ptr - '0');
ptr++;
}
if (*ptr == '.') { // Komt er nog iets na de dp?
ptr++;
result *= 10;
if ((*ptr > '/') && (*ptr < ':')) { // tienden
result += (*ptr - '0');
result *= 10;
ptr++;
if ((*ptr > '/') && (*ptr < ':')) { // honderdsten
result += (*ptr - '0');
}
}
} else { // Geen tienden noch honderdsten
result *= 100;
}
return (result);
}
Deze routine gaat uit van maximaal 2 cijfers na de dp, en retourneert het honderdvoudige van het getal. Aanpassen voor maximaal 1 of 3 cijfers na de dp is vrij eenvoudig.
Wil je meer, dan moet je met floating-point toestanden gaan werken
double getal;
getal = atof(string);
Maar probeer dat te vermijden, tenzij je een overvloed aan flash-geheugen hebt.
char koers [6];
double getal = 000.0;
getal = atof(koers);
displayGotoXY(0,1);
fprintf(display, "MV =%5.1f", getal);Even een paar greepjes uit mijn code want ik kom er nog niet helemaal uit.
Ik heb voor het gemak maar even voor de variant met de double gekozen.
In de bovenste regels initialiseer ik twee variabelen.
Dan heb ik dat regeltje bijna letterlijk gekopiert alleen is mijn string dan de variabele: koers?
vervolgens brobeer ik het geheel op het display weer te geven.
Maar dan komt er alleen "MV = ?"
Wat gaat hier nu mis?
MVG,
JvdB
pros
Prosper, yop la boum, c'est le roi du macadam (aldus Maurice Chevalier)
Oh ja, die printf-functie, in combinatie met floats. De standaard printf voor avr-gcc negeert floats, dit om de uiteindelijke machinecode binnen de perken te houden. Wil je de complete printf(), dan kan je dat als compiler-optie meegeven.
't Is beslist géén aanrader, maar proberen kan geen kwaad...
Niet om af te doen aan deze code,
Deze code is eigenlijk bedoeld voor een commandline interpreter als ik het zo snel door lees. Het is jammer dat je deze code niet kan gebruiken als standaard UART software interface, dus ook voor doeleinden waarbij het commando niet word afgesloten met een 'n'.
Maak er liever een circulaire buffer van die je achter een instructie zoals getchar zet, die je per uC zo groot kan maken als je wilt.
En daarnaast een functie die checkt of jij een string naar binnen werpt. Dat maakt het enorm flexibel door dat je dit kan gebruiken voor elke type controller en toepassing. Het heeft geen zin om een database op te bouwen waarbij de codes op elkaar lijken.