3、第一个工程:创建空白工程、配置时钟树(PLL/Sub-clock)、点亮LED(GPIO输出)

好,咱们正式开始动手了。这一章的目标很明确——从零创建一个RH850工程,把时钟配好,然后让一个LED亮起来。听起来简单,但这里面有几个坑,我当年刚接触RH850时都踩过。咱们一步步来。

3.1 创建空白工程

我个人习惯用CS+(以前叫CubeSuite+)来开发RH850。当然你也可以用Eclipse + GHS,但CS+对新手更友好,配置时钟和外设时图形化界面很直观。

创建工程的步骤大致如下:

  1. 打开CS+,选择 File → New → Project。
  2. 选择芯片系列:RH850,具体型号选你板子上的那颗,比如 R7F7010373。
  3. 工程类型选 Application,编译器用 CC-RH(瑞萨自家的C编译器)。
  4. 指定工程路径和名称,比如 RH850_LED_Blink。
  5. 点击 Finish,CS+会自动生成一个包含main.c、startup.asm和链接脚本的骨架工程。
小提示: 如果你用的是开发板,建议直接选板级支持包(BSP)模板。CS+里有些官方开发板的预设,能省掉不少底层配置的麻烦。

嗯,这里要注意:生成的startup.asm里已经做了最基本的初始化,比如栈指针设置、BSS段清零。但时钟和GPIO还得我们自己配。

3.2 时钟树配置——RH850的心脏

时钟树是MCU的心脏。RH850的时钟系统比8位单片机复杂不少,但说白了就是几个振荡器加一堆分频倍频器。咱们先看一张整体结构图:

RH850 时钟树结构(简化版) 主振荡器 (8MHz) 副振荡器 (32.768kHz) PLL (x20 → 160MHz) 系统时钟分频器 外设时钟分频器 CPU / 内存 / 总线 GPIO / UART / 定时器 说明:PLL输出 = 主振荡器频率 × 倍频系数。RH850典型配置:8MHz × 20 = 160MHz 副振荡器主要用于RTC和低功耗模式,不参与CPU时钟

从图上你能看到,主振荡器(8MHz)经过PLL倍频到160MHz,然后分频供给CPU和外设。副振荡器(32.768kHz)走的是另一条路,主要给RTC用。

3.2.1 配置PLL

配置PLL是时钟树里最关键的一步。我见过有人忘了配PLL,结果芯片一直跑在8MHz,LED闪得跟蜗牛似的。你想想看,160MHz和8MHz差了20倍,性能完全不是一个量级。

在CS+里,配置PLL有两种方式:

  • 图形化方式: 打开 Code Generator(代码生成器),在 Clock 选项卡里直接选PLL倍频系数。CS+会自动生成初始化代码。
  • 手动方式: 直接操作寄存器。我个人建议新手先用图形化方式,等熟悉了再手动配。

手动配置的核心寄存器是 PLLCTL 和 PLLDIV。下面是一个典型配置代码:

/* PLL配置:8MHz x 20 = 160MHz */
PLLCTL = 0x14;      /* 倍频系数 = 20 */
PLLDIV = 0x00;      /* 不分频 */

/* 等待PLL锁定 */
while (!(PLLSTS & 0x01));  /* 等待LOCK位为1 */

/* 切换系统时钟到PLL输出 */
SYSCLK = 0x01;      /* 选择PLL作为系统时钟源 */
注意: 切换时钟源之前,一定要等PLL锁定。我曾经在项目里忘了加这个等待循环,结果芯片跑起来时序全乱,串口数据全是乱码。排查了整整半天才发现是时钟没稳定就切过去了。

3.2.2 配置副时钟(Sub-clock)

副时钟一般用于RTC(实时时钟)和看门狗。如果你不需要低功耗模式下的定时唤醒,副时钟可以跳过。但如果你要做车载的定时唤醒功能,那副时钟就绕不开。

配置副时钟的步骤:

  1. 使能副振荡器:SUBCLKCTL |= 0x01;
  2. 等待振荡稳定:while (!(SUBCLKSTS & 0x01));
  3. 选择副时钟作为RTC时钟源。

