STM32F072从零配置工程-实现delay功能

因为是使用SysTick来作为延时时钟,因此在这里给出SysTick时钟的寄存器;

CTRL:SysTick控制及状态寄存器

位段

名称

类型

复位值

描述

16

COUNTFLAG

R/W

0

如果在上次读取本寄存器后, SysTick 已经计到
了 0,则该位为 1。

2

CLKSOURCE

R/W

0

时钟源选择位,0=AHB/8,1=处理器时钟AHB

1

TICKINT

R/W

0

1=SysTick倒数计数到 0时产生 SysTick异常请
求,0=数到 0 时无动作。也可以通过读取COUNTFLAG标志位来确定计数器是否递减到0

0

ENABLE

R/W

0

SysTick 定时器的使能位

LOAD:SysTick 重装载数值寄存器

位段

名称

类型

复位值

描述

23:0

RELOAD

R/W

0

当倒数计数至零时,将被重装载的值

VAL:SysTick当前数值寄存器

 

位段

名称

类型

复位值

描述

23:0

CURRENT

R/W

0

读取时返回当前倒计数的值,写它则使之清零,同时还会清除在SysTick控制及状态寄存器中的COUNTFLAG 标志

 

此处参考外设库的例子,程序结构和逻辑大致如下:

  通过SystemInit()配置好时钟;

  通过SysTick_Config(SystemCoreClock/1000)配置SysTick时钟;

  创建Delay(__IO uint32_t nTime)毫秒延时函数(设置执行几次中断,时间为nTime×中断时间);

  在SysTick中断SysTick_Handler()中按照配置的减小计数值并检测是否清零;

 

static __IO uint32_t TimingDelay;
void Delay(__IO uint32_t nTime);/*** @brief   Main program* @param  None* @retval None*/
int main(void)
{/*!< At this stage the microcontroller clock setting is already configured, this is done through SystemInit() function which is called from startupfile (startup_stm32f0xx.s) before to branch to application main.To reconfigure the default setting of SystemInit() function, refer tosystem_stm32f0xx.c file*/     /* Setup SysTick Timer for 1 msec interrupts.------------------------------------------1. The SysTick_Config() function is a CMSIS function which configure:- The SysTick Reload register with value passed as function parameter.- Configure the SysTick IRQ priority to the lowest value (0x0F).- Reset the SysTick Counter register.- Configure the SysTick Counter clock source to be Core Clock Source (HCLK).- Enable the SysTick Interrupt.- Start the SysTick Counter.2. You can change the SysTick Clock source to be HCLK_Div8 by calling theSysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8) just after theSysTick_Config() function call. The SysTick_CLKSourceConfig() is definedinside the misc.c file.3. You can change the SysTick IRQ priority by calling theNVIC_SetPriority(SysTick_IRQn,...) just after the SysTick_Config() function call. The NVIC_SetPriority() is defined inside the core_cm0.h file.4. To adjust the SysTick time base, use the following formula:Reload Value = SysTick Counter Clock (Hz) x  Desired Time base (s)- Reload Value is the parameter to be passed for SysTick_Config() function- Reload Value should not exceed 0xFFFFFF*/if (SysTick_Config(SystemCoreClock / 1000)){ /* Capture error */ while (1);}while (1){/* Insert 100 ms delay */Delay(100);}
}/*** @brief  Inserts a delay time.* @param  nTime: specifies the delay time length, in milliseconds.* @retval None*/
void Delay(__IO uint32_t nTime)
{ TimingDelay = nTime;while(TimingDelay != 0);
}/*** @brief  Decrements the TimingDelay variable.* @param  None* @retval None*/
void TimingDelay_Decrement(void)
{if (TimingDelay != 0x00){ TimingDelay--;}
}

 

由于官方的例子中默认配置好了时钟,因此给出的SystemCoreClock是48000000;

  设置好重装值后,就可以确定发生一次中断需要的时间;

uint32_t SystemCoreClock    = 48000000;  /** * @brief  This function handles SysTick Handler. * @param  None * @retval None */  void SysTick_Handler(void)  {  TimingDelay_Decrement();  }  

 

从SysTick_Config()中可以看出配置SysTick的步骤:

       设置重装载寄存器LOAD的值;(设置的是中断时间)

       设置中断优先级;

       清楚当前数值寄存器VAL的值;

       配置控制与状态寄存器CTRL,包括时钟源、系统定时器中断、使能定时器;

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk)  return (1);      /* Reload value impossible */SysTick->LOAD  = ticks - 1;                                  /* set reload register */NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Systick Interrupt */SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |SysTick_CTRL_TICKINT_Msk   |SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */return (0);                                                  /* Function successful */
}

 

