This is a minimal Hello, World! application for Arm Cortex-M devices using the CMSIS Solution project format.
This example demonstrates:
- A portable CMSIS-Core based application.
- Build, load, and debug using Arm Keil Studio for VS Code.
- Simple retargetable output (“Hello, World!”) via a serial I/O message channel.
The project is intentionally simple and designed to scale: changing the board, serial I/O, RTOS , or debug adapter is straightforward using Arm Keil Studio.
The setup in this repository is for the Infineon XMC2Go development board using serial I/O via semihosting to a Telnet port. If you are using a different board refer to Change Target Hardware.
Note
STM32 boards use typically a STM32CubeMX based configuration with different startup code. Refer to an STM32 based example, i.e. Hello_B-U585I-IOT02A.
-
Install Keil Studio for VS Code from the VS Code marketplace.
-
Clone this Git repository into a VS Code workspace.
-
The related tools and software packs are downloaded and installed. Review progress with View - Output - CMSIS Solution.
-
In the CMSIS view, use the Action buttons to build, load and debug the example on the hardware.
-
In the VS Code Panel, click on SERIAL MONITOR. Select the Monitor Mode "TCP" and set Host to
localhostand Port to4444. Observe the output:SEGGER J-Link GDB Server V9.10 - Terminal output channel Hello, World! Hello, World! Hello, World! ...
This application is a generic CMSIS example that will run on any target hardware. If you want to change to another development board/device, edit the following.
In the Hello.csolution.yml file, add the device's/board's CMSIS-Packs:
packs:
- pack: ARM::CMSIS
- pack: Infineon::XMC1000_DFP # Change to the right CMSIS-PackThen, change the target-type:/- type to a new name and enter the correct device:/board: names:
# List different hardware targets that are used to deploy the solution.
target-types:
- type: XMC1100-Q024x0064 # Change to a meaningful name
target-set:
- set:
images:
- project-context: Hello.Debug
debugger:
name: J-Link Server
clock: 4000000
protocol: swd
device: XMC1100-Q024x0064 # Change to actual device
board: XMC 2Go:V1 # Change to actual boardThis example is using the CMSIS-RTOS v2 API with CMSIS-FreeRTOS as the underlying real-time operating system kernel.
To change the kernel to Keil RTX5 exchange in the file Hello.cproject.yml:
components:
- component: CMSIS:RTOS2:FreeRTOS&Cortex-M
- component: RTOS&FreeRTOS:Config&CMSIS RTOS2
- component: RTOS&FreeRTOS:Core&Cortex-M
- component: RTOS&FreeRTOS:Event Groups
- component: RTOS&FreeRTOS:Timers
- component: RTOS&FreeRTOS:Heap&Heap_4
packs:
- pack: ARM::CMSIS-FreeRTOSwith:
components:
- component: CMSIS:RTOS2:Keil RTX5&Source
packs:
- pack: ARM::CMSIS-FreeRTOSWhile semihosting requires not hardware configuration and can be easily used for quick debugging, it is not recommended
for production systems (due to intrusive and slow communication). A viable option is to use a UART (serial port)
instead. The following explains how to use the on-chip UART which is available through the Segger J-Link debug adapter
for printf output.
Caution
The following is specific for the XMC2Go development board. If you have changed the target hardware, consult your board specific documentation to select the right software components and CMSIS-Packs.
To redirect the output to a UART:
- Add the following pack and software components in the
Hello.cproject.ymlfile:
packs:
- pack: ARM::CMSIS-Compiler
components:
- component: CMSIS-Compiler:CORE
- component: CMSIS-Compiler:STDOUT:Custom
- component: CMSIS Driver:USART
- component: Device:RTE_Device
- component: Device:XMClib:GPIO
- component: Device:XMClib:SCU
- component: Device:XMClib:UART- Add the interface code to UART channel
In the Keil Studio CMSIS view, click on the + sign next to Application and select Add From Component Code Template:
Then, select CMSIS-Compiler:STDOUT:Custom which adds a stdout_user.c file to the Application group. Add this
code in the file:
#include "retarget_stdout.h"
#include "Driver_USART.h"
#include <stdint.h>
extern ARM_DRIVER_USART Driver_USART0;
static ARM_DRIVER_USART * const stdout_usart = &Driver_USART0;
static uint8_t stdout_usart_initialized;
#ifndef STDOUT_USART_BAUDRATE
#define STDOUT_USART_BAUDRATE 115200U
#endif
static int stdout_usart_ensure_initialized (void) {
int32_t status;
if (stdout_usart_initialized != 0U) {
return 0;
}
status = stdout_usart->Initialize(NULL);
if (status != ARM_DRIVER_OK) {
return -1;
}
status = stdout_usart->PowerControl(ARM_POWER_FULL);
if (status != ARM_DRIVER_OK) {
return -1;
}
status = stdout_usart->Control(
ARM_USART_MODE_ASYNCHRONOUS |
ARM_USART_DATA_BITS_8 |
ARM_USART_PARITY_NONE |
ARM_USART_STOP_BITS_1 |
ARM_USART_FLOW_CONTROL_NONE,
STDOUT_USART_BAUDRATE
);
if (status != ARM_DRIVER_OK) {
return -1;
}
status = stdout_usart->Control(ARM_USART_CONTROL_TX, 1U);
if (status != ARM_DRIVER_OK) {
return -1;
}
stdout_usart_initialized = 1U;
return 0;
}
/**
Put a character to the stdout
\param[in] ch Character to output
\return The character written, or -1 on write error.
*/
int stdout_putchar (int ch) {
uint8_t c;
if (stdout_usart_ensure_initialized() != 0) {
return -1;
}
/* If caller prints bare '\n', emit CRLF on the wire. */
if ((uint8_t)ch == (uint8_t)'\n') {
(void)stdout_putchar('\r');
}
/* Serialize by waiting for any previous send to complete. */
while (stdout_usart->GetStatus().tx_busy != 0U) {
}
c = (uint8_t)ch;
if (stdout_usart->Send(&c, 1U) != ARM_DRIVER_OK) {
return -1;
}
while (stdout_usart->GetStatus().tx_busy != 0U) {
}
return ch;
}- Configure UART pins
The UART that is connected to the Segger J-Link uses the the ports P2.1 and P2.2 on the device. You need to
configure this in the RTE_Device.h file.
- In the CMSIS view, expand Device_RTE_Device and click on the
RTE_Device.hfile. This file is annotated for Configuration Wizard view which can be enabled by pressing the
button at the top right corner. - In this view, enable
UART0and set theUART0_TX_PintoP2_1and theUART0_RX_PintoP2_2. - Save the file.
- Build and Run Application
If you now build the application and run it on the target, you need to change the SERIAL MONITOR to the following:
- Monitor Mode: Serial
- Port: the Segger J-Link
- Baud rate: 115200
Press Start Monitoring to view the printf massages.
Similar examples which are pre-configured for the UART output are available on GitHub:
