qwerghf 发表于 2017-12-12 22:49

XMC4700 Relax 5V shield 评测:分析XMC4700 Easy Start Project

<div class='showpostmsg'> 本帖最后由 qwerghf 于 2017-12-12 22:55 编辑

今天有空可以休息,继续开始玩转XMC4700,在上一节介绍了如何使用官方的IDE,本节开始使用官方的IDE来创建学习XMC4700。本次我们使用第一个工程选项创建工程,如下:

如上就是我们基于XMC4700创建Easy Start Project,根据以前开发的经验,我们先从系统时钟开始分析XMC4700工程。
对于M内核的MCU来说,都是先从汇编启动文件,然后跑到系统配置函数,系统时钟默认配置文件为system_XMC4700.c,如下所示


打开文件,根据硬件我们可以知道系统外部时钟源为12M晶振,可以从代码中看到
/*
//    <o> External crystal frequency
//       <8000000=> 8MHz
//       <12000000=> 12MHz
//       <16000000=> 16MHz
//    <i> Defines external crystal frequency
//    <i> Default: 8MHz
*/
#define OSCHP_FREQUENCY (12000000U)

/* USB PLL settings, fUSBPLL = 48MHz and fUSBPLLVCO = 384 MHz */
/* Note: Implicit divider of 2 and fUSBPLLVCO >= 260 MHz and fUSBPLLVCO <= 520 MHz*/
#if OSCHP_FREQUENCY == 8000000U
#define USB_PDIV (1U)
#define USB_NDIV (95U)

#elif OSCHP_FREQUENCY == 12000000U
#define USB_PDIV (1U)
#define USB_NDIV (63U)

#elif OSCHP_FREQUENCY == 16000000U
#define USB_PDIV (1U)
#define USB_NDIV (47U)

#else
#error "External crystal frequency not supported"

#endif
可以看到系统时钟为12M,符合硬件,代码中也给我出8M和16M对应的配置定义。
系统的时钟配置函数如下:
__WEAK void SystemInit(void)
{
memcpy(g_chipid, CHIPID_LOC, 16);

SystemCoreSetup();
SystemCoreClockSetup();
}

