因为是使用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 startup
file (startup_stm32f0xx.s) before to branch to application main.
To reconfigure the default setting of SystemInit() function, refer to
system_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 the
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8) just after the
SysTick_Config() function call. The SysTick_CLKSourceConfig() is defined
inside the misc.c file. 3. You can change the SysTick IRQ priority by calling the
NVIC_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 / ))
{
/* Capture error */
while ();
} while ()
{
/* Insert 100 ms delay */
Delay();
}
} /**
* @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 != );
} /**
* @brief Decrements the TimingDelay variable.
* @param None
* @retval None
*/
void TimingDelay_Decrement(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}

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

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

uint32_t SystemCoreClock    = ;  

/** 

  * @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 - ) > SysTick_LOAD_RELOAD_Msk) return (); /* Reload value impossible */ SysTick->LOAD = ticks - ; /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (<<__NVIC_PRIO_BITS) - ); /* set Priority for Systick Interrupt */
SysTick->VAL = ; /* 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 (); /* 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 = , HSEStatus = ; /******************************************************************************/
/* 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 == ) && (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) == )
{
} /* 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 = , pllmull = , pllsource = , prediv1factor = ; /* 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 >> ) + ; if (pllsource == 0x00)
{
/* HSI oscillator clock divided by 2 selected as PLL clock entry */
SystemCoreClock = (HSI_VALUE >> ) * pllmull;
}
else
{
prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + ;
/* 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) >> )];
/* 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/;
fac_ms=(u16)fac_us*;
}

延时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&(<<)));
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&(<<)));
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
SysTick->VAL =0X00;
} void delay_ms(u16 nms)
{
u8 repeat=nms/;
u16 remain=nms%;
while(repeat)
{
delay_xms();
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)
{
}
}

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. 芒果TV For Windows10 成长历史 & 迭代历史 & 新闻报道

    芒果TV 是国内领先的基于Windows10操作系统并支持Windows10全系列设备的视频应用和内容服务商. Win10商店版<芒果TV>是湖南快乐阳光互动娱乐传媒有限公司专门为Wind ...

  2. qmake.exe是在Qt安装编译时生成的,里面内嵌了Qt相关的一些路径(最简单的方法是保持一样的安装路径,最方便的办法是设置qt.conf文件)

    在网上直接下载别人编译好的Qt库,为自己使用省了不少事.但往往也会遇到些问题,其中Qt version is not properly installed,please run make instal ...

  3. 微信小程序把玩(五)页面生命周期

    原文:微信小程序把玩(五)页面生命周期 这里只要熟悉页面的基本生命周期即可,业务在指定生命周期函数内书写. 以下是官网给出的生命周期函数方法和状态图 上面的生周期函数图对于做Android 或者IOS ...

  4. mysql 服务压缩包安装,用户创建

    wind7上安装mysql记录: 1.下载的包中没有ini配置文件,需要根目录手动创建my.ini文件 内容如下: [client]port=3306default-character-set=utf ...

  5. 判断当前进程是否以管理员权限运行(AllocateAndInitializeSid后,用CheckTokenMembership与AdministratorsGroup进行比较,和Delphi的那个例子还有点不一样)

    在Win7下(Vista以上的...)有时某些操作就是会让人郁闷 开启了UAC的话,得以管理员权限运行才不会出现Access is denied... 但是,程序又不是非得什么时候都用那破管理员权限的 ...

  6. SetWinEventHook 事件钩子(有些windows事件并没有消息对应,譬如弹出菜单,切换窗口,获得焦点,滚动条滚动等)good

    相信消息钩子大家听的比较多,消息钩子能够在应用程序处理系统消息之前将其截获,提前处理并可以决定是否继续将消息往下传送,有些windows事件并没有消息对应,譬如弹出菜单,切换窗口,获得焦点,滚动条滚动 ...

  7. 模态对话框测试 MFC中的模态对话框与非模态对话框

    http://blog.csdn.net/u010839382/article/details/52972427 http://blog.csdn.net/u010839382/article/det ...

  8. Java基础(六) static五大应用场景

    static和final是两个我们必须掌握的关键字.不同于其他关键字,他们都有多种用法,而且在一定环境下使用,可以提高程序的运行性能,优化程序的结构.上一个章节我们讲了final关键字的原理及用法,本 ...

  9. sentinel 滑动窗口统计机制

    sentinel的滑动窗口统计机制就是根据当前时间,获取对应的时间窗口,并更新该时间窗口中的各项统计指标(pass/block/rt等),这些指标被用来进行后续判断,比如限流.降级等:随着时间的推移, ...

  10. CentOS7.5上FTP服务的安装与使用

    1.FTP简介 1.1FTP:File Transfer Protocol 文件传输协议 FTP是用于在网络上进行文件传输的一套标准协议,使用客户/服务器模式.它属于网络传输协议的应用层.文件传送(f ...