RT-Thread qemu mps2-an385 bsp 移植制作 :系统运行篇

前言

  • 前面已经让 RT-Thread 进入了 entry 入口函数,并且 调整 链接脚本,自动初始化与 MSH shell 的符号已经预留, 进入了 RT-Thread 的初始化流程

  • 接下来:从 内存管理、系统tick 定时器、适配串口 uart 驱动三个模块入手,让RT-Thread 真正运行起来

系统tick定时器

  • 可以称之为 操作系统的心跳,一般是个周期性的定时器,比如 1ms 为周期,周期性的执行。

  • 通过验证,mps2-an385 支持 systick 定时器,简单配置后,就可以实现 系统 tick 定时器功能

  • 修改完善 drv_common.c

#include <rtthread.h>
#include <board.h>
#include "CMSDK_CM3.h"
#include "system_CMSDK_CM3.h"

static uint32_t _systick_ms = 1;

/**
 * This is the timer interrupt service routine.
 *
 */
void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    rt_tick_increase();

    /* leave interrupt */
    rt_interrupt_leave();
}

/* SysTick configuration */
void rt_hw_systick_init(void)
{
    SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);

    NVIC_SetPriority(SysTick_IRQn, 0xFF);

    _systick_ms = 1000u / RT_TICK_PER_SECOND;
    if(_systick_ms == 0)
        _systick_ms = 1;
}
  • rt_hw_systick_init 当前被 board.c 中的 rt_hw_board_init 调用,而 rt_hw_board_init 又被 RT-Thread rtthread_startup 调用, rtthread_startup 被 RT-Thread 入口函数 entry 调用,这个 entry 又被 启动文件 Reset_Handler 调用,Reset_Handler 是 MCU 上电执行的函数。

  • 初始化 rt_hw_systick_init 后,VS Code gdb 调试,发现可以周期性进入 SysTick_Handler,也就是 systick 定时器的中断处理函数,在这个函数中,执行 rt_tick_increase,基于时间片的系统调度、系统定时与延时等,都依赖 系统 tick 定时器,也就是移植 RT-Thread,必须有周期性 tick 定时器

在这里插入图片描述

系统内存管理

  • mps2-an385 的 RAM 4MB,当前内存配置在 board.h 中实现
#ifndef __BOARD_H__
#define __BOARD_H__

#include <rtconfig.h>

#if defined(__CC_ARM)
extern int Image$$RW_IRAM1$$ZI$$Limit;
#define HEAP_BEGIN      ((void*)&Image$$RW_IRAM1$$ZI$$Limit)
#elif defined(__GNUC__)
extern int __bss_end__;
#define HEAP_BEGIN      ((void*)&__bss_end__)
#endif

#define HEAP_END        (void*)(0x20000000 + 4 * 1024 * 1024)

void rt_hw_board_init(void);
void rt_hw_systick_init(void);

#endif
  • mps2-an385 qemu 不需要配置系统的时钟,所以 board.c 主要用于实现 rt_hw_board_init,初始化内存、串口、系统 tick 定时器,并设置 MSH shell 串口终端
#include <rthw.h>
#include <rtthread.h>
#include "board.h"
#include "drv_uart.h"

void idle_wfi(void)
{
    asm volatile ("wfi");
}

/**
 * This function will initialize board
 */
void rt_hw_board_init(void)
{
    /* initialize system heap */
    rt_system_heap_init(HEAP_BEGIN, HEAP_END);

    /* initialize hardware interrupt */

    rt_hw_systick_init();
    rt_hw_uart_init();

    rt_components_board_init();
    rt_console_set_device(RT_CONSOLE_DEVICE_NAME);

    rt_thread_idle_sethook(idle_wfi);
}

串口驱动适配

  • RT-Thread 具有 MSH shell 组件,这个组件在程序调试中非常的有用,合理的利用 shell,可以实现一些复杂的操作

  • mps2-an385 的串口配置并不复杂,不像STM32 那样有各种配置,所以当前简单的适配了一下,实现了串口中断接收、串口发送,即可让 MSH shell 串口正常工作

  • 当前初步验证, mps2-an385 uart0 可以正常用于 MSH shell

  • drv_uart.c 适配如下:

#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#include "board.h"

#include "CMSDK_CM3.h"

enum
{
#ifdef BSP_USING_UART0
    UART0_INDEX,
#endif
#ifdef BSP_USING_UART1
    UART1_INDEX,
#endif
};

/* qemu uart dirver class */
struct uart_instance
{
    const char *name;
    CMSDK_UART_TypeDef *handle;
    IRQn_Type irq_num;
    int uart_index;
    struct rt_serial_device serial;
};

