一、入门
RT-Thread官网 官网文档 Rt-thread学习文档
RT-Thread官方bilibili视频号 GD32官网
教你动手移植RT-Thread到国产MCU 如何移植RT-Thread到GD32单片机上(非studio版)
东方青讲RT-Thread RT-Thread内核入门指南
RT-Thread Studio 教程
rtthread移植
野火rt-thread教程
RT-Thread-学习分析(详细版)huawei
RT-Thread-学习分析(详细版)csdn
二、启动流程
RT-THREAD 自动初始化详解
RT-Thread启动流程详解(硬件初始化篇)
RT-Thread的各种硬件、线程初始化过程
STM32 RT-Thread 系统分析(1)- 启动文件
RT-Thread学习笔记 --(1)RT-Thread开发环境搭建
RT-Thread学习笔记 --(2)RT-Thread启动过程分析
RT-Thread学习笔记 --(3)RT-Thread自动初始化机制分析
RT-Thread学习笔记 --(4)RT-Thread多线程学习总结
RT-Thread学习笔记 --(5)RT-Thread线程间同步学习总结
RT-Thread学习笔记 --(6)RT-Thread线程间通信学习总结
RT-Thread学习笔记 --(7)RT-Thread中断管理学习总结
RT-Thread学习笔记 --(8)RT-Thread时钟管理学习总结
RT-Thread学习笔记 --(9)RT-Thread内存管理学习总结
RT-Thread INIT_BOARD_EXPORT无效或进入不了导出的函数
进入这个界面,下面蓝色部分请添加:–keep .o(.rti_fn.)
RT-Thread的自动初始化依赖宏开关:RT_USING_COMPONENTS_INIT,分为6个等级,可以查看rtdef.h文件
使用INIT_APP_EXPORT(led_init)宏,初始化函数就会被自动初始化,不用在其他地方显式调用 led_init() 。
/* board init routines will be called in board_init() function */
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")/* pre/device/component/env/app init routines will be called in init_thread */
/* components pre-initialization (pure software initilization) */
#define INIT_PREV_EXPORT(fn) INIT_EXPORT(fn, "2")
/* device initialization */
#define INIT_DEVICE_EXPORT(fn) INIT_EXPORT(fn, "3")
/* components initialization (dfs, lwip, ...) */
#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "4")
/* environment initialization (mount disk, ...) */
#define INIT_ENV_EXPORT(fn) INIT_EXPORT(fn, "5")
/* appliation initialization (rtgui application etc ...) */
#define INIT_APP_EXPORT(fn) INIT_EXPORT(fn, "6")
INIT_BOARD_EXPORT(key_gpio_init); /* 先执行 01 */
INIT_BOARD_EXPORT(drv_pm_hw_init); /* 02 */
INIT_BOARD_EXPORT(rt_hw_rtc_init); /* 03 */
- rtthread_startup() 函数是 RT-Thread 规定的统一启动入口。启动调度器之前,系统所创建的线程在执行 rt_thread_startup() 后并不会立马运行,它们会处于就绪状态等待系统调度。MDK 的扩展功能 S u b Sub Sub$ 和 S u p e r Super Super$。
- startup_stm32f103xe.s 开始-> components.c
根据宏跳转到对应函数,如MDK: int S u b Sub Sub$main(void) ->int rtthread_startup(void)
rtthread_startup:关闭中断 ->板级初始化 -> 打印版本信息-> 定时器初始化->调度器初始化 -> 信号初始化->创建初始化线程(main线程) -> 定时器线程初始化 -> 空闲线程初始化->启动调度器。
三、串口相关
RT thread 设备驱动组件之USART设备
Rtthread之串口初始化流程分析
rtthread串口接收不定长数据
四、finsh相关
剖析RT-Thread中console与finsh组件实现(1)
剖析RT-Thread中console与finsh组件实现(2)
剖析RT-Thread中console与finsh组件实现(3)
五、线程
5.1 线程使用
动态方法:rt_thread_create();rt_thread_delete();
静态方法:rt_thread_init();rt_thread_detach();真正开始运行多任务 rt_thread_startup(tid)
线程追寻函数原型:
静态:
rt_err_t rt_thread_init(struct rt_thread *thread,const char *name,void (*entry)(void *parameter),void *parameter,void *stack_start,rt_uint32_t stack_size,rt_uint8_t priority,rt_uint32_t tick);
动态:
rt_thread_t rt_thread_create(const char *name,void (*entry)(void *parameter),void *parameter,rt_uint32_t stack_size,rt_uint8_t priority,rt_uint32_t tick);
系统自带的线程创建实例:
void rt_application_init(void)
{rt_thread_t tid;
#ifdef RT_USING_HEAPtid = rt_thread_create("main", main_thread_entry, RT_NULL,RT_MAIN_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);RT_ASSERT(tid != RT_NULL);
#elsert_err_t result;tid = &main_thread;result = rt_thread_init(tid, "main", main_thread_entry, RT_NULL,main_stack, sizeof(main_stack), RT_MAIN_THREAD_PRIORITY, 20);RT_ASSERT(result == RT_EOK);/* if not define RT_USING_HEAP, using to eliminate the warning */(void)result;
#endifrt_thread_startup(tid);
}
静态创建实例:
/* 变量分配4字节对齐 */
ALIGN(RT_ALIGN_SIZE)
/* 静态线程的 线程堆栈*/
static rt_uint8_t led_stack[512];
/* 静态线程的 线程控制块 */
static struct rt_thread led_thread;static void led_thread_entry(void *parameter)
{while(1){rt_thread_delay(RT_TICK_PER_SECOND / 2);}
]rt_err_t result;result = rt_thread_init(&led_thread, "led", led_thread_entry, RT_NULL,(rt_uint8_t *)&led_stack[0], sizeof(led_stack), 2, 5);
if(result==RT_EOK)
{rt_thread_startup(&main_thread);
}
动态创建实例:
static void dynamic_thread_entry(void* parameter);/* 动态线程的 线程控制块指针 */
rt_thread_t led2_thread;
/* 创建动态线程 : 堆栈大小512 bytes ,优先级 21 ,时间片 2个系统滴答 */
led2_thread = rt_thread_create("led2", dynamic_thread_entry, RT_NULL, 512, 21,2);
if (led2_thread != RT_NULL)rt_thread_startup(led2_thread);
官网给的实例:
static struct rt_thread thread1;
static rt_uint8_t thread1_stack[512];/* 线程 1 入口 */
void thread1_entry(void* parameter)
{int i;while (1){for (i = 0; i < 10; i ++){rt_kprintf("%d\n", i);/* 延时 100ms */rt_thread_mdelay(100);}}
}/* 线程 2 入口 */
void thread2_entry(void* parameter)
{int count = 0;while (1){rt_kprintf("Thread2 count:%d\n", ++count);/* 延时 50ms */rt_thread_mdelay(50);}
}/* 线程例程初始化 */
int thread_sample_init()
{rt_thread_t thread2_ptr;rt_err_t result;/* 初始化线程 1 *//* 线程的入口是 thread1_entry,参数是 RT_NULL* 线程栈是 thread1_stack* 优先级是 200,时间片是 10 个 OS Tick*/result = rt_thread_init(&thread1,"thread1",thread1_entry, RT_NULL,&thread1_stack[0], sizeof(thread1_stack),200, 10);/* 启动线程 */if (result == RT_EOK) rt_thread_startup(&thread1);/* 创建线程 2 *//* 线程的入口是 thread2_entry, 参数是 RT_NULL* 栈空间是 512,优先级是 250,时间片是 25 个 OS Tick*/thread2_ptr = rt_thread_create("thread2",thread2_entry, RT_NULL,512, 250, 25);/* 启动线程 */if (thread2_ptr != RT_NULL) rt_thread_startup(thread2_ptr);return 0;
}
5.2 线程同步
5.2.1 信号量
信号量使用
六、网络通信
SAL:(套接字抽象层)、 Socket Adapter Layer、Socket Abstraction Layer
rt-thread之网络设备与BSD套接字组件
rt-thread示例代码(TCP/UDP)
启用 BSD Socket 的方法是在 env 中输入 menuconfig,找到 SAL 组件,再选择使用 BSD Socket 接口。
各选项顺序:
RT-Thread Components —-> Network —-> Socket abstraction layer —-> Enable BSD socket operated by file system API
七、Kconfig语法
kconfig常用语法,入门必看
Kconfig 语法分析详解
7.1 config条目
config TMPFS_POSIX_ACLbool “Tmpfs POSIX Access Control Lists”depends on TMPFSselect GENERIC_ACLhelpPOSIX Access Control Lists (ACLs) support permissions for users and groups beyond the owner/group/world scheme.
- config是关键字,表示一个配置选项的开始;紧跟着的TMPFS_POSIX_ACL是配置选项的名称,省略了前缀"CONFIG_"
- bool表示变量类型,即"CONFIG_ TMPFS_POSIX_ACL "的类型,有5种类型:bool、tristate、string、hex和int,其中tristate和string是基本的类型
bool变量的值: y和n
tristate变量的值:y、n和m
string变量的值: 字符串- bool之后的字符串“Tmpfs POSIX Access Control Lists”是提示信息(在上面的配置界面中就是通过它来识别CONFIG_TMPFS_POSIX_ACL),在配置界面中上下移动光标选中它时,就可以通过按空格或回车键来设置CONFIG_ TMPFS_POSIX_ACL的值(即选择了哪个值就会把该值赋值给CONFIG_TMPFS_POSIX_ACL)
- depends on:表示依赖于XXX,“depends on TMPFS”表示只有当TMPFS配置选项被选中时,当前配置选项的提示信息才会出现,才能设置当前配置选项
- select:是反向依赖关系的意思,即当前配置选项被选中,则GENERIC_ACL就会被选中。
7.2 menu
用于生成菜单名
menu "Floating point emulation"config FPE_NWFPE..............config FPE_NWFPE_XP.............
endmenu
7.3 choice条目
choice条目将多个类似的配置选项组合在一起,供用户单选或多选,这不同于menu条目
choiceprompt "soc x1000 codec type select"depends on SOC_X1000
config SND_ASOC_INGENIC_PHOENIX_ICDCtristate "Audio support for phoenix with internal codec"select SND_ASOC_DMA_V13select SND_ASOC_JZ_AIC_I2S_V13select SND_ASOC_JZ_ICDC_D3#select SND_ASOC_JZ_PCM_V13#select SND_ASOC_FIIO_PCM5242config SND_ASOC_INGENIC_PHOENIX_SPDIFtristate "Audio support for phoenix with spdif"select SND_ASOC_DMA_V13select SND_ASOC_JZ_AIC_SPDIF_V13select SND_ASOC_JZ_SPDIF_V13#select SND_ASOC_JZ_PCM_V13endchoice
7.4 comment条目
显示注释信息
menu “Floating point emulation”
comment “At least one emulation must be selected”
config FPE_NWFPE
...
config FPE_NWFPE_XP
endmenu
7.5 rt-thread中的Kconfig示例
mainmenu "RT-Thread Configuration"config $BSP_DIRstringoption env="BSP_ROOT"default "."config $RTT_DIRstringoption env="RTT_ROOT"default "../../rt-thread"# you can change the RTT_ROOT default "../.." to your rtthread_root,
# example : default "F:/git_repositories/rt-thread"config $PKGS_DIRstringoption env="PKGS_ROOT"default "packages"config $ENV_DIRstringoption env="ENV_ROOT"default "/"source "$RTT_DIR/Kconfig"
source "$PKGS_DIR/Kconfig"
source "$BSP_DIR/../../drivers/Kconfig"
source "$BSP_DIR/../../libraries/Kconfig"
menu "RT-Thread Kernel"config RT_NAME_MAXint "The maximal size of kernel object name"range 2 32default 8helpEach kernel object, such as thread, timer, semaphore etc, has a name,the RT_NAME_MAX is the maximal size of this object name.config RT_USING_SMPbool "Enable SMP(Symmetric multiprocessing)"default nhelpThis option should be selected by machines which have an SMP-capable CPU.The only effect of this option is to make the SMP-relatedoptions available to the user for configuration.# 太长了,省略。。。endmenu
config LCD_TRULY_TFT240240_2_E tristate "SLCD TRULY TFT240240-2-E with control IC st7789s (240x240)"depends on BACKLIGHT_CLASS_DEVICEdefault n
7.6 使用对照
1、config
//Kconfig中:
config BSP_USING_WDTbool "Enable Watchdog Timer"select RT_USING_WDTdefault nconfig RT_CONSOLE_DEVICE_NAMEstring "the device name for console"default "uart1"config BSP_I2C1_SCL_PINint "I2C1 scl pin number"range 1 176default 116//rtconfig.h中:
#define BSP_USING_WDT
#define RT_USING_WDT#define RT_CONSOLE_DEVICE_NAME "uart1"
#define BSP_I2C1_SCL_PIN 116
2、menu
menu "Hardware Drivers Config"config BSP_USING_COM2bool "Enable COM2 (uart2 pin conflict with Ethernet and PWM)"select BSP_USING_UARTselect BSP_USING_UART2default nconfig BSP_USING_COM3bool "Enable COM3 (uart3 pin conflict with Ethernet)"select BSP_USING_UART3default n
endmenu
3、if/endif
menu "Hardware Drivers Config"menuconfig BSP_USING_CANbool "Enable CAN"default nselect RT_USING_CANif BSP_USING_CANconfig BSP_USING_CAN1bool "Enable CAN1"default nendif
endmenu
4、menuconfig
menu "Hardware Drivers Config"menuconfig BSP_USING_UARTbool "Enable UART"default yselect RT_USING_SERIALif BSP_USING_UARTconfig BSP_USING_UART1bool "Enable UART1"default yconfig BSP_UART1_RX_USING_DMAbool "Enable UART1 RX DMA"depends on BSP_USING_UART1 && RT_SERIAL_USING_DMAdefault nendif
endmenu
5、choice
menu "Hardware Drivers Config"menuconfig BSP_USING_ONCHIP_RTCbool "Enable RTC"select RT_USING_RTCselect RT_USING_LIBCdefault nif BSP_USING_ONCHIP_RTCchoiceprompt "Select clock source"default BSP_RTC_USING_LSEconfig BSP_RTC_USING_LSEbool "RTC USING LSE"config BSP_RTC_USING_LSIbool "RTC USING LSI"endchoiceendif
endmenu
8. env配置
Env官方文档
Env工程配置
8.1 环境配置
在env目录中运行env.exe或env.bat,右击标题栏->settings->Intergration->点击上半部分的register->save settings.
在bsp/stm32f407工程目录下右键->ConEmeu_here,进入当前目录的env控制台
8.2 env配置项目(menuconfig)
拷贝/env/sample/Kconfig文件拷贝至工程目录,修改"RTT_ROOT"路径,才能使用menuconfig。
config BSP_DIRstringoption env="BSP_ROOT"default "."config RTT_DIRstringoption env="RTT_ROOT"default "./rt-thread"# you can change the RTT_ROOT default "rt-thread"
# example : default "F:/git_repositories/rt-thread"
# default: "../../"
工程目录打开env,输入menuconfig(>rtthread3.0),最终配置修改rtconfig.h内容。
项目的配置保存在:.config文件中,而后根据.config内容重新生成rtconfig.h内容。
scons --genconfig #根据 rtconfig.h 逆向生成 .config
8.3 生成mdk5工程(scons)
scons通过读取rtconfig.h文件配置成mdk5、mdk4、iar工程并编译。
进入bsp目录下对应工程目录下,删除原有的project.eww、project.uvproj、project.uvprojx三个文件,在当前目录中打开env,输入:
scons --target=mdk5 #根据rtconfig.h生成project.uvprojx工程文件
scons #采用自带gcc编译
8.4 软件包管理(pkgs)
官网软件包 https://github.com/RT-Thread-packages
工程目录打开env->menuconfig->RT-Thread online packages按空格键选中所需软件包
pkgs --update #更新软件包(需要安装git)
scons --targer=mdk5 #重新生成mdk5工程
(5)env设置
menuconifg -s
#进入Env config可配置自动更新软件包、自动生成工程
9. 工程实践
9.1 GD32450IIH6上移植
RT-Thread-国产MCU移植系列教程汇总
(1)视频版1(没成功)
高手版:如何移植RT-Thread到GD32单片机上(非studio版)
- (1)复制/rtthread/bsp目录下一个相近工程。
- (2)修改gd32_rom.sct中的内存和flash的大小。
- (3)修改template.uvprojx中的工程名和芯片型号等。
- (4)替换/Library下的CMSIS、GD32F4xx_standard_peripheral、GD32F4xx_usb_driver,并修改/Libraries目录下的SConscript文件,将其中的目录进行替换、CPPDEFINES、.c文件、启动文件等。
- (5)修改/drivers下的驱动,并修改board.h中的GD32_SRAM_SIZE改成实际大小(KB)
- (6)修改工程目录下的Kconfig文件,
- (7)menuconfig修改系统配置,并用scons --targer=mdk5生成mdk工程。
- (8)编译修改错误。
(2)文档版(成功)
GD32F405VG 移植RTT
手工向GD32F450移植RT-Thread内核
综合了上面两篇文章,框架用了第1个,调试始终卡在main线程的动态生成那里,考虑是椎的配置有问题,开始以为是大小的配置,后来在受第2篇文章启发,查看board.c中的rt_hw_board_init中对堆的初始化,决定不使用外部SDRAM,将rtconfig.h中的#define BSP_USING_SDRAM注释掉。
(或者是把Kconfig中的注掉,其实用snv生成后,也是转成rhconfig.h中的定义)
//Kconfig中关于使用SDRAM的定义注掉
config BSP_USING_SDRAMbool "Using sdram"default y
//board.c中关于堆初始化的两种方式选择(SDRAM、SRAM)
void rt_hw_board_init()
{/* NVIC Configuration */
#define NVIC_VTOR_MASK 0x3FFFFF80
#ifdef VECT_TAB_RAM/* Set the Vector Table base location at 0x10000000 */SCB->VTOR = (0x10000000 & NVIC_VTOR_MASK);
#else /* VECT_TAB_FLASH *//* Set the Vector Table base location at 0x08000000 */SCB->VTOR = (0x08000000 & NVIC_VTOR_MASK);
#endifSystemClock_Config();#ifdef RT_USING_COMPONENTS_INITrt_components_board_init();
#endif#ifdef RT_USING_CONSOLErt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif#ifdef BSP_USING_SDRAMrt_system_heap_init((void *)EXT_SDRAM_BEGIN, (void *)EXT_SDRAM_END); //用SDRAM没成功
#elsert_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END); //用SRAM成功了
#endif
}
#ifndef __BOARD_H__
#define __BOARD_H__#include <gd32f4xx.h>#define GD32_FLASH_START_ADDRESS ((uint32_t)0x8000000) //自己加的
#define GD32_FLASH_SIZE (2048*1024)#define EXT_SDRAM_BEGIN (0xC0000000U) /* the begining address of external SDRAM */
#define EXT_SDRAM_END (EXT_SDRAM_BEGIN + (32U * 1024 * 1024)) /* the end address of external SDRAM */// <o> Internal SRAM memory size[Kbytes] <8-64>
// <i>Default: 64
#ifdef __ICCARM__
// Use *.icf ram symbal, to avoid hardcode.
extern char __ICFEDIT_region_RAM_end__;
#define GD32_SRAM_END &__ICFEDIT_region_RAM_end__
#else
#define GD32_SRAM_SIZE 64 //128 //原来128,网上说大了有问题,就改成64------
#define GD32_SRAM_END (0x20000000 + GD32_SRAM_SIZE * 1024)
#endif#ifdef __CC_ARM
extern int Image$$RW_IRAM1$$ZI$$Limit;
#define HEAP_BEGIN (&Image$$RW_IRAM1$$ZI$$Limit)
#elif __ICCARM__
#pragma section="HEAP"
#define HEAP_BEGIN (__segment_end("HEAP"))
#else
extern int __bss_end;
#define HEAP_BEGIN (&__bss_end)
#endif#define HEAP_END GD32_SRAM_END#endif
//components.c
int rtthread_startup(void)
{rt_hw_interrupt_disable(); rt_hw_board_init(); /* board level initialization 在这里进行堆初始化*/ rt_show_version(); /* show RT-Thread version */ rt_system_timer_init(); /* timer system initialization */ rt_system_scheduler_init(); /* scheduler system initialization */
#ifdef RT_USING_SIGNALS rt_system_signal_init(); /* signal system initialization */
#endif rt_application_init(); /* create init_thread */ rt_system_timer_thread_init(); /* timer thread initialization */ rt_thread_idle_init(); /* idle thread initialization */ rt_system_scheduler_start(); /* start scheduler */ return 0; /* never reach here */
}
(3)用bsp中的复制移植
参考这篇:教你动手移植RT-Thread到国产MCU
//board.h
#define GD32_SRAM_SIZE 192 //128
board.h中的sram为64M时,开lwip就会在出问题。
board.h中的sram为512M时,堆初始化会出问题,因为不连续。
void rt_system_heap_init(void *begin_addr, void *end_addr)

