开发环境:
IDE:MKD 5.30
开发板:RA-Eco-RA4M2
MCU:R7FA4M2AD3CFP
上一章通过控制GPIO的高低电平实现了流水灯,但只是告诉了大家怎么做,如何实现流水灯,本文将深入剖析的GPIO流水灯的前生今世,深入研究流水灯的调用逻辑和数据结构。
1 GPIO配置概述
前面一章大概讲解GPIO流水灯,关于GPIO的内容很多,具体请参看《RA4M2 Group User’s Manual Hardware》中I/O Ports相关的章节吧。
要想控制LED亮灭,就需要做以上三件事:使能时钟,配置GPIO参数,最后循环控制GPIO的高低电平就能实现流水灯的效果,GPIO的寄存器这里就不说了,更多详细的寄存器描述看官方手册就行,下面先来看看RA4M2的时钟。
2 RA4M2的时钟系统
2.1 RA4M2时钟架构
时钟是整个处理器运行的基础,时钟信号推动处理器内各个部分执行相应的指令。时钟系统就是CPU的脉搏,决定CPU速率,像人的心跳一样 只有有了心跳,人才能做其他的事情,而单片机有了时钟,才能够运行执行指令,才能够做其他的处理 (点灯,串口,ADC),时钟的重要性不言而喻。
RA4M2相对Cortex-3内核系列的MCU更为复杂,不仅是外设非常多,就连时钟来源就有8个之多。但我们实际使用的时候只会用到有限的几个外设,使用任何外设都需要时钟才能启动,但并不是所有的外设都需要系统时钟那么高的频率,为了兼容不同速度的设备,有些高速,有些低速,如果都用高速时钟,势必造成浪费,而且,同一个电路,时钟越快功耗越快,同时抗电磁干扰能力也就越弱,所以较为复杂的MCU都是采用多时钟源的方法来解决这些问题,因此便有了RA4M2的时钟系统和时钟树。
RA4M2不同的时钟源可以用来驱动系统时钟(SCK):
● MOSC(Main Clock Oscillator):主时钟振荡器,连接外部 8 ~ 24 MHz 高速晶振
● SOSC(Sub-Clock Oscillator):副时钟振荡器,连接外部 32.768 kHz 低速晶振(高速外部时钟信号)
● HOCO(High-speed on-chip oscillator):高速片上振荡器,振荡频率: 16/18/20 MHz
● MOCO(Middle-speed on-chip oscillator):中速片上振荡器,振荡频率: 8 MHz
● LOCO(Low-speed on-chip oscillator):低速片上振荡器,振荡频率: 32.768 kHz
● PLL(Phase Locked Loop): 锁相环时钟,可以对输入时钟进行分频和倍频的功能,PLL 输出频率:100 MHz ~ 200 MHz,PLL2 输出频率:120 MHz ~ 240 MHz
RA4M2有两个二级时钟源:
● IWDTLOCO(IWDT-dedicated clock) :IWDT专用片上振荡器,振荡频率: 15 kHz
● TCK/SWCLK(External clock input for JTAG/ External clock input for SWD) :JTAG/SWD的外部时钟输入,振荡频率: 最大25MHz
每个时钟源在不使用时都可以单独被打开或关闭,这样就可以优化系统功耗。
2.2 RA4M2的时钟系统
RA4M2芯片为了实现低功耗,设计了一个功能完善但却非常复杂的时钟系统。普通的MCU 一般只要配置好 GPIO 的寄存器就可以使用了,但 RA4M2还有一个步骤,就是开启外设时钟。
在 RA4M2中,系统时钟源有很多,从来源可分为外部时钟源和内部时钟源,外部时钟源就是从外部通过接晶振的方式获取时钟源,其中 XTAL、XCIN和TCK/SWCLK是外部时钟源,其他的是内部时钟源。
下面我们看看RA4M2的系统时钟源,我们讲解顺序是按图中红圈标示的顺序:
MOSC是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为8MHz~24MHz。我们的开发板接的是24M的晶振。
SOSC是副时钟振荡器,接频率为 32.768kHz 的石英晶体。这个主要是 RTC 的时钟源。
HOCO是高速内部时钟,高速片上振荡器,振荡频率: 16/18/20 MHz。
MOCO是中速片上振荡器,振荡频率为8 MHz。
LOCO是低速片上振荡器,振荡频率为32.768 kHz。
PLL 为锁相环倍频输出,其时钟输入源可选择为MOSC、HOCO。倍频可选择为10~30倍,但是PLL输出频率最大不得超过 200MHz,PLL2输出频率最大不得超过 240MHz。
图中我们用 A~C标示就是系统时钟进过系统时钟主要流向的地方。
A. 此处是FCLK,最高50MHz.
B. B处就是 RA4M2的系统时钟 ICLK,它是供 RA4M2内核中绝大部分部件工作的时钟源,分别是CPU、DMAC、DTC、闪存 和 SRAM。系统时钟可选择HCOC、MCOC、LOCO、XTAL、SUBCLK、PLL作为时钟源。系统时钟最大频率为 100MHz,当然你也可以超频,不过一般情况为了系统稳定性是没有必要冒风险去超频的。
C. 这里的C处是大部分外设的时钟输入源了。从时钟图上可以看出,其他所有外设的时钟最终来源都是 SCK。SCK 通过分频器分频后送给各模块使用。
3 RA4M2的时钟配置剖析
主频的时钟为默认的 200 MHz,来源是MOSC的24MHz时钟,经过PLL倍频,流向系统时钟。详细请查看RA Smart Configurator文件的配置。
当系统复位后就会进入Reset_Handler() -> SystemInit() -> bsp_clock_init(),bsp_clock_init()函数就是整个系统时钟的初始化部分,重点看这里,代码如下:
[文件路径: ra\fsp\src\bsp\mcu\all\bsp_clocks.c]
void bsp_clock_init (void)
{
R_SYSTEM->PRCR = (uint16_t) BSP_PRV_PRCR_UNLOCK;
#if BSP_FEATURE_BSP_FLASH_CACHE
#if !BSP_CFG_USE_LOW_VOLTAGE_MODE && BSP_FEATURE_BSP_FLASH_CACHE_DISABLE_OPM
R_BSP_FlashCacheDisable();
#else
R_BSP_FlashCacheEnable();
#endif
#endif
#if BSP_FEATURE_BSP_FLASH_PREFETCH_BUFFER
R_FACI_LP->PFBER = 0;
#endif
bsp_clock_freq_var_init();
#if BSP_CLOCK_CFG_MAIN_OSC_POPULATED
#if BSP_CFG_STARTUP_CLOCK_REG_NOT_RESET
if (R_SYSTEM->MOSCCR)
{
FSP_HARDWARE_REGISTER_WAIT(R_SYSTEM->OSCSF_b.MOSCSF, 0U);
R_SYSTEM->MOMCR = BSP_PRV_MOMCR;
R_SYSTEM->MOSCWTCR = (uint8_t) BSP_CLOCK_CFG_MAIN_OSC_WAIT;
}
#else
R_SYSTEM->MOMCR = BSP_PRV_MOMCR;
R_SYSTEM->MOSCWTCR = (uint8_t) BSP_CLOCK_CFG_MAIN_OSC_WAIT;
#endif
#endif
#if BSP_FEATURE_CGC_HAS_SOSC
#if BSP_CLOCK_CFG_SUBCLOCK_POPULATED
if (0U == R_SYSTEM->SOSCCR)
{
R_SYSTEM->SOSCCR = 1U;
R_BSP_SoftwareDelay(BSP_PRV_SUBCLOCK_STOP_INTERVAL_US, BSP_DELAY_UNITS_MICROSECONDS);
}
R_SYSTEM->SOMCR = ((BSP_CLOCK_CFG_SUBCLOCK_DRIVE << BSP_FEATURE_CGC_SODRV_SHIFT) & BSP_FEATURE_CGC_SODRV_MASK);
R_SYSTEM->SOSCCR = 0U;
#if (BSP_CLOCKS_SOURCE_CLOCK_SUBCLOCK == BSP_CFG_CLOCK_SOURCE) || (BSP_PRV_HOCO_USE_FLL)
R_BSP_SubClockStabilizeWait(BSP_CLOCK_CFG_SUBCLOCK_STABILIZATION_MS);
#endif
#else
R_SYSTEM->SOSCCR = 1U;
#endif
#endif
#if BSP_FEATURE_CGC_HAS_HOCOWTCR
#if BSP_FEATURE_CGC_HOCOWTCR_64MHZ_ONLY
#if 64000000 == BSP_HOCO_HZ
#if BSP_CFG_USE_LOW_VOLTAGE_MODE
FSP_HARDWARE_REGISTER_WAIT(R_SYSTEM->OSCSF_b.HOCOSF, 1U);
#else
#endif
R_SYSTEM->HOCOWTCR = BSP_FEATURE_CGC_HOCOWTCR_VALUE;
#endif
#else
R_SYSTEM->HOCOWTCR = BSP_FEATURE_CGC_HOCOWTCR_VALUE;
#endif
#endif
#if !BSP_CFG_USE_LOW_VOLTAGE_MODE
#if BSP_CFG_STARTUP_CLOCK_REG_NOT_RESET
bsp_prv_operating_mode_set(BSP_PRV_OPERATING_MODE_HIGH_SPEED);
#elif BSP_FEATURE_CGC_LOW_VOLTAGE_MAX_FREQ_HZ > 0U
bsp_prv_operating_mode_opccr_set(BSP_PRV_OPERATING_MODE_HIGH_SPEED);
#if !BSP_PRV_HOCO_USED
R_SYSTEM->HOCOCR = 1U;
#endif
#elif BSP_FEATURE_CGC_STARTUP_OPCCR_MODE != BSP_PRV_OPERATING_MODE_HIGH_SPEED
bsp_prv_operating_mode_opccr_set(BSP_PRV_OPERATING_MODE_HIGH_SPEED);
#endif
#endif
#if BSP_PRV_HOCO_USE_FLL
R_SYSTEM->FLLCR2 = BSP_PRV_FLL_FLLCR2;
R_SYSTEM->FLLCR1 = 1U;
#endif
#if BSP_PRV_HOCO_USED
R_SYSTEM->HOCOCR = 0U;
#if BSP_PRV_HOCO_USE_FLL && (BSP_CLOCKS_SOURCE_CLOCK_HOCO != BSP_CFG_PLL_SOURCE)
R_BSP_SoftwareDelay(BSP_PRV_FLL_STABILIZATION_TIME_US, BSP_DELAY_UNITS_MICROSECONDS);
#endif
#if BSP_PRV_STABILIZE_HOCO
FSP_HARDWARE_REGISTER_WAIT(R_SYSTEM->OSCSF_b.HOCOSF, 1U);
#endif
#endif
#if BSP_PRV_MOCO_USED
#if BSP_CFG_STARTUP_CLOCK_REG_NOT_RESET
if (0U != R_SYSTEM->MOCOCR)
{
R_SYSTEM->MOCOCR = 0U;
#if BSP_PRV_STABILIZE_MOCO
R_BSP_SoftwareDelay(BSP_FEATURE_CGC_MOCO_STABILIZATION_MAX_US, BSP_DELAY_UNITS_MICROSECONDS);
#endif
}
#endif
#endif
#if BSP_PRV_LOCO_USED
#if BSP_CFG_STARTUP_CLOCK_REG_NOT_RESET
if (0U != R_SYSTEM->LOCOCR)
{
R_SYSTEM->LOCOCR = 0U;
#if BSP_PRV_STABILIZE_LOCO
R_BSP_SoftwareDelay(BSP_FEATURE_CGC_LOCO_STABILIZATION_MAX_US, BSP_DELAY_UNITS_MICROSECONDS);
#endif
}
#else
R_SYSTEM->LOCOCR = 0U;
#if BSP_PRV_STABILIZE_LOCO
R_BSP_SoftwareDelay(BSP_FEATURE_CGC_LOCO_STABILIZATION_MAX_US, BSP_DELAY_UNITS_MICROSECONDS);
#endif
#endif
#endif
#if BSP_PRV_MAIN_OSC_USED
R_SYSTEM->MOSCCR = 0U;
#if BSP_PRV_STABILIZE_MAIN_OSC
FSP_HARDWARE_REGISTER_WAIT(R_SYSTEM->OSCSF_b.MOSCSF, 1U);
#endif
#endif
#if BSP_PRV_STARTUP_OPERATING_MODE != BSP_PRV_OPERATING_MODE_LOW_SPEED
#if BSP_FEATURE_CGC_HAS_PLL2 && BSP_CFG_PLL2_SOURCE != BSP_CLOCKS_CLOCK_DISABLED
R_SYSTEM->PLL2CCR = BSP_PRV_PLL2CCR;
#if (3U == BSP_FEATURE_CGC_PLLCCR_TYPE)
R_SYSTEM->PLL2CCR2 = BSP_PRV_PLL2CCR2;
#endif
R_SYSTEM->PLL2CR = 0U;
#endif
#endif
#if BSP_PRV_PLL_SUPPORTED && BSP_PRV_PLL_USED
#if BSP_CLOCKS_SOURCE_CLOCK_PLL == BSP_CFG_CLOCK_SOURCE
#if 1U == BSP_FEATURE_CGC_PLLCCR_TYPE
R_SYSTEM->PLLCCR = (uint16_t) BSP_PRV_PLLCCR;
#elif 2U == BSP_FEATURE_CGC_PLLCCR_TYPE
R_SYSTEM->PLLCCR2 = (uint8_t) BSP_PRV_PLLCCR;
#elif 3U == BSP_FEATURE_CGC_PLLCCR_TYPE
R_SYSTEM->PLLCCR = (uint16_t) BSP_PRV_PLLCCR;
R_SYSTEM->PLLCCR2 = (uint16_t) BSP_PRV_PLLCCR2;
#endif
#if BSP_FEATURE_CGC_PLLCCR_WAIT_US > 0
bsp_prv_software_delay_loop(BSP_DELAY_LOOPS_CALCULATE(BSP_PRV_MAX_HOCO_CYCLES_PER_US));
#endif
#endif
R_SYSTEM->PLLCR = 0U;
#if BSP_PRV_STABILIZE_PLL
FSP_HARDWARE_REGISTER_WAIT(R_SYSTEM->OSCSF_b.PLLSF, 1U);
#endif
#endif
#if BSP_CFG_STARTUP_CLOCK_REG_NOT_RESET
#if BSP_TZ_SECURE_BUILD
g_bsp_clock_update_callback = NULL;
#endif
#if BSP_FEATURE_CGC_HAS_CPUCLK
bsp_prv_clock_set(BSP_CFG_CLOCK_SOURCE, BSP_PRV_STARTUP_SCKDIVCR, BSP_PRV_STARTUP_SCKDIVCR2);
#else
bsp_prv_clock_set(BSP_CFG_CLOCK_SOURCE, BSP_PRV_STARTUP_SCKDIVCR, 0);
#endif
#else
bsp_prv_clock_set_hard_reset();
#endif
#if !BSP_CFG_USE_LOW_VOLTAGE_MODE
#if BSP_PRV_STARTUP_OPERATING_MODE != BSP_PRV_OPERATING_MODE_HIGH_SPEED
#if BSP_PRV_PLL_SUPPORTED
#if BSP_CFG_STARTUP_CLOCK_REG_NOT_RESET
if (BSP_PRV_OPERATING_MODE_LOW_SPEED == BSP_PRV_STARTUP_OPERATING_MODE)
{
R_SYSTEM->PLLCR = 1U;
FSP_HARDWARE_REGISTER_WAIT(R_SYSTEM->OSCSF_b.PLLSF, 0U);
#if BSP_FEATURE_CGC_HAS_PLL2
R_SYSTEM->PLL2CR = 1U;
FSP_HARDWARE_REGISTER_WAIT(R_SYSTEM->OSCSF_b.PLL2SF, 0U);
#endif
}
#endif
#endif
bsp_prv_operating_mode_set(BSP_PRV_STARTUP_OPERATING_MODE);
#endif
#endif
#if defined(BSP_PRV_POWER_USE_DCDC) && (BSP_PRV_POWER_USE_DCDC == BSP_PRV_POWER_DCDC_STARTUP) && \
(BSP_PRV_STARTUP_OPERATING_MODE <= BSP_PRV_OPERATING_MODE_MIDDLE_SPEED)
R_BSP_PowerModeSet(BSP_CFG_DCDC_VOLTAGE_RANGE);
#endif
#ifdef BSP_CFG_BCLK_OUTPUT
#if BSP_CFG_BCLK_OUTPUT > 0U
R_SYSTEM->BCKCR = BSP_CFG_BCLK_OUTPUT - 1U;
R_SYSTEM->EBCKOCR = 1U;
#else
#if BSP_CFG_STARTUP_CLOCK_REG_NOT_RESET
R_SYSTEM->EBCKOCR = 0U;
#endif
#endif
#endif
#ifdef BSP_CFG_SDCLK_OUTPUT
R_SYSTEM->SDCKOCR = BSP_CFG_SDCLK_OUTPUT;
#endif
#if BSP_CFG_CLKOUT_SOURCE == BSP_CLOCKS_CLOCK_DISABLED
#if BSP_CFG_STARTUP_CLOCK_REG_NOT_RESET
R_SYSTEM->CKOCR = 0U;
#endif
#else
uint8_t ckocr = BSP_CFG_CLKOUT_SOURCE | (BSP_CFG_CLKOUT_DIV << BSP_PRV_CKOCR_CKODIV_BIT);
R_SYSTEM->CKOCR = ckocr;
ckocr |= (1U << BSP_PRV_CKOCR_CKOEN_BIT);
R_SYSTEM->CKOCR = ckocr;
#endif
#if BSP_PRV_STARTUP_OPERATING_MODE != BSP_PRV_OPERATING_MODE_LOW_SPEED
#if BSP_CFG_UCK_SOURCE != BSP_CLOCKS_CLOCK_DISABLED
#if BSP_FEATURE_BSP_HAS_USB_CLOCK_DIV && !BSP_FEATURE_BSP_HAS_USBCKDIVCR
R_SYSTEM->SCKDIVCR2 = BSP_PRV_UCK_DIV << BSP_PRV_SCKDIVCR2_UCK_BIT;
#endif
#if BSP_FEATURE_BSP_HAS_USB_CLOCK_REQ
R_SYSTEM->USBCKCR_b.USBCKSREQ = 1;
FSP_HARDWARE_REGISTER_WAIT(R_SYSTEM->USBCKCR_b.USBCKSRDY, 1U);
R_SYSTEM->USBCKDIVCR = BSP_PRV_UCK_DIV;
R_SYSTEM->USBCKCR = BSP_CFG_UCK_SOURCE | R_SYSTEM_USBCKCR_USBCKSREQ_Msk;
#endif
#if BSP_FEATURE_BSP_HAS_USB_CLOCK_SEL
#if BSP_FEATURE_BSP_HAS_USB_CLOCK_SEL_ALT
#if BSP_CLOCKS_SOURCE_CLOCK_PLL == BSP_CFG_UCK_SOURCE
R_SYSTEM->USBCKCR_ALT = 0;
#elif BSP_CLOCKS_SOURCE_CLOCK_HOCO == BSP_CFG_UCK_SOURCE
R_SYSTEM->USBCKCR_ALT = 1;
#endif
#else
R_SYSTEM->USBCKCR = BSP_CFG_UCK_SOURCE;
#endif
#endif
#if BSP_FEATURE_BSP_HAS_USB_CLOCK_REQ
FSP_HARDWARE_REGISTER_WAIT(R_SYSTEM->USBCKCR_b.USBCKSRDY, 0U);
#endif
#endif
#endif
#if BSP_FEATURE_BSP_HAS_OCTASPI_CLOCK && BSP_CFG_OCTA_SOURCE != BSP_CLOCKS_CLOCK_DISABLED
bsp_octaclk_settings_t octaclk_settings =
{
.source_clock = (bsp_clocks_source_t) BSP_CFG_OCTA_SOURCE,
.divider = (bsp_clocks_octaclk_div_t) BSP_CFG_OCTA_DIV
};
R_BSP_OctaclkUpdate(&octaclk_settings);
#endif
#if BSP_FEATURE_BSP_HAS_CANFD_CLOCK && (BSP_CFG_CANFDCLK_SOURCE != BSP_CLOCKS_CLOCK_DISABLED) && \
(BSP_CFG_CANFDCLK_SOURCE != BSP_CLOCKS_SOURCE_CLOCK_MAIN_OSC)
bsp_peripheral_clock_set(&R_SYSTEM->CANFDCKCR,
&R_SYSTEM->CANFDCKDIVCR,
BSP_CFG_CANFDCLK_DIV,
BSP_CFG_CANFDCLK_SOURCE);
#endif
#if BSP_FEATURE_BSP_HAS_SCISPI_CLOCK && (BSP_CFG_SCISPICLK_SOURCE != BSP_CLOCKS_CLOCK_DISABLED)
bsp_peripheral_clock_set(&R_SYSTEM->SCISPICKCR,
&R_SYSTEM->SCISPICKDIVCR,
BSP_CFG_SCISPICLK_DIV,
BSP_CFG_SCISPICLK_SOURCE);
#endif
#if BSP_FEATURE_BSP_HAS_SCI_CLOCK && (BSP_CFG_SCICLK_SOURCE != BSP_CLOCKS_CLOCK_DISABLED)
bsp_peripheral_clock_set(&R_SYSTEM->SCICKCR, &R_SYSTEM->SCICKDIVCR, BSP_CFG_SCICLK_DIV, BSP_CFG_SCICLK_SOURCE);
#endif
#if BSP_FEATURE_BSP_HAS_SPI_CLOCK && (BSP_CFG_SPICLK_SOURCE != BSP_CLOCKS_CLOCK_DISABLED)
bsp_peripheral_clock_set(&R_SYSTEM->SPICKCR, &R_SYSTEM->SPICKDIVCR, BSP_CFG_SPICLK_DIV, BSP_CFG_SPICLK_SOURCE);
#endif
#if BSP_FEATURE_BSP_HAS_GPT_CLOCK && (BSP_CFG_GPTCLK_SOURCE != BSP_CLOCKS_CLOCK_DISABLED)
bsp_peripheral_clock_set(&R_SYSTEM->GPTCKCR, &R_SYSTEM->GPTCKDIVCR, BSP_CFG_GPTCLK_DIV, BSP_CFG_GPTCLK_SOURCE);
#endif
#if BSP_FEATURE_BSP_HAS_IIC_CLOCK && (BSP_CFG_IICCLK_SOURCE != BSP_CLOCKS_CLOCK_DISABLED)
bsp_peripheral_clock_set(&R_SYSTEM->IICCKCR, &R_SYSTEM->IICCKDIVCR, BSP_CFG_IICCLK_DIV, BSP_CFG_IICCLK_SOURCE);
#endif
#if BSP_FEATURE_BSP_HAS_I3C_CLOCK && (BSP_CFG_I3CCLK_SOURCE != BSP_CLOCKS_CLOCK_DISABLED)
bsp_peripheral_clock_set(&R_SYSTEM->I3CCKCR, &R_SYSTEM->I3CCKDIVCR, BSP_CFG_I3CCLK_DIV, BSP_CFG_I3CCLK_SOURCE);
#endif
#if BSP_FEATURE_BSP_HAS_ADC_CLOCK && (BSP_CFG_ADCCLK_SOURCE != BSP_CLOCKS_CLOCK_DISABLED)
bsp_peripheral_clock_set(&R_SYSTEM->ADCCKCR, &R_SYSTEM->ADCCKDIVCR, BSP_CFG_ADCCLK_DIV, BSP_CFG_ADCCLK_SOURCE);
#endif
#if BSP_FEATURE_BSP_HAS_LCD_CLOCK && (BSP_CFG_LCDCLK_SOURCE != BSP_CLOCKS_CLOCK_DISABLED)
bsp_peripheral_clock_set(&R_SYSTEM->LCDCKCR, &R_SYSTEM->LCDCKDIVCR, BSP_CFG_LCDCLK_DIV, BSP_CFG_LCDCLK_SOURCE);
#endif
#if BSP_FEATURE_BSP_HAS_USBHS_CLOCK && (BSP_CFG_UHSCK_SOURCE != BSP_CLOCKS_CLOCK_DISABLED)
bsp_peripheral_clock_set(&R_SYSTEM->USBHSCKCR, &R_SYSTEM->USBHSCKDIVCR, BSP_CFG_UHSCK_DIV, BSP_CFG_UHSCK_SOURCE);
#endif
R_SYSTEM->PRCR = (uint16_t) BSP_PRV_PRCR_LOCK;
#if BSP_FEATURE_BSP_FLASH_CACHE && BSP_FEATURE_BSP_FLASH_CACHE_DISABLE_OPM
R_BSP_FlashCacheEnable();
#endif
#if BSP_FEATURE_BSP_FLASH_PREFETCH_BUFFER
R_FACI_LP->PFBER = 1;
#endif
}
其中配置时钟的基地址是R_SYSTEM,定义如下:
#define R_SYSTEM ((R_SYSTEM_Type *) R_SYSTEM_BASE)
可以查看配置时钟的结构体是R_SYSTEM_Type,该结构包含了系统时钟的所有寄存器描述,该结构体非常大,笔者只是截取了部分。
总结下,整个bsp_clock_init()函数的主要工作如下:
Step1:时钟配置准备阶段
- 解锁CGC和LPM保护
- 启用闪存缓存
- 初始化时钟参数
Step2:配置系统时钟
- 配置MOSC,HOCO,MOCO,PLL参数
- 设置源时钟和分频器
Step3:配置BCLK/SDRAM/CLKOUT输出和外设时钟
- 配置BCLK/SDRAM/CLKOUT
- 根据需求配置相应的外设时钟
Step4:锁定CGC和LPM
4 RA4M2的GPIO配置剖析
从在SystemInit ()函数中可以看到,在初始化系统的时钟前后,均调用了R_BSP_WarmStart()函数,其函数原型如下:
[文件路径: src\hal_entry.c]
void R_BSP_WarmStart (bsp_warm_start_event_t event)
{
if (BSP_WARM_START_RESET == event)
{
#if BSP_FEATURE_FLASH_LP_VERSION != 0
R_FACI_LP->DFLCTL = 1U;
#endif
}
if (BSP_WARM_START_POST_C == event)
{
R_IOPORT_Open(&g_ioport_ctrl, g_ioport.p_cfg);
}
}
这里可以看到,在系统时钟初始化后,代码如下:
R_BSP_WarmStart(BSP_WARM_START_POST_CLOCK);
因此,这里就进入了GPIO的配置,核心代码如下:
R_IOPORT_Open(&g_ioport_ctrl,
g_ioport.p_cfg);
g_ioport_ctrl和g_ioport是全局变量,定义如下:
ioport_instance_ctrl_t g_ioport_ctrl;
const ioport_instance_t g_ioport =
{
.p_api = &g_ioport_on_ioport,
.p_ctrl = &g_ioport_ctrl,
.p_cfg = &g_bsp_pin_cfg,
};
而g_ioport又内嵌了g_bsp_pin_cfg,g_bsp_pin_cfg相关的定义如下:
const ioport_pin_cfg_t g_bsp_pin_cfg_data[] = {
{
.pin = BSP_IO_PORT_01_PIN_08,
.pin_cfg = ((uint32_t) IOPORT_CFG_PERIPHERAL_PIN | (uint32_t) IOPORT_PERIPHERAL_DEBUG)
},
{
.pin = BSP_IO_PORT_01_PIN_09,
.pin_cfg = ((uint32_t) IOPORT_CFG_PERIPHERAL_PIN | (uint32_t) IOPORT_PERIPHERAL_DEBUG)
},
{
.pin = BSP_IO_PORT_01_PIN_10,
.pin_cfg = ((uint32_t) IOPORT_CFG_PERIPHERAL_PIN | (uint32_t) IOPORT_PERIPHERAL_DEBUG)
},
{
.pin = BSP_IO_PORT_03_PIN_00,
.pin_cfg = ((uint32_t) IOPORT_CFG_PERIPHERAL_PIN | (uint32_t) IOPORT_PERIPHERAL_DEBUG)
},
{
.pin = BSP_IO_PORT_04_PIN_04,
.pin_cfg = ((uint32_t) IOPORT_CFG_PORT_DIRECTION_OUTPUT | (uint32_t) IOPORT_CFG_PORT_OUTPUT_LOW)
},
{
.pin = BSP_IO_PORT_04_PIN_05,
.pin_cfg = ((uint32_t) IOPORT_CFG_PORT_DIRECTION_OUTPUT | (uint32_t) IOPORT_CFG_PORT_OUTPUT_LOW)
},
{
.pin = BSP_IO_PORT_04_PIN_15,
.pin_cfg = ((uint32_t) IOPORT_CFG_PORT_DIRECTION_OUTPUT | (uint32_t) IOPORT_CFG_PORT_OUTPUT_LOW)
},
};
const ioport_cfg_t g_bsp_pin_cfg = {
.number_of_pins = sizeof(g_bsp_pin_cfg_data)/sizeof(ioport_pin_cfg_t),
.p_pin_cfg_data = &g_bsp_pin_cfg_data[0],
};
从结构体数组g_bsp_pin_cfg_data中可以看到,我们芯配置的3个GPIO端口,在后面的初始时就能将其初始化了。
R_IOPORT_Open()函数的核心是r_ioport_pins_config ()。
[文件路径: ra\fsp\src\r_ioport\r_ioport.c]
void r_ioport_pins_config (const ioport_cfg_t * p_cfg)
{
#if BSP_MCU_VBATT_SUPPORT
bsp_vbatt_init(p_cfg);
#endif
uint16_t pin_count;
ioport_cfg_t * p_pin_data;
p_pin_data = (ioport_cfg_t *) p_cfg;
R_BSP_PinAccessEnable();
for (pin_count = 0U; pin_count < p_pin_data->number_of_pins; pin_count++)
{
r_ioport_pfs_write(p_pin_data->p_pin_cfg_data[pin_count].pin, p_pin_data->p_pin_cfg_data[pin_count].pin_cfg);
}
R_BSP_PinAccessDisable();
}
r_ioport_pins_config ()函数中,根据GPIO的梳数量依次进行初始化。
r_ioport_pfs_write()函数的原型如下:
[文件路径: ra\fsp\src\r_ioport\r_ioport.c]
static void r_ioport_pfs_write (bsp_io_port_pin_t pin, uint32_t value)
{
if ((value & IOPORT_PRV_PERIPHERAL_FUNCTION) > 0)
{
R_PFS->PORT[pin >> IOPORT_PRV_PORT_OFFSET].PIN[pin & BSP_IO_PRV_8BIT_MASK].PmnPFS_b.PMR = 0;
R_PFS->PORT[pin >> IOPORT_PRV_PORT_OFFSET].PIN[pin &
BSP_IO_PRV_8BIT_MASK].PmnPFS =
(value & ~((uint32_t) IOPORT_PRV_PERIPHERAL_FUNCTION));
}
R_PFS->PORT[pin >> IOPORT_PRV_PORT_OFFSET].PIN[pin & BSP_IO_PRV_8BIT_MASK].PmnPFS = value;
}
R_PFS就是GPIO的基地址,定义如下:
#define R_PFS ((R_PFS_Type *) R_PFS_BASE)
这里就是对GPIO 进行初始化操作,包括模式、方向等
到此,基本对时钟和GPIO分析玩了,我想大家已经对RA4M2固件库的逻辑有了一定的认识,从本质上讲,都是在配置寄存器,只是地址和值不同罢了。只是RA4M2的固件库设计比较复杂,可能对于习惯ST的风格,初学者可能会有些不适用,但是其本质是一样的。