CLion, a cross-platform IDE by JetBrains for C and C++, is known for its intelligent coding assistance, project management, and powerful built-in tools like debuggers, static analysis, and unit testing frameworks. It also supports remote collaboration and embedded development.
For embedded engineers, CLion offers a significantly improved code-writing experience compared to Keil MDK. However, Keil MDK provides more robust debugging capabilities when using an external debugger with a target microcontroller.
The following table compares the key differences between the two IDEs:
Feature | CLion | Keil MDK |
---|---|---|
Platform Support | Cross-platform (Windows, macOS, Linux) | Primarily Windows |
User Interface | Modern and user-friendly with features like code completion and smart suggestions. | Traditional interface with weaker code completion, focusing mainly on debugging and simulation. |
Debugging | Supports GDB debugging, but it is less powerful than Keil MDK's, lacking real-time variable and register value updates. | Offers extensive on-chip debugging, including real-time debugging, hardware breakpoints, and live variable updates. With an SWO pin, variables can be plotted in a software logic analyzer. |
CMake Support | Comprehensive built-in support and can link to custom installations. | Does not support CMake. |
Code Analysis & Refactoring | Powerful code analysis and refactoring tools. | Limited code analysis and refactoring features. |
Version Control | Tightly integrated with Git and other version control systems. | Basic version control support; users can integrate Git manually. |
Open Source Toolchain | Supports various open-source toolchains like GCC and arm-none-eabi-gcc. | Primarily relies on Keil's proprietary toolchain, including compilers like AC5 and AC6. |
Plugins & Extensibility | Supports a large ecosystem of plugins for customizable functionality. | Limited plugin system and extensibility. |
MCU Support | Limited direct support for specific MCUs; projects for many Chinese ARM MCUs cannot be generated directly. | Optimized for Arm Cortex-M series MCUs, with support packages provided by most global and Chinese manufacturers. |
Learning Curve | Requires learning CMake and CLion, which can be challenging for beginners setting up an environment. | Relatively easy to get started with its all-in-one configuration tools and development environment. |
1. Required Software and Hardware
1.1 Software Environment
- Windows 10
- CLion (latest version)
- GNU Arm Embedded Toolchain
- MinGW-w64 GCC for Windows 64-bit
- CMake (included with CLion)
1.2 Hardware Environment
- CW32F030 development board
- WCH-Link DAP debugger
2. Software Installation and Configuration
2.1 GNU Arm Embedded Toolchain
arm-none-eabi-gcc is a cross-compiler toolchain designed for ARM architecture microcontrollers. "Cross-compilation" means the code is compiled on one platform (like a Windows PC) to produce binaries that run on another platform (an ARM Cortex-M MCU). The "none-eabi" part indicates that the generated code has no operating system dependencies and is suitable for bare-metal execution. This toolchain is the GCC equivalent of the AC5 and AC6 compilers in Keil MDK.
Download the ZIP archive from the Arm developer website:
GNU Arm Embedded Toolchain Downloads – Arm Developer
Extract the downloaded archive into a folder (e.g., "gcc-arm-none-eabi") and add the path to its "bin" directory to your system's Path environment variable. After adding it, open a command prompt and run a test command to verify the installation.
2.2 Installing MinGW-w64 GCC
MinGW is a toolset for compiling native C/C++ applications on Windows.
Download it from the SourceForge website:
https://sourceforge.net/projects/mingw-w64/files/
Extract the downloaded files into a folder. Navigate to the "bin" directory, make a copy of "mingw32-make.exe", and rename the copy to "make.exe". This allows you to use the `make` command directly from the command line. Add the path to this "bin" directory to your system's Path environment variable and run a test command to verify the installation.
2.3 Configuring the CLion Toolchain
Open CLion and navigate to Settings > Build, Execution, Deployment > Toolchains. Click the + button to add a new toolchain configuration. Name it something distinct, like MinGW-ARM, to avoid conflicts with standard C project configurations. When developing for ARM MCUs, you will select this toolchain.
Next, switch to the CMake settings tab. Ensure the active toolchain is set to the newly created MinGW-ARM profile, then save the settings.
3. Creating the Project Template
3.1 Project File Structure
- application: Application layer code, including
app_main.c
andinterrupt_cw32f030.c
. - bspdriver: Board Support Package (BSP) drivers for peripherals like clock, delay, timers, and board-specific initializations.
- moduledriver: Drivers for common modules like LEDs and buttons.
- middlewares: Middleware libraries such as FreeRTOS, logging utilities, and software timers.
- libraries: Low-level libraries provided by the MCU vendor, including CMSIS and peripheral drivers.
- tools: Utility files like the startup file, linker script, and DAP configurations. Note: The startup file (
startup_cw32f030_gcc.s
) and linker script (CW32F030x_FLASH.ld
) are not officially provided for GCC and have been adapted from other F030 series MCUs. Official GCC support would be a welcome addition.
3.2 Writing the CMakeLists.txt File
cmake_minimum_required(VERSION 3.20)
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_VERSION 1)
# Specify the cross-compiler toolchain
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
set(CMAKE_ASM_COMPILER arm-none-eabi-gcc)
set(CMAKE_AR arm-none-eabi-ar)
set(CMAKE_OBJCOPY arm-none-eabi-objcopy)
set(CMAKE_OBJDUMP arm-none-eabi-objdump)
set(SIZE arm-none-eabi-size)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
# Project settings
project(cw32f030 C CXX ASM)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_STANDARD 11)
# NOTE: Change the cortex core type according to your MCU
set(MCPU cortex-m0plus) # TODO: Modify based on actual MCU core, e.g., cortex-m4
# Floating point options
# Uncomment to enable hardware floating point
#add_compile_definitions(ARM_MATH_CM4;ARM_MATH_MATRIX_CHECK;ARM_MATH_ROUNDING)
#add_compile_options(-mfloat-abi=hard -mfpu=fpv4-sp-d16)
#add_link_options(-mfloat-abi=hard -mfpu=fpv4-sp-d16)
# Uncomment to enable software floating point
add_compile_options(-mfloat-abi=soft)
# Common compiler options
add_compile_options(-mcpu=${MCPU} -mthumb -mthumb-interwork -specs=nosys.specs)
add_compile_options(-ffunction-sections -fdata-sections -fno-common -fmessage-length=0)
# Uncomment to suppress C++17 absolute address warnings
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-register")
# Set optimization level based on build type
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
message(STATUS "Maximum optimization for speed")
add_compile_options(-Ofast)
elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
message(STATUS "Maximum optimization for speed, debug info included")
add_compile_options(-Ofast -g)
elseif ("${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel")
message(STATUS "Maximum optimization for size")
add_compile_options(-Os)
else ()
message(STATUS "Minimal optimization, debug info included")
add_compile_options(-Og -g)
endif ()
# Include directories
include_directories(
application/
bspdriver/adc/
bspdriver/delay/
bspdriver/rcc/
bspdriver/timer/
bspdriver/uart/
moduledriver/button/
moduledriver/beep/
moduledriver/dht11/
moduledriver/lcd/
moduledriver/led/
moduledriver/oled/
libraries/cmsis/device/
libraries/cmsis/include/
libraries/drivers/inc/
middlewares/
)
# Preprocessor definitions
add_definitions(
# -D USE_STDPERIPH_DRIVER
)
# Collect source files
file(GLOB_RECURSE SOURCES
"tools/startup_cw32f030_gcc.s"
"application/*.c"
"bspdriver/adc/*.c"
"bspdriver/delay/*.c"
"bspdriver/rcc/*.c"
"bspdriver/timer/*.c"
"bspdriver/uart/*.c"
"moduledriver/button/*.c"
"moduledriver/beep/*.c"
"moduledriver/dht11/*.c"
"moduledriver/lcd/*.c"
"moduledriver/led/*.c"
"moduledriver/oled/*.c"
"libraries/drivers/src/*.c"
)
# Linker script configuration
set(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/tools/CW32F030x_FLASH.ld)
add_link_options(-Wl,-gc-sections,--print-memory-usage,-Map=${PROJECT_BINARY_DIR}/${PROJECT_NAME}.map)
add_link_options(-mcpu=${MCPU} -mthumb -mthumb-interwork --specs=nosys.specs)
add_link_options(-T ${LINKER_SCRIPT})
# Generate ELF executable
add_executable(${PROJECT_NAME}.elf ${SOURCES}${LINKER_SCRIPT})
# Generate HEX and BIN files
set(HEX_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.hex)
set(BIN_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.bin)
# Post-build commands to generate hex and bin files
add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -Oihex $ ${HEX_FILE}
COMMAND ${CMAKE_OBJCOPY} -Obinary $ ${BIN_FILE}
COMMENT "Building ${HEX_FILE}
Building ${BIN_FILE}")
3.3 Open and Compile the Project
Open the project folder in CLion. After CMake finishes loading the project, you can compile the code.
3.4 Key Considerations for printf
The compiler used in Keil MDK is ARMCC (AC5 or AC6), while this setup uses `arm-none-eabi-gcc`. The method for redirecting `printf` output differs between them because the `stdio.h` implementations are different. In Keil, you typically retarget the `fputc` function. In a GCC environment, you need to implement `_write`.
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @brief Retargets the C library printf function to the USART.
*/
PUTCHAR_PROTOTYPE
{
USART_SendData_8bit(DEBUG_USARTx, (uint8_t)ch);
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
return ch;
}
/**
* @brief Redirect C Library function printf to serial port.
* After Redirection, you can use the printf function.
* @param file: Unused.
* @param *ptr: Buffer pointer for data to be sent.
* @param len: Length of data to be sent.
* @retval The number of characters sent.
*/
int _write(int file, char* ptr, int len)
{
int i;
for (i = 0; i < len; i++)
{
__io_putchar(*ptr++);
}
return len;
}
4. Flashing and Debugging
Flashing and debugging in CLion is less straightforward than in IAR or Keil MDK, especially for Chinese MCUs that lack mature ecosystem support. While CLion supports debugging via OpenOCD, OpenOCD does not currently support the CW32 series. Community contributions to add this support would be beneficial.
For this guide, we use the WCH-Link provided by Wuhan CoreSource Semiconductor. Since the project is compiled with the GCC toolchain, you can use the official hex/bin flashing utility, OpenOCD, or PyOCD. As OpenOCD is not an option, PyOCD can be used to flash the CW32. Below are several recommended methods for flashing firmware to the chip.
4.1 Method 1: Flashing via pyOCD Command Line
Install pyOCD and use its command-line interface to flash the firmware.
4.2 Method 2: Using the MCUProg Tool
MCUProg is an open-source visual tool for DAP-Link and PyOCD that simplifies the flashing process.
To use it:
- Select the device pack file (usually provided by the manufacturer).
- Select the target chip.
- Select the firmware file.
- Connect the debugger.
- Start the flashing process.
4.3 Method 3: Using the DAPLinkUtility Tool
DAPLinkUtility is a universal GUI for DAPLink that supports online reading and programming for over 30 MCU manufacturers.
Simply connect your DAP-Link debugger, select the corresponding chip, and flash the firmware.