Minifriese
17.07.2007, 21:34
Moin moin!
Mal wieder das alte Thema: Kommunikation zwischen MC (in diesem Fall ATmega16) und PC (WinXP) über RS232.
Dazu gab's hier schon einige Threads. In einem davon habe ich einen Code gefunden, der endlich mal keine zusätzlichen Treiberprogrämmchen, dlls oder sonstwelche Spielchen erfordert. Sondern einfach als Win32-Konsolenanwendung per MSDevStudio 6 kompiliert werden kann. Vor allem ohne den üblichen Overhead von Windowsprogrammen. Soweit so gut, der Code funktioniert und ich habe ihn der Übersicht halber auf das reduziert, was ich brauche, nämlich das Einlesen von Daten, die der ATmega16 per USART sendet (soll ein Datenlogger werden).
Der AVR sendet testweise in seinem Programm nur einmal ein Telegramm von 10 bytes. Er hängt am STK500, benutzt also dessen 5V-Versorgung und den Spare-RS232-Port. Damit sendet er immer dann ein Telegramm, wenn ich den Resetknopf auf dem STK500 drücke.
Jetzt zum Problem: Nachdem die 10 bytes empfangen worden sind, wird am COM-Port des PCs anscheinend das Event EV_RXCHAR noch einmal gesetzt. Aber ohne daß nochmal Daten empfangen worden wären. Die Ausgabe sieht so aus wie in dem angehängten Bild. Hier wurde zum Beispiel dreimal das Programm im AVR ausgeführt. Also wird dreimal das Telegramm empfangen, aber dann kommt immer noch eine Zeile, weil das Empfangsprogramm per Event nochmal in die ReadFile()-Schleife und damit auch zum printf() springt. Und ich habe keine Ahnung, wieso. Ich habe versucht, das Event an verschiedenen Stellen per ResetEvent() manuell zurückzusetzen (dazu auch beim Erzeugen des Events den Parameter MANUAL_RESET auf eins gesetzt), aber es kommt trotzdem wieder. COM_BUFFER_SIZE steht übrigens normalerweise auf 10, ich hatte es nur auf 11 gesetzt, um zu schauen, ob ich nicht versehentlich noch ein elftes Byte sende oder so... Aber mit 10 sieht es genauso aus.
Hier ist der Code im AVR:
#include <avr/io.h>
#include <avr/interrupt.h>
volatile unsigned int auxcounter=0;
unsigned char data[10]={0,1,2,3,4,5,6,7,8,9};
void init(void) {
UBRRH=103>>8;
UBRRL=103;
UCSRB=(1<<TXEN);
UCSRC=(1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
}
ISR(SIG_OVERFLOW0) {
auxcounter++;
}
void transmit(unsigned char *data) {
int k;
for(k=0;k<10;k++){
while(!(UCSRA & (1<<UDRE))) { };
UDR=data[k];
}
}
int main(void) {
TIFR = (1<<TOV0); //Enable timer overflow interrupt
TIMSK= (1<<TOIE0); //Enable timer overflow interrupt
TCCR0= (1<<CS00)|(1<<CS02); //Prescaler = 1024, Overflow 256
init();
sei();
//while(1) {
// if (auxcounter>122) { //305=5sec, 122=2sec
transmit(data);
//auxcounter=0;
// }
//}
return 0;
}
Und hier der Code im PC (Win32-Konsolenprogramm in MSDevStudio6.0):
#include <windows.h>
#include <stdio.h>
#define COM_BUFFER_SIZE 11 // Read-Buffer-Size
#define BD_RATE CBR_9600 // 9600 Baud
int main (void)
{
DCB dcb;
BOOL bRet = true;
DWORD dwRead = 0;
DWORD dwSetMask = EV_RXCHAR;
DWORD dwEvtMask;
OVERLAPPED o;
COMMTIMEOUTS ct;
unsigned char data[COM_BUFFER_SIZE];
memset (&o, 0, sizeof (OVERLAPPED)); // Struktur mit 0en füllen
memset (&data, 0, sizeof (data)); // Struktur mit 0en füllen
o.hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); // einen Event setzten
HANDLE hCom = CreateFile ("COM5", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, &o);
dcb.DCBlength = sizeof(DCB); // Laenge des Blockes MUSS gesetzt sein!
GetCommState (hCom, &dcb); // COM-Einstellungen holen und aendern
dcb.BaudRate = BD_RATE; // Baudrate
dcb.ByteSize = 8; // Datenbits
dcb.Parity = NOPARITY; // Parität
dcb.StopBits = ONESTOPBIT; // Stopbits
SetCommState (hCom, &dcb); // COM-Einstellungen speichern
GetCommTimeouts (hCom, &ct);
ct.ReadIntervalTimeout = 2; //1000 / BD_RATE * (dcb.ByteSize +(dcb.Parity == NOPARITY ? 0 : 1) + (dcb.StopBits == ONESTOPBIT ? 1 : 2)) * 2;
ct.ReadTotalTimeoutMultiplier = 0; // [ms] wird mit Read-Buffer-Size multipliziert
ct.ReadTotalTimeoutConstant = 50; // wird an ReadTotalTimeoutMultiplier angehängt
SetCommTimeouts (hCom, &ct);
SetupComm (hCom, 2*COM_BUFFER_SIZE, 2*COM_BUFFER_SIZE); // Zwischenspeicher des serial-Drivers einstellen (für read und write):
SetCommMask (hCom, dwSetMask); // Empfangssignale definieren
do {
WaitCommEvent (hCom, &dwEvtMask, &o); // Event mit Empfangssignalen verknüpfen
if (WAIT_OBJECT_0 == WaitForSingleObject (o.hEvent, 2000)) { // warten bis Event
bRet = ReadFile (hCom, &data, sizeof (data), &dwRead, NULL);
if (bRet) {
printf ("\nRxD %2d Byte(s):",dwRead);
for(int n=0;n<COM_BUFFER_SIZE;n++) printf("%4d",data[n]);
memset (&data, 0, sizeof (data)); // Struktur mit 0en füllen
}
}
}
while (1);
CloseHandle (hCom); // COM schließen
CloseHandle (o.hEvent); // Event-Handle zurückgeben
return (0);
}
Hat jemand eine Idee, woran das liegen könnte? Ich komm einfach nicht drauf...
Besten Dank,
Nils
Mal wieder das alte Thema: Kommunikation zwischen MC (in diesem Fall ATmega16) und PC (WinXP) über RS232.
Dazu gab's hier schon einige Threads. In einem davon habe ich einen Code gefunden, der endlich mal keine zusätzlichen Treiberprogrämmchen, dlls oder sonstwelche Spielchen erfordert. Sondern einfach als Win32-Konsolenanwendung per MSDevStudio 6 kompiliert werden kann. Vor allem ohne den üblichen Overhead von Windowsprogrammen. Soweit so gut, der Code funktioniert und ich habe ihn der Übersicht halber auf das reduziert, was ich brauche, nämlich das Einlesen von Daten, die der ATmega16 per USART sendet (soll ein Datenlogger werden).
Der AVR sendet testweise in seinem Programm nur einmal ein Telegramm von 10 bytes. Er hängt am STK500, benutzt also dessen 5V-Versorgung und den Spare-RS232-Port. Damit sendet er immer dann ein Telegramm, wenn ich den Resetknopf auf dem STK500 drücke.
Jetzt zum Problem: Nachdem die 10 bytes empfangen worden sind, wird am COM-Port des PCs anscheinend das Event EV_RXCHAR noch einmal gesetzt. Aber ohne daß nochmal Daten empfangen worden wären. Die Ausgabe sieht so aus wie in dem angehängten Bild. Hier wurde zum Beispiel dreimal das Programm im AVR ausgeführt. Also wird dreimal das Telegramm empfangen, aber dann kommt immer noch eine Zeile, weil das Empfangsprogramm per Event nochmal in die ReadFile()-Schleife und damit auch zum printf() springt. Und ich habe keine Ahnung, wieso. Ich habe versucht, das Event an verschiedenen Stellen per ResetEvent() manuell zurückzusetzen (dazu auch beim Erzeugen des Events den Parameter MANUAL_RESET auf eins gesetzt), aber es kommt trotzdem wieder. COM_BUFFER_SIZE steht übrigens normalerweise auf 10, ich hatte es nur auf 11 gesetzt, um zu schauen, ob ich nicht versehentlich noch ein elftes Byte sende oder so... Aber mit 10 sieht es genauso aus.
Hier ist der Code im AVR:
#include <avr/io.h>
#include <avr/interrupt.h>
volatile unsigned int auxcounter=0;
unsigned char data[10]={0,1,2,3,4,5,6,7,8,9};
void init(void) {
UBRRH=103>>8;
UBRRL=103;
UCSRB=(1<<TXEN);
UCSRC=(1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
}
ISR(SIG_OVERFLOW0) {
auxcounter++;
}
void transmit(unsigned char *data) {
int k;
for(k=0;k<10;k++){
while(!(UCSRA & (1<<UDRE))) { };
UDR=data[k];
}
}
int main(void) {
TIFR = (1<<TOV0); //Enable timer overflow interrupt
TIMSK= (1<<TOIE0); //Enable timer overflow interrupt
TCCR0= (1<<CS00)|(1<<CS02); //Prescaler = 1024, Overflow 256
init();
sei();
//while(1) {
// if (auxcounter>122) { //305=5sec, 122=2sec
transmit(data);
//auxcounter=0;
// }
//}
return 0;
}
Und hier der Code im PC (Win32-Konsolenprogramm in MSDevStudio6.0):
#include <windows.h>
#include <stdio.h>
#define COM_BUFFER_SIZE 11 // Read-Buffer-Size
#define BD_RATE CBR_9600 // 9600 Baud
int main (void)
{
DCB dcb;
BOOL bRet = true;
DWORD dwRead = 0;
DWORD dwSetMask = EV_RXCHAR;
DWORD dwEvtMask;
OVERLAPPED o;
COMMTIMEOUTS ct;
unsigned char data[COM_BUFFER_SIZE];
memset (&o, 0, sizeof (OVERLAPPED)); // Struktur mit 0en füllen
memset (&data, 0, sizeof (data)); // Struktur mit 0en füllen
o.hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); // einen Event setzten
HANDLE hCom = CreateFile ("COM5", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, &o);
dcb.DCBlength = sizeof(DCB); // Laenge des Blockes MUSS gesetzt sein!
GetCommState (hCom, &dcb); // COM-Einstellungen holen und aendern
dcb.BaudRate = BD_RATE; // Baudrate
dcb.ByteSize = 8; // Datenbits
dcb.Parity = NOPARITY; // Parität
dcb.StopBits = ONESTOPBIT; // Stopbits
SetCommState (hCom, &dcb); // COM-Einstellungen speichern
GetCommTimeouts (hCom, &ct);
ct.ReadIntervalTimeout = 2; //1000 / BD_RATE * (dcb.ByteSize +(dcb.Parity == NOPARITY ? 0 : 1) + (dcb.StopBits == ONESTOPBIT ? 1 : 2)) * 2;
ct.ReadTotalTimeoutMultiplier = 0; // [ms] wird mit Read-Buffer-Size multipliziert
ct.ReadTotalTimeoutConstant = 50; // wird an ReadTotalTimeoutMultiplier angehängt
SetCommTimeouts (hCom, &ct);
SetupComm (hCom, 2*COM_BUFFER_SIZE, 2*COM_BUFFER_SIZE); // Zwischenspeicher des serial-Drivers einstellen (für read und write):
SetCommMask (hCom, dwSetMask); // Empfangssignale definieren
do {
WaitCommEvent (hCom, &dwEvtMask, &o); // Event mit Empfangssignalen verknüpfen
if (WAIT_OBJECT_0 == WaitForSingleObject (o.hEvent, 2000)) { // warten bis Event
bRet = ReadFile (hCom, &data, sizeof (data), &dwRead, NULL);
if (bRet) {
printf ("\nRxD %2d Byte(s):",dwRead);
for(int n=0;n<COM_BUFFER_SIZE;n++) printf("%4d",data[n]);
memset (&data, 0, sizeof (data)); // Struktur mit 0en füllen
}
}
}
while (1);
CloseHandle (hCom); // COM schließen
CloseHandle (o.hEvent); // Event-Handle zurückgeben
return (0);
}
Hat jemand eine Idee, woran das liegen könnte? Ich komm einfach nicht drauf...
Besten Dank,
Nils