The USB VCOM driver implements a Virtual Serial (COM) Port to allow communication with a USB host.
In this tutorial, we will use umake and nimolib to create a simple project based around the VCOM driver, see how printf can be redirected to the VCOM driver and how to make an LED respond to a key pressed on the host keyboard, finally we will upgrade the delay timer in the main loop to use the non-blocking delayMillis()
function instead of the blocking delayMs()
function.
Prerequisites
To build this project, you must first install the appropriate tools. It is recommended you follow the Getting Started guide and microBlinky tutorials before this one.
File structure
Only 3 key files are required, however, a .gitignore (or equivalent) is recommended if using source control.
Required files
- umakefile
- main.c
- nimolib.h
Optional files
- .gitignore (other source control programs will generally have an equivalent. Whilst entirely optional, it will help to keep source controlled projects tidy)
Lets go…
This section contains detailed step-by-step instructions for creating a project from scratch. If you prefer the TLDR; then you can download the entire project here and jump to Build It!
Create a new directory for your project and fire up your favourite text editor. We recommend VSCode, however, any editor that creates plain text files (gedit on Linux, notepad on Windows) will work as well.
.gitignore
Create a new file called .gitignore. As above, this file is optional however a nice addition when using git or other source control systems. If you aren’t using any source control then skip this step.
build/*
umake/*
Makefile
umakefile
Create a new file called umakefile. This will describe our project to umake allowing it to create the build environment for our project.
{
"target": "usbVcom",
"microcontroller": "m032lg6ae",
"toolchain": "arm-none-eabi",
"ucLib": "nimolib-m032",
"c_sources": [
"main.c"
],
"includes": [
"./"
],
"buildDir": "./build",
"libraries": [
{
"libName": "nimolib",
"libPath": "https://github.com/nimo-labs/nimolib.git",
"books": [
"microNimo",
"gpio",
"delay",
"usbVcom",
"printf"
]
},
{
"libName": "nimolib-m032",
"libPath": "https://github.com/nimo-labs/nimolib-m032.git"
}
]
}
Further information regarding the umakefile format can be found in the microBlinky tutorial.
The umakefile for this project is almost identical to that of the microBlinky project, the key changes being the new target name, the addition of the microNimo, usbVcom, and printf books as well as the removal of the program target.
The program target has been removed due to the latest version of umake generating the correct target by default.
In the microBlinky tutorial, we created a file called system.h to hold the GPIO definitions for the LED, along with the GPIO definition for the switch has now been moved to a dedicated microNimo book.
nimolib.h
This file is required when using the standard Nimo libraries and contains configuration information for the various books.
For microBlinky, we only needed to define the main processor clock speed, in this case, 48MHz, however this time we are also using the usbVcom and Printf books which both require additional parameters to be defined.
#define UP_CLK 48000000
/* usbVcom */
#define USB_BUFFER_SIZE 64
#define SIMPLE_VCOM_TX_BUFSIZE 100 /* TX buffer size */
/* Printf */
#define PRINTF_BUFF_SIZE 100
#define PRINTF_UART PRINTF_USB_VCOM
USB_BUFFER_SIZE defines the number of bytes sent and received by the USB driver per transfer. This should be 64 unless there is a good reason to change it.
SIMPLE_VCOM_TX_BUFSIZE defines the maximum number of bytes to buffer USB requests, this value can be reduced to save space. However, if you experience missing data then increasing this value is a good starting point.
PRINTF_BUFF_SIZE controls the size of the internal buffer within the printf driver. This, therefore, limits the maximum expanded (i.e. after the parameters have been parsed) length of a simple printf statement. This can be also tailored depending on your application.
Finally, PRINTF_UART tells the printf driver where to send the data, in this case, the data is sent to the USB_VCOM driver.
main.c
This is the entry point for our program, it contains the standard C main function which is the beginning of all C programs.
/* Project configuration */
#include "nimolib.h"
/* Books */
#include <microNimo.h>
#include <gpio.h>
#include <delay.h>
#include <usbVcom.h>
#include <printf.h>
void main(void)
{
uint32_t counter = 0;
uint32_t loopLastTicks = 0;
GPIO_PIN_DIR(MN_LED_PORT, MN_LED_PIN, GPIO_DIR_OUT);
GPIO_PIN_OUT(MN_LED_PORT, MN_LED_PIN, GPIO_OUT_LOW);
delaySetup(DELAY_BASE_MILLI_SEC); /*Clock timer at 1mS*/
usbVcomInit();
delayMs(2000);
printf("Hello from microNimo\r\n");
printf("Counting...\r\n");
loopLastTicks = delayGetTicks();
while (1)
{
if(delayMillis(loopLastTicks, 2000))
{
loopLastTicks = delayGetTicks();
printf("%ld\r\n", counter);
counter ++;
}
}
}
void vcomRecv(uint8_t *data, uint32_t size)
{
if(size > 0) /* Ensure that data was recieved*/
{
if('A' == data[0])
{
GPIO_PIN_TGL(MN_LED_PORT, MN_LED_PIN); /*If the user pressed the letter A then toggle the user LED*/
}
}
}
To build correctly, we have to include some standard header files, nimolib.h
, and system.h
are standard for all projects. In addition, we have to include a header file for each book we are using, in this case, lines 5-9.
On lines 15 and 16, we define 2 variables. The first, counter
is a 32-bit value that is incremented once every 2 seconds by the main loop and printed on the console. The second, loopLastTicks
holds the last know value of the processors internal systick peripheral. This is used to determine if the required delay has elapsed.
Lines 18 – 22 initialise the peripherals that we are going to use. In this tutorial we initialise the microNIMOs user LED as well as the delay and usbVcom drivers.
Line 24 is a 2-second blocking delay. This causes the microcontroller to wait until the USB host has had a chance to enumerate the device. If you don’t see the message Hello from microNimo
as the microprocessor boots then you may wish to increase this delay.
After displaying a couple of welcome messages, we initialise the loopLastTicks
variable with the current timestamp (this is an arbitrary value, not linked to GMT, time since boot, etc), then we begin the main loop.
With each pass of the main loop, we call the function delayMillis()
, this compares the value stored in loopLastTicks
with the current system timestamp to determine if the required time (in this case 2000ms) has elapsed. If the required time has elapsed delayMillis()
returns 1 and the current value or counter is printed on the serial console, if not a 0 is returned and the loop begins again.
Line 43 defines the vcomRecv()
function. This callback is executed upon reception of data from the host by the USB driver.
For each packet received from the host, we first check that there is some data available, then we look at the first (0th) byte of the data packet. The user LED is toggled If this byte equals the letter A, otherwise the data is simply discarded.
Build it!
Building a umake project involves a couple of simple commands. First of all, open a terminal and change to the directory containing your project. If you are using VSCode, then choose New Terminal from the Terminal menu.
If you have just created a new project or have made changes to the umakefile
then run the umake
command to create the project environment. Once umake
has finished without error, umake
will have created build and umake directories, downloaded the required libraries, and created a Makefile
that will build the project.
Next, run the make
command which if successful will compile our project and create usbVcom.bin
in the build directory.
Burn it!
Now that we have successfully built our project, it’s time to burn it into the microcontroller. The microNIMO must be in bootloader mode. The microNIMO is in bootloader mode by default If no application loaded.
If in application mode, hold down the user button whilst pressing and releasing the reset button. Release the user button once the blue LED is flashing twice a second.
Once the board is in bootloader run the command sudo make program
from the terminal.
sudo is currently required to interact with the bootloader. We are working on a udev file to remove that requirement.
The LED will be static in either the on or off state If the build is successful. This actual state is dependant on its state as the bootloader exited.
Now open a serial terminal. There is a number to chose from depending on your operating system. This tutorial assumes that you are familiar with at least one serial terminal.
You will need to determine the name of the serial port assigned to the board by your operating system. On Linux, this is likely to be /dev/ttyACM0
, on Windows this will be a COM port e.g. COM1, COM2, etc. The virtaul serial port ignores Baud rate, parity, and stop bit settings. Flow control (both hardware and software) however, must be disabled.
Once your chosen terminal software has successfully connected, press the reset button on the microNIMO. After about 2 seconds you should see the following:
Hello from microNimo
Counting...
followed by an increasing count every 2 seconds.
Sending the letter A (note case) from the serial console will cause the user LED on the microNIMO to change state. Repeatedly sending the letter A will cause the LED to flash on and off.