一、入门
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之串口初始化流程分析
四、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_HEAP
tid = rt_thread_create("main", main_thread_entry, RT_NULL,
RT_MAIN_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);
RT_ASSERT(tid != RT_NULL);
#else
rt_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;
#endif
rt_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_ACL
bool “Tmpfs POSIX Access Control Lists”
depends on TMPFS
select GENERIC_ACL
help
POSIX 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条目
choice
prompt "soc x1000 codec type select"
depends on SOC_X1000
config SND_ASOC_INGENIC_PHOENIX_ICDC
tristate "Audio support for phoenix with internal codec"
select SND_ASOC_DMA_V13
select SND_ASOC_JZ_AIC_I2S_V13
select SND_ASOC_JZ_ICDC_D3
#select SND_ASOC_JZ_PCM_V13
#select SND_ASOC_FIIO_PCM5242
config SND_ASOC_INGENIC_PHOENIX_SPDIF
tristate "Audio support for phoenix with spdif"
select SND_ASOC_DMA_V13
select SND_ASOC_JZ_AIC_SPDIF_V13
select SND_ASOC_JZ_SPDIF_V13
#select SND_ASOC_JZ_PCM_V13
endchoice
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_DIR
string
option env="BSP_ROOT"
default "."
config $RTT_DIR
string
option 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_DIR
string
option env="PKGS_ROOT"
default "packages"
config $ENV_DIR
string
option 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_MAX
int "The maximal size of kernel object name"
range 2 32
default 8
help
Each 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_SMP
bool "Enable SMP(Symmetric multiprocessing)"
default n
help
This option should be selected by machines which have an SMP-
capable CPU.
The only effect of this option is to make the SMP-related
options 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_DEVICE
default n
7.6 使用对照
1、config
//Kconfig中:
config BSP_USING_WDT
bool "Enable Watchdog Timer"
select RT_USING_WDT
default n
config RT_CONSOLE_DEVICE_NAME
string "the device name for console"
default "uart1"
config BSP_I2C1_SCL_PIN
int "I2C1 scl pin number"
range 1 176
default 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_COM2
bool "Enable COM2 (uart2 pin conflict with Ethernet and PWM)"
select BSP_USING_UART
select BSP_USING_UART2
default n
config BSP_USING_COM3
bool "Enable COM3 (uart3 pin conflict with Ethernet)"
select BSP_USING_UART3
default n
endmenu
3、if/endif
menu "Hardware Drivers Config"
menuconfig BSP_USING_CAN
bool "Enable CAN"
default n
select RT_USING_CAN
if BSP_USING_CAN
config BSP_USING_CAN1
bool "Enable CAN1"
default n
endif
endmenu
4、menuconfig
menu "Hardware Drivers Config"
menuconfig BSP_USING_UART
bool "Enable UART"
default y
select RT_USING_SERIAL
if BSP_USING_UART
config BSP_USING_UART1
bool "Enable UART1"
default y
config BSP_UART1_RX_USING_DMA
bool "Enable UART1 RX DMA"
depends on BSP_USING_UART1 && RT_SERIAL_USING_DMA
default n
endif
endmenu
5、choice
menu "Hardware Drivers Config"
menuconfig BSP_USING_ONCHIP_RTC
bool "Enable RTC"
select RT_USING_RTC
select RT_USING_LIBC
default n
if BSP_USING_ONCHIP_RTC
choice
prompt "Select clock source"
default BSP_RTC_USING_LSE
config BSP_RTC_USING_LSE
bool "RTC USING LSE"
config BSP_RTC_USING_LSI
bool "RTC USING LSI"
endchoice
endif
endmenu
8. 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_DIR
string
option env="BSP_ROOT"
default "."
config RTT_DIR
string
option 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_SDRAM
bool "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);
#endif
SystemClock_Config();
#ifdef RT_USING_COMPONENTS_INIT
rt_components_board_init();
#endif
#ifdef RT_USING_CONSOLE
rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif
#ifdef BSP_USING_SDRAM
rt_system_heap_init((void *)EXT_SDRAM_BEGIN, (void *)EXT_SDRAM_END); //用SDRAM没成功
#else
rt_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 硬件初始化
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_INIT
int 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);
}
#else
const 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_INIT
int 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);
}
#else
const 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_OPS
const 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;
#endif
void *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)