[原创] 【瑞萨RA8D1开发板,基于M85内核的图形MCU测评】GPIO流水灯的前生今世

Bruceou   2024-7-4 21:23 楼主
 
开发环境:
IDE:MKD 5.38a
开发板:CPKCOR-RA8D1B开发板
MCU:R7FA8D1BHEC332AS00
上一章通过控制GPIO的高低电平实现了流水灯,但只是告诉了大家怎么做,如何实现流水灯,本文将深入剖析的GPIO流水灯的前生今世,深入研究流水灯的调用逻辑和数据结构。

1 GPIO配置概述

前面一章大概讲解GPIO流水灯,关于GPIO的内容很多,具体请参看《RA8D1 Group User’s Manual Hardware》中I/O Ports相关的章节吧。
212221bxp09cxdllygztv9.png
Figure 1-1 I/O Ports相关章节
要想控制LED亮灭,就需要做以上三件事:使能时钟,配置GPIO参数,最后循环控制GPIO的高低电平就能实现流水灯的效果,GPIO的寄存器这里就不说了,更多详细的寄存器描述看官方手册就行,下面先来看看RA8D1的时钟。

2 RA8D1的时钟系统

2.1 RA8D1时钟架构

时钟是整个处理器运行的基础,时钟信号推动处理器内各个部分执行相应的指令。时钟系统就是CPU的脉搏,决定CPU速率,像人的心跳一样 只有有了心跳,人才能做其他的事情,而单片机有了时钟,才能够运行执行指令,才能够做其他的处理 (点灯,串口,ADC),时钟的重要性不言而喻。
212221dqzqznr5uww55nsg.png
Figure 2-1 RA8D1时钟树
RA8D1相对Cortex-85内核系列的MCU更为复杂,不仅是外设非常多,就连时钟来源就有8个之多。但我们实际使用的时候只会用到有限的几个外设,使用任何外设都需要时钟才能启动,但并不是所有的外设都需要系统时钟那么高的频率,为了兼容不同速度的设备,有些高速,有些低速,如果都用高速时钟,势必造成浪费,而且,同一个电路,时钟越快功耗越快,同时抗电磁干扰能力也就越弱,所以较为复杂的MCU都是采用多时钟源的方法来解决这些问题,因此便有了RA8D1的时钟系统和时钟树。
RA8D1不同的时钟源可以用来驱动系统时钟(SCK):
MOSC(Main Clock Oscillator):主时钟振荡器,连接外部 8 ~ 48MHz 高速晶振
SOSC(Sub-Clock Oscillator):副时钟振荡器,连接外部 32.768 kHz 低速晶振(高速外部时钟信号)
HOCO(High-speed on-chip oscillator):高速片上振荡器,振荡频率: 16/18/20/32/48/ MHz
MOCO(Middle-speed on-chip oscillator):中速片上振荡器,振荡频率: 8 MHz
LOCO(Low-speed on-chip oscillator):低速片上振荡器,振荡频率: 32.768 kHz
PLL(Phase Locked Loop): 锁相环时钟,可以对输入时钟进行分频和倍频的功能,PLL 输出频率:40 MHz ~ 480 MHz
RA8D1有两个二级时钟源:
IWDTLOCO(IWDT-dedicated clock) :IWDT专用片上振荡器,振荡频率: 15 kHz
TCK/SWCLK(External clock input for JTAG/ External clock input for SWD) :JTAG/SWD的外部时钟输入,振荡频率: 最大25MHz
每个时钟源在不使用时都可以单独被打开或关闭,这样就可以优化系统功耗。

2.2 RA8D1的时钟系统