SysTick中断时间的计算:

       假设fAHB=180MHz,则计数值±1的时间为t=1/fAHB,中断一次的时间=LOAD×t;

       根据计算,中断1us的时间= LOAD×1/fAHB=180×(1/180M)=1us;

       若需要中断1ms的时间=180000×(1/180M)=1ms;

       因此,设置SystemCoreClock=180M/1000=180000=LOAD值;

       这样取LOAD值为180000就可以得到一次中断需要的时间为1ms;

 

由于库例子是默认设置好了时钟,因此这里涉及到一个时钟的配置

在SystemInit中重要任务是清除RCC中的时钟配置:

  将HSI作为默认时钟;

  且SYSCLK不分频、HCLK不分频、ADC时钟不分频、MCO时钟输出不分频;

  关闭HSE、CSS、PLL时钟;

  将外部HSE时钟输入旁路;

  清空PLL系数;

  清空USART1、I2C、CEC和ADC外设的时钟源;

  关闭HSI14时钟;

  关闭所有中断Int;

 

void SystemInit (void)
{    /* Set HSION bit */RCC->CR |= (uint32_t)0x00000001;#if defined(STM32F051)   /* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE and MCOSEL[2:0] bits */RCC->CFGR &= (uint32_t)0xF8FFB80C;
#else/* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE, MCOSEL[2:0], MCOPRE[2:0] and PLLNODIV bits */RCC->CFGR &= (uint32_t)0x08FFB80C;
#endif /* STM32F051 *//* Reset HSEON, CSSON and PLLON bits */RCC->CR &= (uint32_t)0xFEF6FFFF;/* Reset HSEBYP bit */RCC->CR &= (uint32_t)0xFFFBFFFF;/* Reset PLLSRC, PLLXTPRE and PLLMUL[3:0] bits */RCC->CFGR &= (uint32_t)0xFFC0FFFF;/* Reset PREDIV1[3:0] bits */RCC->CFGR2 &= (uint32_t)0xFFFFFFF0;/* Reset USARTSW[1:0], I2CSW, CECSW and ADCSW bits */RCC->CFGR3 &= (uint32_t)0xFFFFFEAC;/* Reset HSI14 bit */RCC->CR2 &= (uint32_t)0xFFFFFFFE;/* Disable all interrupts */RCC->CIR = 0x00000000;/* Configure the System clock frequency, AHB/APBx prescalers and Flash settings */SetSysClock();
}

 

时钟配置的重点在函数SetSysClock()中;

       使能HSE并等待HSE初始化完成;

       若HSE初始化完成后用HSE配置PLL时钟,并将PLL输出时钟作为系统时钟SYSCLK;

/*** @brief  Configures the System clock frequency, AHB/APBx prescalers and Flash*         settings.* @note   This function should be called only once the RCC clock configuration*         is reset to the default reset state (done in SystemInit() function).* @param  None* @retval None*/
static void SetSysClock(void)
{__IO uint32_t StartUpCounter = 0, HSEStatus = 0;/******************************************************************************/
/*            PLL (clocked by HSE) used as System clock source                */
/******************************************************************************//* SYSCLK, HCLK, PCLK configuration ----------------------------------------*//* Enable HSE */    RCC->CR |= ((uint32_t)RCC_CR_HSEON);/* Wait till HSE is ready and if Time out is reached exit */do{HSEStatus = RCC->CR & RCC_CR_HSERDY;StartUpCounter++;  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));if ((RCC->CR & RCC_CR_HSERDY) != RESET){HSEStatus = (uint32_t)0x01;}else{HSEStatus = (uint32_t)0x00;}  if (HSEStatus == (uint32_t)0x01){/* Enable Prefetch Buffer and set Flash Latency */FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;/* HCLK = SYSCLK */RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;/* PCLK = HCLK */RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1;/* PLL configuration */RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLMULL6);/* Enable PLL */RCC->CR |= RCC_CR_PLLON;/* Wait till PLL is ready */while((RCC->CR & RCC_CR_PLLRDY) == 0){}/* Select PLL as system clock source */RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    /* Wait till PLL is used as system clock source */while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)RCC_CFGR_SWS_PLL){}}else{ /* If HSE fails to start-up, the application will have wrong clock configuration. User can add here some code to deal with this error */}  
}

 

而在核心时钟HCLK发生变化时,需要调用SystemCoreClockUpdate()函数来更新SystemCoreClock值,以便调整使用了该参数的配置;

       如果SYSCLK源为HSI,则SystemCoreClock=HSI_VALUE;(HSI_VALUE默认为8MHz,实际值会随电压和温度变化而变化)

       如果SYSCLK源为HSE,则SystemCoreClock=HSE_VALUE;(HSE_VALUE需要确保和实际所使用的外接晶振频率相同,否则此功能可能会失效)

       如果SYSCLK源为PLL,则SystemCoreClock= HSE/HSI_VALUE与PLL因子的计算;

       如果SYSCLK源不确定,默认为HSI时钟源;

       最后考虑HCLK的分频系数;

