首页 > 其他分享 >STM32:rtthread_f1移植

STM32:rtthread_f1移植

时间:2023-07-10 23:33:52浏览次数:39  
标签:rt f1 函数 void rtthread STM32 RT main

本文开始移植rtthread的代码到正点原子的板子上;参考资料为野火的教程,需要搭配野火教程使用;

使用源码是作为pack包放在arm-keil官网下载的nano3.0.3版本;nano版本精简方便解构;gittee上的master版本组件又多又杂不利于初学;

本来想用3.1.5版本源码的,但是移植过程会有代码报错又莫名其妙好了,等3.0.3移植完后再试试吧;

在开始移植rtthread源码前,先看看rtthread源码的文件分布;

1 源码文件

  1.1 bsp:      rtthread移植好的厂商评估板的完整例程文件夹,以及board.c和rtconfig.h;保留board.c和rtconfig.h即可;

  1.2 components    RT-Thread 的各个组件代码,例如 finsh,gui 等;

  1.3 docs:      没啥;

  1.4 include:    RT-Thread 内核的头文件;

  1.5 libcpu:      arm架构和risc-v架构下各类的内核代码;复制用的那一个xx.s以及cpuport.c就可以了,多了报错;    

  1.6 src:      RT-Thread 内核的源文件;

2 代码修改

  board.c中systick相关都删掉,然后rt_hw_board_init()中改用SysTick_Config()函数;然后加个board.h头文件;

  rtconfig.h修改了栈空间为256->512,ststick参数的周期为100->1000;就修改完了rtthread源码配置;

  每次记录删删改改感觉太冗余了,之后每章节代码修改通过查看提交记录可知;

  rtthread_f1demo: 将rtthread nano3.0.3版本移植到stm32f1上; (gitee.com)

3 rt_kprintf()串口调试 

//rtthread.h中,这里是ifndef,不是ifdef;这个字符串的参数居然是用(...),第一次见,先放着;
#ifndef RT_USING_CONSOLE
#define rt_kprintf(...)
#define rt_kputs(str)
#else
void rt_kprintf(const char *fmt, ...);     //定义了RT_USING_CONSOLE,所以执行的是这里的两个函数;
void rt_kputs(const char *str);
#endif
//if定义了RT_USING_DEVICE设备驱动,则使用设备函数;else使用rt_hw_console_output()函数;所以使用的是rt_hw_console_output();
void rt_kprintf(const char *fmt, ...)
{
    va_list args;
    rt_size_t length;
    static char rt_log_buf[RT_CONSOLEBUF_SIZE];

    va_start(args, fmt);//1 这个函数也不知道啥用,先放着不管;
    /* the return value of vsnprintf is the number of bytes that would be
     * written to buffer had if the size of the buffer been sufficiently
     * large excluding the terminating null byte. If the output string
     * would be larger than the rt_log_buf, we have to adjust the output
     * length. */
    //2 这个rtthread的字符处理函数老是看不懂,之前那个rt_object对象的字符处理函数也是一样看不懂;先放着吧;
    length = rt_vsnprintf(rt_log_buf, sizeof(rt_log_buf) - 1, fmt, args);
    if (length > RT_CONSOLEBUF_SIZE - 1)
        length = RT_CONSOLEBUF_SIZE - 1;
#ifdef RT_USING_DEVICE
    if (_console_device == RT_NULL)
    {
        rt_hw_console_output(rt_log_buf);
    }
    else
    {
        rt_uint16_t old_flag = _console_device->open_flag;

        _console_device->open_flag |= RT_DEVICE_FLAG_STREAM;
        rt_device_write(_console_device, 0, rt_log_buf, length);
        _console_device->open_flag = old_flag;
    }
#else
    rt_hw_console_output(rt_log_buf);    //这个函数的弱声明也在kservice.c中,重写在board.c中;
#endif
    va_end(args);                        //3 这个函数也不知道啥用,先放着不管;
}
RTM_EXPORT(rt_kprintf);                  //4 这个函数也不知道啥用,先放着不管;
#endif