RA8D1芯片为了实现低功耗,设计了一个功能完善但却非常复杂的时钟系统。普通的MCU 一般只要配置好 GPIO 的寄存器就可以使用了,但RA8D1还有一个步骤,就是开启外设时钟。
在RA8D1中,系统时钟源有很多,从来源可分为外部时钟源和内部时钟源,外部时钟源就是从外部通过接晶振的方式获取时钟源,其中 XTAL、XCIN和TCK/SWCLK是外部时钟源,其他的是内部时钟源。
下面我们看看RA8D1的系统时钟源,我们讲解顺序是按图中红圈标示的顺序:
MOSC是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为8MHz~48MHz。我们的开发板接的是20M的晶振。
SOSC是副时钟振荡器,接频率为 32.768kHz 的石英晶体。这个主要是 RTC 的时钟源。
HOCO是高速内部时钟,高速片上振荡器,振荡频率: 16/18/20/32/48/ MHz。
MOCO是中速片上振荡器,振荡频率为8 MHz。
LOCO是低速片上振荡器,振荡频率为32.768 kHz。
PLL 为锁相环倍频输出,其时钟输入源可选择为MOSC、HOCO。倍频可选择为10~30倍,但是PLL输出频率最大不得超过 480MHz。
212222yrlcjqh57ph5krgh.png
Figure 2-2 RA8D1的时钟树详解
图中我们用 A~C标示就是系统时钟进过系统时钟主要流向的地方。
  1. 此处是FCLK,最高60MHz.
  2. B处就是 RA8D1的系统时钟 ICLK,它是供 RA8D1内核中绝大部分部件工作的时钟源,分别是CPU、DMAC、DTC、闪存 和 SRAM。系统时钟可选择HCOC、MCOC、LOCO、XTAL、SUBCLK、PLL作为时钟源。系统时钟最大频率为 240MHz,当然你也可以超频,不过一般情况为了系统稳定性是没有必要冒风险去超频的。
  3. 这里的C处是大部分外设的时钟输入源了。从时钟图上可以看出,其他所有外设的时钟最终来源都是 SCK。SCK 通过分频器分频后送给各模块使用。

3 RA8D1的时钟配置剖析

