因为是使用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()中按照配置的减小计数值并检测是否清零;

  1. static __IO uint32_t TimingDelay;
  2. void Delay(__IO uint32_t nTime);
  3.  
  4. /**
  5. * @brief Main program
  6. * @param None
  7. * @retval None
  8. */
  9. int main(void)
  10. {
  11. /*!< At this stage the microcontroller clock setting is already configured,
  12. this is done through SystemInit() function which is called from startup
  13. file (startup_stm32f0xx.s) before to branch to application main.
  14. To reconfigure the default setting of SystemInit() function, refer to
  15. system_stm32f0xx.c file
  16. */
  17.  
  18. /* Setup SysTick Timer for 1 msec interrupts.
  19. ------------------------------------------
  20. 1. The SysTick_Config() function is a CMSIS function which configure:
  21. - The SysTick Reload register with value passed as function parameter.
  22. - Configure the SysTick IRQ priority to the lowest value (0x0F).
  23. - Reset the SysTick Counter register.
  24. - Configure the SysTick Counter clock source to be Core Clock Source (HCLK).
  25. - Enable the SysTick Interrupt.
  26. - Start the SysTick Counter.
  27.  
  28. 2. You can change the SysTick Clock source to be HCLK_Div8 by calling the
  29. SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8) just after the
  30. SysTick_Config() function call. The SysTick_CLKSourceConfig() is defined
  31. inside the misc.c file.
  32.  
  33. 3. You can change the SysTick IRQ priority by calling the
  34. NVIC_SetPriority(SysTick_IRQn,...) just after the SysTick_Config() function
  35. call. The NVIC_SetPriority() is defined inside the core_cm0.h file.
  36.  
  37. 4. To adjust the SysTick time base, use the following formula:
  38.  
  39. Reload Value = SysTick Counter Clock (Hz) x Desired Time base (s)
  40.  
  41. - Reload Value is the parameter to be passed for SysTick_Config() function
  42. - Reload Value should not exceed 0xFFFFFF
  43. */
  44. if (SysTick_Config(SystemCoreClock / ))
  45. {
  46. /* Capture error */
  47. while ();
  48. }
  49.  
  50. while ()
  51. {
  52. /* Insert 100 ms delay */
  53. Delay();
  54. }
  55. }
  56.  
  57. /**
  58. * @brief Inserts a delay time.
  59. * @param nTime: specifies the delay time length, in milliseconds.
  60. * @retval None
  61. */
  62. void Delay(__IO uint32_t nTime)
  63. {
  64. TimingDelay = nTime;
  65.  
  66. while(TimingDelay != );
  67. }
  68.  
  69. /**
  70. * @brief Decrements the TimingDelay variable.
  71. * @param None
  72. * @retval None
  73. */
  74. void TimingDelay_Decrement(void)
  75. {
  76. if (TimingDelay != 0x00)
  77. {
  78. TimingDelay--;
  79. }
  80. }

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

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

  1. uint32_t SystemCoreClock = ;
  2.  
  3. /**
  4.  
  5. * @brief This function handles SysTick Handler.
  6.  
  7. * @param None
  8.  
  9. * @retval None
  10.  
  11. */
  12.  
  13. void SysTick_Handler(void)
  14.  
  15. {
  16.  
  17.   TimingDelay_Decrement();
  18.  
  19. }

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

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

