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 ...
随机推荐
- Win8Metro(C#)数字图像处理--2.27图像加法运算
原文:Win8Metro(C#)数字图像处理--2.27图像加法运算 [函数名称] 图像加法函数AddProcess(WriteableBitmap src, WriteableBitmap a ...
- 微信小程序把玩(三)tabBar底部导航
原文:微信小程序把玩(三)tabBar底部导航 tabBar相对而言用的还是比较多的,但是用起来并没有难,在app.json中配置下tabBar即可,注意tabBar至少需要两个最多五个Item选项 ...
- mysql三种修改密码的方式
[root@MySQL ~]# mysqladmin -uroot -proot -S /data/3307/mysql.sock password '123'; 其中-p是现在的密码,passwor ...
- WPF 设置只能运行一个实例
codereview上的帖子 https://codereview.stackexchange.com/questions/20871/single-instance-wpf-application ...
- 三星860 evo 250g 开启AHCI模式读写对比
主板比较老,只支持sata2接口 换用三星860evo后跑分对比
- [Erlang-0016][aque_tcp] 一个 Erlang TCP 组件
项目地址:https://github.com/liangjingyang/aque_tcp 欢迎任何形式的转载,但请务必注明出处:http://www.cnblogs.com/liangjingya ...
- c# 将字符串转换为指定类型的值
private object GetValueByProperty(string key, string value, ref Type typeValue) { Type t = typeof(T) ...
- 使用Arcgis Pro 发布矢量切片
ArcGIS Pro 中的任何地图或底图都可以创建矢量切片,但是有一些局限性和特殊注意事项.为创建矢量切片制作地图的重点是构建一个有效的地图,以快速绘制生成的切片. 软件环境 操作系统:Windows ...
- 每日一问:到底为什么属性动画后 View 在新位置还能响应事件
在 Android 开发中,我们难免会使用动画来处理各种各样的动画效果,以满足 UI 的高逼格设计.对于比较复杂的动画效果,我们通常会采用著名的开源库:lottie-android,或许你会对 lot ...
- sql一关联多查询时否定筛选出现的问题的解决
问题:一方关联多方查询时执行否定筛选,结果包含未通过筛选的项. 我们规定一方为父,多方为子,我们希望子未通过筛选时,结果也不出现对应的父. 查询部门及部门下的所有员工. SELECT * FROM d ...