//rtdef.h  不知道啥用,先放着;
#define va_start(v,l)       __builtin_va_start(v,l)
#define va_end(v)           __builtin_va_end(v)
//使用usart1作为rt_kprintf() output,函数弱声明在kservice.c中;与单片机串口1的重定向并不冲突;
//这个函数是可以被打断的,通过静态变量rt_scheduler_lock_nest来统计嵌套的次数;
void rt_hw_console_output(const char *str)
{
    rt_enter_critical();
    //"..."字符串的数据末尾会自动添加 "\0" ,所以可以通过"\0"来判断字符串是否结尾;
    while (*str!='\0'){
        if (*str=='\n'){
            USART_SendData(USART1, '\r'); 
            while (!USART_GetFlagStatus(USART1, USART_FLAG_TXE))
                ;
        }
        USART_SendData(USART1, *str++); 				
        while (!USART_GetFlagStatus(USART1, USART_FLAG_TXE))
            ;	
    }	
    rt_exit_critical();
}

4 main函数扩展

  MDK编译器中当原函数已经封装成外部库函数不可修改,或者已经编码烧录在rom中的时候,可以通过标识符对原函数进行打补丁操作;

  在rtthread中使用其对main函数进行了补丁操作,扩展了main函数,在调用main函数之前先初始化了rtthread系统;

  4.1 函数补丁demo

    4.1.1 ¥Super$$:标识函数原型,目的是标识函数原型给编译器回调;(博客园格式问题此处用¥代替了$,具体格式见代码;)

    4.1.2 ¥Sub$$:   标识函数原型的补丁函数,编译器会将补丁函数放到原型函数之前或之后执行;

    4.1.3 被标识函数及原型函数需要是全局函数或者弱声明函数,且被标识函数需要是代码编译的时候就编译好的静态链接,不能是实时执行的动态链接;想想也知道啦;

extern void ExtraFunc(void);    //补丁操作;
 
extern void $Super$$foo(void):
void $Sub$$foo(void)
{
    ExtraFunc();                //补丁操作;    
    $Super$$foo();              //通过调用$Super$$foo()去回调原函数;
                                
}

  4.2 函数扩展main( )

    以下为rtthread中main函数扩展的逻辑流程;

    那么问题来了,这个main_thread_entry( )函数是什么时候从优先级表中删除的呢?他要是没有从优先级表中移除那岂不是会一直调用main函数?

    这个问题先放着;

//components.c 在执行main函数之前会先调用$Sub$$main()函数,等会通过调用$Super$$main()函数回到main函数;
#if defined (__CC_ARM)
extern int $Super$$main(void);    //通过$Super$$来标识函数扩展函数原型是main函数;

int $Sub$$main(void)              //通过$Sub$$对main函数进行打补丁操作;
{
    rt_hw_interrupt_disable();
    rtthread_startup();
    return 0;
}
#elif defined(__ICCARM__)
//....
#elif defined(__GNUC__)
//...
#endif



//components.c 初始化了$Super$$main()函数所在线程,定时器线程,空闲线程,然后启动switch_to调度函数;
int rtthread_startup(void)
{
    rt_hw_interrupt_disable();

    rt_hw_board_init();            //NOTE: please initialize heap inside board initialization
    //rt_show_version();
    rt_system_timer_init();
    rt_system_scheduler_init();

#ifdef RT_USING_SIGNALS
    rt_system_signal_init();
#endif

    rt_application_init();            //main_thread_entry的节点放入优先级表中、悬起线程;
    rt_system_timer_thread_init();    //timer定时器初始化;
    rt_thread_idle_init();            //idle空闲线程初始化;
    rt_system_scheduler_start();      //启动调度函数switch_to汇编;
	
    /* never reach here */
    return 0;
}


//components.c
#ifndef RT_USING_HEAP
ALIGN(8)
static rt_uint8_t main_stack[RT_MAIN_THREAD_STACK_SIZE];
struct rt_thread main_thread;
#endif

void main_thread_entry(void *parameter)
{
    extern int main(void);
    extern int $Super$$main(void);
    rt_components_init();
#if defined (__CC_ARM)
    $Super$$main();        //回调执行main函数;
#elif defined(__ICCARM__) || defined(__GNUC__)
    main();
#endif
}

5 小结

  至此,rtthread系统初始化完成,可以使用串口1调试,在main函数中创建线程,然后将线程挂载到优先级表后即可运行;

  rtthread_f1demo: 将rtthread nano3.0.3版本移植到stm32f1上; (gitee.com)

 

标签:rt,f1,函数,void,rtthread,STM32,RT,main
From: https://www.cnblogs.com/caesura-k/p/17542666.html

