Discovery: UARTs Part 3 - The Final Step
Port Entrance
Previously, we looked at what a UART does, and how to get one wired so that we can use it on our PC. This week we look at the software side of UARTs. We’ll look at getting an output port working this time around.
Port Authority
Our processor has 6 UARTs to choose from. Now our choice becomes important. I chose UART3, and soldered wires to it, so I can easily use it with my RTOS (RTEMS). If you chose a different UART then, in the following instructions, replace 3 with 2 or 6 and it should just work.
Which port did you use? We’ll need that now.
Fire up Cube, open your project to the Pinout tab, and scroll the left column down to the UARTs.
Due to the set of features that ST put on the board, there will be some issues where other peripherals have already claimed pins that we need. Look at UARTs 1, 4, and 5 in figure 1, the red X beside them tell us that there would be a serious problem using these ports as they interfere with peripherals that have already been set up. If you hover over the names, you get a popup box that tells you what the problem is. These particular port’s pin’s have been claimed by the I2C bus that goes to the audio DAC, the push button, and a USB signal.
UART6 has a yellow triangle. It has a conflict but it isn’t serious. Selecting UART6 interferes with the I2S bus number 3, but since that bus is disabled, UART6 could be used, but not at the same time as I2S3.
UARTs 2 and 3 are unencumbered so let’s use UART3. Click on the name of the UART and change the Mode from Disable to Asynchronous.
Port Window
We could generate code right now, but there is a couple of parameters that you should see. Click into the Configuration tab. Click on your UART in the Connectivity section. Under the Parameter Settings tab, notice that the baud rate has been set to 115200 Bits/second.
From my setting up the UART post, you should remember baud, word length, parity, and stop bits. Our mantra on this project is 115200 baud, 8 bits, none (parity), and 1 stop bit. If you need to communicate with a particular device that needs different settings (such as a GPS that is typically 4800 or 9600 baud) change it here and regenerate your code.
Hook up your board to your computer and fire up the serial communications program of your choice. Windows people can use hyperterm, TeraTerm, or PuTTY. For macOS, use CoolTerm, serial, or screen /dev/tty.u<tab> 115200. On Linux, use screen, gtkterm, or minicom.
Now you can generate your code and import your project into your IDE.
Output Port
In your main.c file, look for your while(1) loop and insert this line below the USER CODE BEGIN 3 comment:
printf(“Hello world\n”);
Now compile, load, and run your code.
And voila (pronounced vwa-la, it’s French) nothing happens. You might expect Hello world to scroll down your screen. But it doesn't. Did you hook everything up right? Did you fry your processor? Did you set something up wrong?
No.
Hard To Port!
Here’s the issue, we need to add a little snippet of code to direct the output of printf to our UART. We shall put this code into main.c for now. Put it in the USER CODE BLOCK 4, towards the bottom of the file.
If you are using gcc add in this code:
int _write(int file, char *outgoing, int len) {
HAL_UART_Transmit(&huart3, outgoing, len, 100);
return len;
}
For the IAR EWARM or Keil compilers:
int32_t fputc(int32_t ch, FILE *f) {
HAL_UART_Transmit(&huart3, &ch, 1, 100);
return ch;
}
Compile, load and run. Hook up your serial or USB cable to your PC, fire up your terminal program, choose the port, and start receiving data. It should just work.
Okay, that worked like magic, but why?
Vintage Port
Magic? No, library code.
Compilers convert your code into machine language, but it is the library code which comes with the compilers, that really make compilers useful. The function printf isn’t a part of the C language, it is a part of the C standard library. Historically, C compilers for UNIX used a standard library called libc. As computers and operating systems evolved, libc mutated to match.
Our embedded systems work with very restricted resources. They might not have a host operating system and a bunch of hardware features may be missing as well, so many of the calls in the libc standard library would not make any sense. For instance, a call to time, to give you the time of day, won’t work very well if you don’t have a real time clock. And calling getlogin, to see who is logged in, makes no sense on our little processors. So the standard library is adjusted to fit the nature of our systems.
With the gcc compiler that I am using, I get to use a standard library called newlib. Newlib has a set of standard functions, including printf and the other standard I/O functions, string manipulation functions, time functions, locale functions to get your international numbers looking right, and a math library. Take a look at the documentation for your compiler’s library, there’s a lot of stuff in there that you don’t have to write.
Printf is a complex piece of code. It takes a text string that contains formatting information, combines it with a bunch of optional parameter values; integers, floating point values, characters, and strings, making a single long text string. That is then passed to another routine that is used to print strings; it is broken up into characters, and then the whole thing falls apart.
The low level routines eventually have to send single characters to the data register in one of six UART peripherals. But which one? The compiler people left hooks for us, partially complete functions that we need to complete. GCC needs us to fill in a function called _write. IAR and Keil need us to fill in fputc. When you fill in these hook functions for a new system, the processes is known as porting. The functions that we inserted into main.c, overrode the hook functions and got called when printf got called.
That’s it for now, I’m going to take a break until the new year. I really have to get some Christmas shopping done before I’m left buying Russian knock-off Cheetos down at the Piggly-Wiggly. Next time we’ll get into serial input and interrupts. Until then, keep your brooms on the ice.
This post is part of a series. Please see the other posts here.
This week’s music to work by: Moby - Long Ambients 1