rt-thread 移植调试记录

news2025/2/28 20:06:43

rt-thread 移植调试记录

记录rt-thread移植的过程。这里移植仅仅是利用rt-thread源码目录已经移植好的文件,组建自己的工程,不需要自己编写汇编完成底层移植。

1. 搭建基础工程

这里使用的是正点原子的潘多拉开发板,MCU为stm32l475。需要先通过CubeMX搭建基础工程。

选择从芯片创建工程。

1.1 CubeMX创建工程

开启RCC:

在这里插入图片描述

开启串口:

在这里插入图片描述

开启LED对应的GPIO:

在这里插入图片描述

配置时钟,stml475主频为80M:

最后生成MDK工程。

1.2 工程配置

1.2.1 KEIL工程配置

使用KEIL打开MDK工程。

配置microlib:

在这里插入图片描述

配置Debug下载程序重启芯片:

在这里插入图片描述

1.2.2 添加串口打印

#include <stdio.h>
int fputc(int ch, FILE *f)
{
    uint8_t c = ch;
	HAL_UART_Transmit(&huart1, &c, 1, 5000);
	return (ch);
}

1.2.3 点亮LED

int main()
{
    ......
   while (1)
  {
      HAL_GPIO_WritePin(GPIOE, GPIO_PIN_7, GPIO_PIN_RESET);
      printf("test\r\n");
      HAL_Delay(1000);
  } 
}

1.2.4 编译下载

编译下载程序。可以看到开发板的LED灯变红。串口输出“test”字符串。

2. 移植rt-thread

完成基础工程创建后,接下来就是移植工作了。

先将rt-thread源码目录下的srcinclude, libcpu 目录复制一分到MDK工程中,移植时,直接添加需要的文件即可。

2.1 添加平台相关文件

参考RT-Thread Studio IDE的工程,就能够发现,主要的移植工作就在libcpu目录中,主要为context_gcc.S。根据我们使用的芯片架构,进行移植。由于rt-thread源码目录提供了各个平台的已经移植完成的文件,我们只需要根据芯片架构进行添加即可。

在这里插入图片描述

主要是开关中断,线程切换,PendSV相关的函数的移植。这部分内容需要会汇编才能做。因此直接使用现成已经移植好的直接使用即可。 比如上述stm32l475芯片,架构是Contex-M4。可以直接在rt-thread源码目录下libcpu\arm\cortex-m4找到对应的文件:

在这里插入图片描述

这里,context_gcc.S用于RT-Thread Studio IDE工具链使用的,context_rvds.S 用于MDK的工具链使用,另外一个看名字就知道是IAR使用的了。

除了上述文件,还需要将cpuport.c也添加到工程中。另外还有上层目录的common目录下,也有部分移植文件,这部分内容暂时可不添加。

2.2 添加内核相关文件

将src目录的所有文件添加到工程中。

2.3 添加头文件路径

2.4 添加rtconfig.h

找到一份基础的rtconfig.h文件,添加到工程中。我这里的基础文件是通过RT-Thread Studio创建的基础工程,直接复制使用的。

添加完成后,KEIL工程结构如下:

整体工程结构如下:

在这里插入图片描述

2.5 编译

完成上述添加,基本上就是添加了我们需要的纯rtt内核了,没有使用到rtt的组件。直接编译会报很多错误,因此针对上面的工程还需要做一些修改。

先直接编译看看错误:

在这里插入图片描述

主要看到的就是va_list未定义的错误。这个错误是因为缺少头文件,我们在rtconfig.h中添加头文件即可:

#include <stdarg.h>

接着编译,依然报错:

在这里插入图片描述

没有找到finsh.h头文件,由于我们rtconfig.h中启用了finsh组件,但是没有添加这个组件相关的文件导致的报错,在rtconfig.h文件中取消使用finsh即可,注释掉相关宏定义:

/* Command shell */

//#define RT_USING_FINSH
//#define FINSH_THREAD_NAME "tshell"
//#define FINSH_USING_HISTORY
//#define FINSH_HISTORY_LINES 5
//#define FINSH_USING_SYMTAB
//#define FINSH_USING_DESCRIPTION
//#define FINSH_THREAD_PRIORITY 20
//#define FINSH_THREAD_STACK_SIZE 4096
//#define FINSH_CMD_SIZE 80
//#define FINSH_USING_MSH
//#define FINSH_USING_MSH_DEFAULT
//#define FINSH_USING_MSH_ONLY
//#define FINSH_ARG_MAX 10
/* end of Command shell */

接着编译,报错:

在这里插入图片描述