#if defined(BSP_USING_UART0)
#ifndef UART0_CONFIG
#define UART0_CONFIG                                                \
    {                                                               \
        .name = "uart0",                                            \
        .handle = CMSDK_UART0,                                      \
        .irq_num = UART0RX_IRQn,                                    \
        .uart_index = UART0_INDEX,                                  \
    }
#endif /* UART0_CONFIG */
#endif /* BSP_USING_UART0 */

#if defined(BSP_USING_UART1)
#ifndef UART1_CONFIG
#define UART1_CONFIG                                                \
    {                                                               \
        .name = "uart1",                                            \
        .handle = CMSDK_UART1,                                      \
        .irq_num = UART1RX_IRQn,                                    \
        .uart_index = UART1_INDEX,                                  \
    }
#endif /* UART1_CONFIG */
#endif /* BSP_USING_UART1 */

static struct uart_instance uart_obj[] =
{
#ifdef BSP_USING_UART0
    UART0_CONFIG,
#endif
#ifdef BSP_USING_UART1
    UART1_CONFIG,
#endif
};

static void uart_isr(struct rt_serial_device *serial)
{
    /* UART in mode Receiver */
    rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
}

void UART0RX_Handler(void)
{
#ifdef BSP_USING_UART0
    uint32_t irq_status = 0x00;
    /* enter interrupt */
    rt_interrupt_enter();

    uart_isr(&(uart_obj[UART0_INDEX].serial));

    irq_status = uart_obj[UART0_INDEX].handle->INTCLEAR;
    uart_obj[UART0_INDEX].handle->INTCLEAR = irq_status;
    /* leave interrupt */
    rt_interrupt_leave();
#endif
}

void UART1RX_Handler(void)
{
#ifdef BSP_USING_UART1
    uint32_t irq_status = 0x00;
    /* enter interrupt */
    rt_interrupt_enter();

    uart_isr(&(uart_obj[UART1_INDEX].serial));
    irq_status = uart_obj[UART1_INDEX].handle->INTCLEAR;
    uart_obj[UART1_INDEX].handle->INTCLEAR = irq_status;

    /* leave interrupt */
    rt_interrupt_leave();
#endif
}

static rt_err_t uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{
    struct uart_instance *instance;

    RT_ASSERT(serial != RT_NULL);
    instance = (struct uart_instance *)serial->parent.user_data;

    uart_obj[instance->uart_index].handle->BAUDDIV = 16;
    uart_obj[instance->uart_index].handle->CTRL = CMSDK_UART_CTRL_RXIRQEN_Msk | CMSDK_UART_CTRL_RXEN_Msk | CMSDK_UART_CTRL_TXEN_Msk;
    NVIC_EnableIRQ(uart_obj[instance->uart_index].irq_num);
    uart_obj[instance->uart_index].handle->STATE = 0;

    return RT_EOK;
}

static rt_err_t uart_control(struct rt_serial_device *serial, int cmd, void *arg)
{
    struct uart_instance *instance;

    RT_ASSERT(serial != RT_NULL);
    instance = (struct uart_instance *)serial->parent.user_data;

    switch (cmd)
    {
    case RT_DEVICE_CTRL_CLR_INT:
        /* disable rx irq */
        instance->handle->CTRL &= ~CMSDK_UART_CTRL_RXIRQEN_Msk;
        break;

    case RT_DEVICE_CTRL_SET_INT:
        /* enable rx irq */
        instance->handle->CTRL |= CMSDK_UART_CTRL_RXIRQEN_Msk;
        break;
    }

    return RT_EOK;
}

static int uart_putc(struct rt_serial_device *serial, char c)
{
    struct uart_instance *instance;

    RT_ASSERT(serial != RT_NULL);
    instance = (struct uart_instance *)serial->parent.user_data;

    instance->handle->DATA = c;

    return 1;
}

static int uart_getc(struct rt_serial_device *serial)
{
    int ch;
    uint32_t state = 0;
    struct uart_instance *instance;

    RT_ASSERT(serial != RT_NULL);
    instance = (struct uart_instance *)serial->parent.user_data;

    ch = -1;
    if (!instance)
        return ch;

    state = instance->handle->STATE;
    if (state)
    {
        ch = instance->handle->DATA & 0xff;
        instance->handle->STATE = 0;
    }

    return ch;
}

static const struct rt_uart_ops _uart_ops =
{
    uart_configure,
    uart_control,
    uart_putc,
    uart_getc,
};