board.h中的sram为连续的128或192时,才能正常。
9.2 更改kprintf()串口输出
一般 rt-thread 发布的 bsp 库默认的 rt_kprintf 函数的输出设备是串口1,想要更改输出设备为串口1,以 stm32 为例步骤如下:
首先,打开 UART2 设备
其次,在 menuconfig 中 RT-Thread Kernel — Kernel Device Object — Using console for rt_kprintf 修改 the device name for console 的值为 uart2
最后,在文件 <stm32f1xx_hal_msp.c> 中加入串口2相关的时钟、引脚配置信息即可
9.3 添加网络
基于STM32F429实现web服务器功能
RT-Thread进阶笔记之网络框架
RT-thread 项目实战–添加wifi和net双网卡
这个是没有添加drv_enet.c、synopsys_emac.c的报错:
9.3.1 添加网络驱动文件
修改/bsp/GD32450I/drivers/SConscript文件:使Kconfig中的配置与具体的驱动文件对应
# add uart drivers.
if GetDepend('RT_USING_SERIAL'):src += ['drv_usart.c']# add sdram drivers.
if GetDepend('BSP_USING_SDRAM'):src += ['drv_exmc_sdram.c']# add eth drivers.
if GetDepend('BSP_USING_ETH'):src += ['drv_enet.c']# add eth drivers.
if GetDepend('BSP_USING_ETH'):src += ['synopsys_emac.c']
9.3.2 mac结构体
eth_device:
struct eth_device
{/* inherit from rt_device */struct rt_device parent;/* network interface for lwip */struct netif *netif;struct rt_semaphore tx_ack;rt_uint16_t flags;rt_uint8_t link_changed;rt_uint8_t link_status;/* eth device interface */struct pbuf* (*eth_rx)(rt_device_t dev);rt_err_t (*eth_tx)(rt_device_t dev, struct pbuf* p);
};
gd32_emac:
struct gd32_emac
{/* inherit from Ethernet device */struct eth_device parent;rt_uint8_t phy_mode;/* interface address info. */rt_uint8_t dev_addr[MAX_ADDR_LEN]; /* hw address */struct rt_synopsys_eth * ETHERNET_MAC;IRQn_Type ETHER_MAC_IRQ;EMAC_DMADESCTypeDef *DMATxDescToSet;EMAC_DMADESCTypeDef *DMARxDescToGet;#pragma pack(4)EMAC_DMADESCTypeDef DMARxDscrTab[EMAC_RXBUFNB];
#pragma pack(4)EMAC_DMADESCTypeDef DMATxDscrTab[EMAC_TXBUFNB];
#pragma pack(4)rt_uint8_t Rx_Buff[EMAC_RXBUFNB][EMAC_MAX_PACKET_SIZE];
#pragma pack(4)rt_uint8_t Tx_Buff[EMAC_TXBUFNB][EMAC_MAX_PACKET_SIZE];struct rt_semaphore tx_buf_free;
};
9.4 硬件初始化
RT-Thread 自动初始化详解
1 INIT_BOARD_EXPORT(fn) 非常早期的初始化,此时**调度器还未启动**
2 INIT_PREV_EXPORT(fn) 主要是用于纯软件的初始化、**没有太多依赖的函数**
3 INIT_DEVICE_EXPORT(fn) 外设驱动初始化相关,比如网卡设备
4 INIT_COMPONENT_EXPORT(fn) 组件初始化,比如文件系统或者 LWIP
5 INIT_ENV_EXPORT(fn) 系统环境初始化,比如挂载文件系统
6 INIT_APP_EXPORT(fn) 应用初始化,比如 GUI 应用
#define INIT_EXPORT(fn, level) RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn."level) = fn
/* board init routines will be called in board_init() function */
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
//commponents.c
#ifdef RT_USING_COMPONENTS_INIT
/** Components Initialization will initialize some driver and components as following* order:* rti_start --> 0* BOARD_EXPORT --> 1* rti_board_end --> 1.end** DEVICE_EXPORT --> 2* COMPONENT_EXPORT --> 3* FS_EXPORT --> 4* ENV_EXPORT --> 5* APP_EXPORT --> 6** rti_end --> 6.end** These automatically initializaiton, the driver or component initial function must* be defined with:* INIT_BOARD_EXPORT(fn);* INIT_DEVICE_EXPORT(fn);* ...* INIT_APP_EXPORT(fn);* etc.*/
static int rti_start(void)
{return 0;
}
INIT_EXPORT(rti_start, "0");static int rti_board_start(void)
{return 0;
}
INIT_EXPORT(rti_board_start, "0.end");static int rti_board_end(void)
{return 0;
}
INIT_EXPORT(rti_board_end, "1.end");static int rti_end(void)
{return 0;
}
INIT_EXPORT(rti_end, "6.end");/*** RT-Thread Components Initialization for board*/
void rt_components_board_init(void)
{
#if RT_DEBUG_INITint result;const struct rt_init_desc *desc;for (desc = &__rt_init_desc_rti_board_start; desc < &__rt_init_desc_rti_board_end; desc ++){rt_kprintf("initialize %s", desc->fn_name);result = desc->fn();rt_kprintf(":%d done\n", result);}
#elseconst init_fn_t *fn_ptr;for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++){(*fn_ptr)();}
#endif
}/*** RT-Thread Components Initialization*/
void rt_components_init(void)
{
#if RT_DEBUG_INITint result;const struct rt_init_desc *desc;rt_kprintf("do components intialization.\n");for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++){rt_kprintf("initialize %s", desc->fn_name);result = desc->fn();rt_kprintf(":%d done\n", result);}
#elseconst init_fn_t *fn_ptr;for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++){(*fn_ptr)();}
#endif
}
int rt_hw_usart_init(void)
{struct stm32_uart *uart;struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;uart = &uart1;config.baud_rate = BAUD_RATE_115200;serial1.ops = &stm32_uart_ops;serial1.config = config;MX_USART_UART_Init(&uart->huart);/* register UART1 device */rt_hw_serial_register(&serial1, "uart1",RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,uart);return 0;
}
INIT_BOARD_EXPORT(rt_hw_usart_init);
int rt_hw_pin_init(void)
{int result;result = rt_device_pin_register("pin", &_stm32_pin_ops, RT_NULL);return result;
}
INIT_BOARD_EXPORT(rt_hw_pin_init);
9.5 堆的初始化
上面的移植出现问题,有两个部分,一个是使用sdram,而板子上没有sdram,另一个是sram的大小,太小lwip有问题,太大则超过了sram的连续部分,在初始化堆时报错。
//使用内存堆时,必须要在系统初始化的时候进行堆的初始化,这个函数会把参数 begin_addr,end_addr 区域的内存空间作为内存堆来使用:
void rt_system_heap_init(void* begin_addr, void* end_addr);
//在使用 memheap 堆内存时,必须要在系统初始化的时候进行堆内存的初始化
rt_err_t rt_memheap_init(struct rt_memheap *memheap,const char *name,void *start_addr,rt_uint32_t size)
//如果有多个不连续的 memheap 可以多次调用该函数将其初始化并加入 memheap_item 链表。
9.6 关于程序的起始地址
9.6.1 设置一致性
三个地方的设置要一致:gd32_rom.sct、gd32_rom.ld、rt_hw_board_init()中断向量设置。
gd32_rom.ld是魔术棒Linker当中自定义,默认是取的魔术棒Target对话框中rom、ram的设置。其生成的定义在xxx
9.6.2 实际经历
rom起始设在0x800400,问题卡在:
void rt_mp_free(void *block)level = rt_hw_interrupt_disable();
rom起始设在0x800000,问题卡在:(原因是中断向量表向后偏了)
void rt_system_scheduler_start(void)rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp);
KEIL工程boot跳转失败,死在rt_system_scheduler_start()问题的解决
//board.c中对中断向量偏移进行了改动
void rt_hw_board_init()//SCB->VTOR = (0x08004000 & NVIC_VTOR_MASK);SCB->VTOR = (0x08000000 & NVIC_VTOR_MASK);

