Skip to main content

How to Run a Hello World on the Cortex-M

Introduction

The objective of this article is to explain how to run a "Hello World" application the Cortex-M core of Toradex System on Modules (SoMs) alongside an embedded Linux distribution.

info

If you are looking for information for Colibri iMX7 521MB/256MB (NAND), refer to the steps described at FreeRTOS on the Cortex-M4 of a Colibri iMX7 and connect the hardware in the same way described at Colibri iMX7 1GB eMMC tab on Hardware Requirements.

This article complies with the typographic conventions.

Typographic Conventions

Throughout the Toradex documentation, the following typographic conventions are used:

$ (dollar sign) Command in the host computer (e.g. your PC)

$ Command in your PC

$$ (double dollar sign) Command in a container in the host computer (e.g. your PC)

$$ Command inside a container in your PC

# (hashtag) Command in the target device/board (e.g. Linux terminal)

# Command in the target board, e.g. Colibri iMX6

## (double hashtag) Command inside a container in the target device (Torizon)

## Command inside a container in Torizon

> (greater-than sign) Command in the bootloader (e.g. U-Boot console)

> Command in the Bootloader

No symbol: Command output

$ Command waiting for output
Output

Prerequisites

Hardware Requirements

See the specific hardware requirements for each Toradex SoM:

  • Apalis Evaluation Board.
  • USB to RS232 converter or USB to TTL converter.
  • USB to RS232 for debugging (access Linux terminal and U-Boot).
caution

Remove the jumpers of the pins that you’re currently using!

Software Requirements

See the specific software requirements for each Toradex SoM:

info

If you are using Colibri iMX7, you can get the SDK and toolchain from Toradex Fork: GitHub - toradex/FreeRTOS-Colibri-iMX7: FreeRTOS for Colibri iMX7.

Hardware Connections

See the specific hardware connections for each Toradex SoM:

Apalis iMX8

Core 0

First, set the jumper wires on the Evaluation Board:

X7 ConnectorX5 Connector
MXM3_110 (X7 Pin 40)UART1_DSR (X5 Pin 40)
MXM3_120 (X7 pin 35)UART1_DTR (X5 Pin 35)

Then, connect your USB to RS232 connector to the Apalis Evaluation Board X28B Lower Connector:

RS232 Converter Pin X28B Lower Connector Pin
2 (RX)4 (TX)
3 (TX)6 (RX)
5 (GND)5 (GND)

Core 1

Connect the USB/TTL to your Evaluation Board:

USB/TTL Converter Pin X2 Connector
RXMXM3_8 (Pin 12)
TXMXM3_6 (Pin 13)
GNDAny GND from the board
caution

Because none of the UART pins used by Cortex M41 pass through an RS232 converter, it is only possible with TTL.

Colibri iMX8X

USB/TTL Converter Pin X8 Row A
RXSODIMM 144 (A40)
TXSODIMM 146 (A41)
GNDAny GND from the board

Colibri iMX7 1GB (eMMC)

RS232 Converter Pin X25A Upper Connector
2 (RX)3 (TX)
3 (TX)2 (RX)
5 (GND)5 (GND)

Verdin iMX8M Mini and iMX8M Plus

Simply connect the USB debug (X18 on Dahlia board and X66 on Development Board) to your computer. At your terminal emulator, you’ll see four different serial ports: ttyUSB0, ttyUSB1, ttyUSB2, and ttyUSB3. Access ttyUSB2 for cortex-M4 UART and ttyUSB3,to U-Boot and/or Linux Kernel terminal.

info

Open the devices using picocom, minicom or any terminal emulator of your choice and configure the baud rate as 115200.

Run the Firmware

After compiling the project and sending it to your board, we need to run it using U-Boot. Please, read these guides to check how to do it:

After that, you should see “Hello World!“ from Cortex-M4 on your screen.

