目录
error:cannot all be FIRST/LAST
写在前面
ThreadX 是由 Express Logic 公司开发的实时操作系统。该公司已被微软收购,并且已经开源,就这点来说微软这波开源操作有点狠,有点打压其它RTOS全家桶的意思,有点针对亚马逊的FreeRTOS和国内的RTT,而且其全家桶一旦推广起来,对其他RTOS和中间件几乎是毁灭性打击。
为什么这么说呢?
首先,ThreadX几乎是小型RTOS的巅峰之作,不管你之前用的那个OS,如果有精力建议还是学习下。62亿电子设备采用,是其它RTOS无法逾越的高山。而且还拥有大量的不同行业的认证。
虽然ThreadX很强大,但是本次例程结束后将会开始RTT的移植,做为中国人,必须支持国产。
闲话少说,开坛讲法。
STM32CubeMX
本次使用的CubeMX版本为6.4.0,MCU为ST的STM32F103C8T6,flash为64K,ram为20K,。
CubeMX中选择该芯片
不贴图了,大家应该都会。
时钟配置
选择外部时钟
系统配置
1、采用swd的串行调试;
2、由于ThreadX需要使用Systick,因此hal库的时基选择为TIM1;
中断配置
同样由于ThreadX需要使用PendSV_Handler,应该此处将该中断去掉;
引脚配置
PA0作为呼吸灯
配置串口为9600
生成工程
设置工程名和路径,选择MDK,完成相关配置,点击生成工程
工程目录
其中有些文件是git需要的文件以及从正点原子拿过来的keilkill文件。至此,CubeMX操作完成。
验证
工程生成后在移植前最好在mdk编译一遍,确保工程是没问题的,我是写了个呼吸灯和串口的程序。
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* Enter the ThreadX kernel. */
tx_kernel_enter();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);
HAL_UART_Transmit(&huart1,TxBuf,sizeof(TxBuf),100);
}
/* USER CODE END 3 */
}
下载源码
首先到github上下载threadx的源码,建议使用git,下载zip的话非常慢,而且容易失败,点击链接threadx下载。下载后目录如下,我们移植需要以下2个文件夹的内容
1:源码;
2:启动文件,启动文件内内容较多,选择的mcu为m3内核,使用mdk软件,因此需要的是threadx-master\ports\cortex_m33\keil下的文件
将上述2个文件夹复制到工程目录中,我放在了Drivers目录中
系统移植
在mdk中添加相应的文件
添加Source下在所有文件
添加port下的文件
最终的工程结构如下
移植编译
移植完成后我们先来编译下,发现error:
error:SysTick_Handler重复定义
此时找到文件Core\Src\stm32f1xx_it.c 将里面的SysTick_Handler整个注释掉。
systick明明选择了TIM1,不知道为什么还会重复定义,有知道的可以留言告诉我。
error:cannot all be FIRST/LAST
居然只有几个碰到类似问题。看了下http://www.amobbs.com/thread-3308421-1-1.html 即
发现是startup文件的问题,原来文件tx_initialize_low_level.s接管了xxx.S启动文件已经实现的功能。从ThreadX的角度来说,他们保证了自己发布新软件包的独立性,直接面向内核出,不需要再面向各个厂家单独出一个系列,否则工作量就超级大,各大厂家加起来有几千个上万的启动文件。要他们专门去做适配,基本做不到的。所以他们接管xxxx.S启动文件,但他们接管的文件仅写了几个重要的中断向量表入口。这就给让移植的人非常不方便。所以我们做的例子不接管XXX.S启动文件了,方便大家移植,直接将里面修正后的代码覆盖当前tx_initialize_low_level.s中的代码即可。ThreadX的MDK AC5版底层移植修正版
下面的代码跟链接中的一样
;/**************************************************************************/
;/* */
;/* 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
;
;
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}
; BL _tx_execution_isr_enter
BL _tx_timer_interrupt
; BL _tx_execution_isr_exit
POP {r0, lr}
BX LR
; }
EXPORT __tx_NMIHandler
__tx_NMIHandler
B __tx_NMIHandler
EXPORT __tx_DBGHandler
__tx_DBGHandler
B __tx_DBGHandler
ALIGN
LTORG
END
系统节拍修改
修改tx_initialize_low_level.s中的系统节拍,mcu为72M,系统节拍设置为1000,即1ms,重新编译,0error,0警告,编译完成。
SYSTEM_CLOCK EQU 72000000
SYSTICK_CYCLES EQU ((SYSTEM_CLOCK / 1000) -1)
系统运行
编译完成之后,我们还需要写一个示例程序,来让系统跑起来,这部分比较简单,可以参考\Drivers\ThreadX\ports\cortex_m3\keil\example_build文件夹里的sample_threadx.c的例程,我这里直接放源码了,编译下载后呼吸灯正常,串口发送数据正常。移植完成。
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "tx_api.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "tx_api.h"
#include "SEGGER_RTT.h"
/* USER CODE END Includes */
#define DEMO_STACK_SIZE 1000
#define DEMO_QUEUE_SIZE 10
TX_THREAD thread_0;
UCHAR thread_0_stack[DEMO_STACK_SIZE];
void thread_0_entry(ULONG thread_input);
VOID tx_application_define(void *first_unused_memory)
{
/* Create the main thread. */
tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0,
thread_0_stack, DEMO_STACK_SIZE,
1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);
}
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* Enter the ThreadX kernel. */
tx_kernel_enter();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
}
}
/* USER CODE BEGIN 4 */
void thread_0_entry(ULONG thread_input)
{
//UINT status = 8;
/* This thread simply sits in while-forever-sleep loop. */
while(1)
{
tx_thread_sleep(500);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);
tx_thread_sleep(500);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);
HAL_UART_Transmit(&huart1,TxBuf,sizeof(TxBuf),100);
}
}