主频的时钟为默认的 240 MHz,来源是MOSC的24MHz时钟,经过PLL倍频,流向系统时钟。详细请查看RA Smart Configurator文件的配置。
212222tg6e7ceml67bvnnz.png
Figure 3-1 RA Smart Configurator时钟配置
当系统复位后就会进入Reset_Handler() -> SystemInit() -> bsp_clock_init(),bsp_clock_init()函数就是整个系统时钟的初始化部分,重点看这里,代码如下:
[文件路径: ra\fsp\src\bsp\mcu\all\bsp_clocks.c]
/*******************************************************************************************************************//**
* Initializes system clocks. Makes no assumptions about current register settings.
**********************************************************************************************************************/
void bsp_clock_init (void)
{
/* Unlock CGC and LPM protection registers. */
#if BSP_FEATURE_TZ_VERSION == 2 && BSP_TZ_NONSECURE_BUILD == 1
R_SYSTEM->PRCR_NS = (uint16_t) BSP_PRV_PRCR_UNLOCK;
#else
R_SYSTEM->PRCR = (uint16_t) BSP_PRV_PRCR_UNLOCK;
#endif
#if BSP_FEATURE_BSP_FLASH_CACHE
#if !BSP_CFG_USE_LOW_VOLTAGE_MODE && BSP_FEATURE_BSP_FLASH_CACHE_DISABLE_OPM
/* Disable flash cache before modifying MEMWAIT, SOPCCR, or OPCCR. */
R_BSP_FlashCacheDisable();
#else
/* Enable the flash cache and don't disable it while running from flash. On these MCUs, the flash cache does not
* need to be disabled when adjusting the operating power mode. */
R_BSP_FlashCacheEnable();
#endif
#endif
#if BSP_FEATURE_BSP_FLASH_PREFETCH_BUFFER
/* Disable the flash prefetch buffer. */
R_FACI_LP->PFBER = 0;
#endif
bsp_clock_freq_var_init();
#if BSP_CFG_STARTUP_CLOCK_REG_NOT_RESET
/* Transition to an intermediate clock configuration in order to prepare for writing the new clock configuraiton. */
bsp_soft_reset_prepare();
#endif
#if BSP_CLOCK_CFG_MAIN_OSC_POPULATED
#if BSP_CFG_STARTUP_CLOCK_REG_NOT_RESET
/* Update the main oscillator drive, source, and wait states if the main oscillator is stopped. If the main
* oscillator is running, the drive, source, and wait states are assumed to be already set appropriately. */
if (R_SYSTEM->MOSCCR)
{
/* Don't write to MOSCWTCR unless MOSTP is 1 and MOSCSF = 0. */
FSP_HARDWARE_REGISTER_WAIT(R_SYSTEM->OSCSF_b.MOSCSF, 0U);
/* Configure main oscillator drive. */
R_SYSTEM->MOMCR = BSP_PRV_MOMCR;
/* Set the main oscillator wait time. */
R_SYSTEM->MOSCWTCR = (uint8_t) BSP_CLOCK_CFG_MAIN_OSC_WAIT;
}
#else
/* Configure main oscillator drive. */
R_SYSTEM->MOMCR = BSP_PRV_MOMCR;
/* Set the main oscillator wait time. */
R_SYSTEM->MOSCWTCR = (uint8_t) BSP_CLOCK_CFG_MAIN_OSC_WAIT;
#endif
#endif
/* Initialize the sub-clock according to the BSP configuration. */
bsp_sosc_init();
#if BSP_FEATURE_CGC_HAS_HOCOWTCR
#if BSP_FEATURE_CGC_HOCOWTCR_64MHZ_ONLY
/* These MCUs only require writes to HOCOWTCR if HOCO is set to 64 MHz. */
#if 64000000 == BSP_HOCO_HZ
#if BSP_CFG_USE_LOW_VOLTAGE_MODE
/* Wait for HOCO to stabilize before writing to HOCOWTCR. */
FSP_HARDWARE_REGISTER_WAIT(R_SYSTEM->OSCSF_b.HOCOSF, 1U);
#else
/* HOCO is assumed to be stable because these MCUs also require the HOCO to be stable before changing the operating
* power control mode. */
#endif
R_SYSTEM->HOCOWTCR = BSP_FEATURE_CGC_HOCOWTCR_VALUE;
#endif
#else
/* These MCUs require HOCOWTCR to be set to the maximum value except in snooze mode. There is no restriction to
* writing this register. */
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
/* Switch to high-speed to prevent any issues with the subsequent clock configurations. */
bsp_prv_operating_mode_set(BSP_PRV_OPERATING_MODE_HIGH_SPEED);
#elif BSP_FEATURE_CGC_LOW_VOLTAGE_MAX_FREQ_HZ > 0U
/* MCUs that support low voltage mode start up in low voltage mode. */
bsp_prv_operating_mode_opccr_set(BSP_PRV_OPERATING_MODE_HIGH_SPEED);
#if !BSP_PRV_HOCO_USED
/* HOCO must be running during startup in low voltage mode. If HOCO is not used, turn it off after exiting low
* voltage mode. */
R_SYSTEM->HOCOCR = 1U;
#endif
#elif BSP_FEATURE_CGC_STARTUP_OPCCR_MODE != BSP_PRV_OPERATING_MODE_HIGH_SPEED
/* Some MCUs do not start in high speed mode. */
bsp_prv_operating_mode_opccr_set(BSP_PRV_OPERATING_MODE_HIGH_SPEED);
#endif
#endif
/* The FLL function can only be used when the subclock is running. */
#if BSP_PRV_HOCO_USE_FLL
/* If FLL is to be used configure FLLCR1 and FLLCR2 before starting HOCO. */
R_SYSTEM->FLLCR2 = BSP_PRV_FLL_FLLCR2;
R_SYSTEM->FLLCR1 = 1U;
#endif
/* Start all clocks used by other clocks first. */
#if BSP_PRV_HOCO_USED
R_SYSTEM->HOCOCR = 0U;
#if BSP_PRV_HOCO_USE_FLL && (BSP_CLOCKS_SOURCE_CLOCK_HOCO != BSP_CFG_PLL_SOURCE)
/* If FLL is enabled, wait for the FLL stabilization delay (1.8 ms) */
R_BSP_SoftwareDelay(BSP_PRV_FLL_STABILIZATION_TIME_US, BSP_DELAY_UNITS_MICROSECONDS);
#endif
#if BSP_PRV_STABILIZE_HOCO
/* Wait for HOCO to stabilize. */
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 the MOCO is not running, start it and wait for it to stabilize using a software delay. */
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 the LOCO is not running, start it and wait for it to stabilize using a software delay. */
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
#if BSP_CFG_STARTUP_CLOCK_REG_NOT_RESET
if (R_SYSTEM->MOSCCR)
#endif
{
R_SYSTEM->MOSCCR = 0U;
#if BSP_PRV_STABILIZE_MAIN_OSC
/* Wait for main oscillator to stabilize. */
FSP_HARDWARE_REGISTER_WAIT(R_SYSTEM->OSCSF_b.MOSCSF, 1U);
#endif
}
#endif
/* Start clocks that require other clocks. At this point, all dependent clocks are running and stable if needed. */
#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
#if BSP_CFG_STARTUP_CLOCK_REG_NOT_RESET
if (R_SYSTEM->PLL2CR)
#endif
{
R_SYSTEM->PLL2CCR = BSP_PRV_PLL2CCR;
#if (3U == BSP_FEATURE_CGC_PLLCCR_TYPE)
R_SYSTEM->PLL2CCR2 = BSP_PRV_PLL2CCR2;
#endif
/* Start PLL2. */
R_SYSTEM->PLL2CR = 0U;
}
#endif /* BSP_FEATURE_CGC_HAS_PLL2 && BSP_CFG_PLL2_ENABLE */
#endif
#if BSP_PRV_PLL_SUPPORTED && BSP_PRV_PLL_USED
#if BSP_CFG_STARTUP_CLOCK_REG_NOT_RESET
if (R_SYSTEM->PLLCR)
#endif
{
#if (1U == BSP_FEATURE_CGC_PLLCCR_TYPE) || (4U == 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
/* This loop is provided to ensure at least 1 us passes between setting PLLMUL and clearing PLLSTP on some
* MCUs (see PLLSTP notes in Section 8.2.4 "PLL Control Register (PLLCR)" of the RA4M1 manual R01UH0887EJ0100).
* Five loops are needed here to ensure the most efficient path takes at least 1 us from the setting of
* PLLMUL to the clearing of PLLSTP. HOCO is the fastest clock we can be using here since PLL cannot be running
* while setting PLLCCR. */
bsp_prv_software_delay_loop(BSP_DELAY_LOOPS_CALCULATE(BSP_PRV_MAX_HOCO_CYCLES_PER_US));
#endif
R_SYSTEM->PLLCR = 0U;
#if BSP_PRV_STABILIZE_PLL
/* Wait for PLL to stabilize. */
FSP_HARDWARE_REGISTER_WAIT(R_SYSTEM->OSCSF_b.PLLSF, 1U);
#endif
}
#endif
/* Set source clock and dividers. */
#if BSP_CFG_STARTUP_CLOCK_REG_NOT_RESET
#if BSP_TZ_SECURE_BUILD
/* In case of soft reset, make sure callback pointer is NULL initially. */
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 the MCU can run in a lower power mode, apply the optimal operating speed mode. */
#if !BSP_CFG_USE_LOW_VOLTAGE_MODE
#if BSP_PRV_STARTUP_OPERATING_MODE != BSP_PRV_OPERATING_MODE_HIGH_SPEED
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)
/* Start DCDC as part of BSP startup when configured (BSP_CFG_DCDC_ENABLE == 2). */
R_BSP_PowerModeSet(BSP_CFG_DCDC_VOLTAGE_RANGE);
#endif
/* Configure BCLK if it exists on the MCU. */
#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
/* Configure SDRAM clock if it exists on the MCU. */
#ifdef BSP_CFG_SDCLK_OUTPUT
R_SYSTEM->SDCKOCR = BSP_CFG_SDCLK_OUTPUT;
#endif
/* Configure CLKOUT. */
#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 the USB clock has a divider setting in SCKDIVCR2. */
#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 /* BSP_FEATURE_BSP_HAS_USB_CLOCK_DIV && !BSP_FEATURE_BSP_HAS_USBCKDIVCR */
/* If there is a REQ bit in USBCKCR, then follow sequence from section 8.2.29 in RA6M4 hardware manual R01UH0890EJ0050. */
#if BSP_FEATURE_BSP_HAS_USB_CLOCK_REQ
/* Request to change the USB Clock. */
R_SYSTEM->USBCKCR_b.USBCKSREQ = 1;
/* Wait for the clock to be stopped. */
FSP_HARDWARE_REGISTER_WAIT(R_SYSTEM->USBCKCR_b.USBCKSRDY, 1U);
/* Write the settings. */
R_SYSTEM->USBCKDIVCR = BSP_PRV_UCK_DIV;
/* Select the USB Clock without enabling it. */
R_SYSTEM->USBCKCR = BSP_CFG_UCK_SOURCE | R_SYSTEM_USBCKCR_USBCKSREQ_Msk;
#endif /* BSP_FEATURE_BSP_HAS_USB_CLOCK_REQ */
#if BSP_FEATURE_BSP_HAS_USB_CLOCK_SEL
/* Some MCUs use an alternate register for selecting the USB clock source. */
#if BSP_FEATURE_BSP_HAS_USB_CLOCK_SEL_ALT
#if BSP_CLOCKS_SOURCE_CLOCK_PLL == BSP_CFG_UCK_SOURCE
/* Write to USBCKCR to select the PLL. */
R_SYSTEM->USBCKCR_ALT = 0;
#elif BSP_CLOCKS_SOURCE_CLOCK_HOCO == BSP_CFG_UCK_SOURCE
/* Write to USBCKCR to select the HOCO. */
R_SYSTEM->USBCKCR_ALT = 1;
#endif
#else
/* Select the USB Clock. */
R_SYSTEM->USBCKCR = BSP_CFG_UCK_SOURCE;
#endif
#endif /* BSP_FEATURE_BSP_HAS_USB_CLOCK_REQ */
#if BSP_FEATURE_BSP_HAS_USB_CLOCK_REQ
/* Wait for the USB Clock to be started. */
FSP_HARDWARE_REGISTER_WAIT(R_SYSTEM->USBCKCR_b.USBCKSRDY, 0U);
#endif /* BSP_FEATURE_BSP_HAS_USB_CLOCK_REQ */
#endif /* BSP_CFG_USB_ENABLE */
#endif /* BSP_PRV_STARTUP_OPERATING_MODE != BSP_PRV_OPERATING_MODE_LOW_SPEED */
/* Set the OCTASPI clock if it exists on the MCU (See section 8.2.30 of the RA6M4 hardware manual R01UH0890EJ0050). */
#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 /* BSP_FEATURE_BSP_HAS_OCTASPI_CLOCK && BSP_CFG_OCTASPI_CLOCK_ENABLE */
/* Set the CANFD clock if it exists on the MCU */
#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
/* Set the SCISPI clock if it exists on the MCU */
#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
/* Set the SCI clock if it exists on the MCU */
#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
/* Set the SPI clock if it exists on the MCU */
#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
/* Set the GPT clock if it exists on the MCU */
#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
/* Set the IIC clock if it exists on the MCU */
#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
/* Set the CEC clock if it exists on the MCU */
#if BSP_FEATURE_BSP_HAS_CEC_CLOCK && (BSP_CFG_CECCLK_SOURCE != BSP_CLOCKS_CLOCK_DISABLED)
bsp_peripheral_clock_set(&R_SYSTEM->CECCKCR, &R_SYSTEM->CECCKDIVCR, BSP_CFG_CECCLK_DIV, BSP_CFG_CECCLK_SOURCE);
#endif
/* Set the I3C clock if it exists on the MCU */
#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
/* Set the LCD clock if it exists on the MCU */
#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
/* Set the USB-HS clock if it exists on the MCU */
#if BSP_FEATURE_BSP_HAS_USB60_CLOCK_REQ && (BSP_CFG_U60CK_SOURCE != BSP_CLOCKS_CLOCK_DISABLED)
bsp_peripheral_clock_set(&R_SYSTEM->USB60CKCR, &R_SYSTEM->USB60CKDIVCR, BSP_CFG_U60CK_DIV, BSP_CFG_U60CK_SOURCE);
#endif
/* Set the SDADC clock if it exists on the MCU. */
#if BSP_FEATURE_BSP_HAS_SDADC_CLOCK && (BSP_CFG_SDADC_CLOCK_SOURCE != BSP_CLOCKS_CLOCK_DISABLED)
#if BSP_CFG_SDADC_CLOCK_SOURCE == BSP_CLOCKS_SOURCE_CLOCK_HOCO
uint8_t sdadcckcr = 1U;
#elif BSP_CFG_SDADC_CLOCK_SOURCE == BSP_CLOCKS_SOURCE_CLOCK_PLL
uint8_t sdadcckcr = 2U;
#else /* BSP_CLOCK_SOURCE_CLOCK_MOSC */
uint8_t sdadcckcr = 0U;
#endif
/* SDADC isn't controlled like the other peripheral clocks so we cannot use the generic setter. */
R_SYSTEM->SDADCCKCR = sdadcckcr & R_SYSTEM_SDADCCKCR_SDADCCKSEL_Msk;
#endif
/* Lock CGC and LPM protection registers. */
#if BSP_FEATURE_TZ_VERSION == 2 && BSP_TZ_NONSECURE_BUILD == 1
R_SYSTEM->PRCR_NS = (uint16_t) BSP_PRV_PRCR_LOCK;
#else
R_SYSTEM->PRCR = (uint16_t) BSP_PRV_PRCR_LOCK;
#endif
#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,该结构包含了系统时钟的所有寄存器描述,该结构体非常大,笔者只是截取了部分。
EEWORLDIMGTK4
总结下,整个bsp_clock_init()函数的主要工作如下:
Step1:时钟配置准备阶段
  • 解锁CGC和LPM保护
  • 启用闪存缓存
  • 初始化时钟参数
