Styra Arduino med Gamepad (2 / 5 steg)
Steg 2: Lärande seriell kommunikation
Innan vi dyker in i utvecklingsprocessen, ber jag er att gå över några preliminära läsning för att förstå vad vi försöker göra. Jag har redan sammanställt en enkel handledning om seriell kommunikation (andra länken), så när du är klar, vi kan börja utveckla ett fullt fungerande program för att passa våra ändamål.
BEHANDLINGEN MATERIAL:
Vi börjar med att skapa 2 enkla funktioner, som gör det möjligt att öppna och stänga UART anslutning.
För detta måste du MS Visual C ++, par händer och koffein-infunderas hjärnan.
COM-port initieringen är en enkel process: först skapar vi port konfiguration portDCB, som innehåller alla kommunikationsinställningar, och sedan vi tilldela Portreferensen. Observera, att porten initieras med CreateFile() funktionsanrop och precis som med konventionella filer kan vi använda readfile () och WriteFile() för utbyte av data.
Vi tilldela den nya konfigurationen med SetCommState() funktionsanrop. Om vid varje steg i denna process vi stöter på ett fel kan vi skriva ut lämpligt meddelande och returnera FALSE.
Annars kan vi returnera sant och till följd av genomförandet av UART_Init(), port variabel nu pekar på en seriell port handtag.
För flexibilitet ger vi namnet COM port och dess överföringshastighet som argument för funktionen. Standardinställningarna anges till 8 bitars överföring längd med 1 stoppbit. Paritet, felkorrigering och någon typ av flödesreglering är inaktiverade som standard.
/*
* UART_Init()
* Öppnar com-porten med ID "portName" på baud "baud"
* HANTERA * hamn blir en pekare till en aktiv COM portanslutning
* Returnerar om anslutningen är framgångsrik eller inte.
*/
BOOL UART_Init(HANDLE *port, LPCWSTR portName, DWORD baud)
{
DCB portDCB; _DCB struct för seriell konfiguration
bool resultatet = FALSE; Returvärdet
COMMTIMEOUTS comTOUT; Kommunikationen
* port = CreateFile (portName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL);
Prova att öppna port kommunikation
IF(*port==INVALID_HANDLE_VALUE)
{
wprintf (L "fel: kan inte öppna port %s\n",portName);
returnera FALSE;
}
NYA INSTÄLLNINGAR
portDCB.DCBlength = sizeof(DCB); / / Setup config längd
GetCommState (* port & portDCB); Få standard hamnstatens
portDCB.BaudRate = baud; Ange baudvärde
portDCB.fBinary = sant; Aktivera binärt läge
portDCB.fParity = FALSE; Inaktivera paritet
portDCB.fOutxCtsFlow = FALSE; Nr CTS
portDCB.fOutxDsrFlow = FALSE; Ingen DSR
portDCB.fDtrControl = DTR_CONTROL_DISABLE; Ingen DTR
portDCB.fDsrSensitivity = FALSE; Ingen DSR känslighet
portDCB.fTXContinueOnXoff = sant; TX på XOFF
portDCB.fOutX = FALSE; Ingen XON/XOFF
portDCB.fInX = FALSE; //
portDCB.fErrorChar = FALSE; Ingen felkorrigering
portDCB.fNull = FALSE; Hålla NULL-värden
portDCB.fRtsControl = RTS_CONTROL_DISABLE; Inaktivera RTS
portDCB.fAbortOnError = FALSE; Inaktivera abortera vid fel
portDCB.ByteSize = 8; 8-bitars ramar
portDCB.Parity = NOPARITY; Paritet: ingen
portDCB.StopBits = ONESTOPBIT; StopBits: 1
Försöker du konfigurera COM-port
IF (!. SetCommState (* port & portDCB))
{
wprintf (L "fel: kan inte konfigurera port %s\n",portName);
returnera FALSE;
}
Time-out-värden för kommunikation
resultat = GetCommTimeouts (* port & comTOUT);
comTOUT.ReadIntervalTimeout = 10;
comTOUT.ReadTotalTimeoutMultiplier = 1;
comTOUT.ReadTotalTimeoutConstant = 1;
Ställa in nya timeout-värden
resultat = SetCommTimeouts (* port & comTOUT);
Return TRUE;
}
Utgående COM-porten är mycket lätt. Allt vi behöver göra är att släppa handtaget (linje 2) och ställa in * port pekare till NULL, så att vi inte oavsiktligt komma åt gamla handtaget.
UART_Close() funktionen returnerar FALSE om vi försöker stänga en oinitierad eller tidigare stängda portreferens.
BOOL UART_Close(HANDLE *port)
{
om (* port == NULL) return FALSE;
CloseHandle(*port);
* port = NULL;
Return TRUE;
}
Som ni redan har gissat, kommer nästa logiska steg att genomföra funktioner för att skicka och ta emot UART meddelanden. Det avgörande ögonblicket i denna del är att vi kommer att använda kommunikationshändelser, beskrivs i MSDN-artikeln nämnde tidigare.
BOOL UART_Send(HANDLE port, char *Buffer)
{
DWORD-bytesTransmitted;
IF (!. WriteFile(port,Buffer, strlen(Buffer), &bytesTransmitted, NULL))
{
DWORD-fel;
COMSTAT Status.
ClearCommError (port, & fel & Status);
printf ("fel: det gick inte att skicka data. \n");
returnera FALSE;
}
annat
{
Return TRUE;
}
}
Förutsatt att våra arduino kan upptas vid tidpunkten för överföringen och inte kunde ge ett korrekt svar, vill vi vänta för EV_RXCHAR händelse inträffa varje gång RX har inkommande data. Att lösa detta problem vi kommer att inrätta en kommunikation mask och vänta på vårt event innan du läser den nästa byten.
BOOL UART_Receive(HANDLE port, char *Buffer)
{
DWORD-bytesTransmitted = 0; Byte counter
DWORD-status = EV_RXCHAR; överföring status mask
MEMSET (buffert, 0, BUFFER_SIZE); Rensa indatabufferten
SetCommMask (hamn, EV_RXCHAR); Ställ in händelsen mask
WaitCommEvent (port, och status, 0); Lyssna efter RX händelse
IF(status & EV_RXCHAR) / / om händelsen inträffade
{
DWORD-framgång = 0;
char c = 0;
göra
{
IF (!. ReadFile (port, & c, 1, och framgång, NULL)) / / läsa 1 char
{
Om fel uppstod, skriva ut meddelande och exit
DWORD-fel;
COMSTAT Status.
ClearCommError (port, & fel & Status); Uppenbart fel
MEMSET (buffert, 0, BUFFER_SIZE); Rensa indatabufferten
printf ("fel: det gick inte att ta emot data. \n"); Skriva ut felmeddelande
returnera FALSE;
}
annat
{
Buffert [bytesTransmitted] = c; Lägg till sista tecknet
bytesTransmitted ++; Öka översättning av Tjekhovs counter
}
} while((success==1) & & (c! = '\n')); inte förrän i slutet av meddelandet
}
Return TRUE;
}
Dessa fyra funktioner bör vara tillräckligt för att hantera grundläggande UART kommunikation mellan Arduino och datorn.
Nu, låt oss utvärdera funktionaliteten i vår kod med en enkel UART loopback-test. Vi måste avsluta programmets _tmain() funktion först:
int _tmain (int argc, _TCHAR * argv[])
{
HANTERA hamn.
char Buffer [BUFFER_SIZE] = "TEST MESSAGE\n";
Det går inte att öppna? avsluta med kod 1
IF (!. UART_Init(&port, L"COM8:", CBR_115200))
{
system("PAUSE");
tillbaka 1.
}
: fortsätta körningen
annat
{
Här skickar vi strängen från bufferten och skrivas ut.
Vårt Arduino loopback ska returnera samma sträng
int medd = 0; återställa # meddelanden
While((port!=INVALID_HANDLE_VALUE) & & (medd < 100)) / / skicka/ta emot 100 meddelanden
{
printf ("Skicka: %s\n", buffert);
UART_Send (port, buffert); Skicka data till UART-port
om (UART_Receive (port, buffert)) / / tar emot data
printf ("Received: %s\n", buffert);
PurgeComm (port, PURGE_RXCLEAR | PURGE_TXCLEAR); Spola RX och TX
Medd ++; Öka antal meddelanden
}
UART_Close(&Port); Nära port
}
system("PAUSE");
Return 0;
}
Här koden initierar port COM8, som är min USB-UART kabel (Glöm inte att ändra den del till din port #). Sedan skickar 100 meddelanden över UART och skriver ut både ursprungliga meddelande och svar. Genomföra kommunikation händelseavlyssnaren tidigare verkligen lönade sig i slutet. Om du tittar på detta program noga, ser du att vi har bara använt om ett dussin rader effektiv kod för att få det att fungera!
Nu, låt oss ställa våra Arduino att fungera som UART loopback-enhet. Vi kommer också att genomföra en event-driven UART kommunikation för att göra några andra saker samtidigt som inte sänder.
Öppna upp din Arduino IDE och använda den här koden som ett exempel:
Strängbuffert = ""; en sträng att lagra inkommande data
void setup() {
buffer.Reserve(255); Reserve 255 tecken
Serial.BEGIN(115200); Initiera UART
}
void loop() {
NOP
}
SerialEvent inträffar varje gång vi får RX-avbrott
void serialEvent() {
samtidigt (Serial.available()) {
char c = (char)Serial.read(); Läs karaktär
buffert += c; Lägga till den i buffert
Om end-of-line, återställa buffert och skicka tillbaka data
om (c == "\n") {
Serial.Print(buffer); Loopback
buffert = ""; Tydlig buffert
}
}
}
Nu kan du ladda upp skissen till Arduino, kompilera C++ projektet och testa det!