10. bootloader实现OTA在线升级
基于STM32F4实现RT-Thread的串口OTA(Ymodem_ota方式)
RT-Thread在线升级(Ymodem_OTA)
11. webnet应用
【rt-thread官网】webnet介绍
【gitee】webnet参考文档
webnet使用指南(CGI)
rt-thread应用篇(03)—基于STM32F429实现web服务器功能
文件系统要求:
WebNet 软件包使用,需要文件系统的支持(FAT 文件系统,ROMFS 文件系统等,支持 RT-Thread 的设备虚拟文件系统),用于 WebNet 软件包中访问的静态页面的存储、上传下载文件的存储等功能。
解决struct timeval报错:
#include <sys/time.h>
默认网页的位置:
//wn_sample.c
static void asp_var_version(struct webnet_session* session)
{RT_ASSERT(session != RT_NULL);static const char *version = "<html><body><font size=\"+2\">RT-Thread %d.%d.%d</font><br><br>""<a href=\"javascript:history.go(-1);\">Go back to root</a></html></body>";webnet_session_printf(session, version, RT_VERSION_MAJOR, RT_VERSION_MINOR, RT_VERSION_PATCH); //RT_VERSION, RT_SUBVERSION, RT_REVISION
}
int webnet_module_cgi(struct webnet_session* session, int event)
{if (event == WEBNET_EVENT_INIT){/* set default cgi path */if (_cgi_root[0] == '\0'){strcpy(_cgi_root, "/cgi-bin/");}}else if (event == WEBNET_EVENT_URI_PHYSICAL){struct webnet_request* request;char *cgi_path = RT_NULL;RT_ASSERT(session != RT_NULL);request = session->request;RT_ASSERT(request != RT_NULL);/* check whether a cgi request */cgi_path = strstr(request->path, _cgi_root);if (cgi_path != RT_NULL){char* cgi_name;rt_uint32_t index;//judge contain ".cgi"-------自己加的--------char* lastname = cgi_path + strlen(cgi_path)-4; if(strncasecmp(lastname,".cgi",4) == 0){int len = strlen(cgi_path);cgi_path[len-4] = '\0';}//----------end-----------------------------cgi_name = cgi_path + strlen(_cgi_root);for (index = 0; index < _cgi_count; index ++){if ((strlen(cgi_name) == strlen(_cgi_items[index].name))&& strncasecmp(cgi_name, _cgi_items[index].name, strlen(_cgi_items[index].name)) == 0){/* found it */_cgi_items[index].handler(session);return WEBNET_MODULE_FINISHED;}}/* set 404 not found error */request->result_code = 404;}}return WEBNET_MODULE_CONTINUE;
}
12. 文件系统
官网fal介绍
fal的api介绍
DFS文件系统管理与devfs/elmfat示例
DFS( Device File System)框架:
没有开启fatfs:
12.1 虚拟文件系统使用步骤:
- 初始化 DFS 组件。
- 注册具体类型的文件系统。
- 挂载文件系统
- 当文件系统不再使用,可以将它卸载。
//初始化
int dfs_init(void)
//注册文件系统
int dfs_register(const struct dfs_filesystem_ops *ops);
//挂载
int dfs_mount(const char *device_name,const char *path,const char *filesystemtype,unsigned long rwflag,const void *data);
//格式化设备(device_name)为(fs_name)文件格式
int dfs_mkfs(const char *fs_name, const char *device_name)
int dfs_file_open(struct dfs_fd *fd, const char *path, int flags)
int open(const char *file, int flags, ...)
//卸载
int dfs_unmount(const char *specialfile);
12.2 初始化
加入自动初始化
INIT_PREV_EXPORT(dfs_init);
初始化 DFS:
- 清除文件系统操作表
- 清除文件系统表
- 清除文件描述符表
- 初始化互斥量
- 设置当前工作目录为“/”
13.3 fal
官网FAL的API
1、这个是在lwip之前初始化fal:
///..\rt-thread\components\net\lwip-2.0.2\src\arch\sys_arch.c(214) : void sys_init(void)
#include <fal.h>
void sys_init(void)
{/* nothing on RT-Thread porting */rt_kprintf("in sys_init: fal_init()----\r\n");/*flash initial here for read to set ipaddr*/fal_init(); // ppp-----
}
2、错误解决
(1)“expected an expression” 错误解决
USED static const struct fal_partition partition_table_def[] SECTION("FalPartTable") = FAL_PART_TABLE;
把其中一行注释,而应该删除,且上面那个换行符后面有空格也不行。
(2)dfs_mount(FS_PARTITION_NAME, “/webnet”, “lfs”, 0, 0)失败
主要是在:dfs_mount()中的dfs_file_open()失败,调试时感觉走的不对,该返回的也不返回。
这个是dfs的问题,将/components/dfs整个文件夹替换成可用的。
后来仔细对了下,是/filesystems/romfs/romfs.c中直接将dummy换成webnet了,感觉没从根本上解决,只是取了个巧:
RT_WEAK const struct romfs_dirent _root_dirent[] =
{{ROMFS_DIRENT_DIR, "webnet", (rt_uint8_t *)_dummy, sizeof(_dummy) / sizeof(_dummy[0])},//{ROMFS_DIRENT_FILE, "dummy.txt", _dummy_txt, sizeof(_dummy_txt)},
};
(3)rtthread4.1.1以上的虚拟文件系统中由于结构体调整,在dfs_file_open()中没有对data进行赋值,导致文件打开失败,修改如下:
int dfs_file_open(struct dfs_fd *fd, const char *path, int flags)fd->flags = flags; fd->data = fs; //fa->data没给值,加上 ppp----
3、打印分区表
void fal_show_part_table(void)
//fal_def.h
#define FAL_PRINTF rt_kprintf //printf

4、设备表和分区表
- 分区表名称不能重复
- 设备名称必须与设备表里定义设备的名称一致(.name参数)
- 分区表相对设备的起始地址
- 该分区表的大小,以字节为单位。
board.h中的定义也以字节为单位:
13.4 littlefs(lfs)
rtthread利用片上flash挂载littlefs文件系统并操作
基于RTT系统的LITTLEFS文件系统移植说明(STM32片内FLASH)
13. TFTP
netutils应用笔记
TFTP:简单文件传输协议
netutils软件包中有TFTP小工具。TFTP (Trivial File Transfer Protocol),端口号为 69。在板卡上开启TFTP Server后,就可以在PC上使用TFTP Client软件将HTML网页文件上传到板卡的SPI FLASH中。
传输文件写出现错误,原因是创建tftpserver时的目录为"/",这个文件系统不可写,要改成“/webnet”。总之要根据自己创建的文件系统来决定。
static int _tftp_msh(int argc, char *argv[])//server = tftp_server_create(path[0], port);server = tftp_server_create("/webnet", port);
板子创建tftp服务端:
//finsh中输入
tftp -s
tftp工具软件选client:
14. 设备管理
RT-Thread IO设备管理模型
RT-Thread设备管理框架
RTThread IO设备和驱动学习

IO设备类型:
/* include/rtdef.h */
enum rt_device_class_type
{RT_Device_Class_Char = 0, /**< character device */RT_Device_Class_Block, /**< block device */RT_Device_Class_NetIf, /**< net interface */RT_Device_Class_MTD, /**< memory device */RT_Device_Class_CAN, /**< CAN device */RT_Device_Class_RTC, /**< RTC device */RT_Device_Class_Sound, /**< Sound device */RT_Device_Class_Graphic, /**< Graphic device */RT_Device_Class_I2CBUS, /**< I2C bus device */RT_Device_Class_USBDevice, /**< USB slave device */RT_Device_Class_USBHost, /**< USB host bus */RT_Device_Class_SPIBUS, /**< SPI bus device */RT_Device_Class_SPIDevice, /**< SPI device */RT_Device_Class_SDIO, /**< SDIO bus device */RT_Device_Class_PM, /**< PM pseudo device */RT_Device_Class_Pipe, /**< Pipe device */RT_Device_Class_Portal, /**< Portal device */RT_Device_Class_Timer, /**< Timer device */RT_Device_Class_Miscellaneous, /**< Miscellaneous device */RT_Device_Class_Sensor, /**< Sensor device */RT_Device_Class_Touch, /**< Touch device */RT_Device_Class_Unknown /**< unknown device */
};
设备结构体:
/* include/rtdef.h */
struct rt_device
{struct rt_object parent; /**< inherit from rt_object */enum rt_device_class_type type; /**< device type */rt_uint16_t flag; /**< device flag */rt_uint16_t open_flag; /**< device open flag */rt_uint8_t ref_count; /**< reference count */rt_uint8_t device_id; /**< 0 - 255 *//* device call back */rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);#ifdef RT_USING_DEVICE_OPSconst struct rt_device_ops *ops;
#else/* common device interface */rt_err_t (*init) (rt_device_t dev);rt_err_t (*open) (rt_device_t dev, rt_uint16_t oflag);rt_err_t (*close) (rt_device_t dev);rt_size_t (*read) (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);rt_size_t (*write) (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);rt_err_t (*control)(rt_device_t dev, int cmd, void *args);
#endif#if defined(RT_USING_POSIX)const struct dfs_file_ops *fops;struct rt_wqueue wait_queue;
#endifvoid *user_data; /**< device private data */
};
rt_device_create()
rt_device_destroy()
rt_device_register()
rt_device_find()
rt_device_init()
rt_device_open()
rt_device_close()
rt_device_control()
rt_device_read()
rt_device_write()
rt_device_set_rx_indicate() //设置接收回调
rt_device_set_tx_complete() //设置发送回调
14.1 i2c设备驱动
《rt-thread驱动框架分析》-i2c驱动
【rtthread设备】第六篇:i2c设备
15. lwip
lwip官方文档(重点)
【野火】LwIP应用开发实战指南—基于RT1052
LWIP学习笔记6——使用 NETCONN 接口编程
LWIP使用解析【网卡驱动 比较好】
LWIP使用经验—变态级(树状图比较好)
LwIP提供了三种编程接口,分别为 RAW/Callback API、Netconn API、Socket API。他们的易用性从左到右依次提高,而执行效率从左到右依次降低,用户可以根据实际情况,平衡利弊,选择合适的API进行网络应用程序的开发。
启动时序(图不错):
15.1 api
RAW:
err_t raw_bind (struct raw_pcb *pcb, const ip_addr_t *ipaddr)
void raw_bind_netif (struct raw_pcb *pcb, const struct netif *netif)
err_t raw_connect (struct raw_pcb *pcb, const ip_addr_t *ipaddr)
void raw_disconnect (struct raw_pcb *pcb)
void raw_recv (struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr)
err_t raw_sendto_if_src (struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, struct netif *netif, const ip_addr_t *src_ip)
err_t raw_send (struct raw_pcb *pcb, struct pbuf *p)
void raw_remove (struct raw_pcb *pcb)
TCP:
//connect----
tcp_new()
tcp_bind()
tcp_listen() and tcp_listen_with_backlog()
tcp_accept()
tcp_connect()
//send----
tcp_write()
tcp_output()
tcp_sent()
//recv----
tcp_recv()
tcp_recved()
void tcp_backlog_delayed (struct tcp_pcb *pcb)
void tcp_backlog_accepted (struct tcp_pcb *pcb)
err_t tcp_close (struct tcp_pcb *pcb)
err_t tcp_shutdown (struct tcp_pcb *pcb, int shut_rx, int shut_tx)
void tcp_abort (struct tcp_pcb *pcb)
err_t tcp_bind (struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
void tcp_bind_netif (struct tcp_pcb *pcb, const struct netif *netif)
struct tcp_pcb * tcp_listen_with_backlog (struct tcp_pcb *pcb, u8_t backlog)
struct tcp_pcb * tcp_listen_with_backlog_and_err (struct tcp_pcb *pcb, u8_t backlog, err_t *err)
void tcp_recved (struct tcp_pcb *pcb, u16_t len)
err_t tcp_connect (struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port, tcp_connected_fn connected)
struct tcp_pcb * tcp_new (void)
struct tcp_pcb * tcp_new_ip_type (u8_t type)
void tcp_arg (struct tcp_pcb *pcb, void *arg)
void tcp_recv (struct tcp_pcb *pcb, tcp_recv_fn recv)
void tcp_sent (struct tcp_pcb *pcb, tcp_sent_fn sent)
void tcp_err (struct tcp_pcb *pcb, tcp_err_fn err)
void tcp_accept (struct tcp_pcb *pcb, tcp_accept_fn accept)
void tcp_poll (struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval)
err_t tcp_write (struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
err_t tcp_output (struct tcp_pcb *pcb)
UDP:
err_t udp_send (struct udp_pcb *pcb, struct pbuf *p)
err_t udp_sendto (struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, u16_t dst_port)
err_t udp_sendto_if (struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif)
err_t udp_sendto_if_src (struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif, const ip_addr_t *src_ip)
err_t udp_bind (struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
void udp_bind_netif (struct udp_pcb *pcb, const struct netif *netif)
err_t udp_connect (struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
void udp_disconnect (struct udp_pcb *pcb)
void udp_recv (struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
void udp_remove (struct udp_pcb *pcb)
struct udp_pcb * udp_new (void)
struct udp_pcb * udp_new_ip_type (u8_t type)