设置中断优先级;

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

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

  1. __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
  2. {
  3. if ((ticks - ) > SysTick_LOAD_RELOAD_Msk) return (); /* Reload value impossible */
  4.  
  5. SysTick->LOAD = ticks - ; /* set reload register */
  6. NVIC_SetPriority (SysTick_IRQn, (<<__NVIC_PRIO_BITS) - ); /* set Priority for Systick Interrupt */
  7. SysTick->VAL = ; /* Load the SysTick Counter Value */
  8. SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
  9. SysTick_CTRL_TICKINT_Msk |
  10. SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
  11. return (); /* Function successful */
  12. }

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;

  1. void SystemInit (void)
  2. {
  3. /* Set HSION bit */
  4. RCC->CR |= (uint32_t)0x00000001;
  5.  
  6. #if defined(STM32F051)
  7. /* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE and MCOSEL[2:0] bits */
  8. RCC->CFGR &= (uint32_t)0xF8FFB80C;
  9. #else
  10. /* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE, MCOSEL[2:0], MCOPRE[2:0] and PLLNODIV bits */
  11. RCC->CFGR &= (uint32_t)0x08FFB80C;
  12. #endif /* STM32F051 */
  13.  
  14. /* Reset HSEON, CSSON and PLLON bits */
  15. RCC->CR &= (uint32_t)0xFEF6FFFF;
  16.  
  17. /* Reset HSEBYP bit */
  18. RCC->CR &= (uint32_t)0xFFFBFFFF;
  19.  
  20. /* Reset PLLSRC, PLLXTPRE and PLLMUL[3:0] bits */
  21. RCC->CFGR &= (uint32_t)0xFFC0FFFF;
  22.  
  23. /* Reset PREDIV1[3:0] bits */
  24. RCC->CFGR2 &= (uint32_t)0xFFFFFFF0;
  25.  
  26. /* Reset USARTSW[1:0], I2CSW, CECSW and ADCSW bits */
  27. RCC->CFGR3 &= (uint32_t)0xFFFFFEAC;
  28.  
  29. /* Reset HSI14 bit */
  30. RCC->CR2 &= (uint32_t)0xFFFFFFFE;
  31.  
  32. /* Disable all interrupts */
  33. RCC->CIR = 0x00000000;
  34.  
  35. /* Configure the System clock frequency, AHB/APBx prescalers and Flash settings */
  36. SetSysClock();
  37. }

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

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

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

  1. /**
  2. * @brief Configures the System clock frequency, AHB/APBx prescalers and Flash
  3. * settings.
  4. * @note This function should be called only once the RCC clock configuration
  5. * is reset to the default reset state (done in SystemInit() function).
  6. * @param None
  7. * @retval None
  8. */
  9. static void SetSysClock(void)
  10. {
  11. __IO uint32_t StartUpCounter = , HSEStatus = ;
  12.  
  13. /******************************************************************************/
  14. /* PLL (clocked by HSE) used as System clock source */
  15. /******************************************************************************/
  16.  
  17. /* SYSCLK, HCLK, PCLK configuration ----------------------------------------*/
  18. /* Enable HSE */
  19. RCC->CR |= ((uint32_t)RCC_CR_HSEON);
  20.  
  21. /* Wait till HSE is ready and if Time out is reached exit */
  22. do
  23. {
  24. HSEStatus = RCC->CR & RCC_CR_HSERDY;
  25. StartUpCounter++;
  26. } while((HSEStatus == ) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
  27.  
  28. if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  29. {
  30. HSEStatus = (uint32_t)0x01;
  31. }
  32. else
  33. {
  34. HSEStatus = (uint32_t)0x00;
  35. }
  36.  
  37. if (HSEStatus == (uint32_t)0x01)
  38. {
  39. /* Enable Prefetch Buffer and set Flash Latency */
  40. FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
  41.  
  42. /* HCLK = SYSCLK */
  43. RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
  44.  
  45. /* PCLK = HCLK */
  46. RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1;
  47.  
  48. /* PLL configuration */
  49. RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
  50. RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLMULL6);
  51.  
  52. /* Enable PLL */
  53. RCC->CR |= RCC_CR_PLLON;
  54.  
  55. /* Wait till PLL is ready */
  56. while((RCC->CR & RCC_CR_PLLRDY) == )
  57. {
  58. }
  59.  
  60. /* Select PLL as system clock source */
  61. RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
  62. RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
  63.  
  64. /* Wait till PLL is used as system clock source */
  65. while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)RCC_CFGR_SWS_PLL)
  66. {
  67. }
  68. }
  69. else
  70. { /* If HSE fails to start-up, the application will have wrong clock
  71. configuration. User can add here some code to deal with this error */
  72. }
  73. }

