diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day2-thead.c" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day2-thead.c" new file mode 100644 index 0000000000000000000000000000000000000000..d644ad4b349379630159111cc8e76eac50a7ed24 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\344\275\234\344\270\232/Day2-thead.c" @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-11-06 SummerGift first version + * 2018-11-19 flybreak add stm32f407-atk-explorer bsp + */ + +#include +#include +#include + +void user_thread1(void){ + while(1){ + rt_kprintf("run in user1 thread\r\n"); + rt_thread_delay(5); + + } +} +void user_thread2(void){ + while(1){ + rt_kprintf("run in user2 thread\r\n"); + rt_thread_delay(5); + + } +} +void user_thread3(void){ + while(1){ + rt_kprintf("run in user3 thread\r\n"); + rt_thread_delay(5); + + } +} +rt_thread_t tid1 = RT_NULL; +rt_thread_t tid2 = RT_NULL; +rt_thread_t tid3 = RT_NULL; +int main(void) +{ + /* thread 1 pri:11 tick:50 */ + tid1 = rt_thread_create("user1", user_thread1, RT_NULL, 1024, 11, 50); + if (tid1 != RT_NULL){ + rt_thread_startup(tid1); + } + + /* thread 2 pri:11 tick:100 */ + tid2 = rt_thread_create("user2", user_thread2, RT_NULL, 1024, 11, 100); + if (tid2 != RT_NULL){ + rt_thread_startup(tid2); + } + + /* thread 3 pri:10 tick:100 */ + tid2 = rt_thread_create("user3", user_thread3, RT_NULL, 1024, 10, 100); + if (tid3 != RT_NULL){ + rt_thread_startup(tid3); + } + return RT_EOK; +} \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day2-SystemCore.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day2-SystemCore.md" new file mode 100644 index 0000000000000000000000000000000000000000..1dba4afad6f663c1a9bf113e11366436deb1dc33 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/Day2-SystemCore.md" @@ -0,0 +1,398 @@ +# 第二天笔记 +今天主要介绍了IDE的安装,如何在IDE上新建工程等.还有RTT整体初始化,堆栈/Systick/线程等初始化操作,我挑一些比较有意思的记录一下. + +## RT-Thread 的初始化 +根据系统初始化流程,以及对应的编译器选择. 针对GCC编译器的整体初始化方法如下. + +1. startup_stm32f407xx.s +``` c + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + ldr sp, =_estack /* set stack pointer */ + +/* Copy the data segment initializers from flash to SRAM */ + movs r1, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r3, =_sidata + ldr r3, [r3, r1] + str r3, [r0, r1] + adds r1, r1, #4 + +LoopCopyDataInit: + ldr r0, =_sdata + ldr r3, =_edata + adds r2, r0, r1 + cmp r2, r3 + bcc CopyDataInit + ldr r2, =_sbss + b LoopFillZerobss +/* Zero fill the bss segment. */ +FillZerobss: + movs r3, #0 + str r3, [r2], #4 + +LoopFillZerobss: + ldr r3, = _ebss + cmp r2, r3 + bcc FillZerobss + +/* Call the clock system intitialization function.*/ + bl SystemInit +/* Call static constructors */ +/* bl __libc_init_array */ +/* Call the application's entry point.*/ + bl entry + bx lr +.size Reset_Handler, .-Reset_Handler +``` +ARM Cortex-M处理器的复位入口程序,芯片复位后初始化堆栈、数据段、BSS段,并调用系统初始化函数以及应用程序的入口点。进入`entry`函数获取RTT系统初始化. + +2. components.c + +``` c +#elif defined(__GNUC__) +/* Add -eentry to arm-none-eabi-gcc argument */ +int entry(void) +{ + rtthread_startup(); + return 0; +} +#endif +``` +根据对应的编译器初始化不同的函数名. 殊途同归最终都是调用`rtthread_startup()`函数进行系统的初始化. + +在`.s`文件中其实隐含了一个系统时钟初始化, 复位了时钟为内部HSI/复位了中断.切换到RTT之后的首要任务是进行时钟、中断的重新设置,并且尽快的初始化系统的TimeBase用于tick的计算. + +``` c +/** + * @brief This function will call all levels of initialization functions to complete + * the initialization of the system, and finally start the scheduler. + */ +int rtthread_startup(void) +{ + rt_hw_interrupt_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(); + +#ifdef RT_USING_SMP + rt_hw_spin_lock(&_cpus_lock); +#endif /* RT_USING_SMP */ + + /* start scheduler */ + rt_system_scheduler_start(); + + /* never reach here */ + return 0; +} +#endif /* RT_USING_USER_MAIN */ +``` + +在上述的函数中,从底层到系统/应用层逐步进行初始化,本节聚焦与`rt_hw_board_init()`,看下内部到底做了那些操作. + +3. drv_common.c +``` c +/** + * This function will initial STM32 board. + */ +RT_WEAK void rt_hw_board_init() +{ +#ifdef SCB_EnableICache + /* Enable I-Cache---------------------------------------------------------*/ + SCB_EnableICache(); +#endif + +#ifdef SCB_EnableDCache + /* Enable D-Cache---------------------------------------------------------*/ + SCB_EnableDCache(); +#endif + + /* HAL_Init() function is called at the beginning of the program */ + HAL_Init(); + + /* enable interrupt */ + __set_PRIMASK(0); + /* System clock initialization */ + SystemClock_Config(); + /* disable interrupt */ + __set_PRIMASK(1); + + rt_hw_systick_init(); + + /* Heap initialization */ +#if defined(RT_USING_HEAP) + rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END); +#endif + + /* Pin driver initialization is open by default */ +#ifdef RT_USING_PIN + rt_hw_pin_init(); +#endif + + /* USART driver initialization is open by default */ +#ifdef RT_USING_SERIAL + rt_hw_usart_init(); +#endif + + /* Set the shell console output device */ +#ifdef RT_USING_CONSOLE + rt_console_set_device(RT_CONSOLE_DEVICE_NAME); +#endif + + /* Board underlying hardware initialization */ +#ifdef RT_USING_COMPONENTS_INIT + rt_components_board_init(); +#endif +} +``` + +本节函数非常清晰,本质上是对上电后整个环境进行逐个初始化. 若存在Cache, 先I/D Cache进行处理. 之后则进行HAL层/Clock/Systick/Heap进行初始化. 之后则是对基础外设进行初始化. + +分节来逐步讲解: + +``` c +HAL_StatusTypeDef HAL_Init(void) +{ + /* Configure Flash prefetch, Instruction cache, Data cache */ +#if (INSTRUCTION_CACHE_ENABLE != 0U) + __HAL_FLASH_INSTRUCTION_CACHE_ENABLE(); +#endif /* INSTRUCTION_CACHE_ENABLE */ + +#if (DATA_CACHE_ENABLE != 0U) + __HAL_FLASH_DATA_CACHE_ENABLE(); +#endif /* DATA_CACHE_ENABLE */ + +#if (PREFETCH_ENABLE != 0U) + __HAL_FLASH_PREFETCH_BUFFER_ENABLE(); +#endif /* PREFETCH_ENABLE */ + + /* Set Interrupt Group Priority */ + HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); + + /* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */ + HAL_InitTick(TICK_INT_PRIORITY); + + /* Init the low level hardware */ + HAL_MspInit(); + + /* Return function status */ + return HAL_OK; +} +``` + +自动生成的代码,初始化默认中断分组;初始化Systick中断优先级; + +``` c +void SystemClock_Config(void) +{ + RCC_OscInitTypeDef RCC_OscInitStruct = {0}; + RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; + RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; + + /**Configure the main internal regulator output voltage + */ + __HAL_RCC_PWR_CLK_ENABLE(); + __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); + /**Initializes the CPU, AHB and APB busses clocks + */ + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE + |RCC_OSCILLATORTYPE_LSE; + RCC_OscInitStruct.HSEState = RCC_HSE_ON; + RCC_OscInitStruct.LSEState = RCC_LSE_ON; + RCC_OscInitStruct.LSIState = RCC_LSI_ON; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + RCC_OscInitStruct.PLL.PLLM = 4; + RCC_OscInitStruct.PLL.PLLN = 168; + RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; + RCC_OscInitStruct.PLL.PLLQ = 7; + if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) + { + Error_Handler(); + } + /**Initializes the CPU, AHB and APB busses clocks + */ + RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK + |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; + + if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) + { + Error_Handler(); + } + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC; + PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) + { + Error_Handler(); + } +} +``` + +初始化对应的时钟源为选择的时钟源,获取最大性能. + +``` c +/* SysTick configuration */ +void rt_hw_systick_init(void) +{ +#if defined (SOC_SERIES_STM32H7) + HAL_SYSTICK_Config((HAL_RCCEx_GetD1SysClockFreq()) / RT_TICK_PER_SECOND); +#elif defined (SOC_SERIES_STM32MP1) + HAL_SYSTICK_Config(HAL_RCC_GetSystemCoreClockFreq() / RT_TICK_PER_SECOND); +#else + HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / RT_TICK_PER_SECOND); +#endif +#if !defined (SOC_SERIES_STM32MP1) + HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); +#endif + NVIC_SetPriority(SysTick_IRQn, 0xFF); +} +``` + +设置对应的systick,每秒产生对应`RT_TICK_PER_SECOND`指定的中断次数. + +```c +/** + * @brief This function will init system heap. + * + * @param begin_addr the beginning address of system page. + * + * @param end_addr the end address of system page. + */ +RT_WEAK void rt_system_heap_init(void *begin_addr, void *end_addr) +{ + rt_ubase_t begin_align = RT_ALIGN((rt_ubase_t)begin_addr, RT_ALIGN_SIZE); + rt_ubase_t end_align = RT_ALIGN_DOWN((rt_ubase_t)end_addr, RT_ALIGN_SIZE); + + RT_ASSERT(end_align > begin_align); + + /* Initialize system memory heap */ + _MEM_INIT("heap", begin_addr, end_align - begin_align); + /* Initialize multi thread contention lock */ + _heap_lock_init(); +} + +// 内存对齐的额外变量 (board.h) +extern int __bss_end; +#define HEAP_BEGIN ((void *)&__bss_end) +#endif + +#define HEAP_END STM32_SRAM_END +``` + +本处进行Heap的初始化,这里还是挺有意思的,这里将RAM除了`data`和`bss`区之外的所有空间都利用上了, 将上述空间做了4字节对齐后直接作为指定的Heap区. 不展开, 具体可以看`.map`文件和`__bss_end`. + +到此为止就完成了时钟和heap的初始化, 完成了最小内核的初始化. + +## 线程的初始化 +这也是比较有意思的第二点, 如何进行线程的初始化呢? + +无论是什么操作系统,本质上都是切换前保存对应的寄存器组, 切换对应的`PSP`和恢复寄存器组. + +``` c +struct exception_stack_frame +{ + rt_uint32_t r0; + rt_uint32_t r1; + rt_uint32_t r2; + rt_uint32_t r3; + rt_uint32_t r12; + rt_uint32_t lr; + rt_uint32_t pc; + rt_uint32_t psr; +}; + +struct stack_frame +{ +#if USE_FPU + rt_uint32_t flag; +#endif /* USE_FPU */ + + /* r4 ~ r11 register */ + rt_uint32_t r4; + rt_uint32_t r5; + rt_uint32_t r6; + rt_uint32_t r7; + rt_uint32_t r8; + rt_uint32_t r9; + rt_uint32_t r10; + rt_uint32_t r11; + + struct exception_stack_frame exception_stack_frame; +}; + +rt_uint8_t *rt_hw_stack_init(void *tentry, + void *parameter, + rt_uint8_t *stack_addr, + void *texit) +{ + struct stack_frame *stack_frame; + rt_uint8_t *stk; + unsigned long i; + + stk = stack_addr + sizeof(rt_uint32_t); + stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8); + stk -= sizeof(struct stack_frame); + + stack_frame = (struct stack_frame *)stk; + + /* init all register */ + for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++) + { + ((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef; + } + + stack_frame->exception_stack_frame.r0 = (unsigned long)parameter; /* r0 : argument */ + stack_frame->exception_stack_frame.r1 = 0; /* r1 */ + stack_frame->exception_stack_frame.r2 = 0; /* r2 */ + stack_frame->exception_stack_frame.r3 = 0; /* r3 */ + stack_frame->exception_stack_frame.r12 = 0; /* r12 */ + stack_frame->exception_stack_frame.lr = (unsigned long)texit; /* lr */ + stack_frame->exception_stack_frame.pc = (unsigned long)tentry; /* entry point, pc */ + stack_frame->exception_stack_frame.psr = 0x01000000L; /* PSR */ + +#if USE_FPU + stack_frame->flag = 0; +#endif /* USE_FPU */ + + /* return task's current stack address */ + return stk; +} +``` + +在上述代码中, Cortex-M响应中断时处理器硬件会将当前运行部分的上下文寄存器自动压入中断栈中,这部分的寄存器包括 PSR、PC、LR、R12、R3-R0 寄存器.`exception_stack_frame`刚好包含了上述寄存器,并且压栈顺序与对应结构体定义相同. 其他寄存器则需要手动进行保存. + + +本章节剩余内容,在学习完调度器后我补上. \ No newline at end of file diff --git "a/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/\347\254\254\344\270\200\346\254\241\347\254\224\350\256\260.md" "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/\347\254\254\344\270\200\346\254\241\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..aa6cbdd16dc0cb10140a0dee96fd614e61716771 --- /dev/null +++ "b/2025/\347\254\2544\347\273\204(FRDM-MCXA156)/\347\236\277\345\255\220\346\226\207/\347\254\224\350\256\260/\347\254\254\344\270\200\346\254\241\347\254\224\350\256\260.md" @@ -0,0 +1,42 @@ +第一次上传笔记 +# 第一天笔记 +跟晶体矿一样提交过bsp +## env的使用 +基本上没什么特殊的地方, 就是注意下怎么舒服的使用就好了 + +`pkgs --upgrade` 升级软件包 + +`pkgs --update` 更新软件包 + +`scons` 对文件进行编译 + +`menuconfig` 打开配置菜单,记得保存后再退出 + + +## 关于scons工具 +### sconscript语法 +创建新文件夹需要添加sconscript文件,每个文件夹都有一个,python通过该文件递归遍历每一个文件夹以控制编译内容.没有sconscript的话编译器是找不到文件的.新建文件夹的时候顺便复制一下同级目录下的对应的sconscript文件就行, 这样本目录下的对应文件就会添加到编译中去了. 如果感觉少了点什么东西, 可以看下sconscript文件里面是否有对应的后缀名. + + +## git +git是一个版本管理工具,组成为 工作区--暂存区--本地仓库--远端仓库 + +### git重要命令 +`git push/pull` 推送/拉取 + +`git add .` 添加所有修改的文件到暂存区 + +`git commit -m "log"` commit,log是commit的标题 + +`git log` 查看修改日志 + +`git status` 查看文件状态 + +`git checkout -b first_branch `创建一个分支名为first_branch,可以通过`git switch`切换分支,`git branch` 查看分支 + +`git reset --hard HEAD~` 硬重置,强制删除上一个commit + +`git reset --soft HEAD~` 软重置,把上一个commit退回暂存区里,后面还可以重新commit + +### 提示 +新手还是使用图形化界面比较好,直接上命令有点难了, 推荐sourcetree, 只要不rebase我觉得功能都够用. \ No newline at end of file