7.5 第3步,修改驱动初始化文件bsp.c(含MPU配置)
这个bsp.c文件比较重要,移植阶段,直接将我们移植好的模板内容复制过去即可,这里把相关的内容为大家做个说明。
7.5.1 函数System_Init
系统初始化,主要是MPU,Cache和系统时钟配置,需要在ThreadX初始化之前调用。
/*********************************************************************************************************** 函 数 名: System_Init* 功能说明: 系统初始化,主要是MPU,Cache和系统时钟配置* 形 参:无* 返 回 值: 无**********************************************************************************************************/void System_Init(void){ /* 配置MPU */ MPU_Config(); /* 使能L1 Cache */ CPU_CACHE_Enable(); /* STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟: - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。 - 设置NVIV优先级分组为4。 */ HAL_Init(); /* 配置系统时钟到400MHz - 切换使用HSE。 - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。 - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder并开启 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart();#endif}
7.5.2 函数bsp_Init
硬件外设的初始化,这个函数在RTX5的启动任务里面调用。
/*********************************************************************************************************** 函 数 名: bsp_Init* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次* 形 参: 无* 返 回 值: 无**********************************************************************************************************/void bsp_Init(void){ bsp_InitDWT(); /* 初始化DWT时钟周期计数器 */ bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */ bsp_InitUart(); /* 初始化串口 */ bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */ bsp_InitLed(); /* 初始化LED */ bsp_InitTimer(); /* 初始化滴答定时器 */}
7.5.3 函数SystemClock_Config
这个函数主要是完成系统时钟配置。
/*********************************************************************************************************** 函 数 名: SystemClock_Config* 功能说明: 初始化系统时钟* System Clock source = PLL (HSE)* SYSCLK(Hz) = 400000000 (CPU Clock)* HCLK(Hz) = 200000000 (AXI and AHBs Clock)* AHB Prescaler = 2* D1 APB3 Prescaler = 2 (APB3 Clock 100MHz)* D2 APB1 Prescaler = 2 (APB1 Clock 100MHz)* D2 APB2 Prescaler = 2 (APB2 Clock 100MHz)* D3 APB4 Prescaler = 2 (APB4 Clock 100MHz)* HSE Frequency(Hz) = 25000000* PLL_M = 5* PLL_N = 160* PLL_P = 2* PLL_Q = 4* PLL_R = 2* VDD(V) = 3.3* Flash Latency(WS) = 4* 形 参: 无* 返 回 值: 无**********************************************************************************************************/static void SystemClock_Config(void){ 省略未写 /* AXI SRAM的时钟是上电自动使能的,而D2域的SRAM1,SRAM2和SRAM3要单独使能 */ #if 1 __HAL_RCC_D2SRAM1_CLK_ENABLE(); __HAL_RCC_D2SRAM2_CLK_ENABLE(); __HAL_RCC_D2SRAM3_CLK_ENABLE(); __HAL_RCC_BKPRAM_CLKAM_ENABLE(); __HAL_RCC_D3SRAM1_CLKAM_ENABLE();#endif}
这里的RAM时钟初始化比较重要,这几个RAM的时钟都要单独使能。
7.5.4 函数MPU_Config
ThreadX例子默认采用AXI SRAM作为主RAM空间,因为空间比较大,方便我们后制作综合例子使用:
/*********************************************************************************************************** 函 数 名: MPU_Config* 功能说明: 配置MPU* 形 参: 无* 返 回 值: 无**********************************************************************************************************/static void MPU_Config( void ){ MPU_Region_InitTypeDef MPU_InitStruct; /* 禁止 MPU */ HAL_MPU_Disable();/* 最高性能,读Cache和写Cache都开启 */ #if 1 /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct);/* 最低性能,读Cache和写Cache都关闭 */ #else /* 配置AXI SRAM的MPU属性为NORMAL, NO Read allocate,NO Write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct);#endif /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x60000000; MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER1; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* 使能 MPU */ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);}
7.5.5 函数bsp_RunPer10ms
这个函数里面默认有个按键扫描,如果大家移植的程序里面没有按键初始化,务必要把这个按键扫描函数注释掉。
/*********************************************************************************************************** 函 数 名: bsp_RunPer10ms* 功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求* 不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。* 形 参: 无* 返 回 值: 无**********************************************************************************************************/void bsp_RunPer10ms(void){ bsp_KeyScan10ms();}
7.6 第4步,更新bsp_timer.c和bsp.h文件
更新bsp_timer.c文件是因为此文件跟ThreadX都要使用滴答定时器,有冲突。所以大家直接将我们工程模板里面此文件覆盖移植的这个文件即可。
bsp.h文件里面要添加一个宏定义,因为bsp_timer.c文件里面做了些条件编译:
#define USE_THREADX 1
另外,bsp.h文件将大部分头文件都添加进来了,大家可以根据需要,用到那些头文件,使能那些,用不到的,可以注释掉。当然,不注释也是没问题的:
/* 通过取消注释或者添加注释的方式控制是否包含底层驱动模块 *///#include "bsp_msg.h"//#include "bsp_user_lib.h"#include "bsp_timer.h"#include "bsp_led.h"#include "bsp_key.h"#include "bsp_dwt.h"//#include "bsp_cpu_rtc.h"//#include "bsp_cpu_adc.h"//#include "bsp_cpu_dac.h"#include "bsp_uart_fifo.h"//#include "bsp_uart_gps.h"//#include "bsp_uart_esp8266.h"//#include "bsp_uart_sim800.h"//#include "bsp_spi_bus.h"//#include "bsp_spi_ad9833.h"//#include "bsp_spi_ads1256.h"//#include "bsp_spi_dac8501.h"//#include "bsp_spi_dac8562.h"//#include "bsp_spi_flash.h"//#include "bsp_spi_tm7705.h"//#include "bsp_spi_vs1053b.h"//#include "bsp_fmc_sdram.h"//#include "bsp_fmc_nand_flash.h"//#include "bsp_fmc_ad7606.h"//#include "bsp_fmc_oled.h"#include "bsp_fmc_io.h"//#include "bsp_i2c_gpio.h"//#include "bsp_i2c_bh1750.h"//#include "bsp_i2c_bmp085.h"//#include "bsp_i2c_eeprom_24xx.h"//#include "bsp_i2c_hmc5883l.h"//#include "bsp_i2c_mpu6050.h"//#include "bsp_i2c_si4730.h"//#include "bsp_i2c_wm8978.h"//#include "bsp_tft_h7.h"//#include "bsp_tft_429.h"//#include "bsp_tft_lcd.h"//#include "bsp_ts_touch.h"//#include "bsp_ts_ft5x06.h"//#include "bsp_ts_gt811.h"//#include "bsp_ts_gt911.h"//#include "bsp_ts_stmpe811.h"#include "bsp_beep.h"#include "bsp_tim_pwm.h"//#include "bsp_sdio_sd.h"//#include "bsp_dht11.h"//#include "bsp_ds18b20.h"//#include "bsp_ps2.h"//#include "bsp_ir_decode.h"//#include "bsp_camera.h"//#include "bsp_rs485_led.h"//#include "bsp_can.h"
7.7 第5步,修改文件stm3h7xx_it.c
删除此文件里面带的如下函数,ThreadX要使用,冲突了。
/** * @brief This function handles PendSVC exception. * @param None * @retval None */void PendSV_Handler(void){}
7.8 第6步,修改文件tx_initalize_low_level.s
主要是修改下面代码中置红部分的参数需要大家配置,即芯片主频和系统时钟节拍。400000000是系统时钟主频,1000对应的就是系统时钟节拍,这里1000就表示1000Hz。
.global _tx_thread_system_stack_ptr .global _tx_initialize_unused_memory .global __RAM_segment_used_end__ .global _tx_timer_interrupt .global __main .global __tx_SVCallHandler .global __tx_PendSVHandler .global _vectors .global __tx_NMIHandler @ NMI .global __tx_BadHandler @ HardFault .global __tx_SVCallHandler @ SVCall .global __tx_DBGHandler @ Monitor .global __tx_PendSVHandler @ PendSV .global __tx_SysTickHandler @ SysTick .global __tx_IntHandler @ Int 0@@SYSTEM_CLOCK = 400000000SYSTICK_CYCLES = ((SYSTEM_CLOCK / 1000) -1)
7.9 第7步,ThreadX配置文件tx_user.h
ThreadX内核相关的配置,已经全部整理到了这个文件中,并且做中文注释,大家可以更新需要使能宏定义。
7.10 第8步,添加应用程序
应用程序比较简单,大家可以直接复制本章教程配置例子的main.c文件中的内容到自己工程里面测试。主要创建了如下几个任务:
App Task Start任务 :启动任务,这里用作BSP驱动包处理。
App Task MspPro任务 :消息处理,这里用作浮点数串口打印。
App Task UserIF任务 :按键消息处理。
App Task COM任务 :浮点数串口打印。
App Task STAT任务 :统计任务
App Task IDLE任务 :空闲任务
System Timer Thread任务:系统定时器任务
任务栈大小和任务控制块定义如下:
/*********************************************************************************************************** 任务优先级,数值越小优先级越高**********************************************************************************************************/#define APP_CFG_TASK_START_PRIO 2u#define APP_CFG_TASK_MsgPro_PRIO 3u#define APP_CFG_TASK_USER_IF_PRIO 4u#define APP_CFG_TASK_COM_PRIO 5u#define APP_CFG_TASK_STAT_PRIO 30u#define APP_CFG_TASK_IDLE_PRIO 31u/*********************************************************************************************************** 任务栈大小,单位字节**********************************************************************************************************/#define APP_CFG_TASK_START_STK_SIZE 4096u#define APP_CFG_TASK_MsgPro_STK_SIZE 4096u#define APP_CFG_TASK_COM_STK_SIZE 4096u#define APP_CFG_TASK_USER_IF_STK_SIZE 4096u#define APP_CFG_TASK_IDLE_STK_SIZE 1024u#define APP_CFG_TASK_STAT_STK_SIZE 1024u/*********************************************************************************************************** 静态全局变量**********************************************************************************************************/ static TX_THREAD AppTaskStartTCB;static uint64_t AppTaskStartStk[APP_CFG_TASK_START_STK_SIZE/8];static TX_THREAD AppTaskMsgProTCB;static uint64_t AppTaskMsgProStk[APP_CFG_TASK_MsgPro_STK_SIZE/8];static TX_THREAD AppTaskCOMTCB;static uint64_t AppTaskCOMStk[APP_CFG_TASK_COM_STK_SIZE/8];static TX_THREAD AppTaskUserIFTCB;static uint64_t AppTaskUserIFStk[APP_CFG_TASK_USER_IF_STK_SIZE/8];static TX_THREAD AppTaskIdleTCB;static uint64_t AppTaskIdleStk[APP_CFG_TASK_IDLE_STK_SIZE/8];static TX_THREAD AppTaskStatTCB;static uint64_t AppTaskStatStk[APP_CFG_TASK_STAT_STK_SIZE/8];
优先创建的启动任务,统计任务和空闲任务:
/*********************************************************************************************************** 函 数 名: tx_application_define* 功能说明: ThreadX专用的任务创建,通信组件创建函数* 形 参: first_unused_memory 未使用的地址空间* 返 回 值: 无**********************************************************************************************************/void tx_application_define(void *first_unused_memory){ /* 如果实现任务CPU利用率统计的话,此函数仅用于实现启动任务,统计任务和空闲任务,其它任务在函数 AppTaskCreate里面创建。 */ /**************创建启动任务*********************/ tx_thread_create(&AppTaskStartTCB, /* 任务控制块地址 */ "App Task Start", /* 任务名 */ AppTaskStart, /* 启动任务函数地址 */ 0, /* 传递给任务的参数 */ &AppTaskStartStk[0], /* 堆栈基地址 */ APP_CFG_TASK_START_STK_SIZE, /* 堆栈空间大小 */ APP_CFG_TASK_START_PRIO, /* 任务优先级*/ APP_CFG_TASK_START_PRIO, /* 任务抢占阀值 */ TX_NO_TIME_SLICE, /* 不开启时间片 */ TX_AUTO_START); /* 创建后立即启动 */ /**************创建统计任务*********************/ tx_thread_create(&AppTaskStatTCB, /* 任务控制块地址 */ "App Task STAT", /* 任务名 */ AppTaskStat, /* 启动任务函数地址 */ 0, /* 传递给任务的参数 */ &AppTaskStatStk[0], /* 堆栈基地址 */ APP_CFG_TASK_STAT_STK_SIZE, /* 堆栈空间大小 */ APP_CFG_TASK_STAT_PRIO, /* 任务优先级*/ APP_CFG_TASK_STAT_PRIO, /* 任务抢占阀值 */ TX_NO_TIME_SLICE, /* 不开启时间片 */ TX_AUTO_START); /* 创建后立即启动 */ /**************创建空闲任务*********************/ tx_thread_create(&AppTaskIdleTCB, /* 任务控制块地址 */ "App Task IDLE", /* 任务名 */ AppTaskIDLE, /* 启动任务函数地址 */ 0, /* 传递给任务的参数 */ &AppTaskIdleStk[0], /* 堆栈基地址 */ APP_CFG_TASK_IDLE_STK_SIZE, /* 堆栈空间大小 */ APP_CFG_TASK_IDLE_PRIO, /* 任务优先级*/ APP_CFG_TASK_IDLE_PRIO, /* 任务抢占阀值 */ TX_NO_TIME_SLICE, /* 不开启时间片 */ TX_AUTO_START); /* 创建后立即启动 */ }
在启动任务中优先执行一次任务统计,然后创建其它任务:
/*********************************************************************************************************** 函 数 名: AppTaskStart* 功能说明: 启动任务。* 形 参: thread_input 是在创建该任务时传递的形参* 返 回 值: 无 优 先 级: 2**********************************************************************************************************/static void AppTaskStart (ULONG thread_input){ (void)thread_input; /* 先挂起定时器组 */#ifndef TX_NO_TIMER tx_thread_suspend(&_tx_timer_thread);#endif /* 优先执行任务统计 */ OSStatInit(); /* 恢复定时器组 */#ifndef TX_NO_TIMER tx_thread_resume(&_tx_timer_thread);#endif /* 内核开启后,恢复HAL里的时间基准 */ HAL_ResumeTick(); /* 外设初始化 */ bsp_Init(); /* 创建任务 */ AppTaskCreate(); /* 创建任务间通信机制 */ AppObjCreate(); while (1) { /* 需要周期性处理的程序,对应裸机工程调用的SysTick_ISR */ bsp_ProPer1ms(); tx_thread_sleep(1); }}
7.11 实验例程
本章节配套了如下几个例子供大家移植参考:
裸机模板,方便大家添加ThreadX内核源码,含有GCC,IAR,MDK AC5和AC6四个版本工程。
- V7-3002_Threadx Kernel Template
ThreadX内核模板,用于大家移植GUIX的参考Demo,含有GCC,IAR,MDK AC5和AC6四个版本工程。
IAR,MDK AC5和AC6工程可以串口打印任务执行情况:按开发板的按键K1可以打印,波特率 115200,数据位 8,奇偶校验位无,停止位 1:
Embedded Studio(GCC)平台的串口打印是通过其调试组件SEGGER RTT做的串口打印,速度也非常快,打印效果如下:
展示里面有乱码是因为Embedded Studio不支持中文。
7.12 总结
本章节为大家讲解的内容涉及到的知识较多,信息量较大,部分知识点没有弄明白是没有关系的,但是一定要掌握ThreadX内核框架设计,掌握后再分析细节,事半功倍。
7.5 第3步,修改驱动初始化文件bsp.c(含MPU配置)
这个bsp.c文件比较重要,移植阶段,直接将我们移植好的模板内容复制过去即可,这里把相关的内容为大家做个说明。
7.5.1 函数System_Init
系统初始化,主要是MPU,Cache和系统时钟配置,需要在ThreadX初始化之前调用。
/*********************************************************************************************************** 函 数 名: System_Init* 功能说明: 系统初始化,主要是MPU,Cache和系统时钟配置* 形 参:无* 返 回 值: 无**********************************************************************************************************/void System_Init(void){ /* 配置MPU */ MPU_Config(); /* 使能L1 Cache */ CPU_CACHE_Enable(); /* STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟: - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。 - 设置NVIV优先级分组为4。 */ HAL_Init(); /* 配置系统时钟到400MHz - 切换使用HSE。 - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。 - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder并开启 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart();#endif}
7.5.2 函数bsp_Init
硬件外设的初始化,这个函数在RTX5的启动任务里面调用。
/*********************************************************************************************************** 函 数 名: bsp_Init* 功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次* 形 参: 无* 返 回 值: 无**********************************************************************************************************/void bsp_Init(void){ bsp_InitDWT(); /* 初始化DWT时钟周期计数器 */ bsp_InitKey(); /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */ bsp_InitUart(); /* 初始化串口 */ bsp_InitExtIO(); /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */ bsp_InitLed(); /* 初始化LED */ bsp_InitTimer(); /* 初始化滴答定时器 */}
7.5.3 函数SystemClock_Config
这个函数主要是完成系统时钟配置。
/*********************************************************************************************************** 函 数 名: SystemClock_Config* 功能说明: 初始化系统时钟* System Clock source = PLL (HSE)* SYSCLK(Hz) = 400000000 (CPU Clock)* HCLK(Hz) = 200000000 (AXI and AHBs Clock)* AHB Prescaler = 2* D1 APB3 Prescaler = 2 (APB3 Clock 100MHz)* D2 APB1 Prescaler = 2 (APB1 Clock 100MHz)* D2 APB2 Prescaler = 2 (APB2 Clock 100MHz)* D3 APB4 Prescaler = 2 (APB4 Clock 100MHz)* HSE Frequency(Hz) = 25000000* PLL_M = 5* PLL_N = 160* PLL_P = 2* PLL_Q = 4* PLL_R = 2* VDD(V) = 3.3* Flash Latency(WS) = 4* 形 参: 无* 返 回 值: 无**********************************************************************************************************/static void SystemClock_Config(void){ 省略未写 /* AXI SRAM的时钟是上电自动使能的,而D2域的SRAM1,SRAM2和SRAM3要单独使能 */ #if 1 __HAL_RCC_D2SRAM1_CLK_ENABLE(); __HAL_RCC_D2SRAM2_CLK_ENABLE(); __HAL_RCC_D2SRAM3_CLK_ENABLE(); __HAL_RCC_BKPRAM_CLKAM_ENABLE(); __HAL_RCC_D3SRAM1_CLKAM_ENABLE();#endif}
这里的RAM时钟初始化比较重要,这几个RAM的时钟都要单独使能。
7.5.4 函数MPU_Config
ThreadX例子默认采用AXI SRAM作为主RAM空间,因为空间比较大,方便我们后制作综合例子使用:
/*********************************************************************************************************** 函 数 名: MPU_Config* 功能说明: 配置MPU* 形 参: 无* 返 回 值: 无**********************************************************************************************************/static void MPU_Config( void ){ MPU_Region_InitTypeDef MPU_InitStruct; /* 禁止 MPU */ HAL_MPU_Disable();/* 最高性能,读Cache和写Cache都开启 */ #if 1 /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct);/* 最低性能,读Cache和写Cache都关闭 */ #else /* 配置AXI SRAM的MPU属性为NORMAL, NO Read allocate,NO Write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct);#endif /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x60000000; MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER1; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /* 使能 MPU */ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);}
7.5.5 函数bsp_RunPer10ms
这个函数里面默认有个按键扫描,如果大家移植的程序里面没有按键初始化,务必要把这个按键扫描函数注释掉。
/*********************************************************************************************************** 函 数 名: bsp_RunPer10ms* 功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求* 不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。* 形 参: 无* 返 回 值: 无**********************************************************************************************************/void bsp_RunPer10ms(void){ bsp_KeyScan10ms();}
7.6 第4步,更新bsp_timer.c和bsp.h文件
更新bsp_timer.c文件是因为此文件跟ThreadX都要使用滴答定时器,有冲突。所以大家直接将我们工程模板里面此文件覆盖移植的这个文件即可。
bsp.h文件里面要添加一个宏定义,因为bsp_timer.c文件里面做了些条件编译:
#define USE_THREADX 1
另外,bsp.h文件将大部分头文件都添加进来了,大家可以根据需要,用到那些头文件,使能那些,用不到的,可以注释掉。当然,不注释也是没问题的:
/* 通过取消注释或者添加注释的方式控制是否包含底层驱动模块 *///#include "bsp_msg.h"//#include "bsp_user_lib.h"#include "bsp_timer.h"#include "bsp_led.h"#include "bsp_key.h"#include "bsp_dwt.h"//#include "bsp_cpu_rtc.h"//#include "bsp_cpu_adc.h"//#include "bsp_cpu_dac.h"#include "bsp_uart_fifo.h"//#include "bsp_uart_gps.h"//#include "bsp_uart_esp8266.h"//#include "bsp_uart_sim800.h"//#include "bsp_spi_bus.h"//#include "bsp_spi_ad9833.h"//#include "bsp_spi_ads1256.h"//#include "bsp_spi_dac8501.h"//#include "bsp_spi_dac8562.h"//#include "bsp_spi_flash.h"//#include "bsp_spi_tm7705.h"//#include "bsp_spi_vs1053b.h"//#include "bsp_fmc_sdram.h"//#include "bsp_fmc_nand_flash.h"//#include "bsp_fmc_ad7606.h"//#include "bsp_fmc_oled.h"#include "bsp_fmc_io.h"//#include "bsp_i2c_gpio.h"//#include "bsp_i2c_bh1750.h"//#include "bsp_i2c_bmp085.h"//#include "bsp_i2c_eeprom_24xx.h"//#include "bsp_i2c_hmc5883l.h"//#include "bsp_i2c_mpu6050.h"//#include "bsp_i2c_si4730.h"//#include "bsp_i2c_wm8978.h"//#include "bsp_tft_h7.h"//#include "bsp_tft_429.h"//#include "bsp_tft_lcd.h"//#include "bsp_ts_touch.h"//#include "bsp_ts_ft5x06.h"//#include "bsp_ts_gt811.h"//#include "bsp_ts_gt911.h"//#include "bsp_ts_stmpe811.h"#include "bsp_beep.h"#include "bsp_tim_pwm.h"//#include "bsp_sdio_sd.h"//#include "bsp_dht11.h"//#include "bsp_ds18b20.h"//#include "bsp_ps2.h"//#include "bsp_ir_decode.h"//#include "bsp_camera.h"//#include "bsp_rs485_led.h"//#include "bsp_can.h"
7.7 第5步,修改文件stm3h7xx_it.c
删除此文件里面带的如下函数,ThreadX要使用,冲突了。
/** * @brief This function handles PendSVC exception. * @param None * @retval None */void PendSV_Handler(void){}
7.8 第6步,修改文件tx_initalize_low_level.s
主要是修改下面代码中置红部分的参数需要大家配置,即芯片主频和系统时钟节拍。400000000是系统时钟主频,1000对应的就是系统时钟节拍,这里1000就表示1000Hz。
.global _tx_thread_system_stack_ptr .global _tx_initialize_unused_memory .global __RAM_segment_used_end__ .global _tx_timer_interrupt .global __main .global __tx_SVCallHandler .global __tx_PendSVHandler .global _vectors .global __tx_NMIHandler @ NMI .global __tx_BadHandler @ HardFault .global __tx_SVCallHandler @ SVCall .global __tx_DBGHandler @ Monitor .global __tx_PendSVHandler @ PendSV .global __tx_SysTickHandler @ SysTick .global __tx_IntHandler @ Int 0@@SYSTEM_CLOCK = 400000000SYSTICK_CYCLES = ((SYSTEM_CLOCK / 1000) -1)
7.9 第7步,ThreadX配置文件tx_user.h
ThreadX内核相关的配置,已经全部整理到了这个文件中,并且做中文注释,大家可以更新需要使能宏定义。
7.10 第8步,添加应用程序
应用程序比较简单,大家可以直接复制本章教程配置例子的main.c文件中的内容到自己工程里面测试。主要创建了如下几个任务:
App Task Start任务 :启动任务,这里用作BSP驱动包处理。
App Task MspPro任务 :消息处理,这里用作浮点数串口打印。
App Task UserIF任务 :按键消息处理。
App Task COM任务 :浮点数串口打印。
App Task STAT任务 :统计任务
App Task IDLE任务 :空闲任务
System Timer Thread任务:系统定时器任务
任务栈大小和任务控制块定义如下:
/*********************************************************************************************************** 任务优先级,数值越小优先级越高**********************************************************************************************************/#define APP_CFG_TASK_START_PRIO 2u#define APP_CFG_TASK_MsgPro_PRIO 3u#define APP_CFG_TASK_USER_IF_PRIO 4u#define APP_CFG_TASK_COM_PRIO 5u#define APP_CFG_TASK_STAT_PRIO 30u#define APP_CFG_TASK_IDLE_PRIO 31u/*********************************************************************************************************** 任务栈大小,单位字节**********************************************************************************************************/#define APP_CFG_TASK_START_STK_SIZE 4096u#define APP_CFG_TASK_MsgPro_STK_SIZE 4096u#define APP_CFG_TASK_COM_STK_SIZE 4096u#define APP_CFG_TASK_USER_IF_STK_SIZE 4096u#define APP_CFG_TASK_IDLE_STK_SIZE 1024u#define APP_CFG_TASK_STAT_STK_SIZE 1024u/*********************************************************************************************************** 静态全局变量**********************************************************************************************************/ static TX_THREAD AppTaskStartTCB;static uint64_t AppTaskStartStk[APP_CFG_TASK_START_STK_SIZE/8];static TX_THREAD AppTaskMsgProTCB;static uint64_t AppTaskMsgProStk[APP_CFG_TASK_MsgPro_STK_SIZE/8];static TX_THREAD AppTaskCOMTCB;static uint64_t AppTaskCOMStk[APP_CFG_TASK_COM_STK_SIZE/8];static TX_THREAD AppTaskUserIFTCB;static uint64_t AppTaskUserIFStk[APP_CFG_TASK_USER_IF_STK_SIZE/8];static TX_THREAD AppTaskIdleTCB;static uint64_t AppTaskIdleStk[APP_CFG_TASK_IDLE_STK_SIZE/8];static TX_THREAD AppTaskStatTCB;static uint64_t AppTaskStatStk[APP_CFG_TASK_STAT_STK_SIZE/8];
优先创建的启动任务,统计任务和空闲任务:
/*********************************************************************************************************** 函 数 名: tx_application_define* 功能说明: ThreadX专用的任务创建,通信组件创建函数* 形 参: first_unused_memory 未使用的地址空间* 返 回 值: 无**********************************************************************************************************/void tx_application_define(void *first_unused_memory){ /* 如果实现任务CPU利用率统计的话,此函数仅用于实现启动任务,统计任务和空闲任务,其它任务在函数 AppTaskCreate里面创建。 */ /**************创建启动任务*********************/ tx_thread_create(&AppTaskStartTCB, /* 任务控制块地址 */ "App Task Start", /* 任务名 */ AppTaskStart, /* 启动任务函数地址 */ 0, /* 传递给任务的参数 */ &AppTaskStartStk[0], /* 堆栈基地址 */ APP_CFG_TASK_START_STK_SIZE, /* 堆栈空间大小 */ APP_CFG_TASK_START_PRIO, /* 任务优先级*/ APP_CFG_TASK_START_PRIO, /* 任务抢占阀值 */ TX_NO_TIME_SLICE, /* 不开启时间片 */ TX_AUTO_START); /* 创建后立即启动 */ /**************创建统计任务*********************/ tx_thread_create(&AppTaskStatTCB, /* 任务控制块地址 */ "App Task STAT", /* 任务名 */ AppTaskStat, /* 启动任务函数地址 */ 0, /* 传递给任务的参数 */ &AppTaskStatStk[0], /* 堆栈基地址 */ APP_CFG_TASK_STAT_STK_SIZE, /* 堆栈空间大小 */ APP_CFG_TASK_STAT_PRIO, /* 任务优先级*/ APP_CFG_TASK_STAT_PRIO, /* 任务抢占阀值 */ TX_NO_TIME_SLICE, /* 不开启时间片 */ TX_AUTO_START); /* 创建后立即启动 */ /**************创建空闲任务*********************/ tx_thread_create(&AppTaskIdleTCB, /* 任务控制块地址 */ "App Task IDLE", /* 任务名 */ AppTaskIDLE, /* 启动任务函数地址 */ 0, /* 传递给任务的参数 */ &AppTaskIdleStk[0], /* 堆栈基地址 */ APP_CFG_TASK_IDLE_STK_SIZE, /* 堆栈空间大小 */ APP_CFG_TASK_IDLE_PRIO, /* 任务优先级*/ APP_CFG_TASK_IDLE_PRIO, /* 任务抢占阀值 */ TX_NO_TIME_SLICE, /* 不开启时间片 */ TX_AUTO_START); /* 创建后立即启动 */ }
在启动任务中优先执行一次任务统计,然后创建其它任务:
/*********************************************************************************************************** 函 数 名: AppTaskStart* 功能说明: 启动任务。* 形 参: thread_input 是在创建该任务时传递的形参* 返 回 值: 无 优 先 级: 2**********************************************************************************************************/static void AppTaskStart (ULONG thread_input){ (void)thread_input; /* 先挂起定时器组 */#ifndef TX_NO_TIMER tx_thread_suspend(&_tx_timer_thread);#endif /* 优先执行任务统计 */ OSStatInit(); /* 恢复定时器组 */#ifndef TX_NO_TIMER tx_thread_resume(&_tx_timer_thread);#endif /* 内核开启后,恢复HAL里的时间基准 */ HAL_ResumeTick(); /* 外设初始化 */ bsp_Init(); /* 创建任务 */ AppTaskCreate(); /* 创建任务间通信机制 */ AppObjCreate(); while (1) { /* 需要周期性处理的程序,对应裸机工程调用的SysTick_ISR */ bsp_ProPer1ms(); tx_thread_sleep(1); }}
7.11 实验例程
本章节配套了如下几个例子供大家移植参考:
裸机模板,方便大家添加ThreadX内核源码,含有GCC,IAR,MDK AC5和AC6四个版本工程。
- V7-3002_Threadx Kernel Template
ThreadX内核模板,用于大家移植GUIX的参考Demo,含有GCC,IAR,MDK AC5和AC6四个版本工程。
IAR,MDK AC5和AC6工程可以串口打印任务执行情况:按开发板的按键K1可以打印,波特率 115200,数据位 8,奇偶校验位无,停止位 1:
Embedded Studio(GCC)平台的串口打印是通过其调试组件SEGGER RTT做的串口打印,速度也非常快,打印效果如下:
展示里面有乱码是因为Embedded Studio不支持中文。
7.12 总结
本章节为大家讲解的内容涉及到的知识较多,信息量较大,部分知识点没有弄明白是没有关系的,但是一定要掌握ThreadX内核框架设计,掌握后再分析细节,事半功倍。
举报