前言
目前网上对于ThreadX+NetXDUO的移植资料比较少,资料较为详细的只有安富莱的教程,但是在我移植过程中有一些细节不是讲的很清楚,所以还是花费了挺长的时间移植,为了让后面的朋友能有一些参考,故编写了该移植教程,希望能够帮助到大家。
因为目前直接用CUBEMX选择ThreadX的RTOS,是存在问题的,在网上查阅资料用CUBEIDE打开的CUBEMX才能够配置,所以本编使用的是CUBEMX建立工程,配置引脚,然后通过获取源码手动加入MDK工程的方式移植ThreadX+NetXDUO,本编先讲移植ThreadX的步骤
移植基于cubemx版本Version 6.4.0、MDK5.32其他版本可能有差异
安富莱关于ThreadX的教程可以参考 https://www.armbbs.cn/forum.php?mod=viewthread&tid=99514
1、CubeMx配置
打开CubeMX,选择完芯片后:
首先个工程命名,选择生成工程的IDE,配置堆栈大一点
工程配置
使能CPU ICache\CPU DCache,(M7内核相对于其他常见的学习板不同,是有缓存的,而且芯片内部有两段内存是不允许外设访问的)后面网口的使用对于这个配置会有要求,目前先使能缓存,后面在配置MPU
配置DEBUG
配置外部晶振,如图
时钟配置
根据自己的板子配置串口及波特率,使能中断,方便打印调试信息
至此基本配置完成,点击GENERATE CODE生成工程
2、获取ThreadX源码,并加入工程
1)通过https://github.com/azure-rtos/threadx,官方获取最新源码
2)安富莱提供的示例代码中移植源码
获取的源码目录如图,目前我们只需要common\ports目录中的文件,在我们新建的工程中新建Threadx目录,将源码放入该目录中
如图,添加完成后打开工程,将文件添加入工程中
新建对应工程目录
将common/src目录中的文件全部添加进工程中
E:\git\H743_ThreadX_NetXDUO\ThradX\ports\cortex_m7\ac5\src目录中的文件全部加入工程,点击ok
添加tx_initialize_low_level.s文件,在E:\git\H743_ThreadX_NetXDUO\ThradX\ports\cortex_m7\ac5\example_build目录有该源文件,需要对其部分修改,我这边直接复制安富莱修改好后的文件
;/**************************************************************************/
;/* */
;/* Copyright (c) Microsoft Corporation. All rights reserved. */
;/* */
;/* This software is licensed under the Microsoft Software License */
;/* Terms for Microsoft Azure RTOS. Full text of the license can be */
;/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
;/* and in the root directory of this software. */
;/* */
;/**************************************************************************/
;
;
;/**************************************************************************/
;/**************************************************************************/
;/** */
;/** ThreadX Component */
;/** */
;/** Initialize */
;/** */
;/**************************************************************************/
;/**************************************************************************/
;
;#define TX_SOURCE_CODE
;
;
;/* Include necessary system files. */
;
;#include "tx_api.h"
;#include "tx_initialize.h"
;#include "tx_thread.h"
;#include "tx_timer.h"
;
;
IMPORT _tx_thread_system_stack_ptr
IMPORT _tx_initialize_unused_memory
IMPORT _tx_thread_context_save
IMPORT _tx_thread_context_restore
IMPORT _tx_timer_interrupt
IMPORT __main
IMPORT __Vectors
IMPORT __initial_sp
IF :DEF:TX_ENABLE_EXECUTION_CHANGE_NOTIFY
IMPORT _tx_execution_isr_exit
IMPORT _tx_execution_isr_enter
ENDIF
;
;
SYSTEM_CLOCK EQU 400000000
SYSTICK_CYCLES EQU ((SYSTEM_CLOCK / 1000) -1)
;
;
;
;
AREA ||.text||, CODE, READONLY
PRESERVE8
;/**************************************************************************/
;/* */
;/* FUNCTION RELEASE */
;/* */
;/* _tx_initialize_low_level Cortex-M7/AC5 */
;/* 6.0.1 */
;/* AUTHOR */
;/* */
;/* William E. Lamie, Microsoft Corporation */
;/* */
;/* DESCRIPTION */
;/* */
;/* This function is responsible for any low-level processor */
;/* initialization, including setting up interrupt vectors, setting */
;/* up a periodic timer interrupt source, saving the system stack */
;/* pointer for use in ISR processing later, and finding the first */
;/* available RAM memory address for tx_application_define. */
;/* */
;/* INPUT */
;/* */
;/* None */
;/* */
;/* OUTPUT */
;/* */
;/* None */
;/* */
;/* CALLS */
;/* */
;/* None */
;/* */
;/* CALLED BY */
;/* */
;/* _tx_initialize_kernel_enter ThreadX entry function */
;/* */
;/* RELEASE HISTORY */
;/* */
;/* DATE NAME DESCRIPTION */
;/* */
;/* 06-30-2020 William E. Lamie Initial Version 6.0.1 */
;/* */
;/**************************************************************************/
;VOID _tx_initialize_low_level(VOID)
;{
EXPORT _tx_initialize_low_level
_tx_initialize_low_level
;
; /* Disable interrupts during ThreadX initialization. */
;
CPSID i
;
; /* Set base of available memory to end of non-initialised RAM area. */
;
LDR r0, =_tx_initialize_unused_memory ; Build address of unused memory pointer
LDR r1, =__initial_sp ; Build first free address
ADD r1, r1, #4 ;
STR r1, [r0] ; Setup first unused memory pointer
;
; /* Setup Vector Table Offset Register. */
;
MOV r0, #0xE000E000 ; Build address of NVIC registers
LDR r1, =__Vectors ; Pickup address of vector table
STR r1, [r0, #0xD08] ; Set vector table address
; /* Enable the cycle count register. */
LDR r0, =0xE0001000 ; Build address of DWT register
LDR r1, [r0] ; Pickup the current value
ORR r1, r1, #1 ; Set the CYCCNTENA bit
STR r1, [r0] ; Enable the cycle count register
; /* Set system stack pointer from vector value. */
LDR r0, =_tx_thread_system_stack_ptr ; Build address of system stack pointer
LDR r1, =__Vectors ; Pickup address of vector table
LDR r1, [r1] ; Pickup reset stack pointer
STR r1, [r0] ; Save system stack pointer
;
; /* Configure SysTick. */
;
MOV r0, #0xE000E000 ; Build address of NVIC registers
LDR r1, =SYSTICK_CYCLES
STR r1, [r0, #0x14] ; Setup SysTick Reload Value
MOV r1, #0x7 ; Build SysTick Control Enable Value
STR r1, [r0, #0x10] ; Setup SysTick Control
;
; /* Configure handler priorities. */
;
LDR r1, =0x00000000 ; Rsrv, UsgF, BusF, MemM
STR r1, [r0, #0xD18] ; Setup System Handlers 4-7 Priority Registers
LDR r1, =0xFF000000 ; SVCl, Rsrv, Rsrv, Rsrv
STR r1, [r0, #0xD1C] ; Setup System Handlers 8-11 Priority Registers
; Note: SVC must be lowest priority, which is 0xFF
LDR r1, =0x40FF0000 ; SysT, PnSV, Rsrv, DbgM
STR r1, [r0, #0xD20] ; Setup System Handlers 12-15 Priority Registers
; Note: PnSV must be lowest priority, which is 0xFF
;
; /* Return to caller. */
;
BX lr
;}
;
;
;/* Define shells for each of the unused vectors. */
;
EXPORT __tx_BadHandler
__tx_BadHandler
B __tx_BadHandler
EXPORT __tx_SVCallHandler
__tx_SVCallHandler
B __tx_SVCallHandler
EXPORT __tx_IntHandler
__tx_IntHandler
; VOID InterruptHandler (VOID)
; {
PUSH {r0, lr}
; /* Do interrupt handler work here */
; /* .... */
POP {r0, lr}
BX LR
; }
EXPORT __tx_SysTickHandler
EXPORT SysTick_Handler
__tx_SysTickHandler
SysTick_Handler
; VOID TimerInterruptHandler (VOID)
; {
;
PUSH {r0, lr}
IF :DEF:TX_ENABLE_EXECUTION_CHANGE_NOTIFY
BL _tx_execution_isr_enter
ENDIF
BL _tx_timer_interrupt
IF :DEF:TX_ENABLE_EXECUTION_CHANGE_NOTIFY
BL _tx_execution_isr_exit
ENDIF
POP {r0, lr}
BX LR
; }
EXPORT __tx_NMIHandler
__tx_NMIHandler
B __tx_NMIHandler
EXPORT __tx_DBGHandler
__tx_DBGHandler
B __tx_DBGHandler
ALIGN
LTORG
END
大家可以和源码对比,我这边就不讲解了
将其添加入工程中,这个汇编文件里面有个重要参数需要大家配置,即芯片主频和系统时钟节拍。
SYSTEM_CLOCK EQU 400000000
SYSTICK_CYCLES EQU ((SYSTEM_CLOCK / 1000) -1)
400000000 是系统时钟主频,1000 对应的就是系统时钟节拍,这里 1000 就表示 1000Hz。
添加头文件目录
3、工程配置
勾选使用微库
取消勾选GNU extenions
如图添加,–cpreproc
屏蔽PendSV_Handler、SysTick_Handler,因为ThreadX内核已定义,需屏蔽掉
添加串口重映射,移植printf
#include <stdio.h>
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&hlpuart1, (unsigned char *)&ch, 1, 1000);
return 1;
}
int fgetc(FILE *f)
{
return 0;
}
添加完成后,编译工程
tx_application_define ().函数为Threadx留给用户创建线程的,待会创建线程时使用
4、线程创建使用
新建Thread文件夹,添加线程文件如图
添加进入工程
#include "main.h"
#include "thread.h"
/*
*********************************************************************************************************
* 任务优先级,数值越小优先级越高
*********************************************************************************************************
*/
#define APP_CFG_TASK_START_PRIO 2u
/*
*********************************************************************************************************
* 任务栈大小,单位字节
*********************************************************************************************************
*/
#define APP_CFG_TASK_START_STK_SIZE 4096u
/*
*********************************************************************************************************
* 静态全局变量
*********************************************************************************************************
*/
static TX_THREAD AppTaskStartTCB;
static uint64_t AppTaskStartStk[APP_CFG_TASK_START_STK_SIZE/8];
/*
*********************************************************************************************************
* 函数声明
*********************************************************************************************************
*/
static void AppTaskStart (ULONG thread_input);
/*
*********************************************************************************************************
* 函 数 名: tx_application_define
* 功能说明: ThreadX专用的任务创建,通信组件创建函数
* 形 参: first_unused_memory 未使用的地址空间
* 返 回 值: 无
*********************************************************************************************************
*/
void tx_application_define(void *first_unused_memory)
{
/*
此函数仅用于实现启动任务,其它任务在函数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); /* 创建后立即启动 */
}
/*
*********************************************************************************************************
* 函 数 名: AppTaskStart
* 功能说明: 启动任务。
* 形 参: thread_input 是在创建该任务时传递的形参
* 返 回 值: 无
优 先 级: 2
*********************************************************************************************************
*/
static void AppTaskStart (ULONG thread_input)
{
(void)thread_input;
/* 内核开启后,恢复HAL里的时间基准 */
HAL_ResumeTick();
while (1)
{
printf("AppTaskStart RUN");
tx_thread_sleep(1000);
}
}
并使用tx_application_define创建启动线程,打印串口信息
添加路径包含,thread的头文件路径
#ifndef __THREAD_H__
#define __THREAD_H__
/*
*********************************************************************************************************
* 标准库
*********************************************************************************************************
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdint.h>
/*
*********************************************************************************************************
* OS
*********************************************************************************************************
*/
#include "tx_api.h"
#include "tx_timer.h"
#endif
HAL_SuspendTick();
tx_kernel_enter();
在main.c文件中添加启动内核代码后,编译工程
5、烧录验证
线程间隔1s打印串口信息,自此移植Threadx,完成