Get access to the new Intel® IoT Developer Kit, a complete hardware and software solution that allows developers to create exciting new solutions with the Intel® Galileo and Intel® Edison boards. Visit the Intel® Developer Zone for IoT.
Contents
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: https://software.intel.com/en-us/intel-system-studio-microcontrollers (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.
<img alt="" src="https://software.intel.com/sites/default/files/managed/77/5b/using-uarts-img-1.jpg" />
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);
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);
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
<img alt="" class="floatLeft" src="https://software.intel.com/sites/default/files/managed/77/5b/using-uarts-img-2.png" />
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:
clk_periph_enable(CLK_PERIPH_CLK | CLK_PERIPH_UARTA_REGISTER);
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:
typedef enum {
QM_UART_LC_5N1 = 0x00,
QM_UART_LC_5N1_5 = 0x04,
QM_UART_LC_5E1 = 0x18,
QM_UART_LC_5E1_5 = 0x1c,
QM_UART_LC_5O1 = 0x08,
QM_UART_LC_5O1_5 = 0x0c,
QM_UART_LC_6N1 = 0x01,
QM_UART_LC_6N2 = 0x05,
QM_UART_LC_6E1 = 0x19,
QM_UART_LC_6E2 = 0x1d,
QM_UART_LC_6O1 = 0x09,
QM_UART_LC_6O2 = 0x0d,
QM_UART_LC_7N1 = 0x02,
QM_UART_LC_7N2 = 0x06,
QM_UART_LC_7E1 = 0x1a,
QM_UART_LC_7E2 = 0x1e,
QM_UART_LC_7O1 = 0x0a,
QM_UART_LC_7O2 = 0x0e,
QM_UART_LC_8N1 = 0x03,
QM_UART_LC_8N2 = 0x07,
QM_UART_LC_8E1 = 0x1b,
QM_UART_LC_8E2 = 0x1f,
QM_UART_LC_8O1 = 0x0b,
QM_UART_LC_8O2 = 0x0f
} 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
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
#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;
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);
qm_pmux_select(QM_PIN_ID_20, QM_PMUX_FN_2);
qm_pmux_select(QM_PIN_ID_21, QM_PMUX_FN_2);
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_21, true);
qm_pmux_input_en(QM_PIN_ID_22, true);
qm_uart_get_config(QM_UART_0, &uart0_cfg);
uart0_cfg.baud_divisor = QM_UART_CFG_BAUD_DL_PACK(0, 17, 6);
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);
clk_periph_enable(CLK_PERIPH_CLK | CLK_PERIPH_UARTA_REGISTER);
clk_periph_enable(CLK_PERIPH_CLK | CLK_PERIPH_UARTB_REGISTER);
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) {
if (qm_uart_get_status(QM_UART_0) && QM_UART_RX_BUSY) {
if (qm_uart_read(QM_UART_0, &data) == QM_UART_OK) {
qm_uart_write(QM_UART_1, data);
if (data == '\r') {
qm_uart_write(QM_UART_1, '\n');
}
}
}
if (qm_uart_get_status(QM_UART_1) && QM_UART_RX_BUSY) {
if (qm_uart_read(QM_UART_1, &data) == QM_UART_OK) {
qm_uart_write(QM_UART_0, data);
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
#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;
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;
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");
uart_set_loopback_mode(QM_UART_0, true);
qm_irq_request(QM_IRQ_UART_0, qm_uart_0_isr);
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;
qm_uart_irq_write(QM_UART_0, &tx_xfer);
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;
qm_uart_irq_read(QM_UART_0, &rx_xfer);
while (!tx_xfer_complete || !rx_xfer_complete) {
cpu_halt();
}
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);
}
}