In Zephyr, there are three different ways to access the UART peripheral, all with different API functions; polling, interrupt-driven and asynchronous. In this lesson, we will be covering the asynchronous API.
The Asynchronous API is the most efficient way to use UART. It allows you to read and write data in the background using EasyDMA. In addition, the asynchronous mode supports features that allow us to enable receive timeouts and control the amount of data received before an interrupt is triggered. The asynchronous API is quite powerful and covers most use-cases.
Polling is the most basic method to access the UART peripheral. The reading function, uart_poll_in()
, is a non-blocking function and returns a character or -1 when no valid data is available. The writing function, uart_poll_out()
, is a blocking function and the thread waits until the given character is sent. We will not cover the method in this course.
With the Interrupt-driven API (raw interrupts), the UART driver ISR will manage the data, while the user application can continue other tasks. The Kernel’s Data Passing features (f.ex FIFO) can be used to communicate between the user application and the UART driver. We will not cover the method in this course.
You can learn more about the other APIs here.
1. As always, when it comes to drivers, the first thing we need to do is to enable the serial driver (UART driver). This is done by adding these two lines in prj.conf
.
CONFIG_SERIAL=y
CONFIG_UART_ASYNC_API=y
KconfigThe first line is usually enabled by default through the board’s devicetree as we have seen in-depth in Lesson 2. However, the second line is important to enable the asynchronous API of the serial driver.
2. Include the header file of the UART driver in your source code.
#include <zephyr/drivers/uart.h>
C3. As we have seen in the previous lessons, a peripheral (GPIO, UART, I2C, SPI, etc.) is instantiated as a device pointer, which is a structure to hold information about the peripheral in a standard way.
Some drivers in Zephyr have API-specific structures and calls that encapsulate all the information needed to control the device in one structure. The UART driver does not have this, so we will use the macro call DEVICE_DT_GET()
that was covered in the Device driver model section.
const struct device *uart = DEVICE_DT_GET(DT_NODELABEL(uart0));
if (!device_is_ready(uart)) {
return;
}
CThe pointer uart
of type struct device
is the structure that is used when interacting with the UART API.
On the other hand, uart0
is the node label of the devicetree node that represents the UART hardware controller on the chip.
Information about the nodes and node names can also be obtained in VS Code in the devicetree panel. Clicking the “Show Compiled DeviceTree Output” displays the final devicetree configuration where you can search for specific controllers and know their state, as shown in the picture below. Note that the Compiled DeviceTree Output is only avaiable after you build your nRF Connect SDK application.
One more thing to notice in the devicetree is that the default speed (baud rate) is set to 115200.
1. UART configurations like baudrate and parity bit can be configured both statically at build time and dynamically at run time as the Kconfig option (CONFIG_UART_USE_RUNTIME_CONFIGURE
) is enabled by default.
The default static configuration of the UART hardware is obtained from the devicetree as we have seen in the previous step.
On the other hand, to change the UART configurations dynamically, you need to create a variable of type uart_config
.
An example of creating a variable of type uart_config
is shown below:
const struct uart_config uart_cfg = {
.baudrate = 115200,
.parity = UART_CFG_PARITY_NONE,
.stop_bits = UART_CFG_STOP_BITS_1,
.data_bits = UART_CFG_DATA_BITS_8,
.flow_ctrl = UART_CFG_FLOW_CTRL_NONE
};
CThe header-file uart.h
has enumerations of all available options.
After that, call the UART API function uart_configure()
function and pass it the variable of type uart_config
.
int err = uart_configure(uart, &uart_cfg);
if (err == -ENOSYS) {
return -ENOSYS;
}
C2. Define the application callback function for the UART.
A callback function (also known as an interrupt handler or an ISR) runs asynchronously in response to a hardware or software interrupt. In general, ISRs have a higher priority than all threads (covered in Lesson 7). It preempts the execution of the current thread, allowing an action to take place immediately. Thread execution resumes only once all ISR work has been finished. Always try to keep the ISR as short as possible to guarantee your system’s responsiveness and prevent thread starvation.
We have the freedom to choose which UART events of interest to listen to.
Below are the available UART events:
Event | Description |
---|---|
UART_TX_DONE | The whole TX buffer was transmitted |
UART_TX_ABORTED | Transmitting aborted due to timeout or uart_tx_abort () call |
UART_RX_RDY | Some data was received and receive timeout occurred (if RX timeout is enabled) or when the receive buffer is full |
UART_RX_BUF_REQUEST | Driver requests next buffer for continuous reception |
UART_RX_BUF_RELEASED | Buffer is no longer used by UART driver |
UART_RX_DISABLED | This event is generated whenever receiver has been stopped, disabled or finished its operation (receive buffer filled) and can be enabled again using uart_rx_enable() |
UART_RX_STOPPED | RX has stopped due to external event |
UART_ASYNC_API
uart_event_type
The callback function should have the following signature:
static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
switch (evt->type) {
case UART_TX_DONE:
// do something
break;
case UART_TX_ABORTED:
// do something
break;
case UART_RX_RDY:
// do something
break;
case UART_RX_BUF_REQUEST:
// do something
break;
case UART_RX_BUF_RELEASED:
// do something
break;
case UART_RX_DISABLED:
// do something
break;
case UART_RX_STOPPED:
// do something
break;
default:
break;
}
}
CWe do not have to include all of these switch cases. Only include the ones relevant to your application code as we will see in the exercise section of this lesson.
The uart_event
struct contains the UART event type and a union, which could be one of the four members (tx
, rx
, rx_buf
, rx_stop
).
The rx
member is of type uart_event_rx,
which will hold the incoming data over UART.
3. Register the callback function by calling the function uart_callback_set
()
, which takes three parameters as shown in the screenshot below:
err = uart_callback_set(uart, uart_cb, NULL);
if (err) {
return err;
}
CIn this part, we will explain the needed steps to start receiving data over UART using the asynchronous API of the UART driver.
1. Declare a receive buffer to store the incoming data. The size and the type of the buffer must be selected with your application requirements in mind. For the simple exercises we have in this lesson, which controls LEDs through UART, we will simply declare a buffer of type uint8_t
(a byte) of size 10 bytes. In future lessons, we will introduce some more capable data structures (FIFO, circular buffer, etc.) that can be used to store the incoming data with more flexibility.
static uint8_t rx_buf[10] = {0}; //A buffer to store incoming UART data
C2. To start receiving, call the uart_rx_enable()
function, and pass the address of the receive buffer.
uart_rx_enable(uart, rx_buf, sizeof(rx_buf), 100);
CThe last parameter is the timeout, which in the context of the uart_rx_enable()
function will determine how fast we get notified on incoming data that is less than the whole buffer size. It is called the inactivity period, which is measured after receiving at least a byte. Pick a value that fits your application’s requirements. You can also disable the timeout by passing SYS_FOREVER_US
.
Note that this function returns immediately. Inside the UART ISR we can do (or delegate) the work of copying received data to the specified receive buffer.
3. The data received is accessible through the UART callback on the UART_RX_RDY
event.
Item | How to access it |
---|---|
Data Length | evt->data.rx.len |
Offset to where in the buffer data is stored | evt->data.rx.offset |
Actual data received | evt->rx.buf[rx.offset] to evt->rx.buf[rx.offset+rx.len] |
4. Continuous reception is not enabled by default, which means once the receive buffer is full, you must manually enable reception. Inside the UART_RX_DISABLED
case of the UART callback, you must re-enable UART to have continuous reception, like below:
case UART_RX_DISABLED:
uart_rx_enable(dev, rx_buf, sizeof(rx_buf), 100);
break;
CThe UART asynchronous API offers a way to perform chained buffer reception. You can declare multiple buffers to seamlessly switch between them when the current buffer is full. To do this you need to call uart_rx_buf_rsp()
on the event UART_RX_BUF_REQUEST
, which will provide the next buffer. When the current buffer is filled, receiving will automatically go to the next buffer.
Transmitting is a straightforward task as we only need to specify the transmission buffer.
1. Define a transmission buffer to hold the data to be sent. The size and the type of the buffer must be selected with your application requirements in mind. In the exercise section, we will simply send a welcome message. Therefore we will define the transmission buffer to be of type uint8_t
.
static uint8_t tx_buf[] = {"nRF Connect SDK Fundamentals Course \n\r"};
C2. Call the function uart_tx()
to send the data over UART.
The timeout feature (last parameter) is only valid if flow control is enabled, which is not the default.
err = uart_tx(uart, tx_buf, sizeof(tx_buf), SYS_FOREVER_US);
if (err) {
return err;
}
CThe function returns immediately and the sending is actually managed internally by the UART driver.
(Optional) 3. If your application needs to take action once the whole transmission buffer is transmitted, you could do that by using the UART_TX_DONE
event in the UART callback function.
case UART_TX_DONE:
// Do something here if needed
break;
CIn Zephyr, there are three different ways to access the UART peripheral, all with different API functions; polling, interrupts-driven and asynchronous.
Polling is the most basic method to access the UART peripheral. The reading function, uart_poll_in()
, is a non-blocking function and returns a character or -1 when no valid data is available. The writing function, uart_poll_out()
, is a blocking function and the user application waits until the given character is sent. We will not cover the method in this course.
With the Interrupt-driven API (raw interrupts), the UART driver ISR will manage the data, while the user application can continue other tasks. The Kernel’s Data Passing features(Ex: FIFO) can be used to communicate between the user application and the UART driver. We will not cover the method in this course.
The Asynchronous API is the most efficient way to use UART, it allows to read and write data in the background using EasyDMA. This is the method we will study in here.
In addition, the asynchronous mode supports features that allow us to enable receive timeouts and control the amount of data received before an interrupt is triggered.
As mentioned, the UART driver also has other ways to interact with it that we will not cover in this course. You can learn more about the other APIs here. The asynchronous API is quite powerful and covers most use-cases.
1. As always, when it comes to drivers, the first thing we need to do is to enable the serial driver (UART driver). This is done by adding these two lines in prj.conf
.
CONFIG_SERIAL=y
CONFIG_UART_ASYNC_API=y
KconfigThe first line is usually enabled by default through the board’s devicetree as we have seen in-depth in Lesson 2. However, the second line is important to enable the asynchronous API of the serial driver.
2. Include the header file of the UART driver in your source code.
#include <zephyr/drivers/uart.h>
C3. As we have seen in the previous lessons, a peripheral (GPIO, UART, I2C, SPI, etc.) is instantiated as a device pointer, which is a structure to hold information about the peripheral in a standard way.
Some drivers in Zephyr have API-specific structures and calls that encapsulate all the information needed to control the device in one structure. The UART driver does not have this, so we will use the macro call DEVICE_DT_GET()
that was covered in the Device driver model section.
const struct device *uart = DEVICE_DT_GET(DT_NODELABEL(uart0));
if (!device_is_ready(uart)) {
return;
}
CThe pointer uart
of type struct device
is the structure that is used when interacting with the UART API.
On the other hand, uart0
is the node label of the devicetree node that represents the UART hardware controller on the chip.
Information about the nodes and node names can also be obtained in VS Code in the devicetree panel. Clicking the “Show Compiled DeviceTree Output” displays the final devicetree configuration where you can search for specific controllers and know their state, as shown in the picture below. Note that the Compiled DeviceTree Output is only avaiable after you build your nRF Connect SDK application.
One more thing to notice in the devicetree is that the default speed (baud rate) is set to 115200.
1. UART configurations like baudrate and parity bit can be configured both statically at build time and dynamically at run time as the Kconfig option (CONFIG_UART_USE_RUNTIME_CONFIGURE
) is enabled by default.
The default static configuration of the UART hardware is obtained from the devicetree as we have seen in the previous step.
On the other hand, to change the UART configurations dynamically, you need to create a variable of type uart_config
.
An example of creating a variable of type uart_config
is shown below:
const struct uart_config uart_cfg = {
.baudrate = 115200,
.parity = UART_CFG_PARITY_NONE,
.stop_bits = UART_CFG_STOP_BITS_1,
.data_bits = UART_CFG_DATA_BITS_8,
.flow_ctrl = UART_CFG_FLOW_CTRL_NONE
};
CThe header-file uart.h
has enumerations of all available options.
After that, call the UART API function uart_configure()
function and pass it the variable of type uart_config
.
int err = uart_configure(uart, &uart_cfg);
if (err == -ENOSYS) {
return -ENOSYS;
}
C2. Define the application callback function for the UART.
A callback function (also known as an interrupt handler or an ISR) runs asynchronously in response to a hardware or software interrupt. In general, ISRs have a higher priority than all threads (covered in Lesson 7). It preempts the execution of the current thread, allowing an action to take place immediately. Thread execution resumes only once all ISR work has been finished. Always try to keep the ISR as short as possible to guarantee your system’s responsiveness and prevent thread starvation.
We have the freedom to choose which UART events of interest to listen to.
Below are the available UART events:
Event | Description |
---|---|
UART_TX_DONE | The whole TX buffer was transmitted |
UART_TX_ABORTED | Transmitting aborted due to timeout or uart_tx_abort () call |
UART_RX_RDY | Some data was received and receive timeout occurred (if RX timeout is enabled) or when the receive buffer is full |
UART_RX_BUF_REQUEST | Driver requests next buffer for continuous reception |
UART_RX_BUF_RELEASED | Buffer is no longer used by UART driver |
UART_RX_DISABLED | This event is generated whenever receiver has been stopped, disabled or finished its operation (receive buffer filled) and can be enabled again using uart_rx_enable() |
UART_RX_STOPPED | RX has stopped due to external event |
UART_ASYNC_API
uart_event_type
The callback function should have the following signature:
static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
switch (evt->type) {
case UART_TX_DONE:
// do something
break;
case UART_TX_ABORTED:
// do something
break;
case UART_RX_RDY:
// do something
break;
case UART_RX_BUF_REQUEST:
// do something
break;
case UART_RX_BUF_RELEASED:
// do something
break;
case UART_RX_DISABLED:
// do something
break;
case UART_RX_STOPPED:
// do something
break;
default:
break;
}
}
CWe do not have to include all of these switch cases. Only include the ones relevant to your application code as we will see in the exercise section of this lesson.
The uart_event
struct contains the UART event type and a union, which could be one of the four members (tx
, rx
, rx_buf
, rx_stop
).
The rx
member is of type uart_event_rx,
which will hold the incoming data over UART.
3. Register the callback function by calling the function uart_callback_set
()
, which takes three parameters as shown in the screenshot below:
err = uart_callback_set(uart, uart_cb, NULL);
if (err) {
return err;
}
CIn this part, we will explain the needed steps to start receiving data over UART using the asynchronous API of the UART driver.
1. Declare a receive buffer to store the incoming data. The size and the type of the buffer must be selected with your application requirements in mind. For the simple exercises we have in this lesson, which controls LEDs through UART, we will simply declare a buffer of type uint8_t
(a byte) of size 10 bytes. In future lessons, we will introduce some more capable data structures (FIFO, circular buffer, etc.) that can be used to store the incoming data with more flexibility.
static uint8_t rx_buf[10] = {0}; //A buffer to store incoming UART data
C2. To start receiving, call the uart_rx_enable()
function, and pass the address of the receive buffer.
uart_rx_enable(uart, rx_buf, sizeof(rx_buf), 100);
CThe last parameter is the timeout, which in the context of the uart_rx_enable()
function will determine how fast we get notified on incoming data that is less than the whole buffer size. It is called the inactivity period, which is measured after receiving at least a byte. Pick a value that fits your application’s requirements. You can also disable the timeout by passing SYS_FOREVER_US
.
Note that this function returns immediately. Inside the UART ISR we can do (or delegate) the work of copying received data to the specified receive buffer.
3. The data received is accessible through the UART callback on the UART_RX_RDY
event.
Item | How to access it |
---|---|
Data Length | evt->data.rx.len |
Offset to where in the buffer data is stored | evt->data.rx.offset |
Actual data received | evt->rx.buf[rx.offset] to evt->rx.buf[rx.offset+rx.len] |
4. Continuous reception is not enabled by default, which means once the receive buffer is full, you must manually enable reception. Inside the UART_RX_DISABLED
case of the UART callback, you must re-enable UART to have continuous reception, like below:
case UART_RX_DISABLED:
uart_rx_enable(dev, rx_buf, sizeof(rx_buf), 100);
break;
CThe UART asynchronous API offers a way to perform chained buffer reception. You can declare multiple buffers to seamlessly switch between them when the current buffer is full. To do this you need to call uart_rx_buf_rsp()
on the event UART_RX_BUF_REQUEST
, which will provide the next buffer. When the current buffer is filled, receiving will automatically go to the next buffer.
Transmitting is a straightforward task as we only need to specify the transmission buffer.
1. Define a transmission buffer to hold the data to be sent. The size and the type of the buffer must be selected with your application requirements in mind. In the exercise section, we will simply send a welcome message. Therefore we will define the transmission buffer to be of type uint8_t
.
static uint8_t tx_buf[] = {"nRF Connect SDK Fundamentals Course \n\r"};
C2. Call the function uart_tx()
to send the data over UART.
The timeout feature (last parameter) is only valid if flow control is enabled, which is not the default.
err = uart_tx(uart, tx_buf, sizeof(tx_buf), SYS_FOREVER_US);
if (err) {
return err;
}
CThe function returns immediately and the sending is actually managed internally by the UART driver.
(Optional) 3. If your application needs to take action once the whole transmission buffer is transmitted, you could do that by using the UART_TX_DONE
event in the UART callback function.
case UART_TX_DONE:
// Do something here if needed
break;
CIn Zephyr, there are three different ways to access the UART peripheral, all with different API functions; polling, raw-interrupts and asynchronous.
In this part, we will learn how to use the UART peripheral in asynchronous mode. Asynchronous mode means we will get an interrupt every time new data is received through the UART driver. As opposed to raw-interrupts, the asynchronous mode supports features that allow us to enable receive timeouts and control the amount of data received before an interrupt is triggered.
As mentioned, the UART driver also has other ways to interact with it that we will not cover in this course. You can learn more about the other APIs here. The asynchronous API is quite powerful and covers most use-cases.
1. As always, when it comes to drivers, the first thing we need to do is to enable the serial driver (UART driver). This is done by adding these two lines in prj.conf
.
CONFIG_SERIAL=y
CONFIG_UART_ASYNC_API=y
CONFIG_SERIAL
enables options for the serial drivers, and is usually enabled by default through the board’s devicetree as we have seen in-depth in Lesson 2. CONFIG_UART_ASYNC_API
enables the asynchronous API of the serial driver.
2. Include the header file of the UART driver in your source code.
#include <drivers/uart.h>
3. As we have seen in the previous lessons, a peripheral (GPIO, UART, I2C, SPI, etc.) is instantiated as a device struct, which is a structure to hold information about the peripheral in a standard way.
const struct device *uart= device_get_binding(DT_LABEL(DT_NODELABEL(uart0)));
if (uart == NULL) {
printk("Could not find %s!\n\r", DT_LABEL(DT_NODELABEL(uart0)));
return;
}
The pointer uart
of type struct device
is the structure that is used when interacting with the UART API.
On the other hand, uart0
is the name of the devicetree node that represents the UART hardware controller on the chip.
Information about the nodes and node names can also be obtained in VS Code in nRF Connect -> Devicetree viewer -> Buses.
One more thing to notice in the devicetree is that the default speed (baud rate) is set to 115200.
1. UART configurations like baud rate and parity bit can be configured both statically at build time and dynamically at run time as the Kconfig option (CONFIG_UART_USE_RUNTIME_CONFIGURE
) is enabled by default.
The default static configuration of the UART hardware is obtained from the devicetree as we have seen in the previous step.
On the other hand, to change the UART configurations dynamically, you need to create a variable of type uart_config
.
An example of creating a variable of type uart_config
is shown below:
const struct uart_config uart_cfg = {
.baudrate = 115200,
.parity = UART_CFG_PARITY_NONE,
.stop_bits = UART_CFG_STOP_BITS_1,
.data_bits = UART_CFG_DATA_BITS_8,
.flow_ctrl = UART_CFG_FLOW_CTRL_NONE
};
The header-file uart.h
has enumerations of all available options.
After that, call the UART API function uart_configure()
function and pass it the variable of type uart_config
.
int err = uart_configure(uart, &uart_cfg);
if (err == -ENOSYS) {
return -ENOSYS;
}
2. Define the application callback function for the UART.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Euismod in pellentesque massa placerat duis. Consectetur purus ut faucibus pulvinar elementum integer. Tempor nec feugiat nisl pretium fusce id velit. Sed sed risus pretium quam vulputate. Ultrices vitae auctor eu augue ut lectus arcu bibendum.
We have the freedom to choose which UART events of interest to listen to.
Below are the available UART events:
Event | Description |
---|---|
UART_TX_DONE | The whole TX buffer was transmitted |
UART_TX_ABORTED | Transmitting aborted due to timeout or uart_tx_abort call |
UART_RX_RDY | Some data was received and receive timeout occurred (if RX timeout is enabled) or when the receive buffer is full |
UART_RX_BUF_REQUEST | Driver requests next buffer for continuous reception |
UART_RX_BUF_RELEASED | Buffer is no longer used by UART driver |
UART_RX_DISABLED | This event is generated whenever receiver has been stopped, disabled or finished its operation (receive buffer filled) and can be enabled again using uart_rx_enable() |
UART_RX_STOPPED | RX has stopped due to external event |
UART_ASYNC_API
uart_event_type
The callback function should have the following signature:
static void uart_cb(const struct device *dev, struct uart_event *evt, void *user_data)
{
switch (evt->type) {
case UART_TX_DONE:
// do something
break;
case UART_TX_ABORTED:
// do something
break;
case UART_RX_RDY:
// do something
break;
case UART_RX_BUF_REQUEST:
// do something
break;
case UART_RX_BUF_RELEASED:
// do something
break;
case UART_RX_DISABLED:
// do something
break;
case UART_RX_STOPPED:
// do something
break;
default:
break;
}
}
We do not have to include all of these switch cases. Only include the ones relevant to your application code as we will see in the exercise section of this lesson.
The uart_event
struct contains the UART event type and a union, which could be one of the four members (tx
, rx
, rx_buf
, rx_stop
).
The rx
member is of type uart_event_rx,
which will hold the incoming data over UART.
3. Register the callback function by calling the function uart_callback_set
()
, which takes three parameters as shown in the screenshot below:
err = uart_callback_set(uart, uart_cb, NULL);
if (err) {
return err;
}
In this part, we will explain the needed steps to start receiving data over UART using the asynchronous API of the UART driver.
1. Declare a receive buffer to store the incoming data. The size and the type of the buffer must be selected with your application requirements in mind. For the simple exercises we have in this lesson, which controls LEDs through UART, we will simply declare a buffer of type uint8_t
(a byte) of size 10 bytes. In future lessons, we will introduce some more capable data structures (FIFO, circular buffer, etc.) that can be used to store the incoming data with more flexibility.
static uint8_t rx_buf[10] = {0}; //A buffer to store incoming UART data
2. To start receiving, call the uart_rx_enable()
function, and pass the address of the receive buffer.
uart_rx_enable(uart ,rx_buf,sizeof rx_buf,100);
The last parameter is the timeout, which in the context of the uart_rx_enable()
function will determine how fast we get notified on incoming data that is less than the whole buffer size. It is called the inactivity period, which is measured after receiving at least a byte. Pick a value that fits your application’s requirements. You can also disable the timeout by passing SYS_FOREVER_US
.
Note that this function returns immediately. Inside the UART ISR we can do (or delegate) the work of copying received data to the specified receive buffer.
3. The data received is accessible through the UART callback on the UART_RX_RDY
event.
Item | How to access it |
---|---|
Data Length | evt->data.rx.len |
Offset to where in the buffer data is stored | evt->data.rx.offset |
Actual data received | evt->rx.buf[rx.offset] to evt->rx.buf[rx.offset+rx.len] |
4. Continuous reception is not enabled by default, which means once the receive buffer is full, you must manually enable reception. Inside the UART_RX_DISABLED
case of the UART callback, you must re-enable UART to have continuous reception, like below:
case UART_RX_DISABLED:
uart_rx_enable(dev, rx_buf, sizeof(rx_buf), 100);
break;
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Euismod in pellentesque massa placerat duis. Consectetur purus ut faucibus pulvinar elementum integer. Tempor nec feugiat nisl pretium fusce id velit. Sed sed risus pretium quam vulputate. Ultrices vitae auctor eu augue ut lectus arcu bibendum.
Transmitting is a straightforward task as we only need to specify the transmission buffer.
1. Define a transmission buffer to hold the data to be sent. The size and the type of the buffer must be selected with your application requirements in mind. In the exercise section, we will simply send a welcome message. Therefore we will define the transmission buffer to be of type uint8_t
.
static uint8_t tx_buf[] = {"nRF Connect SDK Fundamentals Course \n\r"};
2. Call the function uart_tx()
to send the data over UART.
The timeout feature (last parameter) is only valid if flow control is enabled, which is not the default.
err = uart_tx(uart, tx_buf, sizeof(tx_buf), SYS_FOREVER_US);
if (err) {
return err;
}
The function returns immediately and the sending is actually managed internally by the UART driver.
(Optional) 3. If your application needs to take action once the whole transmission buffer is transmitted, you could do that by using the UART_TX_DONE
event in the UART callback function.
case UART_TX_DONE:
// Do something here if needed
break;