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:

term1

 

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); 
 
}

 

 

Comments are closed.