而在核心时钟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的分频系数;

  1. void SystemCoreClockUpdate (void)
  2. {
  3. uint32_t tmp = , pllmull = , pllsource = , prediv1factor = ;
  4.  
  5. /* Get SYSCLK source -------------------------------------------------------*/
  6. tmp = RCC->CFGR & RCC_CFGR_SWS;
  7.  
  8. switch (tmp)
  9. {
  10. case 0x00: /* HSI used as system clock */
  11. SystemCoreClock = HSI_VALUE;
  12. break;
  13. case 0x04: /* HSE used as system clock */
  14. SystemCoreClock = HSE_VALUE;
  15. break;
  16. case 0x08: /* PLL used as system clock */
  17. /* Get PLL clock source and multiplication factor ----------------------*/
  18. pllmull = RCC->CFGR & RCC_CFGR_PLLMULL;
  19. pllsource = RCC->CFGR & RCC_CFGR_PLLSRC;
  20. pllmull = ( pllmull >> ) + ;
  21.  
  22. if (pllsource == 0x00)
  23. {
  24. /* HSI oscillator clock divided by 2 selected as PLL clock entry */
  25. SystemCoreClock = (HSI_VALUE >> ) * pllmull;
  26. }
  27. else
  28. {
  29. prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + ;
  30. /* HSE oscillator clock selected as PREDIV1 clock entry */
  31. SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull;
  32. }
  33. break;
  34. default: /* HSI used as system clock */
  35. SystemCoreClock = HSI_VALUE;
  36. break;
  37. }
  38. /* Compute HCLK clock frequency ----------------*/
  39. /* Get HCLK prescaler */
  40. tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> )];
  41. /* HCLK clock frequency */
  42. SystemCoreClock >>= tmp;
  43. }

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

首先初始化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;

  1. void delay_init(u8 SYSCLK)
  2. {
  3. SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
  4. fac_us=SYSCLK/;
  5. fac_ms=(u16)fac_us*;
  6. }

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

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

清空计数寄存器VAL;

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

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

  1. void delay_us(u32 nus)
  2. {
  3. u32 temp;
  4. SysTick->LOAD=nus*fac_us;
  5. SysTick->VAL=0x00;
  6. SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;
  7. do
  8. {
  9. temp=SysTick->CTRL;
  10. }while((temp&0x01)&&!(temp&(<<)));
  11. SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
  12. SysTick->VAL =0X00;
  13. }
  14.  
  15. void delay_xms(u16 nms)
  16. {
  17. u32 temp;
  18. SysTick->LOAD=(u32)nms*fac_ms;
  19. SysTick->VAL =0x00;
  20. SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;
  21. do
  22. {
  23. temp=SysTick->CTRL;
  24. }while((temp&0x01)&&!(temp&(<<)));
  25. SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
  26. SysTick->VAL =0X00;
  27. }
  28.  
  29. void delay_ms(u16 nms)
  30. {
  31. u8 repeat=nms/;
  32. u16 remain=nms%;
  33. while(repeat)
  34. {
  35. delay_xms();
  36. repeat--;
  37. }
  38. if(remain)delay_xms(remain);
  39. }

2019-7-18更新

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

  1. /**
  2. * @brief This function handles System tick timer.
  3. */
  4. void SysTick_Handler(void)
  5. {
  6. /* USER CODE BEGIN SysTick_IRQn 0 */
  7.  
  8. /* USER CODE END SysTick_IRQn 0 */
  9. HAL_IncTick();
  10. /* USER CODE BEGIN SysTick_IRQn 1 */
  11.  
  12. /* USER CODE END SysTick_IRQn 1 */
  13. }

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

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

  1. /**
  2. * @brief This function provides accurate delay (in milliseconds) based
  3. * on variable incremented.
  4. * @note In the default implementation , SysTick timer is the source of time base.
  5. * It is used to generate interrupts at regular time intervals where uwTick
  6. * is incremented.
  7. * @note ThiS function is declared as __weak to be overwritten in case of other
  8. * implementations in user file.
  9. * @param Delay specifies the delay time length, in milliseconds.
  10. * @retval None
  11. */
  12. __weak void HAL_Delay(__IO uint32_t Delay)
  13. {
  14. uint32_t tickstart = HAL_GetTick();
  15. uint32_t wait = Delay;
  16.  
  17. /* Add a period to guarantee minimum wait */
  18. if (wait < HAL_MAX_DELAY)
  19. {
  20. wait++;
  21. }
  22.  
  23. while((HAL_GetTick() - tickstart) < wait)
  24. {
  25. }
  26. }