__WEAK void SystemCoreSetup(void)
{
uint32_t temp;
      
/* relocate vector table */
__disable_irq();
SCB->VTOR = (uint32_t)(&__Vectors);
__DSB();
__enable_irq();
   
#if ((__FPU_PRESENT == 1) && (__FPU_USED == 1))
SCB->CPACR |= ((3UL << 10*2) |               /* set CP10 Full Access */
               (3UL << 11*2));               /* set CP11 Full Access */
#endif

/* Enable unaligned memory access - SCB_CCR.UNALIGN_TRP = 0 */
SCB->CCR &= ~(SCB_CCR_UNALIGN_TRP_Msk);

temp = FLASH0->FCON;
temp &= ~FLASH_FCON_WSPFLASH_Msk;
temp |= PMU_FLASH_WS;
FLASH0->FCON = temp;
}
__WEAK void SystemCoreClockSetup(void)
{
#if FOFI_CALIBRATION_MODE == FOFI_CALIBRATION_MODE_FACTORY
/* Enable factory calibration */
SCU_PLL->PLLCON0 |= SCU_PLL_PLLCON0_FOTR_Msk;
#else
/* Automatic calibration uses the fSTDBY */

/* Enable HIB domain */
/* Power up HIB domain if and only if it is currently powered down */
if((SCU_POWER->PWRSTAT & SCU_POWER_PWRSTAT_HIBEN_Msk) == 0)
{
    SCU_POWER->PWRSET |= SCU_POWER_PWRSET_HIB_Msk;

    while((SCU_POWER->PWRSTAT & SCU_POWER_PWRSTAT_HIBEN_Msk) == 0)
    {
      /* wait until HIB domain is enabled */
    }
}

/* Remove the reset only if HIB domain were in a state of reset */
if((SCU_RESET->RSTSTAT) & SCU_RESET_RSTSTAT_HIBRS_Msk)
{
    SCU_RESET->RSTCLR |= SCU_RESET_RSTCLR_HIBRS_Msk;
    delay(DELAY_CNT_150US_50MHZ);
}

#if STDBY_CLOCK_SRC == STDBY_CLOCK_SRC_OSCULP
/* Enable OSC_ULP */
if ((SCU_HIBERNATE->OSCULCTRL & SCU_HIBERNATE_OSCULCTRL_MODE_Msk) != 0UL)
{
    /*enable OSC_ULP*/
    while (SCU_GENERAL->MIRRSTS & SCU_GENERAL_MIRRSTS_OSCULCTRL_Msk)
    {
      /* check SCU_MIRRSTS to ensure that no transfer over serial interface is pending */
    }
    SCU_HIBERNATE->OSCULCTRL &= ~SCU_HIBERNATE_OSCULCTRL_MODE_Msk;

    /* Check if the clock is OK using OSCULP Oscillator Watchdog*/
    while (SCU_GENERAL->MIRRSTS & SCU_GENERAL_MIRRSTS_HDCR_Msk)
    {
      /* check SCU_MIRRSTS to ensure that no transfer over serial interface is pending */
    }
    SCU_HIBERNATE->HDCR |= SCU_HIBERNATE_HDCR_ULPWDGEN_Msk;

    /* wait till clock is stable */
    do
    {
      while (SCU_GENERAL->MIRRSTS & SCU_GENERAL_MIRRSTS_HDCLR_Msk)
      {
      /* check SCU_MIRRSTS to ensure that no transfer over serial interface is pending */
      }
      SCU_HIBERNATE->HDCLR |= SCU_HIBERNATE_HDCLR_ULPWDG_Msk;

      delay(DELAY_CNT_50US_50MHZ);

    } while ((SCU_HIBERNATE->HDSTAT & SCU_HIBERNATE_HDSTAT_ULPWDG_Msk) != 0UL);

}

/* now OSC_ULP is running and can be used*/
/* Select OSC_ULP as the clock source for RTC and STDBY*/
while (SCU_GENERAL->MIRRSTS & SCU_GENERAL_MIRRSTS_HDCR_Msk)
{
    /* check SCU_MIRRSTS to ensure that no transfer over serial interface is pending */
}
SCU_HIBERNATE->HDCR |= SCU_HIBERNATE_HDCR_RCS_Msk | SCU_HIBERNATE_HDCR_STDBYSEL_Msk;
#endif /* STDBY_CLOCK_SRC == STDBY_CLOCK_SRC_OSCULP */

/* Enable automatic calibration of internal fast oscillator */
SCU_PLL->PLLCON0 |= SCU_PLL_PLLCON0_AOTREN_Msk;
#endif /* FOFI_CALIBRATION_MODE == FOFI_CALIBRATION_MODE_AUTOMATIC */

delay(DELAY_CNT_50US_50MHZ);

#if ENABLE_PLL

/* enable PLL */
SCU_PLL->PLLCON0 &= ~(SCU_PLL_PLLCON0_VCOPWD_Msk | SCU_PLL_PLLCON0_PLLPWD_Msk);

#if PLL_CLOCK_SRC != PLL_CLOCK_SRC_OFI
/* enable OSC_HP */
if ((SCU_OSC->OSCHPCTRL & SCU_OSC_OSCHPCTRL_MODE_Msk) != 0U)
{
    SCU_OSC->OSCHPCTRL &= ~(SCU_OSC_OSCHPCTRL_MODE_Msk | SCU_OSC_OSCHPCTRL_OSCVAL_Msk);
    SCU_OSC->OSCHPCTRL |= ((OSCHP_GetFrequency() / FOSCREF) - 1UL) << SCU_OSC_OSCHPCTRL_OSCVAL_Pos;

    /* select OSC_HP clock as PLL input */
    SCU_PLL->PLLCON2 &= ~SCU_PLL_PLLCON2_PINSEL_Msk;

    /* restart OSC Watchdog */
    SCU_PLL->PLLCON0 &= ~SCU_PLL_PLLCON0_OSCRES_Msk;

    while ((SCU_PLL->PLLSTAT & SCU_PLL_PLLSTAT_OSC_USABLE) != SCU_PLL_PLLSTAT_OSC_USABLE)
    {
      /* wait till OSC_HP output frequency is usable */
    }   
}
#else /* PLL_CLOCK_SRC != PLL_CLOCK_SRC_OFI */

/* select backup clock as PLL input */
SCU_PLL->PLLCON2 |= SCU_PLL_PLLCON2_PINSEL_Msk;
#endif

/* Go to bypass the Main PLL */
SCU_PLL->PLLCON0 |= SCU_PLL_PLLCON0_VCOBYP_Msk;

/* disconnect Oscillator from PLL */
SCU_PLL->PLLCON0 |= SCU_PLL_PLLCON0_FINDIS_Msk;

/* Setup divider settings for main PLL */
SCU_PLL->PLLCON1 = ((PLL_NDIV << SCU_PLL_PLLCON1_NDIV_Pos) |
                      (PLL_K2DIV_24MHZ << SCU_PLL_PLLCON1_K2DIV_Pos) |
                      (PLL_PDIV << SCU_PLL_PLLCON1_PDIV_Pos));

/* Set OSCDISCDIS */
SCU_PLL->PLLCON0 |= SCU_PLL_PLLCON0_OSCDISCDIS_Msk;

/* connect Oscillator to PLL */
SCU_PLL->PLLCON0 &= ~SCU_PLL_PLLCON0_FINDIS_Msk;

/* restart PLL Lock detection */
SCU_PLL->PLLCON0 |= SCU_PLL_PLLCON0_RESLD_Msk;

while ((SCU_PLL->PLLSTAT & SCU_PLL_PLLSTAT_VCOLOCK_Msk) == 0U)
{
    /* wait for PLL Lock at 24MHz*/
}

/* Disable bypass- put PLL clock back */
SCU_PLL->PLLCON0 &= ~SCU_PLL_PLLCON0_VCOBYP_Msk;
while ((SCU_PLL->PLLSTAT & SCU_PLL_PLLSTAT_VCOBYST_Msk) != 0U)
{
    /* wait for normal mode */
}
#endif /* ENABLE_PLL */

/* Before scaling to final frequency we need to setup the clock dividers */
SCU_CLK->SYSCLKCR = __SYSCLKCR;
SCU_CLK->PBCLKCR = __PBCLKCR;
SCU_CLK->CPUCLKCR = __CPUCLKCR;
SCU_CLK->CCUCLKCR = __CCUCLKCR;
SCU_CLK->WDTCLKCR = __WDTCLKCR;
SCU_CLK->EBUCLKCR = __EBUCLKCR;
SCU_CLK->USBCLKCR = __USBCLKCR | USB_DIV;
SCU_CLK->EXTCLKCR = __EXTCLKCR;

#if ENABLE_PLL
/* PLL frequency stepping...*/
/* Reset OSCDISCDIS */
SCU_PLL->PLLCON0 &= ~SCU_PLL_PLLCON0_OSCDISCDIS_Msk;

SCU_PLL->PLLCON1 = ((PLL_NDIV << SCU_PLL_PLLCON1_NDIV_Pos) |
                        (PLL_K2DIV_48MHZ << SCU_PLL_PLLCON1_K2DIV_Pos) |
                        (PLL_PDIV << SCU_PLL_PLLCON1_PDIV_Pos));

delay(DELAY_CNT_50US_48MHZ);

SCU_PLL->PLLCON1 = ((PLL_NDIV << SCU_PLL_PLLCON1_NDIV_Pos) |
                        (PLL_K2DIV_72MHZ << SCU_PLL_PLLCON1_K2DIV_Pos) |
                        (PLL_PDIV << SCU_PLL_PLLCON1_PDIV_Pos));

delay(DELAY_CNT_50US_72MHZ);

SCU_PLL->PLLCON1 = ((PLL_NDIV << SCU_PLL_PLLCON1_NDIV_Pos) |
                        (PLL_K2DIV_96MHZ << SCU_PLL_PLLCON1_K2DIV_Pos) |
                        (PLL_PDIV << SCU_PLL_PLLCON1_PDIV_Pos));

delay(DELAY_CNT_50US_96MHZ);

SCU_PLL->PLLCON1 = ((PLL_NDIV << SCU_PLL_PLLCON1_NDIV_Pos) |
                        (PLL_K2DIV_120MHZ << SCU_PLL_PLLCON1_K2DIV_Pos) |
                        (PLL_PDIV << SCU_PLL_PLLCON1_PDIV_Pos));

delay(DELAY_CNT_50US_120MHZ);

SCU_PLL->PLLCON1 = ((PLL_NDIV << SCU_PLL_PLLCON1_NDIV_Pos) |
                        (PLL_K2DIV << SCU_PLL_PLLCON1_K2DIV_Pos) |
                        (PLL_PDIV << SCU_PLL_PLLCON1_PDIV_Pos));

delay(DELAY_CNT_50US_144MHZ);

#endif /* ENABLE_PLL */

#if ENABLE_USBPLL
/* enable USB PLL first */
SCU_PLL->USBPLLCON &= ~(SCU_PLL_USBPLLCON_VCOPWD_Msk | SCU_PLL_USBPLLCON_PLLPWD_Msk);

/* USB PLL uses as clock input the OSC_HP */
/* check and if not already running enable OSC_HP */
if ((SCU_OSC->OSCHPCTRL & SCU_OSC_OSCHPCTRL_MODE_Msk) != 0U)
{
    /* check if Main PLL is switched on for OSC WDG*/
    if ((SCU_PLL->PLLCON0 &(SCU_PLL_PLLCON0_VCOPWD_Msk | SCU_PLL_PLLCON0_PLLPWD_Msk)) != 0UL)
    {
      /* enable PLL first */
      SCU_PLL->PLLCON0 &= ~(SCU_PLL_PLLCON0_VCOPWD_Msk | SCU_PLL_PLLCON0_PLLPWD_Msk);
    }

    SCU_OSC->OSCHPCTRL &= ~(SCU_OSC_OSCHPCTRL_MODE_Msk | SCU_OSC_OSCHPCTRL_OSCVAL_Msk);
    SCU_OSC->OSCHPCTRL |= ((OSCHP_GetFrequency() / FOSCREF) - 1UL) << SCU_OSC_OSCHPCTRL_OSCVAL_Pos;

    /* restart OSC Watchdog */
    SCU_PLL->PLLCON0 &= ~SCU_PLL_PLLCON0_OSCRES_Msk;

    while ((SCU_PLL->PLLSTAT & SCU_PLL_PLLSTAT_OSC_USABLE) != SCU_PLL_PLLSTAT_OSC_USABLE)
    {
      /* wait till OSC_HP output frequency is usable */
    }
}


/* Setup USB PLL */
/* Go to bypass the USB PLL */
SCU_PLL->USBPLLCON |= SCU_PLL_USBPLLCON_VCOBYP_Msk;

/* disconnect Oscillator from USB PLL */
SCU_PLL->USBPLLCON |= SCU_PLL_USBPLLCON_FINDIS_Msk;

/* Setup Divider settings for USB PLL */
SCU_PLL->USBPLLCON = ((USB_NDIV << SCU_PLL_USBPLLCON_NDIV_Pos) |
                        (USB_PDIV << SCU_PLL_USBPLLCON_PDIV_Pos));

/* Set OSCDISCDIS */
SCU_PLL->USBPLLCON |= SCU_PLL_USBPLLCON_OSCDISCDIS_Msk;

/* connect Oscillator to USB PLL */
SCU_PLL->USBPLLCON &= ~SCU_PLL_USBPLLCON_FINDIS_Msk;

/* restart PLL Lock detection */
SCU_PLL->USBPLLCON |= SCU_PLL_USBPLLCON_RESLD_Msk;

while ((SCU_PLL->USBPLLSTAT & SCU_PLL_USBPLLSTAT_VCOLOCK_Msk) == 0U)
{
    /* wait for PLL Lock */
}
#endif


/* Enable selected clocks */
SCU_CLK->CLKSET = __CLKSET;

#if __EXTCLKPIN != 0
#if __EXTCLKPIN == EXTCLK_PIN_P1_15
/* P1.15 */
PORT1->PDR1 &= ~PORT1_PDR1_PD15_Msk;
PORT1->IOCR12 = (PORT1->IOCR12 & ~PORT0_IOCR12_PC15_Msk) | (0x11U << PORT0_IOCR12_PC15_Pos);
#else
/* P0.8 */
PORT0->HWSEL &= ~PORT0_HWSEL_HW8_Msk;
PORT0->PDR1 &= ~PORT0_PDR1_PD8_Msk;
PORT0->IOCR8 = (PORT0->IOCR8 & ~PORT0_IOCR8_PC8_Msk) | (0x11U << PORT0_IOCR8_PC8_Pos);
#endif
#endif/* ENABLE_EXTCLK == 1*/

SystemCoreClockUpdate();
}