Step2:配置系统时钟
  • 配置MOSC,HOCO,MOCO,PLL参数
  • 设置源时钟和分频器
Step3:配置BCLK/SDRAM/CLKOUT输出和外设时钟
  • 配置BCLK/SDRAM/CLKOUT
  • 根据需求配置相应的外设时钟
Step4:锁定CGCLPM

4 RA8D1的GPIO配置剖析

从在SystemInit ()函数中可以看到,在初始化系统的时钟前后,均调用了R_BSP_WarmStart()函数,其函数原型如下:
[文件路径: src\hal_entry.c]
/*******************************************************************************************************************//**
* This function is called at various points during the startup process. This implementation uses the event that is
* called right before main() to set up the pins.
*
* @param[in] event Where at in the start up process the code is currently at
**********************************************************************************************************************/
void R_BSP_WarmStart (bsp_warm_start_event_t event)
{
if (BSP_WARM_START_RESET == event)
{
#if BSP_FEATURE_FLASH_LP_VERSION != 0
/* Enable reading from data flash. */
R_FACI_LP->DFLCTL = 1U;
/* Would normally have to wait tDSTOP(6us) for data flash recovery. Placing the enable here, before clock and
* C runtime initialization, should negate the need for a delay since the initialization will typically take more than 6us. */
#endif
}
if (BSP_WARM_START_POST_C == event)
{
/* C runtime environment and system clocks are setup. */
/* Configure pins. */
IOPORT_CFG_OPEN(&IOPORT_CFG_CTRL, &IOPORT_CFG_NAME);
}
}
这里可以看到,在系统时钟初始化后,代码如下:
R_BSP_WarmStart(BSP_WARM_START_POST_C);
因此,这里就进入了GPIO的配置,核心代码如下:
IOPORT_CFG_OPEN(&IOPORT_CFG_CTRL, &IOPORT_CFG_NAME);
IOPORT_CFG_CTRL和IOPORT_CFG_NAME宏定义如下图所示。
#define IOPORT_CFG_NAME g_bsp_pin_cfg
#define IOPORT_CFG_CTRL g_ioport_ctrl
g_ioport_ctrl和g_bsp_pin_cfg是全局变量,定义如下:
ioport_instance_ctrl_t g_ioport_ctrl;
typedef struct st_ioport_instance_ctrl
{
uint32_t open;
void const * p_context;
} ioport_instance_ctrl_t;
而g_bsp_pin_cfg又内嵌了g_bsp_pin_cfg_data,g_bsp_pin_cfg_data相关的定义如下:
const ioport_pin_cfg_t g_bsp_pin_cfg_data[] = {
{
.pin = BSP_IO_PORT_02_PIN_08,
.pin_cfg = ((uint32_t) IOPORT_CFG_PERIPHERAL_PIN | (uint32_t) IOPORT_PERIPHERAL_DEBUG)
},
{
.pin = BSP_IO_PORT_02_PIN_09,
.pin_cfg = ((uint32_t) IOPORT_CFG_PERIPHERAL_PIN | (uint32_t) IOPORT_PERIPHERAL_DEBUG)
},
{
.pin = BSP_IO_PORT_02_PIN_10,
.pin_cfg = ((uint32_t) IOPORT_CFG_PERIPHERAL_PIN | (uint32_t) IOPORT_PERIPHERAL_DEBUG)
},
{
.pin = BSP_IO_PORT_02_PIN_11,
.pin_cfg = ((uint32_t) IOPORT_CFG_PERIPHERAL_PIN | (uint32_t) IOPORT_PERIPHERAL_DEBUG)
},
{
.pin = BSP_IO_PORT_10_PIN_01,
.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]
/*******************************************************************************************************************//**
* Configures pins.
*
* @param[in] p_cfg Pin configuration data
**********************************************************************************************************************/
void r_ioport_pins_config (const ioport_cfg_t * p_cfg)
{
#if BSP_FEATURE_SYSC_HAS_VBTICTLR || BSP_FEATURE_RTC_HAS_TCEN
/* Handle any VBATT domain pin configuration. */
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(); // Protect PWPR from re-entrancy
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]
/*******************************************************************************************************************//**
* Writes to the specified pin's PFS register
*
* @param[in] pin Pin to write PFS data for
* @param[in] value Value to be written to the PFS register
*
**********************************************************************************************************************/
static void r_ioport_pfs_write (bsp_io_port_pin_t pin, uint32_t value)
{
/* PMR bits should be cleared before specifying PSEL. Reference section "20.7 Notes on the PmnPFS Register Setting"
* in the RA6M3 manual R01UH0886EJ0100. */
if ((value & IOPORT_PRV_PERIPHERAL_FUNCTION) > 0)
{
/* Clear PMR */
R_PFS->PORT[pin >> IOPORT_PRV_PORT_OFFSET].PIN[pin & BSP_IO_PRV_8BIT_MASK].PmnPFS_b.PMR = 0;
/* New config with 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));
}
/* Write configuration */
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分析玩了,我想大家已经对RA8D1固件库的逻辑有了一定的认识,从本质上讲,都是在配置寄存器,只是地址和值不同罢了。只是RA8D1的固件库设计比较复杂,可能对于习惯ST的风格,初学者可能会有些不适用,但是其本质是一样的。

回复评论 (1)

帖的代码太多了,可挑一些主要能说明配置的说说。

点赞  2024-7-6 11:51
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复