做智能网站软件下载,网站怎么引入微信支付,购物软件,京东网上商城电脑版这个系列参考了《嵌入式实时操作系统RT-Thread设计与实现》#xff0c;会详细介绍RT-Thread的启动流程#xff0c;即是如何从零开始在开发板上运行起一个RTOS内核的。本文将会以 ch32v307VCT6 开发板为例展开进行详细介绍。主要包括#xff1a;startup.S、初始化与系统相关的… 这个系列参考了《嵌入式实时操作系统RT-Thread设计与实现》会详细介绍RT-Thread的启动流程即是如何从零开始在开发板上运行起一个RTOS内核的。本文将会以 ch32v307VCT6 开发板为例展开进行详细介绍。主要包括startup.S、初始化与系统相关的硬件、初始化系统内核对象例如定时器、调度器、信号、创建main线程、初始化线程并启动调度器 这五大部分。 在这一小节中本文将讲述通过wsl烧录RT-Thread的基本流程。并在最后讲解startup_ch32v30x.S文件的执行过程。
一、系统运行环境 本文在Linux系统上通过命令行的方式实现RT-Thread的下载与调试。这有几个准备步骤 RT-Thread的源码获取, 在linux命令行中输入如下命令 git clone https://gitee.com/rtthread/rt-thread.git 交叉编译工具链的下载MRS_Toolchain_Linux_x64_V1.92.1.tar.xz 下载地址。下载好后在linux环境中解压。记录好下载路径。Scons工具下载 在Linux命令行中输入以下命令 sudo apt install scons 准备完毕后切换目录到./RT_thread/rt-thread/bsp/wch/risc-v/ch32v307v-r1 下运行下文称其为根目录 scons --exec-path../../../../../Toolchain_linux/MRS_Toolchain_Linux_x64_V1.92.1/RISC-V_Embedded_GCC/bin 其中--exec-path后接下载的交叉编译工具链的路径。运行成功如下所示 至此在当前目录下会生成rtthread.bin 和 rtthread.elf两个文件。前期的准备工作就到此结束。
二、烧录至开发板 本文使用wsl安装教程WSL2 最新最全帮助小白一步步详细安装教程。 由于wsl本身不支持usb连接因此需要把插入电脑的usbWindows下接入到wslLinux下中所以还需要安装usbipd-win开源项目usbipd-win安装。想要便捷操作的可以额外下载图形化界面wsl usb guiwsl usb gui 下载。 下载完成并且usb成功接入到wsl后为了方便操作一般都会有一个Makefile执行相关命令其中的相对路径均从之前下载的交叉编译工具链给出
CROSS_COMPILE : ../../../../../Toolchain_linux/MRS_Toolchain_Linux_x64_V1.92.1/RISC-V_Embedded_GCC/bin/riscv-none-embed-
OPENOCD_DIR ../../../../../Toolchain_linux/MRS_Toolchain_Linux_x64_V1.92.1/OpenOCD/bin
OPENOCD ${OPENOCD_DIR}/openocd
CFG ${OPENOCD_DIR}/wch-riscv.cfg
GDB : ${CROSS_COMPILE}gdb
OBJDUMP : ${CROSS_COMPILE}objdump
.DEFAULT_GOAL : allall:scons --exec-path../../../../../Toolchain_linux/MRS_Toolchain_Linux_x64_V1.92.1/RISC-V_Embedded_GCC/bin.PHONY : flash
flash: echo ------------------------echo Flashing os.bin to boardecho ------------------------${OPENOCD} -f ${CFG} -c init -c halt -c program rtthread.bin -c exit.PHONY : run_openocd
run_openocd: echo -----------------------------------------------echo Please manually kill the openocd after gbd quitecho -----------------------------------------------${OPENOCD} -f ${CFG} .PHONY : run_gdb
run_gdb:${GDB} rtthread.elf -q -ex target remote : 3333.PHONY : code
code: ${OBJDUMP} -d rtthread.elf rtthread.asm.PHONY : clean
clean:rm -rf *.o *.bin *.elf 把以上Makefile文件放在根目录下并执行sudo make flash即可成功烧录 使用picocom串口工具观察现象picocom工具直接命令行输入sudo apt install picocom下载。在这里波特率为115200默认8个数据位1个停止位不启用校验位。 /dev/ttyACM0为Linux系统所识别的新插入的开发板USB设备文件。按下ctrlA ctrlX退出。 当出现如下图所示的 RT 字样即说明成功启动RT-Thread。 三、startup_ch32v30x.S 现在本文将从汇编文件开始讲述RT-Thread是如何一步一步启动的。startup_ch32v30x.S位置在RT_thread/rt-thread/bsp/wch/risc-v/Libraries/ch32v30x_libraries/bmsis/source。 汇编文件中的入口地址为 _start 由链接脚本ENTRY( _start )指定。汇编文件中第13行至332行均在设置中断向量入口地址。 第345行在设置堆栈指针 第348行至357行在将.data段中的数据从flash搬移至ram 第360行至366行在清零 .bss 段中的数据 第368行至373行在写控制状态寄存器具体作用不知 第377行至378行在写mstatus寄存器mstatus寄存器结构如下图其中比较重要的位有 MIE全局trap中断和异常开关。 MPIEtrap前系统MIE位的状态也可以理解为执行mret时MIE会被赋值位MPIE。 MPPtrap前系统运行的模式。也可以理解为mret时系统会运行于哪种模式。 为11则表示系统会运行在机器模式。 FS11表示启动浮点运算 因此这里在把MPP写为11后执行mret就打开了全局trap中断和异常的开关 。 第380行至382行在设置中断向量入口基地址其中_vector_base在此汇编文件第29行指定。 第384行至387行在执行开发板时钟初始化SystemInit并进入RT-Thread的初始化函数 在这里mepc寄存器中存入了entry函数地址在mret时系统会跳转到mepc寄存器存放的地址继续执行从而实现了entry函数的跳转。 SystemInit函数位置 RT_thread/rt-thread/bsp/wch/risc-v/Libraries/ch32v30x_libraries/bmsis/source/system_ch32v30x.c:85 首先执行SystemInit个人认为由于系统复位不管是哪种复位形式寄存器都会被设置为复位值所以SystemInit中除了SetSysClock()函数的跳转其他操作意义不明我太菜了。本文使用的ch32v307VCT6属于CH32V30x_D8C类别。 除非是软件直接跳转到handel_reset执行复位逻辑。 void SystemInit (void)
{RCC-CTLR | (uint32_t)0x00000001; #ifdef CH32V30x_D8CRCC-CFGR0 (uint32_t)0xF8FF0000;
#elseRCC-CFGR0 (uint32_t)0xF0FF0000;
#endif RCC-CTLR (uint32_t)0xFEF6FFFF;RCC-CTLR (uint32_t)0xFFFBFFFF;RCC-CFGR0 (uint32_t)0xFF80FFFF;#ifdef CH32V30x_D8CRCC-CTLR (uint32_t)0xEBFFFFFF;RCC-INTR 0x00FF0000;RCC-CFGR2 0x00000000;
#elseRCC-INTR 0x009F0000;
#endif SetSysClock();
} 在SetSysClock中将系统时钟设置为定义好的144MHz static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSESetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHzSetSysClockTo24();
#elif defined SYSCLK_FREQ_48MHzSetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHzSetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHzSetSysClockTo72();
#elif defined SYSCLK_FREQ_96MHzSetSysClockTo96();
#elif defined SYSCLK_FREQ_120MHzSetSysClockTo120();
#elif defined SYSCLK_FREQ_144MHz //仅144MHz有宏定义SetSysClockTo144();#endif/* If none of the define above is enabled, the HSI is used as System clock* source (default after reset) */
} 在SetSysClockTo144()函数中。主要工作在 启用HSE时钟配置PLL倍频、设置系统时钟、HCLK、PB1\PB2外设时钟。 逻辑是先启用HSE外部8MHz高速时钟并PLL 18倍频至 144MHz 作为SYSCLOCK系统时钟。 系统时钟不分频作为HCLK的输入。 HCLK不分频作为PB2CLK输入 HCLK 2分频作为PB1CLK输入。 static void SetSysClockTo144(void)
{__IO uint32_t StartUpCounter 0, HSEStatus 0;RCC-CTLR | ((uint32_t)RCC_HSEON); //启用外部高速时钟/* 等待HSE时钟准备就绪 */do{HSEStatus RCC-CTLR RCC_HSERDY;StartUpCounter;} while((HSEStatus 0) (StartUpCounter ! HSE_STARTUP_TIMEOUT));/* 判断HSE是否准备就绪 */if ((RCC-CTLR RCC_HSERDY) ! RESET){HSEStatus (uint32_t)0x01;}else{HSEStatus (uint32_t)0x00;}/* 如果HSE准备就绪则配置PLL倍频、SYSCLK、HCLK、PB1CLK、PB2CLK */if (HSEStatus (uint32_t)0x01){/* HCLK SYSCLK */RCC-CFGR0 | (uint32_t)RCC_HPRE_DIV1; //设置HCLK为SYSCLK不分频输入/* PCLK2 HCLK */RCC-CFGR0 | (uint32_t)RCC_PPRE2_DIV1; //设置PB2CLK为HCLK不分频输入/* PCLK1 HCLK/2 */RCC-CFGR0 | (uint32_t)RCC_PPRE1_DIV2; //设置PB1CLK为HCLK2分频输入/* PLL 配置: PLLCLK HSE * 18 144 MHz */RCC-CFGR0 (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLMULL));#ifdef CH32V30x_D8RCC-CFGR0 | (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL18);
#elseRCC-CFGR0 | (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLMULL18_EXTEN); //配置PLL时钟源为HSE并设置18倍频。 实际上这里配置的PLL时钟源是prediv1只不过prediv1系统默认输入是HSE不分频输入。
#endif/* 启用 PLL */RCC-CTLR | RCC_PLLON;/* 等待 PLL 准备就绪 */while((RCC-CTLR RCC_PLLRDY) 0){}/* 设置 PLL倍频时钟 作为系统时钟 */RCC-CFGR0 (uint32_t)((uint32_t)~(RCC_SW));RCC-CFGR0 | (uint32_t)RCC_SW_PLL;/* Wait till PLL is used as system clock source */while ((RCC-CFGR0 (uint32_t)RCC_SWS) ! (uint32_t)0x08){}}else{/** If HSE fails to start-up, the application will have wrong clock* configuration. User can add here some code to deal with this error*/}
} 至此系统从汇编文件启动到跳转到RT-Thread的初始化入口entry函数的逻辑就讲解完毕。这一部分主要是开发板启动的常规操作数据段搬运、bss段清零、中断向量设置以及 系统时钟的配置。接下来将介绍entry函数中是如何对RT-Thread进行初始化的。
四、RT-Thread初始化 单核初始化过程包括中断关闭、板级初始化、定时器初始化、调度器初始化、信号初始化、初始化创建应用main线程、初始化创建定时器线程、空闲线程初始化、僵尸线程初始化、打开中断并进行线程调度。
int rtthread_startup(void)
{
#ifdef RT_USING_SMPrt_hw_spin_lock_init(_cpus_lock);
#endifrt_hw_local_irq_disable(); //关中断/* board level initialization* NOTE: please initialize heap inside board initialization.*/rt_hw_board_init(); //板级初始化/* show RT-Thread version */rt_show_version();/* timer system initialization */rt_system_timer_init(); //定时器初始化/* scheduler system initialization */rt_system_scheduler_init(); //调度器初始化#ifdef RT_USING_SIGNALS/* signal system initialization */rt_system_signal_init();
#endif /* RT_USING_SIGNALS *//* create init_thread */rt_application_init(); //创建用户线程/* timer thread initialization */rt_system_timer_thread_init(); //定时器线程初始化/* idle thread initialization */ rt_thread_idle_init(); //空闲线程初始化/* defunct thread initialization */rt_thread_defunct_init(); //僵尸线程初始化#ifdef RT_USING_SMPrt_hw_spin_lock(_cpus_lock);
#endif /* RT_USING_SMP *//* start scheduler */rt_system_scheduler_start(); //线程调度开中断并执行main函数/* never reach here */return 0;
} 其中rt_hw_boart_init板级初始化过程需要我们根据具体的开发板进行修改。需要在其中完成系统时钟配置、为系统提供心跳、串口初始化、将系统输入输出终端绑定到这个串口。
extern uint32_t SystemCoreClock;static uint32_t _SysTick_Config(rt_uint32_t ticks)
{NVIC_SetPriority(SysTicK_IRQn, 0xf0);NVIC_SetPriority(Software_IRQn, 0xf0);NVIC_EnableIRQ(SysTicK_IRQn);NVIC_EnableIRQ(Software_IRQn);SysTick-CTLR 0;SysTick-SR 0;SysTick-CNT 0;SysTick-CMP ticks - 1;SysTick-CTLR 0xF;return 0;
}/*** This function will initial your board.*/
void rt_hw_board_init()
{/* System Tick Configuration */_SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);#if defined(RT_USING_USER_MAIN) defined(RT_USING_HEAP)rt_system_heap_init((void *) HEAP_BEGIN, (void *) HEAP_END);
#endif/* USART driver initialization is open by default */
#ifdef RT_USING_SERIALrt_hw_usart_init();
#endif
#ifdef RT_USING_CONSOLErt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif
#ifdef RT_USING_PIN/* pin must initialized before i2c */rt_hw_pin_init();
#endif/* Call components board initial (use INIT_BOARD_EXPORT()) */
#ifdef RT_USING_COMPONENTS_INITrt_components_board_init();
#endif}void SysTick_Handler(void) __attribute__((interrupt(WCH-Interrupt-fast)));
void SysTick_Handler(void)
{GET_INT_SP();/* enter interrupt */rt_interrupt_enter();SysTick-SR 0;rt_tick_increase();/* leave interrupt */rt_interrupt_leave();FREE_INT_SP();}