However, as soon as we boot, we will see the UART from Cortex-M stop working. Change the code a bit, for example, you can add the “Hello World!“ inside an infinite loop. Go to hello_world.c inside demo_apps folder and proceed with the following modifications into main:

${SDK}/boards/<processor>/demo_apps/hello_world/hello_world.c
int main(void)
{
...
PRINTF("hello world.\r\n");

uint8_t count = 0;

while (1)
{
PRINTF("hello world. [%d]\r\n", count);
if (count == 255) count = 0;
else count++;
}
}
info

For the code above, replace ${SDK} for the path to your SDK folder, and <processor> for the corresponding board's processor folder (for example mekmimx8qx for iMX8qx).

This way, as soon as you boot the board, you will note that the hello_world has stopped. That’s because Linux Kernel will request ownership of the pins that Cortex-M is using. Therefore, to avoid this behavior, apply an overlay to disable those pins. See the specific overlays for each Toradex SoM:

Apalis iMX8

Overlay for Core 0

/dts-v1/;
/plugin/;

/* Disable UART1 Control Pins to use M4 Core 0 and 1 UART */

/ {
compatible = "toradex,apalis-imx8";
};

&iomuxc {
pinctrl-0 = <&pinctrl_cam1_gpios>, <&pinctrl_dap1_gpios>,
<&pinctrl_esai0_gpios>, <&pinctrl_fec2_gpios>,
<&pinctrl_gpio3>, <&pinctrl_gpio4>, <&pinctrl_gpio_keys>,
<&pinctrl_gpio_usbh_oc_n>,
<&pinctrl_lvds0_i2c0_gpio>, <&pinctrl_lvds1_i2c0_gpios>,
<&pinctrl_mipi_dsi_0_1_en>, <&pinctrl_mipi_dsi1_gpios>,
<&pinctrl_mlb_gpios>, <&pinctrl_qspi1a_gpios>,
<&pinctrl_sata1_act>, <&pinctrl_sim0_gpios>,
<&pinctrl_usdhc1_gpios>;
};

Overlay for Core 1:

/dts-v1/;
/plugin/;

/* Disable PWM0 and PMW1 to use M4 Core 1 UART */

/ {
compatible = "toradex,apalis-imx8-v1.1-eval",
"toradex,apalis-imx8-eval",
"toradex,apalis-imx8",
"fsl,imx8qm";
};

&pwm0 {
status = "disabled";
};

&pwm1 {
status = "disabled";
};

&lpuart2 {
status = "disabled";
};

The Colibri iMX8X overlay responsible disable cm40 UART is already available at the Toradex overlays repository. You can take a look at colibri-imx8x_disable-cm40-uart_overlay.dts.

Refer to Device Tree Overlays (Linux) to learn how to apply a device tree overlay. If you are using Torizon OS, follow the process described at Device Tree Overlays on Torizon.

With the overlay applied, boot the Linux kernel and M4 will run without problems.

Colibri iMX8X

For Colibri iMX8X, there are two available pair of pins from the SOC that have the M40_UART function: ADC_IN2, ADC_IN3 and SCU_GPIO0_00, SCU_GPIO0_01. Unfortunately, only the SCU_GPIO0_00, SCU_GPIO0_01 pair is available to be used.

The hello_world from NXP uses the ADC pins by default, soyou must change them accordingly to the diffs below in other to see the UART on the screen.

