Chapter 8.3: Communicating Asynchronously: USART Interrupts, Clocking and Electrical Signaling
12/344/2015 | 06:19 AM
In the last section, we configured the USART0 for serial data transfer through the use of the configurator tool. In this section, we will write some software that springs into action whenever a data arrives at USART0 from an external device. We will also examine the waveforms seen on the serial bus with an oscilloscope, so that you know what to look for if you don’t see anything on your board. We will wrap up this section with a look at how to calculate the USART clock frequencies that are reachable with the clock modules current configuration.
USART0 Interrupts
Now that I have initialized the USART0 instance of the USART peripheral, I will enable interrupts and set up an interrupt handler to do something when new data arrives.
Compile and run this code, and place a breakpoint on the line that says “char test_char = USART_Rx(USART0);” in the USART0 interrupt handler. Press the run button in Simplicity Studio, and it should NOT hit the breakpoint because there should be no data arriving at the USART RX buffers yet.
Connect your PC0 pin (USART TX) on the Starter Kit to the RXD pin on the CP2104 MINIEK board. Connect PC1 pin (USART RX) to the TXD pin on the CP2104. Also connect the GND pin on the Starter Kit to a ground pin on the CP2104. DO NOT connect any of the VDD pins, as each board gets power from their own USB cable in this configuration.. The only time that you will connect the VDD of the CP2104 device to a target device is when you intend to power that device over USB. That can be done, but it is not how we are currently using the Starter Kit. Plug in the CP2104 MINIEK board to your computer if you haven’t done that already and fire up your terminal emulator using the settings described previously.
You should then be able to type a character on the terminal emulator on your computer and see the breakpoint event happen in Simplicity Studio. Single step once and then examine the value of the data within the test_char variable. When you hover on the variable, Simplicity Studio shows you the content of the variable using a pop-up window in a binary format as well as an ASCII text format in quotes. The ASCII value should match the character that you typed in the terminal.
If you did not see a breakpoint occur when you typed in your terminal emulator, make sure that you have set the correct serial port in your terminal emulator, and that the baud and other parameters are set correctly. If you have unplugged and reconnected your USB-to-serial port adapter, you will need to restart your terminal emulator or refresh the available COM ports and try again. Also, double check your connections between the serial port adapter’s TX/RX pins to PC0 and PC1. Note that the silkscreen for PC2 is located above the pin unlike all the other pin labels, which may make it appear that PC1 is the last pin in the row, but it is actually the second-to-last pin in the header strip.
Press the resume button and note that the interrupt does not happen again until you press another key on the keyboard. The USART RXDATAV interrupt flag is cleared automatically. Some interrupt flags in EFM32 are cleared automatically and some require a manual clear; you just have to read the Reference Manual when you are unsure.
Now remove the breakpoint and press the resume button in Simplicity Studio. Type more keys on the keyboard and you will notice that nothing at all happens. Inside the MCU, there are surely interrupts happening and running, but it is a poor user experience. Therefore, let’s add some commands to echo the data back from the MCU to the terminal emulator. Add this line inside the USART0 interrupt handler just after the “char test_char = USART_Rx(USART0);” line:
USART_Tx(USART0, test_char);
Disconnect from your debug session in Simplicity Studio and rebuild, then re-launch the debug session. You should now be able to see what you type show up on the terminal emulator screen. Congratulations, you just created a bidirectional communication link to your MCU! And the nice part about this is that if you see text show up in your terminal, you know that the MCU is alive and working. If there is no text when you type, then it tells you that your MCU is either locked up or powered off.
Electrical Waveforms
Hopefully you have been able to follow along and see evidence that everything is working. But what if it is not working? You can only double-check your com port so many times, right? Or what if some of your characters are garbled? At some point, you will need to break out an oscilloscope to see what is happening down at the physical interface level.
I have attached my oscilloscope to the header pins underneath the jumpers and captured the following waveform when I typed the letter j on the keyboard. The blue channel is on the signal that is transmitting from the computer to the MCU, and the red channel is on signal that is transmitting from the MCU to the computer. You can see that the two data packets match and that they are separated by around 20 microseconds, which is the time that it takes for the interrupt handler to leap into action and spit the received data back out on the line to the computer.
The RS-232 standard transmits 10 bits per packet, and in this case the packet is the letter j. The first bit is always a zero, which is the start bit. The last bit is the stop bit, which is 1. The eight bits in between are the ASCII code for the letter j, which is 0x6A or 0b01101010. This is sent backwards from how we write it, so bit zero is sent first, ending with bit seven. I don’t know why the designers didn’t send the most significant bit first, so it would match how we write it, or why people drive on the left side of the road in some countries. It’s just the way it is. You will run into this bit-ordering problem (or endianness) often in embedded development.
Clock Sources for Asynchronous Communication
The Silicon Labs application note “AN0045 USART/UART – Asynchronous mode” states that crystals should be used for asynchronous communication instead of the RC clock sources. This is because the decoding of asynchronous signals requires a clock recovery mechanism.
When data is transmitted without a clock signal, the receiver must create a clock to sample the data packet all on its own. Both the transmitter and receiver have been configured with the same clock rate, but neither side knows exactly when to sample each data bit. The only thing that the receiver gets is a start bit, and that can happen at any moment in time. After that, the receiver has to sample multiple bit times and then find the stop bit. Accurate positioning of these clock edges after the start bit requires an accurate clock, and the HFRCO is not always as accurate as necessary to ensure trouble-free operation. It can be achieved if tuning and temperature compensation is performed on the HFRCO clock source, but it is easier to use a crystal-based clock source.
By using the HFXO (remember, X stands for crystal) as the clock source for the HF clock, the app note states that a range of baud rates between 244 and 8M baud can be reached if the HF clock is running at 32MHz. Things change as your HF divider changes, so you will need to reinitialize the USART at every core clock divider change and ensure that the baud rate you want is reachable.
There are some functions in the USART API to help you figure out if the clock frequency that you want to use is reachable. The first is USART_BaudrateCalc which simply performs math on the given reference clock frequency, clock divisor (between 1 and 32767), the syncmode (false for asynchronous), and the over sampling factor, which is 16 by default but can be set to 4, 6, 8, or 16.
The function USART_BaudrateAsyncSet will attempt to configure the proper clock divisor with a given USART instance, reference frequency, the desired baud rate, and oversampling factor.
The only way to know if the set was successful is to call on the function USART_BaudrateGet and compare that to your desired baud rate that you sent into the USART_BaudrateAsyncSet function. Note that your baud rates will usually be a little bit off as described in the Reference Manual.
uint32_t USART_BaudrateGet(USART_TypeDef *usart)
The USART_BaudrateAsyncSet and USART_BaudrateGet functions should be called after the CMU and USART have been initialized as desired, or after a HF clock divisor change. The USART_BaudrateCalc function can be called at any time because it only performs some math calculations and doesn’t actually set anything in the hardware.
In the next lesson, we will put the serial interface to work as a debug print generator.
I have followed all the steps given in the tutorial(chapters 8.2 and 8.3). But I am getting an error while I am trying to build the application;
"C:\Users\gs-1148\SimplicityStudio\v4_workspace\emptyCxxProject\src\InitDevice.c",214 Error[Pe513]: a value of type "int" cannot be assigned to an entity of type "USART_PrsRxCh_TypeDef"
Chapter 8.3: Communicating Asynchronously: USART Interrupts, Clocking and Electrical Signaling
In the last section, we configured the USART0 for serial data transfer through the use of the configurator tool. In this section, we will write some software that springs into action whenever a data arrives at USART0 from an external device. We will also examine the waveforms seen on the serial bus with an oscilloscope, so that you know what to look for if you don’t see anything on your board. We will wrap up this section with a look at how to calculate the USART clock frequencies that are reachable with the clock modules current configuration.
USART0 Interrupts
Now that I have initialized the USART0 instance of the USART peripheral, I will enable interrupts and set up an interrupt handler to do something when new data arrives.
Compile and run this code, and place a breakpoint on the line that says “char test_char = USART_Rx(USART0);” in the USART0 interrupt handler. Press the run button in Simplicity Studio, and it should NOT hit the breakpoint because there should be no data arriving at the USART RX buffers yet.
Connect your PC0 pin (USART TX) on the Starter Kit to the RXD pin on the CP2104 MINIEK board. Connect PC1 pin (USART RX) to the TXD pin on the CP2104. Also connect the GND pin on the Starter Kit to a ground pin on the CP2104. DO NOT connect any of the VDD pins, as each board gets power from their own USB cable in this configuration.. The only time that you will connect the VDD of the CP2104 device to a target device is when you intend to power that device over USB. That can be done, but it is not how we are currently using the Starter Kit. Plug in the CP2104 MINIEK board to your computer if you haven’t done that already and fire up your terminal emulator using the settings described previously.
You should then be able to type a character on the terminal emulator on your computer and see the breakpoint event happen in Simplicity Studio. Single step once and then examine the value of the data within the test_char variable. When you hover on the variable, Simplicity Studio shows you the content of the variable using a pop-up window in a binary format as well as an ASCII text format in quotes. The ASCII value should match the character that you typed in the terminal.
If you did not see a breakpoint occur when you typed in your terminal emulator, make sure that you have set the correct serial port in your terminal emulator, and that the baud and other parameters are set correctly. If you have unplugged and reconnected your USB-to-serial port adapter, you will need to restart your terminal emulator or refresh the available COM ports and try again. Also, double check your connections between the serial port adapter’s TX/RX pins to PC0 and PC1. Note that the silkscreen for PC2 is located above the pin unlike all the other pin labels, which may make it appear that PC1 is the last pin in the row, but it is actually the second-to-last pin in the header strip.
Press the resume button and note that the interrupt does not happen again until you press another key on the keyboard. The USART RXDATAV interrupt flag is cleared automatically. Some interrupt flags in EFM32 are cleared automatically and some require a manual clear; you just have to read the Reference Manual when you are unsure.
Now remove the breakpoint and press the resume button in Simplicity Studio. Type more keys on the keyboard and you will notice that nothing at all happens. Inside the MCU, there are surely interrupts happening and running, but it is a poor user experience. Therefore, let’s add some commands to echo the data back from the MCU to the terminal emulator. Add this line inside the USART0 interrupt handler just after the “char test_char = USART_Rx(USART0);” line:
Disconnect from your debug session in Simplicity Studio and rebuild, then re-launch the debug session. You should now be able to see what you type show up on the terminal emulator screen. Congratulations, you just created a bidirectional communication link to your MCU! And the nice part about this is that if you see text show up in your terminal, you know that the MCU is alive and working. If there is no text when you type, then it tells you that your MCU is either locked up or powered off.
Electrical Waveforms
Hopefully you have been able to follow along and see evidence that everything is working. But what if it is not working? You can only double-check your com port so many times, right? Or what if some of your characters are garbled? At some point, you will need to break out an oscilloscope to see what is happening down at the physical interface level.
I have attached my oscilloscope to the header pins underneath the jumpers and captured the following waveform when I typed the letter j on the keyboard. The blue channel is on the signal that is transmitting from the computer to the MCU, and the red channel is on signal that is transmitting from the MCU to the computer. You can see that the two data packets match and that they are separated by around 20 microseconds, which is the time that it takes for the interrupt handler to leap into action and spit the received data back out on the line to the computer.
The RS-232 standard transmits 10 bits per packet, and in this case the packet is the letter j. The first bit is always a zero, which is the start bit. The last bit is the stop bit, which is 1. The eight bits in between are the ASCII code for the letter j, which is 0x6A or 0b01101010. This is sent backwards from how we write it, so bit zero is sent first, ending with bit seven. I don’t know why the designers didn’t send the most significant bit first, so it would match how we write it, or why people drive on the left side of the road in some countries. It’s just the way it is. You will run into this bit-ordering problem (or endianness) often in embedded development.
Clock Sources for Asynchronous Communication
The Silicon Labs application note “AN0045 USART/UART – Asynchronous mode” states that crystals should be used for asynchronous communication instead of the RC clock sources. This is because the decoding of asynchronous signals requires a clock recovery mechanism.
When data is transmitted without a clock signal, the receiver must create a clock to sample the data packet all on its own. Both the transmitter and receiver have been configured with the same clock rate, but neither side knows exactly when to sample each data bit. The only thing that the receiver gets is a start bit, and that can happen at any moment in time. After that, the receiver has to sample multiple bit times and then find the stop bit. Accurate positioning of these clock edges after the start bit requires an accurate clock, and the HFRCO is not always as accurate as necessary to ensure trouble-free operation. It can be achieved if tuning and temperature compensation is performed on the HFRCO clock source, but it is easier to use a crystal-based clock source.
By using the HFXO (remember, X stands for crystal) as the clock source for the HF clock, the app note states that a range of baud rates between 244 and 8M baud can be reached if the HF clock is running at 32MHz. Things change as your HF divider changes, so you will need to reinitialize the USART at every core clock divider change and ensure that the baud rate you want is reachable.
There are some functions in the USART API to help you figure out if the clock frequency that you want to use is reachable. The first is USART_BaudrateCalc which simply performs math on the given reference clock frequency, clock divisor (between 1 and 32767), the syncmode (false for asynchronous), and the over sampling factor, which is 16 by default but can be set to 4, 6, 8, or 16.
The function USART_BaudrateAsyncSet will attempt to configure the proper clock divisor with a given USART instance, reference frequency, the desired baud rate, and oversampling factor.
The only way to know if the set was successful is to call on the function USART_BaudrateGet and compare that to your desired baud rate that you sent into the USART_BaudrateAsyncSet function. Note that your baud rates will usually be a little bit off as described in the Reference Manual.
The USART_BaudrateAsyncSet and USART_BaudrateGet functions should be called after the CMU and USART have been initialized as desired, or after a HF clock divisor change. The USART_BaudrateCalc function can be called at any time because it only performs some math calculations and doesn’t actually set anything in the hardware.
In the next lesson, we will put the serial interface to work as a debug print generator.
PREVIOUS | NEXT
Hi,
I have followed all the steps given in the tutorial(chapters 8.2 and 8.3). But I am getting an error while I am trying to build the application;
"C:\Users\gs-1148\SimplicityStudio\v4_workspace\emptyCxxProject\src\InitDevice.c",214 Error[Pe513]: a value of type "int" cannot be assigned to an entity of type "USART_PrsRxCh_TypeDef"
i am using:
Gecko SDK 5.0
Device: EFR32MG1P232F256GM48