3、第一个工程:创建空白工程、配置时钟树(PLL/Sub-clock)、点亮LED(GPIO输出)
好,咱们正式开始动手了。这一章的目标很明确——从零创建一个RH850工程,把时钟配好,然后让一个LED亮起来。听起来简单,但这里面有几个坑,我当年刚接触RH850时都踩过。咱们一步步来。
3.1 创建空白工程
我个人习惯用CS+(以前叫CubeSuite+)来开发RH850。当然你也可以用Eclipse + GHS,但CS+对新手更友好,配置时钟和外设时图形化界面很直观。
创建工程的步骤大致如下:
- 打开CS+,选择 File → New → Project。
- 选择芯片系列:RH850,具体型号选你板子上的那颗,比如 R7F7010373。
- 工程类型选 Application,编译器用 CC-RH(瑞萨自家的C编译器)。
- 指定工程路径和名称,比如
RH850_LED_Blink。 - 点击 Finish,CS+会自动生成一个包含main.c、startup.asm和链接脚本的骨架工程。
嗯,这里要注意:生成的startup.asm里已经做了最基本的初始化,比如栈指针设置、BSS段清零。但时钟和GPIO还得我们自己配。
3.2 时钟树配置——RH850的心脏
时钟树是MCU的心脏。RH850的时钟系统比8位单片机复杂不少,但说白了就是几个振荡器加一堆分频倍频器。咱们先看一张整体结构图:
从图上你能看到,主振荡器(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作为系统时钟源 */
3.2.2 配置副时钟(Sub-clock)
副时钟一般用于RTC(实时时钟)和看门狗。如果你不需要低功耗模式下的定时唤醒,副时钟可以跳过。但如果你要做车载的定时唤醒功能,那副时钟就绕不开。
配置副时钟的步骤:
- 使能副振荡器:
SUBCLKCTL |= 0x01; - 等待振荡稳定:
while (!(SUBCLKSTS & 0x01)); - 选择副时钟作为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位 |
说白了,要让一个引脚输出高电平,你需要做三件事:
- 把引脚功能设为GPIO(PFCx寄存器)。
- 把引脚方向设为输出(PMx寄存器)。
- 往数据寄存器写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常亮 */
}
}
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();
}
}
3.4 常见问题与避坑
- LED不亮? 先检查PFCx有没有配成GPIO模式,再量一下引脚电平。我曾经遇到过焊反了LED极性,结果代码怎么改都不亮。
- 时钟不对? 用示波器测PLLCLK输出引脚(如果有的话),或者用定时器输出一个方波来验证频率。
- 下载后没反应? 检查调试器连接,确保芯片没进入保护模式。RH850有安全位,新芯片第一次下载可能需要先解锁。
好了,到这里你已经完成了第一个RH850工程。时钟树配好了,LED也亮了。别看这只是个点灯程序,背后的时钟配置和GPIO操作是所有外设驱动的基础。后面咱们要做的UART、CAN、定时器,都离不开这两步。