这是两个奇怪的问题,特别是下面这个,找半天也不知道哪儿出错了。最后对比RT-Thread Studio生成的基础工程,发现工程中并没有导入driver.ccpu.c这两个文件。看这个cpu.c应该是多核MCU使用的,因此在KEIL工程中删除这两个文件。

再编译,报错:

这是因为生成MDK工程,需要我们在stm32l4xx_it.c移植这两个函数,但是我们已经在context_rvds.S中移植了这两个中断函数,因此出现了重定义的错误。可以直接注释掉stm32l4xx_it.c中重定义的函数,或者将其生命为弱符号:

__attribute__((weak)) void HardFault_Handler(void)
{
  /* USER CODE BEGIN HardFault_IRQn 0 */

  /* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
    /* USER CODE END W1_HardFault_IRQn 0 */
  }
}

__attribute__((weak)) void PendSV_Handler(void)
{
  /* USER CODE BEGIN PendSV_IRQn 0 */

  /* USER CODE END PendSV_IRQn 0 */
  /* USER CODE BEGIN PendSV_IRQn 1 */

  /* USER CODE END PendSV_IRQn 1 */
}

在编译,还是报错:

在这里插入图片描述

函数rt_hw_board_init未定义,这里也可以抄一抄,在RT-Thread Studio中生成的board.c中的定义,创建board.cboard.h,添加到工程中。将该函数的实现也按照board.c中的样子重写一下。

main.c中的初始化都放到这个函数中来,另外还需要实现SysTick_Handler函数,为rtt提供系统时间。board.c

#include "board.h"
#include "rtthread.h"
#include "usart.h"
#include "gpio.h"

void SystemClock_Config(void);

void rt_hw_board_init()
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    
    rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);
    
    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / RT_TICK_PER_SECOND);
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
    HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

void SysTick_Handler(void)
{
    rt_interrupt_enter();
    HAL_IncTick();
    rt_tick_increase();
    rt_interrupt_leave();
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 1;
  RCC_OscInitStruct.PLL.PLLN = 20;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

board.h

#ifndef __BOARD_H
#define __BOARD_H

#include <stm32l4xx.h>

#define STM32_FLASH_START_ADRESS       ((uint32_t)0x08000000)
#define STM32_FLASH_SIZE               (512 * 1024)
#define STM32_FLASH_END_ADDRESS        ((uint32_t)(STM32_FLASH_START_ADRESS + STM32_FLASH_SIZE))

#define STM32_SRAM1_SIZE               (96)
#define STM32_SRAM1_START              (0x20000000)
#define STM32_SRAM1_END                (STM32_SRAM1_START + STM32_SRAM1_SIZE * 1024)

#if defined(__ARMCC_VERSION)
extern int Image$$RW_IRAM1$$ZI$$Limit;
#define HEAP_BEGIN      ((void *)&Image$$RW_IRAM1$$ZI$$Limit)
#elif __ICCARM__
#pragma section="CSTACK"
#define HEAP_BEGIN      (__segment_end("CSTACK"))
#else
extern int __bss_end;
#define HEAP_BEGIN      ((void *)&__bss_end)
#endif

#define HEAP_END                       STM32_SRAM1_END

void rt_hw_board_init(void);

#endif

最后再编译,完成,没有报错了。

在这里插入图片描述

2.6 调试

下载程序,发现终端没有输出,LED也没有变化。使用KEIL的Debug功能,找到出错的地方。

在这里插入图片描述

回退一点,会发现,程序死在了主线程创建的位置:

    thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread,
                                                    name);
    if (thread == RT_NULL)
        return RT_NULL;

跟踪发现,是由于内存分配失败导致的,需要分配内存的指针始终指向NULL:

#define _MEM_MALLOC(...)     RT_NULL

rt_weak void *rt_malloc(rt_size_t size)
{
    rt_base_t level;
    void *ptr;

    /* Enter critical zone */
    level = _heap_lock();
    /* allocate memory block from system heap */
    ptr = _MEM_MALLOC(size);
    /* Exit critical zone */
    _heap_unlock(level);
    /* call 'rt_malloc' hook */
    RT_OBJECT_HOOK_CALL(rt_malloc_hook, (ptr, size));
    return ptr;
}

查看kservice.c文件,实际可以通过rtconfig.h中的宏定义来控制内存分配的实现方式,这里指定RT_USING_SMALL_MEM_AS_HEAP 作为内存分配方式,将这个宏添加到rtconfig.h中宏定义。

/* Memory Management */
#define RT_USING_MEMPOOL
#define RT_USING_SMALL_MEM
#define RT_USING_HEAP
#define RT_USING_SMALL_MEM_AS_HEAP
/* end of Memory Management */

再次编译,没有报错,稍留程序,LED灯点亮,串口输出正常,输出字符串“test”。

移植控制台输出,在board.c中添加:

