本帖最后由 Liyj336 于 2020-11-28 09:28 编辑
这一次测评我将一步步分享RT-Thread Nano移植到HC32F460的过程。主要分三步讲解工程建立、RT-Thread移植、添加UART控制台和Finsh。
一、工程建立
1、首先我们需要建立程序工程文件,下面的工程我是在HC32F460官方例程基础上建立的。在一个总的工程文件夹下面,建立以下工程文件夹。红色划线部分文件夹可以先忽略。(可以按照自己习惯建立工程)
BSP:打算存放MCU外部外设部分程序。
documents:从官方例程中复制过来,存放HC32F460官方库使用说明。
driver:从官方例程复制过来,存放HC32F460的驱动库。
mcu:从官方例程复制过来,MDK工程需要里面的文件。保留下面两个文件夹就行
MDK:存放MDK工程文件。我从例程icg_hrc_osc_hw_startup复制过来的,该例程跟Systick有关。
source:从官方移植过来的,存放main.c和其他一些程序文件。 2、打开MDK目录下的icg_hrc_osc_hw_startup工程。从新加入project下的文件。忽略打叉或者划线的文件,从刚建立的工程中加入以下文件。
3、新建led.c和led.h文件放入到BSP下的src和inc文件夹内,并把led.c文件加入到MDK工程中。
led.c代码如下,只是一个简单的点灯代码,用来验证程序。
#include <led.h>
void LED_Init(void)
{
/**************************************************************************/
stc_port_init_t stcPortInit;
/* configure structure initialization */
MEM_ZERO_STRUCT(stcPortInit);
/* LED0 Port/Pin initialization */
LED0_OFF();
stcPortInit.enPinMode = Pin_Mode_Out;
PORT_Init(LED0_PORT, LED0_PIN, &stcPortInit);
}
led.h
#ifndef __LED_H
#define __LED_H
#include "hc32_ddl.h"
/*******************************************************************************
* Local type definitions ('typedef')
******************************************************************************/
/*******************************************************************************
* Local pre-processor symbols/macros ('#define')
******************************************************************************/
/* LED0 Port/Pin definition */
#define LED0_PORT (PortE)
#define LED0_PIN (Pin06)
#define LED0_ON() (PORT_SetBits(LED0_PORT, LED0_PIN))
#define LED0_OFF() (PORT_ResetBits(LED0_PORT, LED0_PIN))
#define LED0_TOGGLE() (PORT_Toggle(LED0_PORT, LED0_PIN))
void LED_Init(void);
#endif /*__LED_H*/
4、接下来修改HDSC_HC32F46x.SFR文件路径,如下图红框内,其实我也不知道这个文件有什么作用。
然后从新加入头文件路径。
二、RT-Thread Nano移植
1、首先需要获取RT-Thread Nano源码,RT-Thread Nano是一个精简版的RT-Thread,去除很多板子的BSP和很多功能组件,组件上只保留了Finsh,当然RTOS的核心功能会保留着。RT-Thread Nano源码下载地址:https://www.rt-thread.org/page/download.html,目前最新版为V3.1.3。 获取到的RT-Thread文件夹目录如下。
2、在工程文件夹下,加入RT-thread文件夹,并在下RT-thread文件夹下加入Include、Port、Source文件夹,存放RT-thread所需文件。
3、将路径RT-Thread/3.1.3/include文件夹下的内容复制到新建的工程目录下的RT-thread/Include文件夹
将路径RT-Thread/3.1.3/components/finsh文件夹复制到新建的RT-thread/Include文件夹
4、将路径RT-Thread/3.1.3/source文件夹下面的 rtconfig.h和board.c文件复制到到工程目录下的source文件夹。
5、将路径RT-Thread/3.1.3/libcpu/arm/cortex-m3文件夹下面的 rtconfig.h和board.c文件复制到到工程目录下的port文件夹。
6、打开hc32f46x_interrupts.c把SysTick_Handler、PendSV_Handler、HardFault_Handler这三个中断函数注释掉,要不然编译会出现重定义错误。
7、修改RT-thread配置文件rtconfig.h头文件进行RTT的配置。里面是通过修改宏来实现的,可以根据实际情况进行修改。这里我不进行该文件修改,直接使用。rtconfig.h文件一些配置宏如下所示。
8、在 MDK source 目录下新建一个 board.h 头文件,用来包含HC32F460固件驱动库和BSP 相关的头文件。
board.h代码如下#ifndef __BOARD_H
#define __BOARD_H
#include "hc32_ddl.h"
#include "led.h"
#include "uart.h"
void rt_hw_board_init(void);
void SysTick_Handler(void);
#endif /*__BOARD_H*/
9、修改board.c文件。board.c里面主要完成RT-thread的初始化,并对外设硬件完成初始化。
/*
* Copyright (c) 2006-2019, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2017-07-24 Tanek the first version
* 2018-11-12 Ernest Chen modify copyright
*/
#include <board.h>
#include <stdint.h>
#include <rthw.h>
#include <rtthread.h>
static void SysTick_Init(void);
#if 0
#define _SCB_BASE (0xE000E010UL)
#define _SYSTICK_CTRL (*(rt_uint32_t *)(_SCB_BASE + 0x0))
#define _SYSTICK_LOAD (*(rt_uint32_t *)(_SCB_BASE + 0x4))
#define _SYSTICK_VAL (*(rt_uint32_t *)(_SCB_BASE + 0x8))
#define _SYSTICK_CALIB (*(rt_uint32_t *)(_SCB_BASE + 0xC))
#define _SYSTICK_PRI (*(rt_uint8_t *)(0xE000ED23UL))
// Updates the variable SystemCoreClock and must be called
// whenever the core clock is changed during program execution.
extern void SystemCoreClockUpdate(void);
// Holds the system core clock, which is the system clock
// frequency supplied to the SysTick timer and the processor
// core clock.
extern uint32_t SystemCoreClock;
static uint32_t _SysTick_Config(rt_uint32_t ticks)
{
if ((ticks - 1) > 0xFFFFFF)
{
return 1;
}
_SYSTICK_LOAD = ticks - 1;
_SYSTICK_PRI = 0xFF;
_SYSTICK_VAL = 0;
_SYSTICK_CTRL = 0x07;
return 0;
}
#endif
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
#define RT_HEAP_SIZE 1024
static uint32_t rt_heap[RT_HEAP_SIZE]; // heap default size: 4K(1024 * 4)
RT_WEAK void *rt_heap_begin_get(void)
{
return rt_heap;
}
RT_WEAK void *rt_heap_end_get(void)
{
return rt_heap + RT_HEAP_SIZE;
}
#endif
/**
* This function will initial your board.
*/
void rt_hw_board_init()
{
/* System Clock Update */
//SystemCoreClockUpdate();
/* System Tick Configuration */
// _SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
/* Call components board initial (use INIT_BOARD_EXPORT()) */
CLK_SetSysClkSource(ClkSysSrcHRC); //设置时钟源
SysTick_Init(); //Systick时钟初始化
LED_Init(); //外设LED初始化
LED0_ON();
#ifdef RT_USING_COMPONENTS_INIT
rt_components_board_init();
#endif
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#endif
}
void SysTick_Handler(void)
{
/* enter interrupt */
rt_interrupt_enter();
rt_tick_increase();
/* leave interrupt */
rt_interrupt_leave();
}
static void SysTick_Init(void)
{
stc_clk_freq_t stcClkFreq;
/* configure structure initialization */
MEM_ZERO_STRUCT(stcClkFreq);
/* Config 1 sec trigger interrupt*/
CLK_GetClockFreq(&stcClkFreq);
SysTick_Config(stcClkFreq.sysclkFreq/RT_TICK_PER_SECOND);
}
10、到这里RT_thread移植已经完成了,我们还是写个点灯程序测试一下。修改main.c函数
#include "hc32_ddl.h"
#include "led.h"
#include "board.h"
#include "rtthread.h"
/*定义线程控制块*/
static struct rt_thread led1_thread;
/*定义线程空栈时要求RT_ALIGN_SIZE个字节对齐*/
ALIGN(RT_ALIGN_SIZE)
/*定义线程栈*/
static rt_uint8_t rt_led1_thread_stack[1024];
/*函数声明*/
static void led1_thread_entry(void * parameter);
/*******************************************************************************
** \brief main function for ICG HRC function
**
** \param [in] None
**
** \retval int32_t Return value, if needed
**
******************************************************************************/
int32_t main(void)
{
rt_thread_init(&led1_thread,
"led1",
led1_thread_entry,
RT_NULL,
&rt_led1_thread_stack,
sizeof(rt_led1_thread_stack),
3,
20);
rt_thread_startup(&led1_thread);
}
static void led1_thread_entry(void * parameter)
{
while(1)
{
LED0_ON();
rt_thread_delay(500);
LED0_OFF();
rt_thread_delay(500);
}
}
11、好了,编译下载可以看看LED灯正常闪烁。
三、添加UART控制台和Finsh组件
在 RT-Thread Nano 上添加 UART 控制台打印功能后,就可以在代码中使用 RT-Thread 提供的打印函数 rt_kprintf() 进行信息打印。Finsh组件是 RT-Thread 的命令行组件(shell),提供一套供用户在命令行调用的操作接口,主要用于调试或查看系统信息。
1、在MDK工程中增加RTT_Component目录,并在目录下添加RT-thread/Include/finsh目录下的cmd.c、msh.c、shell.c文件
2、在MDK工程source目录下增加uart.c和uart.h文件。
3、uart.c包含对uart的初始化和对rt_hw_console_output、rt_hw_console_getchar函数的实现。uart.c代码如下:
#include <uart.h>
#include "rtthread.h"
void Uart_Init(void)
{
uint16_t u16RxData;
en_result_t enRet = Ok;
uint32_t u32Fcg1Periph = PWC_FCG1_PERIPH_USART1 | PWC_FCG1_PERIPH_USART2 | \
PWC_FCG1_PERIPH_USART3 | PWC_FCG1_PERIPH_USART4;
const stc_usart_uart_init_t stcInitCfg = {
UsartIntClkCkNoOutput,
UsartClkDiv_1,
UsartDataBits8,
UsartDataLsbFirst,
UsartOneStopBit,
UsartParityNone,
UsartSamleBit8,
UsartStartBitFallEdge,
UsartRtsEnable,
};
/* Enable peripheral clock */
PWC_Fcg1PeriphClockCmd(u32Fcg1Periph, Enable);
/* Initialize USART IO */
PORT_SetFunc(USART_RX_PORT, USART_RX_PIN, USART_RX_FUNC, Disable);
PORT_SetFunc(USART_TX_PORT, USART_TX_PIN, USART_TX_FUNC, Disable);
/* Initialize UART */
USART_UART_Init(USART_CH, &stcInitCfg);
/* Set baudrate */
USART_SetBaudrate(USART_CH, USART_BAUDRATE);
/*Enable RX && TX function*/
USART_FuncCmd(USART_CH, UsartRx, Enable);
USART_FuncCmd(USART_CH, UsartTx, Enable);
}
INIT_BOARD_EXPORT(Uart_Init);
void rt_hw_console_output(const char *str)
{
rt_size_t i = 0, size = 0;
char a = '\r';
size = rt_strlen(str);
for (i = 0; i < size; i++)
{
if (*(str + i) == '\n')
{
while (Reset == USART_GetStatus(USART_CH, UsartTxEmpty)) /* Warit Tx data register empty */
{
}
USART_SendData(USART_CH,(uint16_t)a);
}
while (Reset == USART_GetStatus(USART_CH, UsartTxEmpty)) /* Warit Tx data register empty */
{
}
USART_SendData(USART_CH,(*(str + i)));
}
}
char rt_hw_console_getchar(void)
{
int ch = -1;
if (Set == USART_GetStatus(USART_CH, UsartRxNoEmpty))
{
ch = USART_RecData(USART_CH);
}
return ch;
}
uart.h主要对uart.c内定义的函数进行声明。uart.h代码如下:
#ifndef __UART_H
#define __UART_H
#include "hc32_ddl.h"
/*******************************************************************************
* Local pre-processor symbols/macros ('#define')
******************************************************************************/
/* USART channel definition */
#define USART_CH (M4_USART3)
/* USART baudrate definition */
#define USART_BAUDRATE (115200ul)
/* USART RX Port/Pin definition */
#define USART_RX_PORT (PortE)
#define USART_RX_PIN (Pin04)
#define USART_RX_FUNC (Func_Usart3_Rx)
/* USART TX Port/Pin definition */
#define USART_TX_PORT (PortE)
#define USART_TX_PIN (Pin05)
#define USART_TX_FUNC (Func_Usart3_Tx)
void Uart_Init(void);
void rt_hw_console_output(const char *str);
char rt_hw_console_getchar(void);
#endif /*__UART_H*/
4、串口初始化函数Uart_Init()加入到board.c文件中。Uart_Init()需要在 board.c中的 rt_hw_board_init() 函数中调用。注意不要忘了board.h需要包含uart.h头文件。
void rt_hw_board_init()
{
/* System Clock Update */
//SystemCoreClockUpdate();
/* System Tick Configuration */
// _SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
/* Call components board initial (use INIT_BOARD_EXPORT()) */
CLK_SetSysClkSource(ClkSysSrcHRC);
SysTick_Init();
LED_Init();
LED0_ON();
Uart_Init(); 串口初始化
//LED0_OFF();
// while(1);
#ifdef RT_USING_COMPONENTS_INIT
rt_components_board_init();
#endif
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#e
5、修改RT-thread配置文件rtconfig.h,在文件中加入使用Finsh组件的宏。/* RT-Thread config file */
#ifndef __RTTHREAD_CFG_H__
#define __RTTHREAD_CFG_H__
#if defined(__CC_ARM) || defined(__CLANG_ARM)
#include "RTE_Components.h"
#if defined(RTE_USING_FINSH)
#define RT_USING_FINSH
#endif //RTE_USING_FINSH
#endif //(__CC_ARM) || (__CLANG_ARM)
#define RT_USING_FINSH //使用Finsh组件
// <<< Use Configuration Wizard in Context Menu >>>
// <h>Basic Configuration
// <o>Maximal level of thread priority <8-256>
// <i>Default: 32
#define RT_THREAD_PRIORITY_MAX 8
// <o>OS tick per second
// <i>Default: 1000 (1ms)
#define RT_TICK_PER_SECOND 1000
6、main.c不需重新修改上一章中main函数就行,编译下载验证。
复位开发板,看到输出一下信息。
输入help,输出命令列表。
输入list_thread,输出线程执行情况。
最后,RT-thread和uart控制台和Finsh组件功能增加完毕。
这一次的HC32H460开发板RT-thread Nano移植分享就结束了。有什么写得不对的地方,欢迎各位评论回复,大家一起交流。
@21小跑堂
代码地址:https://gitee.com/Liyj_336/hc32_rtt.git
|