STM32F4 Discovery virtual com port – part II
This is next step in developing virtual COM port application. There arre two other parts required:
STM32F4 discovery – Keil example step-by-step
and first part of virtual com port tutorial:
STM32F4 discovery – Virtual COM port, step-by-step
In this example we will add DTR and RTS signals to two GPIO pins. Next, we will add simple command line interpreter for simple communication with the micorcontroller.
Adding DTR and RTS signals is straightforward. Open file USBD_User_CDC_0.c and add two header files:
31 32 | #include "stm32f4xx.h" // Device header #include "cmsis_os.h" // RTOS:Keil RTX header |
Edit the function USBD_CDC0_ACM_Initialize() and add port initialization code. We will use PD10 for DTR line and PD11 for RTS line. New USBD_CDC0_ACM_Initialize() is:
35 36 37 38 39 40 41 42 43 44 45 46 | // Called during USBD_Initialize to initialize the USB Device class. void USBD_CDC0_ACM_Initialize (void) { // ToDo: add code for initialization // Initialize Port D, PD10 and PD11 for output // PD10 ... DTR // PD11 ... RTS RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // Enable Port D clock GPIOD->MODER |= GPIO_MODER_MODER10_0; // Port D.10 output ---> DTR GPIOD->MODER |= GPIO_MODER_MODER11_0; // Port D.11 output ---> RTS } |
Finally, add code for toggling the pins (it is not the most compact code, but the readability was my primary concern):
83 84 85 86 87 88 89 90 91 92 93 94 95 96 | // Called upon Set Control Line State request. // \param [in] state control line settings bitmap. // - bit 0: DTR state // - bit 1: RTS state // \return true set control line state request processed. // \return false set control line state request not supported or not processed. bool USBD_CDC0_ACM_SetControlLineState (uint16_t state) { // ToDo: add code for set control line state if (state & 0x0001) GPIOD->BSRRH = (1<<10); else GPIOD->BSRRL = (1<<10); // DTR if (state & 0x0002) GPIOD->BSRRH = (1<<11); else GPIOD->BSRRL = (1<<11); // RTS return true; } |
Here’s the full source code file USBD_User_CDC_0.
To test the operation of the control line toggling we need some terminal software. Just download and unpack the zip from link and run terminal.exe. It is well known Bray terminal program, which provides manual handshake line toggling.
You can now toggle pin states by clicking on the control buttons in terminal window:
Now let’s make some more fun stuff….
Add new blank C source file to the project (right click on Source Group 1, Add New Item, C file). Name it command.c. Do same for heder file command.h. This will be our new command interpreter. We need few functions. First is line editor. Edit file command.h and write code for line editor:
#include "cmsis_os.h" #include "rl_usb.h" #define DEL 0x7F #define BACKSPACE 0x08 #define CR 0x0D #define LF 0x0A void process_cmd (void const *argument); // Prototype function osThreadDef (process_cmd, osPriorityNormal, 1, 0); // Define command process thread void getline (char *line, int n) { int cnt = 0; int32_t usb_rx_ch = -1; do { if (USBD_Configured (0)) { /* USB -> MCU */ if (usb_rx_ch == -1) { usb_rx_ch = USBD_CDC_ACM_GetChar (0); } if (usb_rx_ch != -1) { if (usb_rx_ch == CR) usb_rx_ch = LF; if (usb_rx_ch == BACKSPACE || usb_rx_ch == DEL) { /* process backspace */ if (cnt != 0) { cnt--; /* decrement count */ line--; /* and line pointer */ USBD_CDC_ACM_PutChar (0, BACKSPACE); /* echo backspace */ USBD_CDC_ACM_PutChar (0, ' '); USBD_CDC_ACM_PutChar (0, BACKSPACE); } } else { USBD_CDC_ACM_PutChar (0, *line = usb_rx_ch); /* echo and store character */ line++; /* increment line pointer */ cnt++; /* and count */ } } if (usb_rx_ch != LF) usb_rx_ch = -1; } } while (cnt < n - 1 && usb_rx_ch != LF); /* check limit and line feed */ *(line - 1) = 0; /* mark end of string */ USBD_CDC_ACM_PutChar (0, CR); } |
Now wrap this line editor inside the thread. To do so, we first need to write command line processor and then initialize the thread. The command line processor will be inside thread endless loop:
// Command processor function void process_cmd(void const *argument) { #define CMDLEN 40 // Max. command length char cmd[CMDLEN]; for (;;) { USBD_CDC_ACM_PutChar (0, '>'); getline(cmd, CMDLEN); } } |
Write another function to create thread:
void Init_CmdThread (void) { osThreadCreate (osThread(process_cmd), NULL); // Create thread } |
The prototype of function Init_CmdThread() will go also in the header file to be used in main().
Change source code of main.c and add call to this new function:
/*---------------------------------------------------------------------------- * CMSIS-RTOS 'main' function template *---------------------------------------------------------------------------*/ #define osObjectsPublic // define objects in main module #include "osObjects.h" // RTOS object definitions #include "cmsis_os.h" // ARM::CMSIS:RTOS:Keil RTX #include "led.h" #include "rl_usb.h" #include "command.h" /* * main: initialize and start the system */ int main (void) { osKernelInitialize (); // initialize CMSIS-RTOS USBD_Initialize (0); /* USB Device 0 Initialization */ USBD_Connect (0); /* USB Device 0 Connect */ // initialize peripherals here LED_Initialize (); // create 'thread' functions that start executing, // example: tid_name = osThreadCreate (osThread(name), NULL); Init_BlinkyThread(); Init_CmdThread(); osKernelStart (); // start thread execution while (1); } |