void SystemCoreClockUpdate (void)
{uint32_t tmp = 0, pllmull = 0, pllsource = 0, prediv1factor = 0;/* Get SYSCLK source -------------------------------------------------------*/tmp = RCC->CFGR & RCC_CFGR_SWS;switch (tmp){case 0x00:  /* HSI used as system clock */SystemCoreClock = HSI_VALUE;break;case 0x04:  /* HSE used as system clock */SystemCoreClock = HSE_VALUE;break;case 0x08:  /* PLL used as system clock *//* Get PLL clock source and multiplication factor ----------------------*/pllmull = RCC->CFGR & RCC_CFGR_PLLMULL;pllsource = RCC->CFGR & RCC_CFGR_PLLSRC;pllmull = ( pllmull >> 18) + 2;if (pllsource == 0x00){/* HSI oscillator clock divided by 2 selected as PLL clock entry */SystemCoreClock = (HSI_VALUE >> 1) * pllmull;}else{prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1;/* HSE oscillator clock selected as PREDIV1 clock entry */SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull; }      break;default: /* HSI used as system clock */SystemCoreClock = HSI_VALUE;break;}/* Compute HCLK clock frequency ----------------*//* Get HCLK prescaler */tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];/* HCLK clock frequency */SystemCoreClock >>= tmp;  
}

 

另一中延时函数的配置方法,是不利用中断来实现的

首先初始化SysTick时钟

       这里直接将SysTick时钟源设置为HCLK的8分频;

       由于这里SYSCLK=SystemCoreClock=168MHz,因此SysTick时钟为168MHz/8=21MHz;

       因此要延时1us=10^(-6)s=21/21M,此时的重装值为21=SYSCLK/8=fac_us;

       要延时1ms=10^(-3)=21k/21M,此时的重装值为21k=1000*fac_us= fac_ms;

 

void delay_init(u8 SYSCLK)
{SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); fac_us=SYSCLK/8;fac_ms=(u16)fac_us*1000;
}

 

延时1us到延时1ms的函数

       流程大致如下:将x*fac_us加载入重装寄存器LOAD;

       清空计数寄存器VAL;

       通过CTRL寄存器,开启SysTick时钟;

       等待计数完成,关闭SysTick时钟,清空计数寄存器VAL;

void delay_us(u32 nus)  
{         u32 temp;              SysTick->LOAD=nus*fac_us;  SysTick->VAL=0x00;  SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;  do  {  temp=SysTick->CTRL;  }while((temp&0x01)&&!(temp&(1<<16)));  SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;  SysTick->VAL =0X00;  
}  void delay_xms(u16 nms)  
{                   u32 temp;            SysTick->LOAD=(u32)nms*fac_ms;  SysTick->VAL =0x00;  SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;do  {  temp=SysTick->CTRL;  }while((temp&0x01)&&!(temp&(1<<16)));  SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;SysTick->VAL =0X00;  
}   void delay_ms(u16 nms)  
{          u8 repeat=nms/540;  u16 remain=nms%540;  while(repeat)  {  delay_xms(540);  repeat--;  }  if(remain)delay_xms(remain);  
}   

 

2019-7-18更新

从STM32Cube生成的代码已经默认包含了延时功能,但是仅用于实现1ms的延时,其实现是通过系统自带的配置:

/*** @brief This function handles System tick timer.*/
void SysTick_Handler(void)
{/* USER CODE BEGIN SysTick_IRQn 0 *//* USER CODE END SysTick_IRQn 0 */HAL_IncTick();/* USER CODE BEGIN SysTick_IRQn 1 *//* USER CODE END SysTick_IRQn 1 */
}

然后延时的功能采用库函数实现HAL_Delay()来实现:

  可以实现延时1ms的功能;

/*** @brief This function provides accurate delay (in milliseconds) based *        on variable incremented.* @note In the default implementation , SysTick timer is the source of time base.*       It is used to generate interrupts at regular time intervals where uwTick*       is incremented.* @note ThiS function is declared as __weak to be overwritten in case of other*       implementations in user file.* @param Delay specifies the delay time length, in milliseconds.* @retval None*/
__weak void HAL_Delay(__IO uint32_t Delay)
{uint32_t tickstart = HAL_GetTick();uint32_t wait = Delay;/* Add a period to guarantee minimum wait */if (wait < HAL_MAX_DELAY){wait++;}while((HAL_GetTick() - tickstart) < wait){}
}

 

转载于:https://www.cnblogs.com/lpfdezh/p/10943975.html


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部