Étape 2: Apprentissage COMMUNICATION série
Avant de nous plonger dans le processus de développement, je voudrais que vous aller au fil de la lecture préliminaire afin de comprendre ce que nous essayons de faire. J’ai déjà compilé un tutoriel simple sur la communication série (second lien), donc une fois que vous avez terminé, nous pouvons commencer à élaborer un programme entièrement fonctionnel en fonction de nos besoins.
MATÉRIEL DE LECTURE :
Nous allons commencer avec la création de 2 fonctions simples, ce qui permettront d’ouvrir et de fermer la connexion de l’UART.
Pour cela, vous aurez besoin MS Visual C++, paire de mains et le cerveau infusé de caféine.
L’initialisation de port COM est un processus très simple : tout d’abord nous créons port configuration portDCB, qui contient tous les paramètres de communication, et ensuite nous assignons le handle du port. Notez, que le port est initialisé avec l’appel de fonction CreateFile() , et tout comme avec les fichiers classiques nous pouvons utiliser ReadFile() et WriteFile() pour échanger des données.
Ensuite, nous affectons la nouvelle configuration avec l’appel de fonction SetCommState() . Si à n’importe quelle étape de ce processus, nous rencontrons une erreur, nous imprimerons le message approprié et renvoient la valeur FALSE.
Dans le cas contraire, nous retourner TRUE et à la suite de l’exécution UART_Init(), variable de port sera maintenant pointez sur une poignée de port série.
Dans le but de flexibilité, nous fournirons le nom des ports COM et son débit en bauds en tant qu’arguments de cette fonction. Paramètres par défaut sont affectées aux longueur de transmission 8 bits et 1 bit de stop. Parité, de correction d’erreurs et de n’importe quel type de contrôle de flux sont désactivés par défaut.
/* * UART_Init() * Opens the com port with ID "portName" at baud rate "baud" * HANDLE *port becomes a pointer to an active COM port connection * Returns whether the connection is successful or not. */ BOOL UART_Init(HANDLE *port, LPCWSTR portName, DWORD baud) { DCB portDCB; // _DCB struct for serial configuration bool result = FALSE; // Return value COMMTIMEOUTS comTOUT; // Communication timeout *port = CreateFile(portName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, NULL); // Try opening port communication if(*port==INVALID_HANDLE_VALUE) { wprintf(L"ERROR: Cannot open port %s\n",portName); return FALSE; } // NEW SETTINGS portDCB.DCBlength = sizeof(DCB);// Setup config length GetCommState(*port, &portDCB); // Get default port state portDCB.BaudRate = baud; // Set baud rate portDCB.fBinary = TRUE; // Enable Binary mode portDCB.fParity = FALSE; // Disable parity portDCB.fOutxCtsFlow = FALSE; // No CTS portDCB.fOutxDsrFlow = FALSE; // No DSR portDCB.fDtrControl = DTR_CONTROL_DISABLE; // No DTR portDCB.fDsrSensitivity = FALSE; // No DSR sensitivity portDCB.fTXContinueOnXoff = TRUE; // TX on XOFF portDCB.fOutX = FALSE; // No XON/XOFF portDCB.fInX = FALSE; // portDCB.fErrorChar = FALSE; // No error correction portDCB.fNull = FALSE; // Keep NULL values portDCB.fRtsControl = RTS_CONTROL_DISABLE; // Disable RTS portDCB.fAbortOnError = FALSE; // Disable abort-on-error portDCB.ByteSize = 8; // 8-bit frames portDCB.Parity = NOPARITY; // Parity: none portDCB.StopBits = ONESTOPBIT; // StopBits: 1 // Try reconfiguring COM port if (!SetCommState (*port, &portDCB)) { wprintf(L"ERROR: Cannot configure port %s\n",portName); return FALSE; } /// Communication timeout values result = GetCommTimeouts(*port, &comTOUT); comTOUT.ReadIntervalTimeout = 10; comTOUT.ReadTotalTimeoutMultiplier = 1; comTOUT.ReadTotalTimeoutConstant = 1; /// Set new timeout values result = SetCommTimeouts(*port, &comTOUT); return TRUE; }
Fermer le port COM est très facile. Tout ce que nous devons faire est de libérer le handle (ligne 2) et la valeur * port pointeur null, donc nous n’accèdent pas accidentellement l’ancien handle.
UART_Close() fonction retourne FALSE si nous essayons de fermer un descripteur de port non initialisée ou étaient fermés.
BOOL UART_Close(HANDLE *port) { if (*port == NULL) return FALSE; CloseHandle(*port); *port = NULL; return TRUE; }
Comme vous l’avez déjà deviné, la prochaine étape logique mettra en œuvre des fonctions pour envoyer/recevoir des messages de l’UART. Le moment clé de cette partie, c’est que nous allons utiliser des événements de communication, décrits dans l’article MSDN mentionné précédemment.
BOOL UART_Send(HANDLE port, char *Buffer) { DWORD bytesTransmitted; if(!WriteFile(port,Buffer, strlen(Buffer), &bytesTransmitted, NULL)) { DWORD Errors; COMSTAT Status; ClearCommError(port,&Errors,&Status); printf("ERROR: Unable to send data.\n"); return FALSE; } else { return TRUE; } }
En supposant que notre arduino peut être occupé au moment de la transmission et ne pouvait pas fournir une réponse adéquate, nous voulons attendre pour l’événement EV_RXCHAR se produit chaque fois que RX a données entrantes. Pour résoudre ce problème nous va mettre en place un masque de communications et d’attendre pour notre événement avant de lire l’octet suivant.
BOOL UART_Receive(HANDLE port, char *Buffer) { DWORD bytesTransmitted = 0; // Byte counter DWORD status = EV_RXCHAR; // transmission status mask memset(Buffer, 0, BUFFER_SIZE); // Clear input buffer SetCommMask (port, EV_RXCHAR); // Set up event mask WaitCommEvent(port, &status, 0); // Listen for RX event if(status & EV_RXCHAR) // If event occured { DWORD success=0; char c = 0; do { if(!ReadFile(port,&c, 1, &success, NULL)) // Read 1 char { // If error occured, print the message and exit DWORD Errors; COMSTAT Status; ClearCommError(port,&Errors,&Status); // Clear errors memset(Buffer, 0, BUFFER_SIZE); // Clear input buffer printf("ERROR: Unable to receive data.\n"); // Print error message return FALSE; } else { Buffer[bytesTransmitted]=c; // Add last character bytesTransmitted++; // Increase trans. counter } } while((success==1) && (c!='\n')); // do until the end of message } return TRUE; }
Ces quatre fonctions devraient être suffisant pour gérer la communication UART fondamentale entre Arduino et votre PC.
Maintenant, nous allons évaluer les fonctionnalités de notre code par un simple test de bouclage UART. Nous avons besoin terminer la fonction du programme _tmain() tout d’abord :
int _tmain(int argc, _TCHAR* argv[]) { HANDLE port; char Buffer[BUFFER_SIZE] = "TEST MESSAGE\n"; // Unable to open? exit with code 1 if (!UART_Init(&port, L"COM8:", CBR_115200)) { system("PAUSE"); return 1; } // : continue execution else { // Here we send the string from buffer and print the response. // Our Arduino loopback should return the same string int msgs = 0; // reset # of messages while((port!=INVALID_HANDLE_VALUE) && (msgs<100)) // Send/Receive 100 messages { printf("Sending: %s\n", Buffer); UART_Send(port, Buffer); // Send data to UART port if(UART_Receive(port, Buffer)) // Receive data printf("Received: %s\n", Buffer); PurgeComm(port, PURGE_RXCLEAR | PURGE_TXCLEAR); // Flush RX and TX msgs++; // Increment # of messages } UART_Close(&port); // Close port } system("PAUSE"); return 0; }
Ce code initialise port COM8, qui est mon câble USB-UART (n’oubliez pas de changer la partie à votre port #). Ensuite, il envoie 100 messages sur UART et estampes originales message et réponse. Mise en œuvre de l’écouteur d’événements de communication plus tôt vraiment porté ses fruits à la fin. Si vous regardez attentivement ce programme, vous verrez que nous avons seulement utilisé sur une douzaine de lignes de code efficace pour le faire fonctionner !
Maintenant, nous allons configurer notre Arduino pour travailler en tant que périphérique de bouclage UART. Nous appliquerons aussi une communication UART évènementielle afin d’être en mesure de faire d’autres choses tout en ne transmettant ne pas.
Ouvrez votre IDE Arduino et utiliser ce code comme exemple :
String buffer = ""; // a string to hold incoming data void setup() { buffer.reserve(255); // Reserve 255 chars Serial.begin(115200); // Initialize UART } void loop() { // NOP } // SerialEvent occurs every time we receive RX interrupt void serialEvent() { while (Serial.available()) { char c = (char)Serial.read(); // Read character buffer += c; // Add it to buffer // If end-of-line, reset buffer and send back the data if (c == '\n') { Serial.print(buffer); // Loopback buffer = ""; // Clear buffer } } }
Maintenant, vous pouvez télécharger le croquis sur l’Arduino, compiler le projet C++ et testez-le !