${SDK}/boards/<processor>/demo_apps/hello_world/pin_mux.c
diff --git a/boards/mekmimx8qx/demo_apps/hello_world/pin_mux.c b/boards/mekmimx8qx/demo_apps/hello_world/pin_mux.c
index 3e390a3..1d84c50 100644
--- a/boards/mekmimx8qx/demo_apps/hello_world/pin_mux.c
+++ b/boards/mekmimx8qx/demo_apps/hello_world/pin_mux.c
@@ -57,12 +57,12 @@ void BOARD_InitPins(sc_ipc_t ipc) /*!< Function assigne
{
sc_err_t err = SC_ERR_NONE;

- err = sc_pad_set_all(ipc, BOARD_INITPINS_FTDI_M40_UART0_RX_PIN_FUNCTION_ID, 1U, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, 0x0 ,SC_PAD_WAKEUP_OFF);/* IOMUXD_ADC_IN2 register modification value */
+ err = sc_pad_set_all(ipc, BOARD_INITPINS_FTDI_M40_UART0_RX_PIN_FUNCTION_ID, 2U, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, 0x0 ,SC_PAD_WAKEUP_OFF);/* IOMUXD_ADC_IN2 register modification value */
if (SC_ERR_NONE != err)
{
assert(false);
}
- err = sc_pad_set_all(ipc, BOARD_INITPINS_FTDI_M40_UART0_TX_PIN_FUNCTION_ID, 1U, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, 0x0 ,SC_PAD_WAKEUP_OFF);/* IOMUXD_ADC_IN3 register modification value */
+ err = sc_pad_set_all(ipc, BOARD_INITPINS_FTDI_M40_UART0_TX_PIN_FUNCTION_ID, 2U, SC_PAD_CONFIG_NORMAL, SC_PAD_ISO_OFF, 0x0 ,SC_PAD_WAKEUP_OFF);/* IOMUXD_ADC_IN3 register modification value */
if (SC_ERR_NONE != err)
{
assert(false);
${SDK}/boards/<processor>/demo_apps/hello_world/pin_mux.h
diff --git a/boards/mekmimx8qx/demo_apps/hello_world/pin_mux.h b/boards/mekmimx8qx/demo_apps/hello_world/pin_mux.h
index 8bb2a79..b53e387 100644
--- a/boards/mekmimx8qx/demo_apps/hello_world/pin_mux.h
+++ b/boards/mekmimx8qx/demo_apps/hello_world/pin_mux.h
@@ -17,16 +17,16 @@
/* ADC_IN2 (coord V32), FTDI_M40_UART0_RX */
#define BOARD_INITPINS_FTDI_M40_UART0_RX_PERIPHERAL M40__UART0 /*!< Device name: M40__UART0 */
#define BOARD_INITPINS_FTDI_M40_UART0_RX_SIGNAL uart_rx /*!< M40__UART0 signal: uart_rx */
-#define BOARD_INITPINS_FTDI_M40_UART0_RX_PIN_NAME ADC_IN2 /*!< Pin name */
-#define BOARD_INITPINS_FTDI_M40_UART0_RX_PIN_FUNCTION_ID SC_P_ADC_IN2 /*!< Pin function id */
+#define BOARD_INITPINS_FTDI_M40_UART0_RX_PIN_NAME SCU_GPIO0_00 /*!< Pin name */
+#define BOARD_INITPINS_FTDI_M40_UART0_RX_PIN_FUNCTION_ID SC_P_SCU_GPIO0_00 /*!< Pin function id */
#define BOARD_INITPINS_FTDI_M40_UART0_RX_LABEL "FTDI_M40_UART0_RX" /*!< Label */
#define BOARD_INITPINS_FTDI_M40_UART0_RX_NAME "FTDI_M40_UART0_RX" /*!< Identifier name */

/* ADC_IN3 (coord V30), FTDI_M40_UART0_TX */
#define BOARD_INITPINS_FTDI_M40_UART0_TX_PERIPHERAL M40__UART0 /*!< Device name: M40__UART0 */
#define BOARD_INITPINS_FTDI_M40_UART0_TX_SIGNAL uart_tx /*!< M40__UART0 signal: uart_tx */
-#define BOARD_INITPINS_FTDI_M40_UART0_TX_PIN_NAME ADC_IN3 /*!< Pin name */
-#define BOARD_INITPINS_FTDI_M40_UART0_TX_PIN_FUNCTION_ID SC_P_ADC_IN3 /*!< Pin function id */
+#define BOARD_INITPINS_FTDI_M40_UART0_TX_PIN_NAME SCU_GPIO0_01 /*!< Pin name */
+#define BOARD_INITPINS_FTDI_M40_UART0_TX_PIN_FUNCTION_ID SC_P_SCU_GPIO0_01 /*!< Pin function id */
#define BOARD_INITPINS_FTDI_M40_UART0_TX_LABEL "FTDI_M40_UART0_TX" /*!< Label */
#define BOARD_INITPINS_FTDI_M40_UART0_TX_NAME "FTDI_M40_UART0_TX" /*!< Identifier name */
info

For the diffs above, replace ${SDK} for the path to your SDK folder, and <processor> for the corresponding board's processor folder (for example mekmimx8qx for iMX8qx).

Then, modify the pin that is being used and set its function to ALT2, which is the function for M40.UART, with a device tree overlay.

/dts-v1/;
/plugin/;

/{
compatible = "toradex,colibri-imx8";
};

&cm40_lpuart {
status = "disabled";
};

&cm40_uart_lpcg {
status = "disabled";
};

If you are using one of the BSP reference images, refer to Build U-Boot and Linux Kernel from Source Code to learn how to build and Device Tree Overlays (Linux) to learn how to apply a device tree overlay. If you are using Torizon OS follow the process described at Device Tree Overlays on Torizon.

Applied the overlay, boot the Linux kernel and M4 will run without problems.

Colibri iMX7 1GB (eMMC)

Toradex already provides an overlay for that in Colibri iMX7. Follow the process described at Device Tree Overlays on Torizon (if you are using Torizon OS) or Device Tree Overlays (Linux) (if you are using one of the BSP reference images), and enable colibri-imx7_disable-uart-b_overlay.dtbo.

Verdin iMX8M Mini

info

No overlays are needed to run the hello_world demo application on Verdin iMX8M Mini.

Verdin iMX8M Plus

/dts-v1/;
/plugin/;

/ {
compatible = "toradex,verdin-imx8mp-wifi-dahlia",
"toradex,verdin-imx8mp-wifi",
"toradex,verdin-imx8mp",
"fsl,imx8mp";
};

&gpio5 {
gpio-line-names = "SODIMM_42",
"SODIMM_46",
"SODIMM_187",
"SODIMM_20",
"SODIMM_22",
"SODIMM_15",
"SODIMM_196",
"SODIMM_200",
"SODIMM_198",
"SODIMM_202",
"SODIMM_164",
"SODIMM_152",
"SODIMM_116",
"SODIMM_128",
"",
"",
"SODIMM_55",
"SODIMM_53",
"SODIMM_95",
"SODIMM_93",
"SODIMM_14",
"SODIMM_12",
"SODIMM_129",
"SODIMM_131",
"SODIMM_137",
"SODIMM_139",
"SODIMM_147",
"SODIMM_149",
"SODIMM_151",
"SODIMM_153";
};

/* Remove pinctrl_gpio_hog4 and add pinctrl_gpio_hog1 */
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio1>, <&pinctrl_gpio2>,
<&pinctrl_gpio3>, <&pinctrl_gpio4>,
<&pinctrl_gpio7>, <&pinctrl_gpio8>,
<&pinctrl_gpio_hog1>, <&pinctrl_gpio_hog2>, <&pinctrl_gpio_hog3>,
<&pinctrl_hdmi_hog>;
};

/* Verdin UART_4 */
/* Used by the M7 and then should not be enabled here. */
&uart4 {
status = "disabled";
};

If you are using one of the BSP reference images, refer to Build U-Boot and Linux Kernel from Source Code to learn how to build and Device Tree Overlays (Linux) to learn how to apply a device tree overlay. If you are using Torizon OS follow the process described at Device Tree Overlays on Torizon.



Send Feedback!