__WEAK void SystemCoreClockUpdate(void)
{
uint32_t pdiv;
uint32_t ndiv;
uint32_t kdiv;
uint32_t temp;

if (SCU_CLK->SYSCLKCR & SCU_CLK_SYSCLKCR_SYSSEL_Msk)
{
    /* fPLL is clock source for fSYS */
    if(SCU_PLL->PLLCON2 & SCU_PLL_PLLCON2_PINSEL_Msk)
    {
      /* PLL input clock is the backup clock (fOFI) */
      temp = OFI_FREQUENCY;
    }
    else
    {
      /* PLL input clock is the high performance osicllator (fOSCHP) */
      temp = OSCHP_GetFrequency();
    }

    /* check if PLL is locked */
    if (SCU_PLL->PLLSTAT & SCU_PLL_PLLSTAT_VCOLOCK_Msk)
    {
      /* PLL normal mode */
      /* read back divider settings */
      pdiv = ((SCU_PLL->PLLCON1 & SCU_PLL_PLLCON1_PDIV_Msk) >> SCU_PLL_PLLCON1_PDIV_Pos) + 1;
      ndiv = ((SCU_PLL->PLLCON1 & SCU_PLL_PLLCON1_NDIV_Msk) >> SCU_PLL_PLLCON1_NDIV_Pos) + 1;
      kdiv = ((SCU_PLL->PLLCON1 & SCU_PLL_PLLCON1_K2DIV_Msk) >> SCU_PLL_PLLCON1_K2DIV_Pos) + 1;

      temp = (temp / (pdiv * kdiv)) * ndiv;
    }
    else
    {
      /* PLL prescalar mode */
      /* read back divider settings */
      kdiv= ((SCU_PLL->PLLCON1 & SCU_PLL_PLLCON1_K1DIV_Msk) >> SCU_PLL_PLLCON1_K1DIV_Pos) + 1;

      temp = (temp / kdiv);
    }
}
else
{
    /* fOFI is clock source for fSYS */   
    temp = OFI_FREQUENCY;
}

temp = temp / ((SCU_CLK->SYSCLKCR & SCU_CLK_SYSCLKCR_SYSDIV_Msk) + 1);
temp = temp / ((SCU_CLK->CPUCLKCR & SCU_CLK_CPUCLKCR_CPUDIV_Msk) + 1);

SystemCoreClock = temp;
}


