Bootstrap

RT-Thread学习

一、入门

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_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配置

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_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 硬件初始化

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_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)
;