Integrating RT-Thread Nano with STM32F407 Using LL Drivers

CubeMX Configuration

Configure the STM32F407 project in STM32CubeMX with required peripherals (e.g., USART1 for console) and generate code using the Low-Layer (LL) drivers instead of HAL.

Code Modifications

  1. Include RT-Thread Header In source files that use RT-Thread APIs, add:

    #include <rtthread.h>
    
  2. Declare System Clock Function In main.h, declare the clock configuration funciton if not already present:

    extern void SystemClock_Config(void);
    
  3. RT-Thread Configuration (rtconfig.h) Customize rtconfig.h to enable essential features:

    #ifndef __RTTHREAD_CFG_H__
    #define __RTTHREAD_CFG_H__
    
    #define RT_THREAD_PRIORITY_MAX      32
    #define RT_TICK_PER_SECOND          1000
    #define RT_ALIGN_SIZE               4
    #define RT_NAME_MAX                 8
    
    #define RT_USING_COMPONENTS_INIT
    #define RT_USING_USER_MAIN
    #define RT_MAIN_THREAD_STACK_SIZE   1024
    
    #define RT_DEBUG
    #define RT_DEBUG_INIT               1
    
    #define RT_USING_HOOK
    #define RT_USING_IDLE_HOOK
    
    #define RT_TIMER_THREAD_PRIO        4
    #define RT_TIMER_THREAD_STACK_SIZE  512
    
    #define RT_USING_SEMAPHORE
    #define RT_USING_MUTEX
    #define RT_USING_EVENT
    #define RT_USING_MAILBOX
    #define RT_USING_MESSAGEQUEUE
    
    #define RT_USING_HEAP
    #define RT_USING_SMALL_MEM
    #define RT_USING_TINY_SIZE
    
    #define RT_USING_CONSOLE
    #define RT_CONSOLEBUF_SIZE          128
    
    #include "finsh_config.h"
    
    #endif /* __RTTHREAD_CFG_H__ */
    
  4. Board Initialization (board.c) Implement hardware-specific initialization:

    #include <rthw.h>
    #include <rtthread.h>
    #include "main.h"
    #include "usart.h"
    
    #if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
    #define STM32_SRAM1_END    (0x20020000)
    
    #if defined(__CC_ARM) || defined(__CLANG_ARM)
    extern int Image$$RW_IRAM1$$ZI$$Limit;
    #define HEAP_BEGIN         ((void *)&Image$$RW_IRAM1$$ZI$$Limit)
    #endif
    #endif
    
    void SysTick_Handler(void)
    {
        rt_interrupt_enter();
        rt_tick_increase();
        rt_interrupt_leave();
    }
    
    void rt_hw_board_init(void)
    {
        LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
        LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
        NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
    
        SystemClock_Config();
        SystemCoreClockUpdate();
    
        SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
    
    #ifdef RT_USING_COMPONENTS_INIT
        rt_components_board_init();
    #endif
    
    #if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
        rt_system_heap_init((void *)HEAP_BEGIN, (void *)STM32_SRAM1_END);
    #endif
    }
    
    #ifdef RT_USING_CONSOLE
    #define CONSOLE_UART USART1
    extern rt_bool_t usart1_ready;
    extern struct rt_mutex uart_tx_mutex;
    extern rt_bool_t mutex_initialized;
    
    void rt_hw_console_output(const char *str)
    {
        rt_size_t len = rt_strlen(str);
    
        if (mutex_initialized)
            rt_mutex_take(&uart_tx_mutex, RT_WAITING_FOREVER);
    
        if (usart1_ready)
        {
            for (rt_size_t i = 0; i < len; i++)
            {
                if (str[i] == '\n')
                {
                    while (!LL_USART_IsActiveFlag_TXE(CONSOLE_UART));
                    LL_USART_TransmitData8(CONSOLE_UART, '\r');
                }
                while (!LL_USART_IsActiveFlag_TXE(CONSOLE_UART));
                LL_USART_TransmitData8(CONSOLE_UART, str[i]);
            }
        }
    
        if (mutex_initialized)
            rt_mutex_release(&uart_tx_mutex);
    }
    #endif
    
    #ifdef RT_USING_FINSH
    char rt_hw_console_getchar(void)
    {
        if (LL_USART_IsActiveFlag_RXNE(CONSOLE_UART))
            return (char)LL_USART_ReceiveData8(CONSOLE_UART);
        else
            rt_thread_mdelay(10);
        return -1;
    }
    #endif
    
  5. USART Initialization Flag In usart.c, set a global flag after USART1 is fully initialized:

    rt_bool_t usart1_ready = RT_FALSE;
    // ... inside MX_USART1_UART_Init()
    usart1_ready = RT_TRUE;
    

    Similarly, initiailze and manage a mutex for thread-safe UART output if needed.

With these changes, RT-Thread Nano runs on STM32F407 using LL drivers, and FinSH shell becomes available over USART1.

Tags: RT-Thread STM32F407 LL Drivers Embedded Systems Real-Time Operating System

Posted on Sat, 12 Sep 2026 16:40:14 +0000 by gilijk