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() 。
- rtthread_startup() 函數是 RT-Thread 規定的統一啟動入口。啟動調度器之前,系統所創建的線程在執行 rt_thread_startup() 后并不會立馬運行,它們會處于就緒狀態等待系統調度。MDK 的擴展功能 SubSubSub$ 和 SuperSuperSuper$。
- startup_stm32f103xe.s 開始-> components.c
根據宏跳轉到對應函數,如MDK: int SubSubSub$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)
七、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............. endmenu7.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_V13endchoice7.4 comment條目
顯示注釋信息
menu “Floating point emulation” comment “At least one emulation must be selected” config FPE_NWFPE ... config FPE_NWFPE_XP endmenu7.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 n7.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 1162、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 endmenu3、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 endmenu4、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 endmenu5、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 endmenu8. 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內容。
8.3 生成mdk5工程(scons)
scons通過讀取rtconfig.h文件配置成mdk5、mdk4、iar工程并編譯。
進入bsp目錄下對應工程目錄下,刪除原有的project.eww、project.uvproj、project.uvprojx三個文件,在當前目錄中打開env,輸入:
8.4 軟件包管理(pkgs)
官網軟件包 https://github.com/RT-Thread-packages
工程目錄打開env->menuconfig->RT-Thread online packages按空格鍵選中所需軟件包
(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中的定義)
(3)用bsp中的復制移植
參考這篇:教你動手移植RT-Thread到國產MCU
board.h中的sram為64M時,開lwip就會在出問題。
board.h中的sram為512M時,堆初始化會出問題,因為不連續。
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報錯:
默認網頁的位置:
//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 組件。
- 注冊具體類型的文件系統。
- 掛載文件系統
- 當文件系統不再使用,可以將它卸載。
12.2 初始化
加入自動初始化
INIT_PREV_EXPORT(dfs_init);初始化 DFS:
- 清除文件系統操作表
- 清除文件系統表
- 清除文件描述符表
- 初始化互斥量
- 設置當前工作目錄為“/”
13.3 fal
官網FAL的API
1、這個是在lwip之前初始化fal:
2、錯誤解決
(1)“expected an expression” 錯誤解決
把其中一行注釋,而應該刪除,且上面那個換行符后面有空格也不行。
(2)dfs_mount(FS_PARTITION_NAME, “/webnet”, “lfs”, 0, 0)失敗
主要是在:dfs_mount()中的dfs_file_open()失敗,調試時感覺走的不對,該返回的也不返回。
這個是dfs的問題,將/components/dfs整個文件夾替換成可用的。
后來仔細對了下,是/filesystems/romfs/romfs.c中直接將dummy換成webnet了,感覺沒從根本上解決,只是取了個巧:
(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 //printf4、設備表和分區表
- 分區表名稱不能重復
- 設備名稱必須與設備表里定義設備的名稱一致(.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”??傊鶕约簞摻ǖ奈募到y來決定。
static int _tftp_msh(int argc, char *argv[])//server = tftp_server_create(path[0], port);server = tftp_server_create("/webnet", port);板子創建tftp服務端:
//finsh中輸入 tftp -stftp工具軟件選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() //設置發送回調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)總結
以上是生活随笔為你收集整理的RT-Thread学习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 论网卡数据是如何从驱动到桥接/ip层(N
- 下一篇: 玩转华为ENSP模拟器系列 | 配置Do