int rt_hw_uart_init(void)
{
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    rt_err_t result = 0;

    for (rt_size_t i = 0; i < sizeof(uart_obj) / sizeof(struct uart_instance); i++)
    {
        /* init UART object */
        uart_obj[i].serial.ops = &_uart_ops;
        uart_obj[i].serial.config = config;

        /* register UART device */
        result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].name,
                                       RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
                                       &uart_obj[i]);
        RT_ASSERT(result == RT_EOK);
    }

    return result;
}
  • 串口适配的主要流程: 定义串口设备结构,实现 uart_putc, uart_getc, uart_configure,通过 rt_hw_serial_register 注册串口设备,编写 串口中断的处理函数

系统运行

  • 以上适配了 内存、系统 tick 定时器与 MSH shell 串口后,通过 scons --menuconfig 配置 MSH shell 串口为 uart0,RT-Thread 运行起来了

在这里插入图片描述

  • ./qemu.sh 运行信息

在这里插入图片描述

  • 以上,说明RT-Thread qemu mps2-an385 bsp 制作初步完成,当前初步验证,无法支持文件系统,并且其他的外设欠缺资料,因为移植宣告完成。

  • 可以通过 VS Code gdb 调试,熟悉 RT-Thread 系统调用、内存分配、测试验证各个 RT-Thread 功能模块

小结

  • 本篇通过 bsp 适配 内存管理、串口驱动、系统 tick 定时器,让 RT-Thread 跑起来,qemu mps2-an385 bsp 在 RT-Thread 上移植适配完成。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CPU密集型和IO密集型任务的权衡:如何找到最佳平衡点

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、CPU密集型与IO密集型3.1、CPU密集型3.2、I/O密…

【字符流】案例:集合到文件

案例&#xff1a;集合到文件 1.需求&#xff1a; 把ArrayList集合中的字符串数据写入到文本文件。要求&#xff1a;每一个字符串元素作为文件中的一行数据 2.思路 创建ArrayList集合往集合中存储字符串元素创建字符缓冲输出流对象遍历集合&#xff0c;得到每一个字符串数据调…

SpringBoot中间件—ORM(Mybatis)框架实现

目录 定义 需求背景 方案设计 代码展示 UML图 实现细节 测试验证 总结 源码地址&#xff08;已开源&#xff09;&#xff1a;https://gitee.com/sizhaohe/mini-mybatis.git 跟着源码及下述UML图来理解上手会更快&#xff0c;拒绝浮躁&#xff0c;沉下心来搞 定义&#x…

界面控件DevExpress WPF数据编辑器组件,让数据处理更灵活!(二)

界面控件DevExpress WPF编辑器库可以帮助用户提供直观的用户体验&#xff0c;具有无与伦比的运行时选项和灵活性。WPF数据编辑器提供了全面的掩码和数据验证支持&#xff0c;可以独立使用&#xff0c;也可以作为容器控件(如DevExpress WPF Grid和WPF TreeList)中的单元格编辑器…

【pytho】request五种种请求处理为空和非空处理以及上传excel,上传图片处理

一、python中请求处理 request.args获取的是个字典&#xff0c;所以可以通过get方式获取请求参数和值 request.form获取的也是个字典&#xff0c;所以也可以通过get方式获取请求的form参数和值 request.data&#xff0c;使用过JavaScript&#xff0c;api调用方式进行掺入jso…

Cmake笔记记录

工作后开发内容都是在Linux系统下完成&#xff0c;cmake使用比较频繁&#xff0c;找到一篇很不错的CMake笔记。 记录下来方便自己查阅。 目录 1.CMake介绍2.示例一&#xff1a;编译单个文件CMAKE_BUILD_TYPE 3.示例二&#xff1a;编译同个目录下多个文件aux_source_directory()…

QT通过TCP协议发送结构体数据

文章目录 前言1. memcpy方式1.1 发送整个结构体1.2 发送部分数据 2. QDataStream2.1 符号<<2.2 wrieteRawData 总结 前言 做上位机的时候下发数据&#xff0c;数据是一个结构体&#xff0c;这里就阐述一下怎么通过TCP协议发送结构体。 我自己写的时候主要通过两种方式&a…

JavaWeb 速通HTTP

目录 一、HTTP快速入门 1.HTTP简介 : 2.HTTP请求头 : 3.HTTP响应头 : 二、HTTP响应状态码 1.基本介绍 : 2.常见状态码 : 3.状态码的分类 : 4.完整状态码汇总 : 三、HTTP请求包和响应包 1.请求包分析 : 1 GET请求 (1) 说明 (2) doGet返回数据给浏览器 (3) form表单提…