STM32F072从零配置工程-实现delay功能的更多相关文章

  1. STM32F072从零配置工程-建立工程文件

    快速建立工程有两种方法: 第一种是通过官方提供的外设库来搭建,好处是使用库函数,而不需要深入研究寄存器配置: 第二种是通过STM32CubeMX,好处是直观快速,可以直接帮你配置好功能和时钟,不过使用 ...

  2. STM32F072从零配置工程-自定义时钟配置详解

    从自己的板子STM32F407入手,参考官方的SystemInit()函数: 核心在SetSysClock()这个函数,官方默认是采用HSE(设定为8MHz)作为PLL锁相环的输入输出168MHz的S ...

  3. STM32F072从零配置工程-基于HAL库的串口UART中断配置

    先上一个采用串口直接传输的Demo: 此处的思路是完全采用HAL库来实现的,核心是运用HAL_UART_Transmit_IT和HAL_UART_Receive_IT两个函数来实现的,可以作为一个De ...

  4. STM32F072从零配置工程-串口USART配置

    也是使用HAL库进行配置,通过STMCube生成代码,可以通过这个简单的配置过程看到STMCube生成代码的一种规范: 从main函数入手观察其外设配置结构: 首先是HAL_Init()进行所有外设的 ...

  5. Spring Web工程web.xml零配置即使用Java Config + Annotation

    摘要: 在Spring 3.0之前,我们工程中常用Bean都是通过XML形式的文件注解的,少了还可以,但是数量多,关系复杂到后期就很难维护了,所以在3.x之后Spring官方推荐使用Java Conf ...

  6. H5新人福音~零配置搭建现代化的前端工程

    X-BUILD一套基于Webpack(v4.21.0)快速搭建H5场景开发环境的脚手架,只需要几分钟的时间就可以运行起来.X-BUILD是针对H5开发的一套自动化构建工具,致力于提升开发效率,减小开发 ...

  7. struts2 Convention插件零配置,使用注解开发

    从struts21开始,struts2不再推荐使用codebehind作为零配置插件,而是改用Convention插件来支持零配置.与以前相比较,Convention插件更彻底. 使用Conventi ...

  8. Spring 基于注解零配置开发

    本文是转载文章,感觉比较好,如有侵权,请联系本人,我将及时删除. 原文网址:< Spring 基于注解零配置开发 > 一:搜索Bean 再也不用在XML文件里写什么配置信息了. Sprin ...

  9. Struts2 注解零配置方法(convention插件使用)

    最近接触到一个新的项目,是做一个使用S2SH的电子商务商城的二次开发.之前使用过S2SH,在此之前的项目中,Struts2 使用的是XML配置而这个项目是使用注解.在这个项目中,注解还不需要使用Act ...

随机推荐

  1. Win8Metro(C#)数字图像处理--2.27图像加法运算

    原文:Win8Metro(C#)数字图像处理--2.27图像加法运算  [函数名称] 图像加法函数AddProcess(WriteableBitmap src, WriteableBitmap a ...

  2. 微信小程序把玩(三)tabBar底部导航

    原文:微信小程序把玩(三)tabBar底部导航 tabBar相对而言用的还是比较多的,但是用起来并没有难,在app.json中配置下tabBar即可,注意tabBar至少需要两个最多五个Item选项 ...

  3. mysql三种修改密码的方式

    [root@MySQL ~]# mysqladmin -uroot -proot -S /data/3307/mysql.sock password '123'; 其中-p是现在的密码,passwor ...

  4. WPF 设置只能运行一个实例

    codereview上的帖子 https://codereview.stackexchange.com/questions/20871/single-instance-wpf-application ...

  5. 三星860 evo 250g 开启AHCI模式读写对比

    主板比较老,只支持sata2接口 换用三星860evo后跑分对比

  6. [Erlang-0016][aque_tcp] 一个 Erlang TCP 组件

    项目地址:https://github.com/liangjingyang/aque_tcp 欢迎任何形式的转载,但请务必注明出处:http://www.cnblogs.com/liangjingya ...

  7. c# 将字符串转换为指定类型的值

    private object GetValueByProperty(string key, string value, ref Type typeValue) { Type t = typeof(T) ...

  8. 使用Arcgis Pro 发布矢量切片

    ArcGIS Pro 中的任何地图或底图都可以创建矢量切片,但是有一些局限性和特殊注意事项.为创建矢量切片制作地图的重点是构建一个有效的地图,以快速绘制生成的切片. 软件环境 操作系统:Windows ...

  9. 每日一问:到底为什么属性动画后 View 在新位置还能响应事件

    在 Android 开发中,我们难免会使用动画来处理各种各样的动画效果,以满足 UI 的高逼格设计.对于比较复杂的动画效果,我们通常会采用著名的开源库:lottie-android,或许你会对 lot ...

  10. sql一关联多查询时否定筛选出现的问题的解决

    问题:一方关联多方查询时执行否定筛选,结果包含未通过筛选的项. 我们规定一方为父,多方为子,我们希望子未通过筛选时,结果也不出现对应的父. 查询部门及部门下的所有员工. SELECT * FROM d ...