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