相关文章

  • 【计数,DP】CF1081G Mergesort Strikes Back
    ProblemLink现有一归并排序算法,但是算法很天才,设了个递归深度上限,如果递归深度到达\(k\)则立即返回。其它部分都和正常归并排序一样,递归中点是\(\lfloor(l+r)/2\rfloor\),归并每次取两边较小者加入结果。给定\(n,k\),求用这个算法对一个均匀随机的排列\(p\)排序后,\(p\)......
  • CF1585F Non-equal Neighbours - 容斥 - dp - 单调栈
    题目链接:https://codeforces.com/problemset/problem/1585/F题解:难难难考虑容斥:设\(A_i\)表示\(b_i\neqb_{i+1}\)(\(i=1,2,\cdots,n-1\))时对应的\(\{b_i\}\)方案的答案那么答案就是$$\bigcap_{{i=1}}^{n}A_{i}=|U|-\left|\bigcup_{i=1}^n\overline{A_i}\right|$$......
  • CF1421E题解
    题目链接本题作为一道本人思考了50分钟没想出来的大思维题,我觉得可以用来扩宽一下大家的视野。本题中,我们每次都会选取两个相邻的数\(a_i\)和\(a_{i+1}\),同时将这两位变为一位,这一位上填的数为\(-(a_i+a_{i+1})\)。很容易想到的一个\(O(n^3)\)的dp做法是区间dp,设\(f[......
  • CF1545D-题解
    题目链接题目描述有\(n\)个人和\(k\)个间隔相同时间的时刻,每个人都向正方向做匀速直线运动。给出每个时刻(\(0\simk-1\))的所有人的坐标集合(无序),在这\(nk\)个数中有一个数是错误的,找出这个错误的数并将其改正。数据范围:\(5\len\le1000\),\(7\lek\le1000\)。加......
  • CF1827D 题解
    problem&blog。很好的题。用到一些关于重心的trick。不妨认为只有一个重心\(\text{maxx}\)。设当前节点数为\(n\),重儿子所在的子树的大小为\(\text{maxsiz}\),那么答案即\(n-2\times\text{maxsiz}\),方法是往重儿子的那个子树爆加节点。因此需要在线维护\(\text{maxx}......
  • CF1601F Two Sorts 题解--zhengjun
    link这里提供一种不用meetinmiddle的方法,速度比较可观。发现性质开始简单的推一下式子。\(\sum(i-a_i)\bmodp=\sum(rk_i-i-p\times\lfloor\frac{rk_i-i}{p}\rfloor)=-p\times\sum\lfloor\frac{rk_i-i}{p}\rfloor,p=998244353\)于是,只需求出\(\sum\lfloor\frac{rk_i-......
  • [STM32]STM32双机串口通信
    [STM32]STM32双机串口通信上一篇的通信方案在发送端高强度通信下寄了,发现是函数HAL_UART_Transmit()的锅,一个函数居然能跑0.3s左右。。。于是打算选用DMA收发数据,但是DMA在接收数据时遇到一些玄学问题,于是改用DMA发送数据,串口IDLE中断接收数据的策略。cubeMX配置接收端部分开......
  • [STM32 HAL]一种可能不错的DMA处理串口数据方案
    [STM32HAL]一种可能不错的DMA处理数据方案原文链接:https://blog.csdn.net/youmeichifan/article/details/51750435?spm=1001.2014.3001.5506本文配置稍有不同,大体类似。MX配置开启USART1,使能USART1全局中断,打开RX,TX的DMA通道,均为normal模式,内存地址自增,使能TX对应DMA的中断,RX......
  • CF1702G2 Passable Paths (hard version)
    PassablePaths(hardversion)思路题意:判断是否存在一条链包含树上给定点集。考虑把\(1\)当做树的根,将无根树转化为有根树。考虑这样一个性质:若存在满足条件的最短链,则点集中深度最深的点\(u\)是该链的一个端点,点集中距离\(u\)最远的点\(v\)是该链的另一端点。证明......
  • CF1628E
    前置知识线段树\(\text{Kruskal}\)重构树点集\(\text{LCA}\)思路看到询问为\(x\)到所有白色节点的路径上最大可能边权,可以利用\(\text{Kruskal}\)重构树转化为\(x\)与所有白色点的\(\text{lca}\)的权值。问题在于如何快速求出一个点集的\(\text{lca}\),参考CF1......