void rt_hw_console_output(const char *str)
{
    RT_ASSERT(str != RT_NULL);
    char ch;
    while (*str != '\0')
    {
        if (*str == '\n') {
            ch = '\r';
            HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 5000);
        }
        HAL_UART_Transmit(&huart1, (uint8_t *)str++, 1, 5000);
    }
}

完成:

在这里插入图片描述

最后再在主线程中创建一个线程,进行测试:

#define THREAD_PRIORITY  25
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE   5

void led_thread_entry(void *parameter)
{
    while (1)
    {
        rt_kprintf("enter test thread\n");
        rt_thread_delay(RT_TICK_PER_SECOND);
    }
}    

int main() 
{
    rt_thread_t tid;
    tid = rt_thread_create("led", led_thread_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
    if (tid != RT_NULL)
    {
        rt_thread_startup(tid);
    } else {
        rt_kprintf("create thread error\n");
    }

  while (1)
  {
      HAL_GPIO_WritePin(GPIOE, GPIO_PIN_7, GPIO_PIN_RESET);
      rt_kprintf("test\r\n");
      rt_thread_delay(1000);
  }
}

这里注意将API都修改为rtt的,否则打印只会出现testenter test thread 不会出现:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/337803.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【c++】类和对象:让你明白“面向一个对象有多重要”:构造函数,析构函数,拷贝构造函数的深入学习

文章目录 什么是面向对象&#xff1f;一&#xff1a;类是什么&#xff1f; 1.类的访问限定符 2.封装 3.类的实例化 4.this指针二&#xff1a;类的6个默认成员函数 1.构造函数 2.析构函数 3.拷贝构造函数什么是面向对象&#xff1f; c语言是面向…

window系统中安装Jupyter Notebook方法记录

1.初步感受Jupyter Notebook Jupyter Notebook 官网地址&#xff1a;Jupyter Notebook Jupyter Notebook&#xff08;此前被称为 IPython notebook&#xff09;是一个交互式笔记本&#xff0c;支持运行 40 多种编程语言。 Jupyter Notebook 的本质是一个 Web 应用程序&#xf…

Java体系最强干货分享—挑战40天准备Java面试,最快拿到offer!

如何准备java面试&#xff0c;顺利上岸大厂java岗位&#xff1f; 主攻Java的人越来越多&#xff0c;导致行业越来越卷&#xff0c;最开始敲个“hello world”都能进大厂&#xff0c;现在&#xff0c;八股、全家桶、算法等等面试题横行&#xff0c;卷到极致&#xff01;就拿今年…

聊聊什么是架构,你理解对了吗?

什么是架构?软件有架构?建筑也有架构?它们有什么相同点和不同点? 下面咱们就介绍一下,容易混淆的几个概念 一、系统与子系统 系统 泛指由一群有关联的个体组成,根据某种规则运作,能完成个别元件不能单独完成的工作的群体。它的意思是 “总体”、“整体”或“联盟” 子系…

Java基础常见面试题(二)

面向对象基础 面向对象和面向过程的区别 面向过程 优点&#xff1a; 性能比面向对象高&#xff0c;因为类调用时需要实例化&#xff0c;开销比较大&#xff0c;比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发&#xff0c;性能是最重要的因素。 缺点…

如何做迭代规划

敏捷开发中的迭代规划如同使用需求漏斗&#xff0c;对各方需求定期进行优先级排序并层层拆解或合并&#xff0c;最终把高优先级且细粒度的需求从漏斗进入到研发团队&#xff0c;确保研发团队做对的事&#xff08;Do Right Things&#xff09;&#xff0c;避免团队跑偏方向或进度…

Netty之EventLoopGroup详解

目录 目标 Netty版本 Netty官方API NioEventLoopGroup和DefaultEventLoop的区别 EventLoopGroup实现对内部EventLoop的轮询 EventLoop对普通任务和定时任务的实现 执行普通任务 执行定时任务 划分EventLoopGroup职责 简言 实现 指定EventLoopGroup操作ChannelHandl…

手机上怎么在线生成gif?1分钟教你手机图片合成gif

怎样通过手机实现在线制作GIF图片的操作呢&#xff1f;接下来&#xff0c;给大家分享两招gif制作&#xff08;https://www.gif.cn/&#xff09;小窍门-【GIF中文网】不需要下载任何软件&#xff0c;小白也能轻松上手。支持原画质导出&#xff0c;图片无损处理。一起来看看具体步…

前端教学视频分享(视频内容与市场时刻保持紧密相连,火热更新中。。。)

⚠️获取公众号 本次要想大家推荐一下本人的公众号&#xff0c;在微信中搜索公众号 李帅豪在对话框中输入前端视频四个字即可立即获取所有视频&#xff0c;不收费无广告&#xff01;&#xff01;&#xff01; 本公众号收集了近两年来前端最新最优秀的学习视频&#xff0c;涵盖…

大数据技术架构(组件)30——Spark:Optimize--->Submit

2.1.9、Optimize--->Submit调优工作主要从CPU、内存、网络开销和IO四方面入手2.1.9.0、Spark On Yarn2.1.9.0.1、Jar包管理及本地性调优spark.yarn.jars :将jar包放到hdfs上&#xff0c;避免每次driver启动的时候都要进行jar包的分发。yarn.nodemanager.localizer.cache.cle…

ChatGPT告诉你智能制造

ChatGPT自上线以来&#xff0c;几乎得到了外界的一致好评&#xff0c;上线两个月&#xff0c;获得1亿月活跃用户&#xff0c;成为增长最快的面向消费者的应用。 面对ChatGPT拟人一般的问答能力&#xff0c;很多人认为它代表着AlphaGo之后&#xff0c;人工智能应用的第二次浪潮…

电子技术——MOS差分输入对

电子技术——MOS差分输入对 差分输入系统因其极高的共模抑制能力&#xff0c;差分输入几乎是是构建所有通用模拟IC的基本前级输入&#xff0c;也是现代信号传输理论的基础。本节我们讲解MOS差分输入对。 MOS差分输入对 下图展示了MOS差分输入对的基本原理图&#xff1a; 一个…

数据采集协同架构,集成马扎克、西门子、海德汉、广数、凯恩帝、三菱、海德汉、兄弟、哈斯、宝元、新代、发那科、华中各类数控以及各类PLC数据采集软件

文章目录 前言一、采集协同架构是什么&#xff1f;可以做什么&#xff08;数控、PLC配置采集&#xff09;&#xff1f;二、使用步骤 1.打开软件&#xff0c;配置MQTT或者数据库&#xff08;支持sqlserver、mysql等&#xff09;存储转发消息规则2.配置数控系统所采集的参数、转…

项目(今日指数)

一 项目架构1.1 今日指数技术选型【1】前端技术【2】后端技术栈【3】整体概览3.2 核心业务介绍1】业务结构预览【2】业务功能简介1.定时任务调度服务XXL-JOB通过RestTemplate多线程动态拉去股票接口数据&#xff0c;刷入数据库&#xff1b; 2.国内指数服务 3.板块指数服务 4.涨…

使用Harbor构建docker私有仓库

一、概述1、什么是HarborHarbor 是VMware公司开源的云本地 registry 仓库&#xff0c;有可视化的Web管理界面&#xff0c;可以方便的管理和储存 Docker 镜像。Harbor 支持在多个仓库直接进行复制镜像&#xff0c;提供用户管理和访问控制和活动审计。2、Harbor的优势基于角色控制…

mysql高级(事务、存储引擎、索引、锁、sql优化、MVCC)

文章目录1.事务1.1 四大特性ACID1.2 并发事务2.存储引擎2.1 InnoDB2.2 MyISAM2.3 Memory2.4 存储引擎特点2.5 存储引擎的选择3.性能分析3.1 查看执行频次3.2 慢查询日志3.3 profile3.4 explain4.索引4.1 索引结构B-TreeBTreeHash面试题4.2 索引分类思考题4.3 语法4.4 使用规则最…

抖音电商-安全带为例分析

为什么来抖音抖音对好货的扶持力度很大好商品对抖音商城至关

Python-项目实战--飞机大战-英雄登场(7)

目标设计英雄和子弹类使用pygame.key.get_pressed()移动英雄发射子弹1.设计英雄和子弹类1.1英雄需求游戏启动后&#xff0c;英雄出现在屏幕的水平中间位置&#xff0c;距离屏幕底部120像素英雄每隔0.5秒发射一次子弹&#xff0c;每次连发三枚子弹英雄默认不会移动&#xff0c;需…

vulnhub之CLOVER: 1

1.信息收集 输入arp-scan 192.168.239.0/24进行主机存活探测&#xff0c;可以看到192.168.239.171主机存活。 对192.168.239.171主机进行端口扫描&#xff0c;可以看到20、21、22、82、110、443、5781、8080。 发现21端口可以匿名登录&#xff0c;输入&#xff1a;anonymous…

牛客网Python篇数据分析习题(一)

1.现有一个Nowcoder.csv文件&#xff0c;它记录了牛客网的部分用户数据&#xff0c;包含如下字段&#xff08;字段与字段之间以逗号间隔&#xff09;&#xff1a; Nowcoder_ID&#xff1a;用户ID Level&#xff1a;等级 Achievement_value&#xff1a;成就值 Num_of_exercise&a…