STM32F072从零配置工程-实现delay功能
因为是使用SysTick来作为延时时钟,因此在这里给出SysTick时钟的寄存器;
CTRL:SysTick控制及状态寄存器
位段 |
名称 |
类型 |
复位值 |
描述 |
16 |
COUNTFLAG |
R/W |
0 |
如果在上次读取本寄存器后, SysTick 已经计到 |
2 |
CLKSOURCE |
R/W |
0 |
时钟源选择位,0=AHB/8,1=处理器时钟AHB |
1 |
TICKINT |
R/W |
0 |
1=SysTick倒数计数到 0时产生 SysTick异常请 |
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功能的更多相关文章
- STM32F072从零配置工程-建立工程文件
快速建立工程有两种方法: 第一种是通过官方提供的外设库来搭建,好处是使用库函数,而不需要深入研究寄存器配置: 第二种是通过STM32CubeMX,好处是直观快速,可以直接帮你配置好功能和时钟,不过使用 ...
- STM32F072从零配置工程-自定义时钟配置详解
从自己的板子STM32F407入手,参考官方的SystemInit()函数: 核心在SetSysClock()这个函数,官方默认是采用HSE(设定为8MHz)作为PLL锁相环的输入输出168MHz的S ...
- STM32F072从零配置工程-基于HAL库的串口UART中断配置
先上一个采用串口直接传输的Demo: 此处的思路是完全采用HAL库来实现的,核心是运用HAL_UART_Transmit_IT和HAL_UART_Receive_IT两个函数来实现的,可以作为一个De ...
- STM32F072从零配置工程-串口USART配置
也是使用HAL库进行配置,通过STMCube生成代码,可以通过这个简单的配置过程看到STMCube生成代码的一种规范: 从main函数入手观察其外设配置结构: 首先是HAL_Init()进行所有外设的 ...
- Spring Web工程web.xml零配置即使用Java Config + Annotation
摘要: 在Spring 3.0之前,我们工程中常用Bean都是通过XML形式的文件注解的,少了还可以,但是数量多,关系复杂到后期就很难维护了,所以在3.x之后Spring官方推荐使用Java Conf ...
- H5新人福音~零配置搭建现代化的前端工程
X-BUILD一套基于Webpack(v4.21.0)快速搭建H5场景开发环境的脚手架,只需要几分钟的时间就可以运行起来.X-BUILD是针对H5开发的一套自动化构建工具,致力于提升开发效率,减小开发 ...
- struts2 Convention插件零配置,使用注解开发
从struts21开始,struts2不再推荐使用codebehind作为零配置插件,而是改用Convention插件来支持零配置.与以前相比较,Convention插件更彻底. 使用Conventi ...
- Spring 基于注解零配置开发
本文是转载文章,感觉比较好,如有侵权,请联系本人,我将及时删除. 原文网址:< Spring 基于注解零配置开发 > 一:搜索Bean 再也不用在XML文件里写什么配置信息了. Sprin ...
- Struts2 注解零配置方法(convention插件使用)
最近接触到一个新的项目,是做一个使用S2SH的电子商务商城的二次开发.之前使用过S2SH,在此之前的项目中,Struts2 使用的是XML配置而这个项目是使用注解.在这个项目中,注解还不需要使用Act ...
随机推荐
- 芒果TV For Windows10 成长历史 & 迭代历史 & 新闻报道
芒果TV 是国内领先的基于Windows10操作系统并支持Windows10全系列设备的视频应用和内容服务商. Win10商店版<芒果TV>是湖南快乐阳光互动娱乐传媒有限公司专门为Wind ...
- qmake.exe是在Qt安装编译时生成的,里面内嵌了Qt相关的一些路径(最简单的方法是保持一样的安装路径,最方便的办法是设置qt.conf文件)
在网上直接下载别人编译好的Qt库,为自己使用省了不少事.但往往也会遇到些问题,其中Qt version is not properly installed,please run make instal ...
- 微信小程序把玩(五)页面生命周期
原文:微信小程序把玩(五)页面生命周期 这里只要熟悉页面的基本生命周期即可,业务在指定生命周期函数内书写. 以下是官网给出的生命周期函数方法和状态图 上面的生周期函数图对于做Android 或者IOS ...
- mysql 服务压缩包安装,用户创建
wind7上安装mysql记录: 1.下载的包中没有ini配置文件,需要根目录手动创建my.ini文件 内容如下: [client]port=3306default-character-set=utf ...
- 判断当前进程是否以管理员权限运行(AllocateAndInitializeSid后,用CheckTokenMembership与AdministratorsGroup进行比较,和Delphi的那个例子还有点不一样)
在Win7下(Vista以上的...)有时某些操作就是会让人郁闷 开启了UAC的话,得以管理员权限运行才不会出现Access is denied... 但是,程序又不是非得什么时候都用那破管理员权限的 ...
- SetWinEventHook 事件钩子(有些windows事件并没有消息对应,譬如弹出菜单,切换窗口,获得焦点,滚动条滚动等)good
相信消息钩子大家听的比较多,消息钩子能够在应用程序处理系统消息之前将其截获,提前处理并可以决定是否继续将消息往下传送,有些windows事件并没有消息对应,譬如弹出菜单,切换窗口,获得焦点,滚动条滚动 ...
- 模态对话框测试 MFC中的模态对话框与非模态对话框
http://blog.csdn.net/u010839382/article/details/52972427 http://blog.csdn.net/u010839382/article/det ...
- Java基础(六) static五大应用场景
static和final是两个我们必须掌握的关键字.不同于其他关键字,他们都有多种用法,而且在一定环境下使用,可以提高程序的运行性能,优化程序的结构.上一个章节我们讲了final关键字的原理及用法,本 ...
- sentinel 滑动窗口统计机制
sentinel的滑动窗口统计机制就是根据当前时间,获取对应的时间窗口,并更新该时间窗口中的各项统计指标(pass/block/rt等),这些指标被用来进行后续判断,比如限流.降级等:随着时间的推移, ...
- CentOS7.5上FTP服务的安装与使用
1.FTP简介 1.1FTP:File Transfer Protocol 文件传输协议 FTP是用于在网络上进行文件传输的一套标准协议,使用客户/服务器模式.它属于网络传输协议的应用层.文件传送(f ...