副时钟的振荡器启动时间比较长,32.768kHz的晶振需要1~2秒才能稳定。嗯,这里要注意,别在初始化里死等太久,否则会影响上电启动速度。我一般会加个超时处理。

3.3 GPIO输出——点亮LED

时钟配好了,接下来就是让硬件有点反应。点亮LED是嵌入式开发的"Hello World",虽然简单,但GPIO的配置流程是通用的,后面所有外设都要用到。

3.3.1 GPIO模块结构

RH850的GPIO模块和传统单片机不太一样。它把引脚功能分成了几个寄存器组:

寄存器 功能 位宽
Px 端口数据寄存器(读/写引脚电平) 8/16位
PMx 端口模式寄存器(0=输出,1=输入) 8/16位
PFCx 引脚功能选择寄存器(GPIO/外设功能) 8/16位
PIPCx 引脚输入控制寄存器 8/16位

说白了,要让一个引脚输出高电平,你需要做三件事:

  1. 把引脚功能设为GPIO(PFCx寄存器)。
  2. 把引脚方向设为输出(PMx寄存器)。
  3. 往数据寄存器写1(Px寄存器)。

3.3.2 点亮LED的完整代码

假设LED接在P10_0引脚上(低电平点亮),完整代码如下:

#include "iodefine.h"   /* RH850寄存器定义头文件 */

void main(void)
{
    /* 第一步:配置时钟 */
    PLLCTL = 0x14;      /* 8MHz x 20 = 160MHz */
    PLLDIV = 0x00;
    while (!(PLLSTS & 0x01));  /* 等待PLL锁定 */
    SYSCLK = 0x01;      /* 切换到PLL时钟 */

    /* 第二步:配置GPIO */
    PFC10 &= ~0x01;     /* P10_0设为GPIO功能(非外设功能) */
    PM10  &= ~0x01;     /* P10_0设为输出方向 */
    P10   |= 0x01;      /* P10_0输出高电平(假设LED高电平点亮) */

    /* 或者低电平点亮:P10 &= ~0x01; */

    while(1)
    {
        /* 主循环空转,LED常亮 */
    }
}
关键点: 很多新手只配了PMx(方向寄存器),忘了配PFCx(功能选择寄存器)。结果引脚默认是外设功能,GPIO输出根本没生效。我当年第一次调RH850的GPIO时就栽在这个坑里,折腾了半小时才发现PFCx没清零。

3.3.3 让LED闪烁

常亮不过瘾,咱们让LED闪烁起来。加个简单的延时循环:

void delay(void)
{
    volatile unsigned int i;
    for(i = 0; i < 800000; i++);  /* 粗略延时,约500ms @160MHz */
}

void main(void)
{
    /* 时钟配置同上 */
    PLLCTL = 0x14;
    PLLDIV = 0x00;
    while (!(PLLSTS & 0x01));
    SYSCLK = 0x01;

    /* GPIO配置 */
    PFC10 &= ~0x01;
    PM10  &= ~0x01;

    while(1)
    {
        P10 |= 0x01;    /* 点亮 */
        delay();
        P10 &= ~0x01;   /* 熄灭 */
        delay();
    }
}
小技巧: 延时循环里的计数值需要根据实际时钟频率调整。如果你PLL配的是160MHz,800000次循环大约500ms。但不同编译器优化等级会影响循环时间,建议用示波器实测一下。我在项目里一般会用定时器做精确延时,软件延时只用来做初步验证。

3.4 常见问题与避坑

  • LED不亮? 先检查PFCx有没有配成GPIO模式,再量一下引脚电平。我曾经遇到过焊反了LED极性,结果代码怎么改都不亮。
  • 时钟不对? 用示波器测PLLCLK输出引脚(如果有的话),或者用定时器输出一个方波来验证频率。
  • 下载后没反应? 检查调试器连接,确保芯片没进入保护模式。RH850有安全位,新芯片第一次下载可能需要先解锁。

好了,到这里你已经完成了第一个RH850工程。时钟树配好了,LED也亮了。别看这只是个点灯程序,背后的时钟配置和GPIO操作是所有外设驱动的基础。后面咱们要做的UART、CAN、定时器,都离不开这两步。