从上述代码可以看出系统的时钟配置,具体配置的信息如下:
/*
//    <h> Clock tree
//      <o1.16> System clock source selection
//                      <0=> fOFI
//                      <1=> fPLL
//                      <i> Default: fPLL
//      <o1.0..7> System clock divider <1-256><#-1>
//                      <i> Default: 2
//      <o2.0> CPU clock divider
//                      <0=> fCPU = fSYS
//                      <1=> fCPU = fSYS / 2
//                      <i> Default: fCPU = fSYS
//      <o3.0>Peripheral clock divider
//                      <0=> fPB = fCPU
//                      <1=> fPB = fCPU / 2
//                      <i> Default: fPB = fCPU
//      <o4.0>CCU clock divider
//                      <0=> fCCU = fCPU
//                      <1=> fCCU = fCPU / 2
//                      <i> Default: fCCU = fCPU
//      <e.5> Enable WDT clock
//             <o5.16..17> WDT clock source <0=> fOFI
//                                          <1=> fSTDBY
//                                          <2=> fPLL
//                      <i> Default: fOFI
//             <o5.0..7> WDT clock divider <1-256><#-1>
//                      <i> Default: 1
//      </e>
//      <e.3> Enable EBU clock
//             <o6.0..5>EBU clock divider<1-64><#-1>
//             <i> Default: 4
//      </e>
//      <e.2> Enable ETH clock
//      </e>
//      <e.1> Enable MMC clock
//      </e>
//      <e.0> Enable USB clock
//             <o7.16> USB clock source <0=> fUSBPLL
//                                    <1=> fPLL
//             <i> Default: fPLL
//      </e>
//      <e8> Enable external clock
//             <o8.0..1> External Clock Source Selection
//                  <0=> fSYS
//                  <2=> fUSB
//                  <3=> fPLL
//                  <i> Default: fPLL
//             <o8.16..24> External Clock divider <1-512><#-1>
//                  <i> Default: 288
//                  <i> Only valid for USB PLL and PLL clocks
//             <o9.0> External Clock Pin Selection
//                  <0=> Disabled
//                  <1=> P0.8
//                  <2=> P1.15
//                  <i> Default: Disabled
//      </e>
//    </h>

接下来分析main函数程序
int main(void)
{
      /* Setup the system timer to tick every 100us */
      SysTick_Config(SystemCoreClock / TICKS_PER_SECOND);

      /* Configure P3.9 (LED) */
      // P3.9 is used as GPIO for LED indication. Macros can be find in GPIO.h
      P5_9_set_mode(OUTPUT_PP_GP);
      P5_9_set_driver_strength(STRONG);
      P5_9_reset();

      /* Initialize and start ADC */
      ADC0_Init();

      /* Infinite loop */
      for(;;){
                do{
                        adc_result =VADC_G0->RES;
                } while (!(adc_result >> VADC_G_RES_VF_Pos));
                                        // wait until ADC result register 0 value is valid
                                        // VADC.G0RES1.VF = 1: Valid Flag
                                        // Indicates a new result in bitfield RESULT or bit FCR.
                                        // 1B = Bitfield RESULT has been updated with new
                                        // result value and has not yet been read, or bit
                                        // FCR has been updated

                adc_result &= 0xFFF;         // mask ADC result
                Delay100US((adc_result << 1) + 500);
                                       // tune minimum and maximum flashing time

                P5_9_toggle();
                                       // toggle P3.9 (toggle LED) using GPIO.h macros
      }
      return 0;
}先配置系统SysTick为100us中断一次,配置引脚5.9为输出引脚,引脚输出为强输出,先输出为低电平。然后初始化ADC0,并且启动ADC0转换。在while循环中,读取ADC转换的数据,用来产生延时的基数,翻转点灯。

对于DAVE的IDE发现有几个很好的功能,没有实际编译的代码为黑色背景,这样方便我们分析代码。

智能提醒,直接把鼠标停在代码上面,自动给出代码定义,比iar和mdk好很多,不需要再用第三方编辑器看代码。

此内容由EEWORLD论坛网友qwerghf原创,如需转载或用于商业用途需征得作者同意并注明出处




</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                                                                                (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>
页: [1]
查看完整版本: XMC4700 Relax 5V shield 评测:分析XMC4700 Easy Start Project