找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2590

积分

1

好友

359

主题
发表于 昨天 10:00 | 查看: 5| 回复: 0

Cortex-M 是 ARM 面向微控制器 (MCU) 领域的核心架构,其发展至今已有约二十年历史。其中,Cortex-M3 内核堪称经典,是目前市场上应用最广泛的内核之一。

而截至目前,Arm 推出的 Cortex-M85 内核依然是其 Cortex-M 系列中性能最强大的成员。

Cortex-M85、Cortex-M55、Cortex-M52处理器特性对比

Cortex-M85 相较于经典的 Cortex-M3 内核,在功能和性能上都有了大幅升级。那么,对于普通的嵌入式开发者而言,使用基于 Cortex-M85 和 Cortex-M3 内核的单片机,在编程层面究竟有哪些异同呢?

cm3.h 与 cm85.h 的核心文件对比

对于单片机开发者来说,最常接触的内核相关文件莫过于 core_cm3.hcore_cm85.h。这些头文件定义了与内核相关的大部分功能,我们日常调用的许多底层接口也源于此。

首先,我们可以直观地对比一下这两个源文件:

core_cm3.h与core_cm85.h代码对比截图

通过对比源代码可以清晰地看到,cm85.h 的文件行数(4672行)远多于 cm3.h(1943行)。虽然左侧标记的差异部分看起来很多,但其中大部分是新增的代码行和宏定义。

如果仔细比对,你会发现许多基础函数是完全一致的。例如,常用的系统复位函数 __NVIC_SystemReset,其在两个文件中的实现几乎一模一样:

系统复位函数__NVIC_SystemReset代码对比

__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void)
{
  __DSB();                                                          /* Ensure all outstanding memory accesses included
                                                                      buffered write are completed before reset */
  SCB->AIRCR  = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos)    |
                           (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
                           SCB_AIRCR_SYSRESETREQ_Msk    );         /* Keep priority group unchanged */
  __DSB();                                                          /* Ensure completion of memory access */

  for(;;)                                                           /* wait until reset */
  {
    __NOP();
  }
}

再比如,作为本文重点的系统滴答定时器配置函数 SysTick_Config,其实现代码在两个内核中也高度一致:

SysTick_Config函数代码对比

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
  {
    return (1UL);                                                   /* Reload value impossible */
  }

  SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0UL;                                             /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */
  return (0UL);                                                     /* Function successful */
}

由此可见,在 Cortex-M3 单片机上常用的这些核心函数接口,在 Cortex-M85 上基本被原样保留。这充分说明了 Cortex-M85 在软件接口层面做到了对 Cortex-M3 的良好向下兼容,降低了开发者的迁移成本。

Cortex-M85 单片机上的 SysTick 实战

定时器是单片机开发中最常用的模块之一,无论是实现精准延时,还是作为实时操作系统 (RTOS) 的心跳时钟,都离不开它。为此,Arm 在所有 Cortex-M 内核中都集成了一个标准的 SysTick 定时器模块。

下面,我们结合瑞萨电子的 RA8D1(基于 Cortex-M85 内核)单片机,手把手演示 SysTick 的实际用法。

开发环境与工具准备

本次演示使用瑞萨的 e2 studio 集成开发环境及其配套的 FSP 软件包。我们将通过一个简单的 GPIO 翻转示例,来验证 SysTick 的延时精度。

1. 创建 RA8D1 单片机项目

首先,在 e2 studio 中新建一个 C/C++ 项目,并选择对应的“Renesas RA C/C++ Project”模板。

在e2 studio中新建C/C++项目

选择Renesas RA项目模板

将项目命名为 RA8D1_SysTick

设置项目名称和路径

在设备选择页面,找到并选中我们目标芯片的型号:R7FA8D1BEC

选择R7FA8D1BEC设备

后续依次选择项目类型(非安全项目)、构建工件类型(可执行文件)、RTOS(无)和项目模板(Bare Metal - Minimal)。这些步骤只需按照向导默认或推荐选项点击“下一步”即可。

选择非TrustZone项目类型

选择构建工件和RTOS

选择Bare Metal最小模板

最终,一个完整的、包含必要BSP和CMSIS支持的工程就创建好了。

工程创建完成后的摘要信息

2. 配置工程参数

我们需要配置一个 GPIO 引脚用于测试。这里选择 PA01 引脚,并将其配置为通用输出模式。

配置PA01引脚为输出模式

接着,配置系统时钟树。一个正确的时钟配置是 SysTick 能够准确计时的基础。

配置系统时钟树

为了方便烧录,我们还可以在工具链设置中,勾选生成 Intel HEX 格式的输出文件。

配置输出文件格式为Intel HEX

3. 编写代码并演示效果

在自动生成的 hal_entry.c 文件的主循环中,我们添加两行代码:一行用于翻转 PA01 引脚电平,另一行调用 SysTick 延时函数。

while(1)
{
    R_PORT10->PODR ^= 1<<(BSP_IO_PORT_10_PIN_01 & 0xFF);    // PA01电平翻转
    R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS);    // SysTick延时1毫秒
}

在hal_entry.c中添加测试代码

编译并下载程序到开发板后,使用示波器或逻辑分析仪测量 PA01 引脚波形。

当设置为 1毫秒 翻转一次时,波形周期为 2ms,频率 500Hz,占空比 50%。在这个时间尺度下,SysTick 的延时误差极小,几乎可以忽略不计(使用 100kHz 采样率观测不到明显误差)。

1ms延时下的波形测量

如果将采样率提升至 100MHz,则能观察到纳秒级别的微小误差。当然,这类误差是由晶振精度、软件开销等多种因素共同造成的,相对于毫秒级的延时,其影响微乎其微。

100MHz采样率下观察到的微小误差

4. 关键源码解析

为了让代码执行效率最高、软件误差最小,示例中直接通过操作寄存器来翻转 GPIO:

R_PORT10->PODR ^= 1<<(BSP_IO_PORT_10_PIN_01 & 0xFF);

而延时所调用的 R_BSP_SoftwareDelay 函数,是 FSP 软件包提供的一个阻塞式延时接口,其延时单位通过第二个参数指定:

R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS);

其中 BSP_DELAY_UNITS_MILLISECONDS 是一个枚举值,系统共定义了三种时间单位:

typedef enum
{
    BSP_DELAY_UNITS_SECONDS      = 1000000, ///< 延时单位为秒
    BSP_DELAY_UNITS_MILLISECONDS = 1000,    ///< 延时单位为毫秒
    BSP_DELAY_UNITS_MICROSECONDS = 1        ///< 延时单位为微秒
} bsp_delay_units_t;

R_BSP_SoftwareDelay 函数的本质就是利用 SysTick 定时器实现的精准延时。 查看其底层实现(如下图所示)可以发现,它通过计算系统时钟频率和所需延时周期,最终在循环中查询或等待 SysTick 的计数,这与在 Cortex-M0/M23 等内核上使用 SysTick 的原理是完全一致的。

R_BSP_SoftwareDelay函数底层实现代码

因此,从实际使用的角度来看,在 Cortex-M85 上使用 SysTick 进行延时,与在 Cortex-M3 上并无二致。这再次印证了 Arm 在 计算机架构 设计上对软件生态兼容性的重视,确保了开发者能够在升级硬件平台的同时,最大化地复用已有的知识和代码。




上一篇:MySQL开源项目开发陷入停滞:GitHub代码库已超3个月无新提交
下一篇:Google SEO网站被惩罚后如何恢复?分享我的九个月实战经验
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-16 02:04 , Processed in 0.227791 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表