top of page
Search
leowenner7as6

Uart Driver C Code: Understanding the Serial Core API in Linux Kernel



An embedded system often requires a means for communicating with the external world for a number of possible reasons. It could be to transferring data to another device, sending and receiving commands, or simply for debugging purposes. One of the most common interfaces used in embedded systems is the universal asynchronous receiver/transmitter (UART). When a board arrives in the hands of the software/firmware team, the first step is typically to get the debug console functional. The debug console is a serial interface which historically is implemented as RS-232 to connect with a PC serial port. These days most PCs not longer have a serial port, so it is more commonly seen implemented using USB, however the concept is the same. In this lesson, we will learn a bit about the theory behind UART and RS-232, learn how to write a simple UART driver for the MSP430, and create a menu which gives the user the ability to change the frequency of the blinking LED during runtime.




Uart Driver C Code



For this tutorial we want to implement a UART driver with some simple APIs which can be used to print a menu and accept user input. The goal is to create a menu which will allow us to change the frequency of the blinking LED. We will not spend much time on the implementation of the menu as it is not important for the purposes of learning how to use the UART. Get the latest code from github to get started.


When programming for your desktop, there are plenty of ways using the standard library to print and read from the console. The most commonly used is printf, however there are others such as puts, putchar, and getchar which are more limited but simpler to implement. Our UART driver will follow this model, however we do not have the concept of stdin and stdout, file descriptors and all the rest that comes along with the actual implementation. In fact, the standard C library we have as part of gcc (newlib), has the full implementation, however it is too big (takes too much memory) for the MSP430G2553. Try to use snprintf or printf and you will soon run of of space in the text section (where the code goes). Perhaps it would fit on some of the bigger devices, however in embedded programming, unless you are running a full blown OS such as Linux, the standard C library is often specifically written only with the functionality required. For example, printf may not support all the formatters, there are no actual file descriptors and often it accesses the UART directly.


Before implementing the functions to read and write, we must initialize the USCI peripheral. The UART configuration we will be using is 9600 8N1. The MSP430G2553 has one USCI_A module, so we will write a the driver specifically for it. Two new files have been created, uart.c and uart.h located in the src and include directories respectively. The function uart_init is implemented as follows:


The function takes one argument of type uart_config_t from include/uart.h, which is for the most part a placeholder structure for any attributes which need to be configured. For now, the baud rate is the only member.


The first IO function we have is uart_getchar, which reads one character at a time from the UART. If there are no characters to read, it returns -1, commonly referred to in *nix talk as EOF (end of file). In this simple implementation, we will not implement any UART interrupts since polling is not required. However, the interrupt flag IFG2[UCA0RXIFG] can be read to determine if a character has been received. If it has, the character is read from UCA0RXBUF.


The final function is uart_puts, which is really just an extension of uart_putc that can print a string rather than individual characters.The implementation is exactly the same as uart_putc, except we iterate through the string until NULL is found, which indicates the end of the string.


It is important to note, we prefixed all these functions with uart_ not only because they are part of the UART API, but because we do not want to conflict with the standard C library routines. Depending on how the library is implemented, you may be able to override some of the functions, but it can be unsafe and unpredictable. If you really want to write a custom standard C library, there are linker options which can tell gcc to not include them. This means however that none of the standard header files are accessible, and therefore must all be redefined in your software.


The UART driver must now be integrated with our existing application. First we need to add the initialization to the board.c file. In addition, the pin muxing of P1.1 and P1.2 must be configured to USCI TX and RX. Below is an excerpt from the board_init function.


Then in our main() function we print out a welcome message using the uart_write() function. Next the menu is initialized with our main menu, and it will be printed out the terminal. Note that we use the macro ARRAY_SIZE here as well to pass in the number of menu items.


Now that you have your device and PC all set up for UART, reset the device and take a look at the menu. We have only one option for now (we will add to this in the future), which will set the frequency of the blinking LED. Select this option and enter a frequency of 2Hz. From the code described earlier, we know that this only sets a variable containing the timer period. For it to take effect, you must use the push button to start the blinking. Now select the menu option again and change the frequency to 4Hz. Stop and restart the blinking. You should see the LED blink twice as fast. In the next lesson, we will look at improving our UART driver to handle receiving characters even when the CPU is busy doing other stuff.


I see, I understand your question now. So to convert, you can look at the code I wrote as part of the stopwatch code to display the time. Since your integers are 16 bits, your string would need to have a minimum of 5 characters to support the maximum value. Taking the code from line 145 in src/main.c and making it a bit more generic:


Hi Ralph. If it communicates via UART then you may be able to use the driver (USCI) code and only modify the application code for the protocol of the LORA module. What is the specific LORA module you are looking it?


A UART is used to translate data between the chip and a serial port. The UART driver simplifies reading and writing to any of the UART peripherals on the board, with multiple modes of operation and performance. These include blocking, non-blocking, and polling, as well as text/binary mode, echo and return characters.


The UART driver interface provides device independent APIs, data types, and macros. The APIs in this driver serve as an interface to a typical RTOS application. The specific peripheral implementations are responsible for creating all the RTOS specific primitives to allow for thread-safe operation.


Only one UART index can be used at a time; calling UART_open() a second time with the same index previosly passed to UART_open() will result in an error. You can, though, re-use the index if the instance is closed via UART_close(). In the example code, Board_UART0 is passed to UART_open(). This macro is defined in the example's Board.h file.


The UART driver can operate in blocking mode or callback mode, by setting the writeMode and readMode parameters passed to UART_open(). If these parameters are not set, as in the example code, the UART driver defaults to blocking mode. Options for the writeMode and readMode parameters are UART_MODE_BLOCKING and UART_MODE_CALLBACK:


Options for the readEcho parameter are UART_ECHO_OFF and UART_ECHO_ON. This parameter determines whether the driver echoes data back to the UART. When echo is turned on, each character that is read by the target is written back, independent of any write operations. If data is received in the middle of a write and echo is turned on, the echoed characters will be mixed in with the write data.


The UART driver allows full duplex data transfers. Therefore, it is possible to call UART_read() and UART_write() at the same time (for either blocking or callback modes). It is not possible, however, to issue multiple concurrent operations in the same direction. For example, if one thread calls UART_read(uart0, buffer0...), any other thread attempting UART_read(uart0, buffer1...) will result in an error of UART_STATUS_ERROR, until all the data from the first UART_read() has been transferred to buffer0. This applies to both blocking and and callback modes. So applications must either synchronize UART_read() (or UART_write()) calls that use the same UART handle, or check for the UART_STATUS_ERROR return code indicating that a transfer is still ongoing.


In UART_DATA_TEXT mode, the driver will examine the UART_ReturnMode value, to determine whether or not to unblock/callback when a newline is received. Read actions replace a carriage return with a newline, and write actions add a carriage return before a newline. This effectively treats all device line endings as LF, and all host PC line endings as CRLF.


Commands for UART_control() can originate from UART.h or from implementation specific UART*.h (UARTCC26XX.h, UARTMSP432.h, etc.. ) files. While commands from UART.h are API portable across driver implementations, not all implementations may support all these commands. Conversely, commands from driver implementation specific UART*.h files add unique driver capabilities but are not API portable across all UART driver implementations.


The overview describes how to establish communication between an ESP32 and other UART devices using the functions and data types of the UART driver. A typical programming workflow is broken down into the sections provided below: 2ff7e9595c


0 views0 comments

Recent Posts

See All

Comments


bottom of page