华为云CodeArts产品体验的心得体会及想法

文章目录 前言CodeArts 的产品优势一站式软件开发生产线研发安全Built-In华为多年研发实践能力及规范外溢高质高效敏捷交付 功能特性说明体验感受问题描述完结 前言 华为云作为一家全球领先的云计算服务提供商&#xff0c;致力于为企业和个人用户提供高效、安全、可靠的云服务。…

C++---树形DP---树的中心(每日一道算法2023.7.19)

注意事项&#xff1a; 本题为"树形DP—树的最长路径"的近似题&#xff0c;同时涉及到 单链表模拟邻接表存储图 的操作&#xff0c;建议先理解那篇文章。 题目&#xff1a; 给定一棵树&#xff0c;树中包含 n 个结点&#xff08;编号1~n&#xff09;和 n−1 条无向边…

uniapp查看ios打包后的Info.plist文件

最近在用 uni 开发 ios 的时候给项目添加了自定义的 Info.plist 文件&#xff0c;但是打包后发现并没有生效&#xff0c;才有了查看打包后的 Info.plist 文件想法。 HBuilderX3.6.5起&#xff0c;支持直接在应用项目中配置 iOS 平台的 Info.plist 和 资源文件&#xff08;Bundl…

C# List 详解二

目录 5.Clear() 6.Contains(T) 7.ConvertAll(Converter) ,toutput> 8.CopyTo(Int32, T[], Int32, Int32) 9.CopyTo(T[]) 10.CopyTo(T[], Int32) C# List 详解一 1.Add(T)&#xff0c;2.AddRange(IEnumerable)&#xff0c;3.AsReadOnly()&…

RabbitMQ集群搭建

说明&#xff1a;集群&#xff0c;不管是Redis集群&#xff0c;还是MQ集群&#xff0c;都是为了提高系统的可用性&#xff0c;使系统不至于因为Redis、MQ宕机而崩溃。本文介绍RabbitMQ集群搭建&#xff0c;RabbitMQ集群分为以下三类&#xff1a; 普通集群 镜像集群 仲裁队列 …

循环链表的实现

循环链表简介 简单来说&#xff0c;单链表像一个小巷&#xff0c;无论怎么样最终都能从一端走到另一端&#xff0c;循环链表则像一个有传送门的小巷&#xff0c;因为循环链表当你以为你走到结尾的时候&#xff0c;其实你又回到了开头。循环链表和非循环链表其实创建的过程以及…

程序员基础知识—IP地址

文章目录 一、什么是IP地址二、IP地址的分类三、子网掩码 一、什么是IP地址 IP地址就像我们需要打电话时的电话号码一样&#xff0c;它用来标识网络中的一台主机&#xff0c;每台主机至少有一个IP地址&#xff0c;而且这个IP地址是全网唯一的。IP地址由网路号和主机号两部分组…

Elasticsearch/Enterprise Search/Kibana安装记录

目录 Elasticsearch的安装导入 elasticsearch PGP密钥 安装使用APT安装手动下载安装 启动elasticsearch安全功能重新配置节点以加入现有集群启用系统索引的自动创建功能运行Elasticsearch(在systemd下)检查Elasticsearch是否正在运行Elasticsearch配置外网访问 第三方包安装ela…

Python学习(十六)柱状图

zdaPython学习&#xff08;十四&#xff09;折线图开发_yikuaidabin的博客-CSDN博客 案例数据资源 ↑ """演示基础柱状图的开发 """ from pyecharts.charts import Bar from pyecharts.options import LabelOpts # 使用Bar构建基础柱状图 bar …

【网络】socket——TCP网络通信 | 日志功能 | 守护进程

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《网络》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 上篇文章中本喵介绍了UDP网络通信的socket代码&#xff0c;今天介绍TCP网络通信的socket代码。 TCP &a…

深度理解 Spring AOP

一、什么是AOP(面向切面编程)&#xff1f;&#x1f349; AOP 为 Aspect Oriented Programming 的缩写&#xff0c;意思为面向切面编程&#xff0c;是通过预编译方式 和运行期 动态代理 实现程序功能的统一维护的一种技术。 AOP &#xff08;面向切面编程&#xff09;是 OOP&a…

深度理解 Spring IOC

Spring容器高层视图 Spring 启动时读取应用程序提供的Bean配置信息&#xff0c;并在Spring容器中生成一份相应的Bean配置注册表&#xff0c;然后根据这张注册表实例化Bean&#xff0c;装配好Bean之间的依赖关系&#xff0c;为上层应用提供准备就绪的运行环境。 Bean缓存池&…