发新帖本帖赏金 200.00元(功能说明)我要提问
返回列表
打印
[牛人杂谈]

避免弯路:基于新唐BSP标准库函数完美移植RT-Thread注意事项

[复制链接]
18517|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
gaoyang9992006|  楼主 | 2021-8-25 00:10 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 gaoyang9992006 于 2021-8-26 08:45 编辑

#申请原创# @21小跑堂 注意:文末有视频链接。
之前我发过多篇移植RTT到新唐单片机的帖子,不过都是能满足要求,但是还不够完美,本次帖子,完美解决之前的不完美。
该帖基于最新版的RT-Thread Nano 3.1.5版本。
为了体现帖子的完美性,这次我从头开始说关键点。
第一步,基于RTE创建工程,应选择下图的选项

第二步,工程应至少包含以下4个基本库文件


第三步,工程的配置
因为我们可以不使用printf,所以我们可以不包含MicroLIB,甚至我们不用在工程配置里包含STD标准库的头文件进来。
因为BSP的标准库基于AC5编写,所以我们这里最好选择AC5,如果选择AC6,应在警告设置AC5-like ,否则编译会因为打印几百个警告而变慢。
第四步,rtconfig.h配置

第五步,按照#error设定的4个部分配置时钟和硬件初始化,串口初始化,串口输出,串口输入。
时钟初始化,我们可以找到一个厂家提供的模板,复制SYS_Init();并在其中完善滴答定时器的启动与配置。
打开board.c添加系统初始化代码
void SYS_Init(void)
{
    /*---------------------------------------------------------------------------------------------------------*/
    /* Init System Clock                                                                                       */
    /*---------------------------------------------------------------------------------------------------------*/
    /* Unlock protected registers */
    SYS_UnlockReg();

    /* Set XT1_OUT(PF.2) and XT1_IN(PF.3) to input mode */
    PF->MODE &= ~(GPIO_MODE_MODE2_Msk | GPIO_MODE_MODE3_Msk);

    /* Enable HIRC clock (Internal RC 48 MHz) */
    CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN_Msk);

    /* Wait for HIRC clock ready */
    CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk);

    /* Set core clock as 51MHz from PLL */
    CLK_SetCoreClock(FREQ_51MHZ);
        
                /* System Tick Configuration */
                CLK_EnableSysTick(CLK_CLKSEL0_STCLKSEL_HCLK,SystemCoreClock / RT_TICK_PER_SECOND);

    /* Enable UART clock */
    CLK_EnableModuleClock(UART0_MODULE);

    /* Select UART clock source from HIRC */
    CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UART0SEL_HIRC, CLK_CLKDIV0_UART0(1));
               
    /* Update System Core Clock */
    /* User can use SystemCoreClockUpdate() to calculate SystemCoreClock. */
    SystemCoreClockUpdate();

    /* Set GPB multi-function pins for UART0 RXD and TXD */
    SYS->GPB_MFPH = (SYS->GPB_MFPH & ~(SYS_GPB_MFPH_PB12MFP_Msk | SYS_GPB_MFPH_PB13MFP_Msk))    |       \
                    (SYS_GPB_MFPH_PB12MFP_UART0_RXD | SYS_GPB_MFPH_PB13MFP_UART0_TXD);
    /* Lock protected registers */
    SYS_LockReg();
}

CLK_EnableSysTick这个滴答时钟中断使能与配置函数,可以设置滴答定时器的时钟源,重载值,并使能其中断。
然后就要完成滴答定时器中断的内容:
/* systick 中断服务例程 */
void SysTick_Handler(void)
{
        rt_os_tick_callback();
}
其中内部的回调函数,rtt的board.c已经帮我们完成,只需要添加以上代码段即可,也可修改回调函数的名字为中断入口。
将系统初始化代码填入#error "TODO 1: OS Tick Configuration."后面,并注释掉该行,确保编译时候不再报错提示该位置。
串口初始化,按如下代码进行初始化
static int uart_init(void)
{
//#error "TODO 2: Enable the hardware uart and config baudrate."

    /* Reset UART0 */
    SYS_ResetModule(UART0_RST);

    /* Configure UART0 and set UART0 baud rate */
    UART_Open(UART0, 115200);
        
    return 0;
}
串口输出功能
之前我用了printf实现这部分,但是有个缺点就是要启用MicroLIB,这样就增加了代码存储空间,其实可以使用STD标准库函数实现如下所示,串口写入函数一共三个参数,第一个选用的串口名,第二个是要发送的字符串地址,这里因为类型不同进行了转换,也可以不转换而修改该预设函数的参数类型。第三个是要发送的字符串长度,我们这里用rt的库函数计算一下传入的长度,这样就做到了一个函数一行就搞定了串口打印的映射。
void rt_hw_console_output(const char *str)
{
        //#error "TODO 3: Output the string 'str' through the uart."
               
        UART_Write(UART0,(uint8_t *)str,rt_strlen(str));        
}

串口输入功能
串口输入功能的配置在finsh_port.c
我们完善该弱函数的内容即可,注释掉error提示行。我实现的方法如下:这里是参考了重定向文件里的方法
RT_WEAK char rt_hw_console_getchar(void)
{
    /* Note: the initial value of ch must < 0 */
    int ch = -1;

//#error "TODO 4: Read a char from the uart and assign it to 'ch'."

        if((UART0->FIFOSTS & UART_FIFOSTS_RXEMPTY_Msk) == 0)
        {
                        return (UART0->DAT);
        }
        return ch;
}


最后给大家晒一下关键的board.c完整内容
/*
* Copyright (c) 2006-2019, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date           Author       Notes
* 2021-05-24                  the first version
*/

#include <rthw.h>
#include <rtthread.h>
#include <NuMicro.h>
#include <stdio.h>

#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
/*
* Please modify RT_HEAP_SIZE if you enable RT_USING_HEAP
* the RT_HEAP_SIZE max value = (sram size - ZI size), 1024 means 1024 bytes
*/
#define RT_HEAP_SIZE (15*1024)
static rt_uint8_t rt_heap[RT_HEAP_SIZE];

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

void rt_os_tick_callback(void)
{
    rt_interrupt_enter();
   
    rt_tick_increase();

    rt_interrupt_leave();
}

/**
* This function will initial your board.
*/

void SYS_Init(void)
{
    /*---------------------------------------------------------------------------------------------------------*/
    /* Init System Clock                                                                                       */
    /*---------------------------------------------------------------------------------------------------------*/
    /* Unlock protected registers */
    SYS_UnlockReg();

    /* Set XT1_OUT(PF.2) and XT1_IN(PF.3) to input mode */
    PF->MODE &= ~(GPIO_MODE_MODE2_Msk | GPIO_MODE_MODE3_Msk);

    /* Enable HIRC clock (Internal RC 48 MHz) */
    CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN_Msk);

    /* Wait for HIRC clock ready */
    CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk);

    /* Set core clock as 51MHz from PLL */
    CLK_SetCoreClock(FREQ_51MHZ);
        
                /* System Tick Configuration */
                CLK_EnableSysTick(CLK_CLKSEL0_STCLKSEL_HCLK,SystemCoreClock / RT_TICK_PER_SECOND);

    /* Enable UART clock */
    CLK_EnableModuleClock(UART0_MODULE);

    /* Select UART clock source from HIRC */
    CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UART0SEL_HIRC, CLK_CLKDIV0_UART0(1));
               
    /* Update System Core Clock */
    /* User can use SystemCoreClockUpdate() to calculate SystemCoreClock. */
    SystemCoreClockUpdate();

    /* Set GPB multi-function pins for UART0 RXD and TXD */
    SYS->GPB_MFPH = (SYS->GPB_MFPH & ~(SYS_GPB_MFPH_PB12MFP_Msk | SYS_GPB_MFPH_PB13MFP_Msk))    |       \
                    (SYS_GPB_MFPH_PB12MFP_UART0_RXD | SYS_GPB_MFPH_PB13MFP_UART0_TXD);
    /* Lock protected registers */
    SYS_LockReg();
}

/* systick 中断服务例程 */
void SysTick_Handler(void)
{
        rt_os_tick_callback();
}

void rt_hw_board_init(void)
{
//#error "TODO 1: OS Tick Configuration."
        
        SYS_Init();
        
    /*
     * TODO 1: OS Tick Configuration
     * Enable the hardware timer and call the rt_os_tick_callback function
     * periodically with the frequency RT_TICK_PER_SECOND.
     */

    /* Call components board initial (use INIT_BOARD_EXPORT()) */
#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
}

#ifdef RT_USING_CONSOLE

static int uart_init(void)
{
//#error "TODO 2: Enable the hardware uart and config baudrate."

    /* Reset UART0 */
    SYS_ResetModule(UART0_RST);

    /* Configure UART0 and set UART0 baud rate */
    UART_Open(UART0, 115200);
        
    return 0;
}
INIT_BOARD_EXPORT(uart_init);

void rt_hw_console_output(const char *str)
{
        //#error "TODO 3: Output the string 'str' through the uart."
               
        UART_Write(UART0,(uint8_t *)str,rt_strlen(str));        
}

#endif



朋友们,按照这个方法,全部使用BSP提供的库函数就完成了,程序也变的更加友好可读,占用资源更少。
另外注意,在启用了滴答定时器中断后,我们再用CLK_SysTickDelay(1000);延时就会卡住了,这时候系统通过中断接管了滴答定时器的控制权
可以使用rt_thread_mdelay(2000);实现延时。

最后我们测试例子,编写main.c
#include "stdio.h"
#include <NuMicro.h>
#include <rtthread.h>
//配置优先级,栈大小,时间片,设置不对没法运行的。
#define THREAD_PRIORITY         5
#define THREAD_STACK_SIZE       256
#define THREAD_TIMESLICE        10
void led(void *parameter)
{
    printf("\n\nCPU [url=home.php?mod=space&uid=72445]@[/url] %d Hz\n", SystemCoreClock);
    printf("+-------------------------------------------------+\n");
    printf("|    PB14(Output)  Sample Code     |\n");
    printf("+-------------------------------------------------+\n\n");

        rt_kprintf("Hello RTT_NANO\n");
        while(1)
                {
                                        PB14=0;
                                        rt_thread_mdelay(2000);
                                        printf("\nLED1 is ON\n");
                                        PB14=1;
                                        rt_thread_mdelay(2000);
                                        printf("\nLED1 is OFF\n");      
                }
//return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(led, RT-Thread first led sample);
void led2(void *parameter)
{

        rt_kprintf("Hello RTT_NANO\n");
        while(1)
                {
                                        PB14=0;
                                        rt_thread_mdelay(3000);
                                        printf("\nLED2 is ON\n");
                                        PB14=1;
                                        rt_thread_mdelay(3000);
                                        printf("\nLED2 is OFF\n");         
                }
//return 0;
}
MSH_CMD_EXPORT(led2, RT-Thread second led sample);
int led_sample(void)
{
static rt_thread_t tid = RT_NULL;
static rt_thread_t tid2 = RT_NULL;
        /* 创建线程1 */
    tid = rt_thread_create("thread1",
                            led, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY, THREAD_TIMESLICE);
   
    if (tid != RT_NULL)      
        rt_thread_startup(tid);
        /* 创建线程2 */
    tid2 = rt_thread_create("thread2",
                            led2, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY, THREAD_TIMESLICE);
   
    if (tid2 != RT_NULL)      
        rt_thread_startup(tid2);               
                //该例子共用PB14端口,所以一并在创建线程时候初始化为输出模式
                GPIO_SetMode(PB, BIT14, GPIO_MODE_OUTPUT);               
    return 0;
}
MSH_CMD_EXPORT(led_sample, RT-Thread sample);
/*---------------------------------------------------------------------------------------------------------*/
/*  Main Function                                                                                          */
/*---------------------------------------------------------------------------------------------------------*/
int32_t main(void)
{
        led_sample();
        return 0;
}



你学会了吗,这样你就不用等RT-Thread Studio支持新唐的芯片了,自己也可以完美移植RTT到新唐单片机了。最后补上我演示以上内容用的基于M471的工程 M471_RTT_nano.rar (152.94 KB)
补上B站视频https://www.bilibili.com/video/BV1mf4y1n7vR/
基于新唐BSP标准库函数完美移植RT-Thread方法_哔哩哔哩_bilibili



使用特权

评论回复

打赏榜单

21小跑堂 打赏了 200.00 元 2021-08-26
理由:恭喜通过原创奖文章审核!请多多加油哦!

评论
gaoyang9992006 2021-8-26 14:21 回复TA
重定向文件中的硬件错误中断处理函数,已经被系统重写,因此会有重复定义的报错,这里注释掉重定向文件中的__asm int32_t HardFault_Handler(void)函数即可。在视频中已经说明。 
沙发
yangjiaxu| | 2021-8-25 13:07 | 只看该作者
挺不错,支持一波

使用特权

评论回复
板凳
wcheng13| | 2021-8-25 14:45 | 只看该作者
看看,先顶一个

使用特权

评论回复
地板
kiwis66| | 2021-8-25 17:13 | 只看该作者
真不错,学习了~

使用特权

评论回复
5
caizhiwei| | 2021-8-27 15:31 | 只看该作者
200到手,羡慕啊,羔羊哥~

使用特权

评论回复
6
gaoyang9992006|  楼主 | 2021-8-29 12:34 | 只看该作者
caizhiwei 发表于 2021-8-27 15:31
200到手,羡慕啊,羔羊哥~

你也可以啊,申请个蓝V认证,然后写一篇STM32F103的基于HAL库函数完美移植RTT的,目前有很多人想看到这个题目的贴文。你先发了,200就是你的了。

使用特权

评论回复
7
liaoxr| | 2021-9-1 20:52 | 只看该作者
@gaoyang9992006 版主,为啥我用你的程序直接调试啥也没有改,程序全速运行是不行的,直接卡死在Reset_Handler。重定向文件中的__asm int32_t HardFault_Handler(void)我看你的程序已经屏蔽掉了,但是这个程序跑不起来是啥原因呢。
C:\Users\86158\Desktop\1.PNG

使用特权

评论回复
8
liaoxr| | 2021-9-1 20:56 | 只看该作者
补充图片

使用特权

评论回复
9
gaoyang9992006|  楼主 | 2021-9-1 21:19 | 只看该作者

请确认你是否在pack管理器正确安装了RTT nano 3.1.5,另外是否安装好了新唐的pack。

使用特权

评论回复
10
gaoyang9992006|  楼主 | 2021-9-1 21:23 | 只看该作者

你是没有这个开发板吧?然后你用的是仿真模式,这个工程只支持硬件模式,不支持simulation。

使用特权

评论回复
11
liaoxr| | 2021-9-2 10:28 | 只看该作者
gaoyang9992006 发表于 2021-9-1 21:23
你是没有这个开发板吧?然后你用的是仿真模式,这个工程只支持硬件模式,不支持simulation。 ...

版主,RTT nano我有安装了的,M471这个板子比较老,所以使用软件仿真。我这边重新按操作移植到M031SD的板子上,也是出现全速跑不了,直接进入Reset Hardler了。

使用特权

评论回复
12
liaoxr| | 2021-9-2 10:35 | 只看该作者
我是先采用官方的例程测试好GPIO功能和打印功能正常后,我再进行移植RTT Nano的,一直想学下这个,但是移植还是不知道哪里出问题了。
















使用特权

评论回复
13
liaoxr| | 2021-9-2 10:39 | 只看该作者
版主,这是我移植的程序,可以帮忙看下哪里移植有问题不,感谢感谢。
链接:https://pan.baidu.com/s/1rJcTVnVdgsnM8HDSn-a_BA
提取码:lawr

使用特权

评论回复
14
gaoyang9992006|  楼主 | 2021-9-2 15:56 | 只看该作者
liaoxr 发表于 2021-9-2 10:39
版主,这是我移植的程序,可以帮忙看下哪里移植有问题不,感谢感谢。
链接:https://pan.baidu.com/s/1rJcT ...

给你修改了一下,你可能是在重定向文件中屏蔽了不该屏蔽的,这里只需要屏蔽错误中断处理函数即可,其他的不要动。
另外你没有完全按照我帖子说的配置工程。
M031BSP-master.rar (590.2 KB)
你试试,这个功能能否正常运行。

使用特权

评论回复
15
gaoyang9992006|  楼主 | 2021-9-2 15:59 | 只看该作者
liaoxr 发表于 2021-9-2 10:39
版主,这是我移植的程序,可以帮忙看下哪里移植有问题不,感谢感谢。
链接:https://pan.baidu.com/s/1rJcT ...

以后上传工程,先清理一下工程,然后再打包,不然编译过程产生的中间文件都打包进来了,太大。

使用特权

评论回复
评论
liaoxr 2021-9-2 16:39 回复TA
收到,我这边验证下,感谢。 
16
邹姆斯特朗| | 2021-11-5 17:19 | 只看该作者
可以的,很详细

使用特权

评论回复
17
天灵灵地灵灵| | 2021-12-21 21:17 | 只看该作者
完整版也是这么移植的?

使用特权

评论回复
发新帖 本帖赏金 200.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:西安公路研究院南京院
简介:主要工作从事监控网络与通信网络设计,以及从事基于嵌入式的通信与控制设备研发。擅长单片机嵌入式系统物联网设备开发,音频功放电路开发。

1971

主题

15978

帖子

210

粉丝