Introduction
The Intel® Quark™ Microcontroller D2000 features two UART interfaces. This document describes how to program these UART interfaces using the Intel® Quark™ Microcontroller Software Interface (QMSI).
Prerequisites
This document assumes that the reader is familiar with the Intel® System Studio for Microcontrollers suite – an integrated tool set for developing, optimizing and debugging applications for Intel® Quark™ Microcontrollers. The Intel® System Studio for Microcontrollers can be downloaded here: /content/www/us/en/develop/tools/system-studio-microcontrollers.html (make sure to select D2000 as the target). For more information about setting up and using Intel® System Studio for Microcontrollers, please refer to the Getting Started with Intel® System Studio 2016 for Microcontrollers guide.
The document uses Intel® Quark™ Microcontroller Developer Kit D2000 board as a reference, but the information in this document can be applied to any Intel® Quark™ Microcontroller D2000 based projects.
The document discusses Intel® Quark™ Microcontroller Software Interface (QMSI) functions related to UART programming. It does not provide complete QMSI documentation. For more information, please refer to the Intel® Quark™ Microcontroller Software Interface guide.
Intel® Quark™ Microcontroller D2000 – UART Hardware Information
UART Capabilities
The UART interfaces integrated in Intel® Quark™ Microcontroller D2000 are software compatible with the 16550 standard. Each UART has 16 byte TX and RX FIFOs, supports 5 to 9 bit data format, and baud rates from 300 bps to 2 Mbps. CTS/RTS hardware flow control is available. RS485 mode is also supported.
Please refer to Intel® Quark™ Microcontroller D2000 datasheet, section 14 “UART” for more information about UART capabilities.
Intel® Quark™ Microcontroller Developer Kit D2000 Details
On the Intel® Quark™ Microcontroller Developer Kit D2000 board, the UART interface signals are connected as follows:
- UART_A RXD and TXD signals are available on the Arduino breakout pins 0 and 1 respectively. These pins are marked as (1) on the picture below.
- UART_A RST and CTS signals are available on the Arduino breakout pins A2 and A3 respectively.
- UART_B signals are connected to the FTDI TTL-232R-3V3 compatible header J2. This header is marked as (3) on the picture below. These signals can be also connected to the on-board USB to UART/JTAG FT232H interface IC using the jumper group marked as (2) on the picture below.
- The UART signals can be connected to the on-board FT232H IC by setting jumpers J9, J10, and J11 in the CTS, TXD, and N/C positions respectively.
- When using an FTDI cable connected to the J2 header, the UART signals need to be disconnected from the on-board FT232H IC by removing jumpers J15 – RTS/TMS, J17 – RXD/TCK, and moving jumper J11 to the N/C position.
- To use the on-board FT232H IC in JTAG mode, jumpers J9, J10, and J11 need to be set to the TDO, TDI, and TRST positions respectively, and jumpers J15 and J17 need to be connected.
I/O Pin Multiplexing
To enable multiple interfaces given a limited number of I/O pins, Intel® Quark™ Microcontroller D2000 multiplexes the functions of I/O pins. Each I/O pin can be assigned one of up to 3 different functions.
By default the only the RXD signal of the first UART interface (UART_A) is multiplexed to an I/O pin. The second UART interface (UART_B) signals are not connected to I/O pins by default – these pins are used for JTAG interface instead. The table below lists the I/O pins relevant to UART interfaces and their functions.
MCU Pin Number and Name | QMSI Pin Name | Function 0 | Function 1 | Function 2 | Developer Kit D2000 - Arduino Breakout Pin Name |
---|---|---|---|---|---|
4 – F_12 | QM_PIN_ID_12 | GPIO12 | AI12 | UART_A_TXD* |
1 |
5 – F_13 | QM_PIN_ID_13 | GPIO13* |
AI13 | UART_A_RXD | 0 |
6 – F_14 | QM_PIN_ID_14 | GPIO14* |
AI14 | UART_A_RTS / UART_A_DE | A2 |
7 – F_15 | QM_PIN_ID_15 | GPIO15* |
AI15 | UART_A_CTS / UART_A_RE | A3 |
13 – F_20 | QM_PIN_ID_20 | TRST_N* |
GPIO20 | UART_B_TXD | UART_B header – pin 5 USB – FT232H (J11) |
14 – F_21 | QM_PIN_ID_21 | TCK* |
GPIO21 | UART_B_RXD | UART_B header – pin 4 USB – FT232H (J17) |
15 – F_22 | QM_PIN_ID_22 | TMS* |
GPIO22 | UART_B_RTS / UART_B_DE | UART_B header – pin 2 USB – FT232H (J15) |
16 – F_23 | QM_PIN_ID_23 | TDI* |
GPIO23 | UART_B_CTS / UART_B_RE | UART_B header – pin 6 USB – FT232H (J9) |
*default mode
Programming I/O Pin Multiplexing in QMSI
QMSI provides the following functions to set up pin multiplexing:
qm_rc_t qm_pmux_select(qm_pin_id_t pin, qm_pmux_fn_t fn)
The qm_pmux_select function set the I/O pin specified by the pin parameter to the function specified by the fn parameter. For example the following call selects function 2 (UART_A_RXD) on pin QM_PIN_ID_13:
qm_pmux_select(QM_PIN_ID_13, QM_PMUX_FN_2);
qm_rc_t qm_pmux_input_en(qm_pin_id_t pin, bool enable)
The qm_pmux_input_en function enables or disables input mode on the I/O pin specified by the pin parameter, according to the value of the enable parameter. For example the following call enables input mode on pin QM_PIN_ID_13:
qm_pmux_input_en(QM_PIN_ID_13, true);
Snippet for configuring pin multiplexing on UART_A
qm_pmux_select(QM_PIN_ID_12, QM_PMUX_FN_2);
qm_pmux_select(QM_PIN_ID_13, QM_PMUX_FN_2);
qm_pmux_input_en(QM_PIN_ID_13, true);
/* Following 3 calls only needed when using RTS/CTS handshake */
qm_pmux_select(QM_PIN_ID_14, QM_PMUX_FN_2);
qm_pmux_select(QM_PIN_ID_15, QM_PMUX_FN_2);
qm_pmux_input_en(QM_PIN_ID_14, true);
Snippet for configuring pin multiplexing on UART_B
qm_pmux_select(QM_PIN_ID_20, QM_PMUX_FN_2);
qm_pmux_select(QM_PIN_ID_21, QM_PMUX_FN_2);
qm_pmux_input_en(QM_PIN_ID_21, true);
/* Following 3 calls only needed when using RTS/CTS handshake */
qm_pmux_select(QM_PIN_ID_22, QM_PMUX_FN_2);
qm_pmux_select(QM_PIN_ID_23, QM_PMUX_FN_2);
qm_pmux_input_en(QM_PIN_ID_22, true);
Important Note
Once UART_B signals are multiplexed to the I/O pins, the JTAG functionality is no longer available, and the microcontroller cannot be controlled (flashed or debugged) using the JTAG interface. The boot ROM code provides a hook to allow reprogramming the microcontroller in this case. Use the following procedure to reprogram the microcontroller:
- Temporarily connect Intel® Quark™ Microcontroller D2000 pin number 5, package pin name F_13 to the ground.
- On the Intel® Quark™ Microcontroller Developer Kit D2000, connect Arduino breakout pin 0 – RX to the GND pin using a jumper wire.
- Reboot the microcontroller.
- On the Intel® Quark™ Microcontroller Developer Kit D2000, simply press the RESET button.
- Restart OpenOCD. In the Intel® System Studio for Microcontrollers, navigate to the Debug Perspective, find the OpenOCD Session window, click the red Stop OpenOCD button in the top right corner of that window, and then click the green Start OpenOCD button.
- Flash the microcontroller normally.
- Disconnect the pin connected in the step number 1 (otherwise the firmware will not run).
UART Clock Gating
To reduce the power consumption Intel® Quark™ Microcontroller D2000 allows enabling or disabling the clock for on-chip peripherals, including UART interfaces. The clock can be enabled for UART interfaces using the clk_periph_enable QMSI function as follows:
/* enable clock for UART_A */
clk_periph_enable(CLK_PERIPH_CLK | CLK_PERIPH_UARTA_REGISTER);
/* enable clock for UART_B */
clk_periph_enable(CLK_PERIPH_CLK | CLK_PERIPH_UARTB_REGISTER);
Default UART Configuration in QMSI BSP
By default, the QMSI BSP (board support package) configures UART_A as follows:
- Only TX signal is multiplexed to the I/O pin (output only)
- The baud rate is set to 115000 bps
- The UART data format is configured for 8 data bits, no parity, and 1 stop bit
The following QMSI BSP functions handle UART setup:
- _start() in sys/app_entry.c
- stdout_uart_setup() in sys/newlib-syscalls.c
Writing data to UART Using QM_PRINTF
QMSI provides the QM_PRINTF function - a simplified version of the C standard library printf function that can be used to output data to the UART.
Default UART
By default, the QM_PRINTF function uses UART_A for the output. This is defined using the STDOUT_UART macro in include/qm_common.h. To use UART_B for the QM_PRINTF output, define STDOUT_UART_1 to 1. This should be done before #include <qm_common.h> directive. For example:
#define STDOUT_UART_1 (1)
#include <qm_common.h>
QM_PRINTF Limitations
Due to code size constraints QM_PRINTF supports only a subset of the printf format specifiers:
- The following format specifiers are supported:
- %d – signed integer; long “l” sub-specifier is ignored
- %u – unsigned integer; long “l” sub-specifier is ignored
- %X and %x – integer in hexadecimal format
- %s – string
- Padding is not supported. Format specifiers like %02d will result in an incorrect output.
- The character format specifier %c is not supported.
Example using QM_PRINTF
The following code will print a message on the default UART.
QM_PRINTF("Welcome to Intel Quark Microcontroller D2000\r\n");
Configuring UART Parameters
Reading Current UART Configuration
QMSI uses the qm_uart_config_t structure to store the UART configuration. The current UART configuration can be read using the qm_uart_get_config function:
qm_rc_t qm_uart_get_config(const qm_uart_t uart, qm_uart_config_t *cfg)
The uart parameter specifies the UART interface: QM_UART_0 for UART_A, and QM_UART_1 for UARTB_B. The cfg parameter specifies the location of the qm_uart_config_t structure to store the configuration data into. For example, the following code will read the configuration of the UART_A.
qm_uart_config_t uart0_cfg;
qm_uart_get_config(QM_UART_0, &uart0_cfg);
Setting New UART Configuration
The new UART configuration can be set using the qm_uart_set_config function:
qm_rc_t qm_uart_set_config(const qm_uart_t uart, const qm_uart_config_t *cfg)
The uart parameter specifies the UART interface, and the cfg parameter specifies the location of the qm_uart_config_t structure with the new configuration parameters.
Setting Parameters in the qm_uart_config_t Structure
The qm_uart_config_t contains the following members:
line_control
The line_control variable contains the settings for the UART Line Control Register (LCR). This register configures the UART data format: number of data bits, number of stop bits, and parity settings. The include/qm_uart.h file provides definitions for commonly used LCR values:
/**
* UART Line control.
*/
typedef enum {
QM_UART_LC_5N1 = 0x00, /**< 5 data bits, no parity, 1 stop bit */
QM_UART_LC_5N1_5 = 0x04, /**< 5 data bits, no parity, 1.5 stop bits */
QM_UART_LC_5E1 = 0x18, /**< 5 data bits, even parity, 1 stop bit */
QM_UART_LC_5E1_5 = 0x1c, /**< 5 data bits, even parity, 1.5 stop bits */
QM_UART_LC_5O1 = 0x08, /**< 5 data bits, odd parity, 1 stop bit */
QM_UART_LC_5O1_5 = 0x0c, /**< 5 data bits, odd parity, 1.5 stop bits */
QM_UART_LC_6N1 = 0x01, /**< 6 data bits, no parity, 1 stop bit */
QM_UART_LC_6N2 = 0x05, /**< 6 data bits, no parity, 2 stop bits */
QM_UART_LC_6E1 = 0x19, /**< 6 data bits, even parity, 1 stop bit */
QM_UART_LC_6E2 = 0x1d, /**< 6 data bits, even parity, 2 stop bits */
QM_UART_LC_6O1 = 0x09, /**< 6 data bits, odd parity, 1 stop bit */
QM_UART_LC_6O2 = 0x0d, /**< 6 data bits, odd parity, 2 stop bits */
QM_UART_LC_7N1 = 0x02, /**< 7 data bits, no parity, 1 stop bit */
QM_UART_LC_7N2 = 0x06, /**< 7 data bits, no parity, 2 stop bits */
QM_UART_LC_7E1 = 0x1a, /**< 7 data bits, even parity, 1 stop bit */
QM_UART_LC_7E2 = 0x1e, /**< 7 data bits, even parity, 2 stop bits */
QM_UART_LC_7O1 = 0x0a, /**< 7 data bits, odd parity, 1 stop bit */
QM_UART_LC_7O2 = 0x0e, /**< 7 data bits, odd parity, 2 stop bits */
QM_UART_LC_8N1 = 0x03, /**< 8 data bits, no parity, 1 stop bit */
QM_UART_LC_8N2 = 0x07, /**< 8 data bits, no parity, 2 stop bits */
QM_UART_LC_8E1 = 0x1b, /**< 8 data bits, even parity, 1 stop bit */
QM_UART_LC_8E2 = 0x1f, /**< 8 data bits, even parity, 2 stop bits */
QM_UART_LC_8O1 = 0x0b, /**< 8 data bits, odd parity, 1 stop bit */
QM_UART_LC_8O2 = 0x0f /**< 8 data bits, odd parity, 2 stop bits */
} qm_uart_lc_t;
baud_divisor
The baud_divisor variable configures the UART baud rate. It contains the packed settings for UART divisor registers. Each UART has three divisor registers:
- DLH - divisor latch high - 8 bit size
- DLL- divisor latch low - 8 bit size
- DLF - divisor latch fraction - 4 bit size
These divisor registers define the ratio the system clock frequency needs to be divided by to obtain UART clock frequency (baud rate). The divisor registers’ values can be calculated as follows:
- Calculate the divisor, round to the nearest integer:
divisor = system clock frequency / baud rate - Calculate the divisor latch high value, round to the nearest smaller or equal integer (floor):
dlh = divisor / 4096 - Calculate the divisor latch low value, round to the nearest smaller or equal integer (floor):
dll = (divisor - dlh * 4096) / 16 - Calculate the divisor fraction value:
dlf = divisor - dlh * 4096 - dll*16
The packed baud_divisor value can be then obtained using QM_UART_CFG_BAUD_DL_PACK(dlh, dll, dlf) macro.
For example, to get a 9600 baud rate, and assuming the system clock frequency of 32 MHz (as used in Intel® Quark™ Microcontroller Developer Kit D2000), the following calculation is used:
- divisor = 32000000 / 9600 = 3333 (rounded down from 3333.3333)
- dlh = divisor / 4096 = 3333 / 4096 = 0
- dll = (divisor - dlh * 4096) / 16 = (3333 - 0 * 4096) / 16 = 208
- dlf = divisor - dlh * 4096 - dll * 16 = 3333 - 0 * 4096 - 208 * 16 = 5
The table below gives divisor values for commonly used baud rates assuming 32 MHz system clock:
Baud rate | dlh (divisor latch high) | dll (divisor latch low) | dlf (divisor latch fraction) |
---|---|---|---|
115200 bps | 0 | 17 | 6 |
57600 bps | 0 | 34 | 12 |
38400 bps | 0 | 52 | 1 |
19200 bps | 0 | 104 | 3 |
9600 bps | 0 | 208 | 5 |
2400 bps | 3 | 65 | 5 |
1200 bps | 6 | 130 | 11 |
hw_fc
The hw_fc is a Boolean variable that enables or disables hardware flow control. Setting it to 0 disables hardware flow control, while setting it to 1 enables hardware flow control.
Configuring UART Example
The following snippet configures the UART_A to 9600 bps, 8 data bits, no parity, 1 stop bit, no hardware flow control. The calculation of the divisor values is given in the baud_divisor section above.
qm_uart_config_t uart0_cfg;
uart0_cfg.baud_divisor = QM_UART_CFG_BAUD_DL_PACK(0, 208, 5);
uart0_cfg.line_control = QM_UART_LC_8N1;
uart0_cfg.hw_fc = 0;
qm_uart_set_config(QM_UART_0, &uart0_cfg);
QMSI UART Input / Output Functions
Getting UART Status
QMSI provides the qm_uart_get_status function to obtain the current status of an UART.
qm_uart_status_t qm_uart_get_status(const qm_uart_t uart)
The uart parameter specifies the UART to get the status for.
The return value is a bitwise OR value of one or more of the following:
- QM_UART_IDLE – UART TX FIFO and transmit shift registers are empty.
- QM_UART_LSR_OE – Receiver overrun error
- QM_UART_LSR_PE – Receiver parity error
- QM_UART_LSR_FE – Receiver framing error
- QM_UART_LSR_BI – “Break interrupt” condition
- QM_UART_TX_BUSY – UART TX FIFO or transmit shift register are not empty
- QM_UART_RX_BUSY – Received data is ready
Sending and Receiving Data
QMSI provides several functions with slightly different functionality to send and receive the data. They can be broken down into the following categories:
- Blocking I/O – These functions wait for the data to be sent or received.
- Non-blocking I/O – These functions return immediately. These functions do not check for errors, for the data availability, or for the space in TX FIFO. The caller should check for these conditions using the qm_uart_get_status function.
- Interrupt-driven I/O – These functions send or receive data in the background, using UART interrupts to manage send and receive FIFOs.
Blocking I/O Functions
qm_rc_t qm_uart_write(const qm_uart_t uart, const uint8_t data)
The qm_uart_write function writes a single byte of data specified by the data parameter to the UART specified by the uart parameter. This is a blocking I/O function and it will wait (will not return) until the data is sent. Currently the qm_uart_write function always returns the QM_RC_OK value.
qm_uart_status_t qm_uart_read(const qm_uart_t uart, uint8_t *data)
The qm_uart_read function reads a single byte of data from the UART specified by the uart parameter. It stores the data in the location specified by the data pointer. This is a blocking I/O function and it will wait (will not return) until the data is available and can be read, or if an error occurs. The return value of this function is a bitwise OR value of one or more of the following:
- QM_UART_OK – Data was read successfully
- QM_UART_LSR_OE – Receiver overrun error
- QM_UART_LSR_PE – Receiver parity error
- QM_UART_LSR_FE – Receiver framing error
- QM_UART_LSR_BI – “Break interrupt” condition
qm_rc_t qm_uart_write_buffer(const qm_uart_t uart, const uint8_t *const data, uint32_t len)
The qm_uart_write_buffer function is a blocking I/O function. It writes multiple bytes from a buffer specified by the data parameter. The number of the bytes to be written is specified by len parameter. Currently the qm_uart_write_buffer function always returns the QM_RC_OK value.
Non-blocking I/O Functions
qm_rc_t qm_uart_write_non_block(const qm_uart_t uart, const uint8_t data)
The qm_uart_write_non_block function writes a single byte of data specified by the data parameter to the UART specified by the uart parameter. This is a non-blocking I/O function. It will return immediately after writing the data to the UART’s transmitter holding register. It does not check if the space is available in TX FIFO. Currently the qm_uart_write_non_block function always returns the QM_RC_OK value.
uint8_t qm_uart_read_non_block(const qm_uart_t uart)
The qm_uart_read_non_block function reads and returns a single byte of data from the UART specified by the uart parameter. This is a non-blocking I/O function. This function returns immediately, and does not check if received data is available, or if any receive errors had occurred. The return value is the content of the UART’s receive buffer register.
Interrupt-driven I/O – Transfer Structure
The interrupt-driven I/O functions use the qm_uart_transfer_t structure to pass the parameters for the data transfer. This structure contains the following members:
data
The data variable is a uint8_t* pointer to the buffer. For the write function, the buffer should contain the data to be sent. For the read function, it specifies the buffer to store the received data in.
data_len
The data_len variable is a uint32_t value specifying the number of the bytes to transfer.
fin_callback
The fin_callback variable contains the pointer to the callback function to be called once data transfer is complete. The application must define this callback (the err_callback cannot have NULL value).
The signature of the callback function is void fin_callback(uint32_t id, uint32_t len), where the id parameter specifies the transfer identifier (see id variable below), and the len parameter specifies the number of bytes successfully transferred.
err_callback
The err_callback variable contains the pointer to the callback function to be called in case of receive errors. Currently, it is not used for write operations. The application must define this callback for both read or write operations (the err_callback cannot have NULL value).
The signature of this callback function is void err_callback(uint32_t id, qm_uart_status_t status), where the id parameter specifies the transfer identifier (see id variable below), and the status parameter contains the error bits from line status register (LSR) – a bitwise OR value of one or more of the following:
- QM_UART_LSR_OE – Receiver overrun error
- QM_UART_LSR_PE – Receiver parity error
- QM_UART_LSR_FE – Receiver framing error
- QM_UART_LSR_BI – “Break interrupt” condition
id
The id parameter is a uint32_t transfer identifier. The application can use this parameter to identify transfers in callback functions. Note that QMSI supports only one active read and one active write transfer per UART.
Registering Interrupt Service Routine for Interrupt-driven I/O
Prior to using interrupt-driven I/O functions, it is necessary to register the QMSI UART interrupt service routine (ISR). This is done by calling the qm_irq_request function with the following parameters:
- For UART_A
qm_irq_request(QM_IRQ_UART_0, qm_uart_0_isr);
- For UART_B:
qm_irq_request(QM_IRQ_UART_1, qm_uart_1_isr);
Interrupt-driven I/O Functions
qm_uart_status_t qm_uart_irq_write(const qm_uart_t uart, const qm_uart_transfer_t *const xfer)
The qm_uart_irq_write function initiates interrupt-driven UART write transfer. The uart parameter specifies the UART to be used for the write transfer. The xfer parameter specifies the location of the transfer structure populated with data location, length, callback functions pointers, and transfer id.
The function returns one of the following values:
- QM_UART_OK – Transfer was initiated successfully
- QM_UART_TX_BUSY – UART TX FIFO or UART transmitter holding registers are busy. The transfer is not initiated in this case.
qm_uart_status_t qm_uart_irq_read(const qm_uart_t uart, const qm_uart_transfer_t *const xfer)
The qm_uart_irq_read function initiates interrupt-driven UART read transfer. The uart parameter specifies the UART to be used for the read transfer. The xfer parameter specifies the location of the transfer structure populated with data location, length, callback functions pointers, and transfer id.
The function returns one of the following values:
- QM_UART_OK – Transfer was initiated successfully
- QM_UART_RX_BUSY – Previous transfer has not been completed yet. The data length specified in previous request has not been read yet. The transfer is not initiated in this case.
qm_rc_t qm_uart_write_terminate(const qm_uart_t uart)
The qm_uart_write_terminate function terminates the current interrupt-driven write transfer for the UART specified by the uart parameter. It calls the transfer complete callback function, indicating the number of bytes that were actually written. Currently, qm_uart_write_terminate always returns QM_RC_OK.
qm_rc_t qm_uart_read_terminate(const qm_uart_t uart)
The qm_uart_read_terminate function terminates the current interrupt-driven read transfer for the UART specified by the uart parameter. It calls the transfer complete callback function, indicating the number of bytes that were actually read. Currently, the qm_uart_read_terminate function always returns the QM_RC_OK value.
Direct Access to UART Registers
To access the UART functionality that is not exposed through QMSI UART Input / Output functions, you can access the UART registers directly. The UART registers in the Intel® Quark™ Microcontroller D2000 are memory mapped and exposed to the QMSI programmer through the QM_UART array, containing qm_uart_reg_t structures for each one of the UARTs. The qm_uart_reg_t structure contains all the UART registers. For example, to access modem control register (MCR) of the UART_A, and set the loopback bit, you can use the following code:
QM_UART[QM_UART_0].mcr |= BIT(4);
Please refer to qm_soc_regs.h for the definition of the qm_uart_reg_t structure, and to the Intel® Quark™ Microcontroller D2000 Datasheet for the information about UART registers.
UART Programming Example: UART Setup and Blocking I/O
Description
The code below shows how to configure and use both UART interfaces. It configures UART_A for 115200 bps and UART_B for 9600 bps. Next, it scans both UART interfaces for available data. When a character becomes available on a UART, it is read, and written to the other UART.
Follow the next steps to run this code on the Intel® Quark™ Microcontroller Developer Kit D2000 board:
- Open Intel® Systems Studio for Microcontrollers.
- Create a new QMSI BSP project using hello_world as the template
- Replace main.c with the code below.
- Compile and flash the code to the microcontroller.
- Disconnect the board from the USB port, then move jumpers J9, J10, and J11 to CTS, TXD, and N/C positions respectively.
- Connect an FTDI USB TTL Serial Cable TTL-232R-3V3 cable to the Arduino breakout pins as follows:
- FTDI cable pin 1 (GND) to GND pin on the Arduino breakout header
- FTDI cable pin 3 (TX) to pin number 0 (RX) on the Arduino breakout header
- FTDI cable pin 4 (RX) to pin number 1 (TX) on the Arduino breakout header
- Reconnect the board to the USB port. Connect the FTDI cable to the USB port as well.
- On Windows OS, you must change the driver for the board to the FTDI CDM driver.
- Start terminal emulator software (for example PuTTY, screen, or minicom) on both USB virtual serial ports. Configure the port connected to the FTDI cable to 115200 bps, and the port connected to the developer kit board to 9600 bps.
- Reset the board. Observe the sign-in message on both terminals.
- Type some characters on one of the terminals. The typed characters should appear on the other terminal.
- Once done experimenting with this sample, reprogram the board using for example hello_world code, as described in the Important Note in the I/O Pin Multiplexing section.
- On Windows OS, you must change the driver for the board to the WinUSB driver.
Code
/* UART configuration example */
#include <qm_common.h>
#include <qm_pinmux.h>
#include <qm_uart.h>
#include <qm_scss.h>
int main(void)
{
qm_uart_config_t uart0_cfg;
qm_uart_config_t uart1_cfg;
uint8_t uart0_message[] =
"This is UART_A. Characters typed here will appear on UART_B.\r\n";
uint8_t uart1_message[] =
"This is UART_B. Characters typed here will appear on UART_A.\r\n";
uint8_t data;
/* UART_A I/O pins multiplexing setup
* QMSI BSP only configures UART_A_TXD signal.
* The following code configures both TXD and RXD signals
* and sets up RXD pin in input mode
*/
qm_pmux_select(QM_PIN_ID_12, QM_PMUX_FN_2); /* configure UART_A_TXD */
qm_pmux_select(QM_PIN_ID_13, QM_PMUX_FN_2); /* configure UART_A_RXD */
qm_pmux_input_en(QM_PIN_ID_13, true); /* UART_A_RXD is an input */
/* UART_B I/O pins multiplexing setup
* By default UART_B pins are multiplexed to JTAG.
* The following code configures all relevant pins to UART_B signals
* and sets up RXD and RTS pins in input mode
*/
qm_pmux_select(QM_PIN_ID_20, QM_PMUX_FN_2); /* configure UART_B_TXD */
qm_pmux_select(QM_PIN_ID_21, QM_PMUX_FN_2); /* configure UART_B_RXD */
qm_pmux_select(QM_PIN_ID_22, QM_PMUX_FN_2); /* configure UART_B_RTS */
qm_pmux_select(QM_PIN_ID_23, QM_PMUX_FN_2); /* configure UART_B_CTS */
qm_pmux_input_en(QM_PIN_ID_21, true); /* UART_B_RXD is an input */
qm_pmux_input_en(QM_PIN_ID_22, true); /* UART_B_RTS is an input */
/* Stores current UART_A configuration to uart0_cfg */
qm_uart_get_config(QM_UART_0, &uart0_cfg);
/* Configures UART_A for 115200 bps */
uart0_cfg.baud_divisor = QM_UART_CFG_BAUD_DL_PACK(0, 17, 6);
/* Configures UART_B for 9600 bps,
* 8 data bits, no parity, 1 stop bit
*/
uart1_cfg.baud_divisor = QM_UART_CFG_BAUD_DL_PACK(0, 208, 5);
uart1_cfg.line_control = QM_UART_LC_8N1;
uart1_cfg.hw_fc = 1;
qm_uart_set_config(QM_UART_0, &uart0_cfg);
qm_uart_set_config(QM_UART_1, &uart1_cfg);
/* enable clock for UART_A */
clk_periph_enable(CLK_PERIPH_CLK | CLK_PERIPH_UARTA_REGISTER);
/* enable clock for UART_B */
clk_periph_enable(CLK_PERIPH_CLK | CLK_PERIPH_UARTB_REGISTER);
/* this message will only be printed on the default UART */
QM_PRINTF("Welcome to Intel Quark D2000 UART configuration demo.\r\n");
qm_uart_write_buffer(QM_UART_0, uart0_message, sizeof(uart0_message));
qm_uart_write_buffer(QM_UART_1, uart1_message, sizeof(uart1_message));
while(1) {
/* checks if received data is available on UART_A */
if (qm_uart_get_status(QM_UART_0) && QM_UART_RX_BUSY) {
/* reads byte from UART_A */
if (qm_uart_read(QM_UART_0, &data) == QM_UART_OK) {
/* and sends to to UART_B */
qm_uart_write(QM_UART_1, data);
/* adds line feed in case of carriage return */
if (data == '\r') {
qm_uart_write(QM_UART_1, '\n');
}
}
}
/* checks if received data is available on UART_B */
if (qm_uart_get_status(QM_UART_1) && QM_UART_RX_BUSY) {
/* reads byte from UART_B */
if (qm_uart_read(QM_UART_1, &data) == QM_UART_OK) {
/* and sends to to UART_A */
qm_uart_write(QM_UART_0, data);
/* adds line feed in case of carriage return */
if (data == '\r') {
qm_uart_write(QM_UART_0, '\n');
}
}
}
}
return 0;
}
UART Programming Example: Interrupt-driven I/O
Description
The code below shows how to use interrupt-driven I/O. It configures UART_A for 115200 bps, and prints a sign-in message. Next, it configures UART for loopback mode, sets up and starts the interrupt-driven transmit operation, and then sets up and starts interrupt driver receive operation. When both operations are complete, it disables the loopback mode and prints the received data.
Follow the next steps to run this code on the Intel® Quark™ Microcontroller Developer Kit D2000 board:
- Open Intel® Systems Studio for Microcontrollers.
- Create a new QMSI BSP project using hello_world as the template.
- Replace main.c with the code below.
- Compile and flash the code to the microcontroller.
- Connect an FTDI USB TTL Serial Cable TTL-232R-3V3 cable to the Arduino breakout pins as follows:
- FTDI cable pin 1 (GND) to GND pin on the Arduino breakout header
- FTDI cable pin 3 (TX) to pin number 0 (RX) on the Arduino breakout header
- FTDI cable pin 4 (RX) to pin number 1 (TX) on the Arduino breakout header
- Start terminal emulator software (for example PuTTY, screen, or minicom) on the FTDI cable serial port. Configure the port connected to the FTDI cable to 115200 bps.
- Reset the board. Observe the messages on the terminal.
Code
/* UART interrupt driven I/O example */
#include <qm_pinmux.h>
#include <qm_uart.h>
#include <qm_interrupt.h>
#include <qm_scss.h>
#include <qm_power.h>
#define TX_XFER_ID 1
#define RX_XFER_ID 2
static void uart0_tx_callback(uint32_t id, uint32_t len);
static void uart0_rx_callback(uint32_t id, uint32_t len);
static void uart0_error_callback(uint32_t id, qm_uart_status_t status);
void uart_set_loopback_mode(const qm_uart_t uart, bool enable);
static uint8_t tx_buffer[] =
"This is the test data being send and received by the UART.";
static uint8_t rx_buffer[64];
static bool tx_xfer_complete = false;
static bool rx_xfer_complete = false;
static bool rx_xfer_error = 0;
int main(void)
{
qm_uart_config_t uart0_cfg;
qm_uart_transfer_t tx_xfer, rx_xfer;
/* Configures UART_A for 115200 bps */
uart0_cfg.baud_divisor = QM_UART_CFG_BAUD_DL_PACK(0, 17, 6);
uart0_cfg.line_control = QM_UART_LC_8N1;
uart0_cfg.hw_fc = false;
/* Multiplexes the UARTA RXD and RXD pins and enables input for RXD */
qm_pmux_select(QM_PIN_ID_12, QM_PMUX_FN_2);
qm_pmux_select(QM_PIN_ID_13, QM_PMUX_FN_2);
qm_pmux_input_en(QM_PIN_ID_13, true);
clk_periph_enable(CLK_PERIPH_CLK | CLK_PERIPH_UARTA_REGISTER);
qm_uart_set_config(QM_UART_0, &uart0_cfg);
QM_PRINTF("Welcome to Intel Quark D2000 UART interrupt driven I/O demo.\r\n");
/* Enables UART loopback mode */
uart_set_loopback_mode(QM_UART_0, true);
/* Sets up interrupt service routine for UART_A */
qm_irq_request(QM_IRQ_UART_0, qm_uart_0_isr);
/* Sets up interrupt driven transmit request */
tx_xfer.data = tx_buffer;
tx_xfer.data_len = sizeof(tx_buffer);
tx_xfer.fin_callback = uart0_tx_callback;
tx_xfer.err_callback = uart0_error_callback;
tx_xfer.id = TX_XFER_ID;
/* Initiates interrupt driven transmit request */
qm_uart_irq_write(QM_UART_0, &tx_xfer);
/* Sets up interrupt driven receive request */
rx_xfer.data = rx_buffer;
rx_xfer.data_len = sizeof(tx_buffer);
rx_xfer.fin_callback = uart0_rx_callback;
rx_xfer.err_callback = uart0_error_callback;
rx_xfer.id = RX_XFER_ID;
/* Initiates interrupt driven receive request */
qm_uart_irq_read(QM_UART_0, &rx_xfer);
/* Waits for transfers to complete */
while (!tx_xfer_complete || !rx_xfer_complete) {
cpu_halt();
}
/* Disables UART loopback mode */
uart_set_loopback_mode(QM_UART_0, false);
if (0 == rx_xfer_error) {
QM_PRINTF("UART interrupt driven transmit and receive complete.\r\n");
QM_PRINTF("Buffer: %s\r\n", rx_buffer);
} else {
QM_PRINTF("UART interrupt driven I/O error.\r\n");
QM_PRINTF("UART LSR error bits: 0x%d\r\n", rx_xfer_error);
}
while(1) {
cpu_halt();
}
return 0;
}
void uart0_tx_callback(uint32_t id, uint32_t len)
{
switch (id) {
case TX_XFER_ID:
tx_xfer_complete = true;
break;
default:
break;
}
}
void uart0_rx_callback(uint32_t id, uint32_t len)
{
switch (id) {
case RX_XFER_ID:
rx_xfer_complete = true;
break;
default:
break;
}
}
void uart0_error_callback(uint32_t id, qm_uart_status_t status)
{
rx_xfer_error = status;
}
void uart_set_loopback_mode(const qm_uart_t uart, bool enable)
{
if (enable) {
QM_UART[uart].mcr |= BIT(4);
} else {
QM_UART[uart].mcr &= ~BIT(4);
}
}