目錄
前言
基本定時器概念? ? ? ??
定時時間? ? ? ??
定時器時鐘確定
????????倍頻鎖相環被正確配置為168MHz
定時器的庫函數操作
代碼
定時器的寄存器操作
代碼
寄存器
后言
前言
使用平臺:STM32F407ZET6
使用數據手冊:STM32F407數據手冊.pdf
使用參考手冊:STM32F4xx參考手冊(中文).pdf
使用cortex-M手冊:Cortex M3與M4權威指南.pdf
????????定時器的作用一般是為了使用定時功能和中斷功能(洗衣機、微波爐、電風扇、智能空調......),當然在STM32中也可以利用定時器產生周期性的脈沖信號來控制不同的外設(電機的轉速、舵機的角度、燈光的亮度........),所以掌握STM32中的定時器對于項目開發是很有必要的。
????????定時器有多種,常用的外設定時器有基本定時器、通用定時器與高級定時器。?對于STM32F407微處理器而言,內部一共集成了14個定時器,其中有2個基本定時器(TIM6和TIM7)、10個通用定時器(TIM2~TIM5、TIM9~TIM14)、2個高級定時器(TIM1和TIM8)。其中通用定時器TIM2和TIM5為32位定時器,其他為16位定時器,當然,定時器位數越大,定時時間越久。2的32次方--4 294 967 296,2的16次方65 536。
? ? ?
基本定時器概念? ? ? ??
????????本文使用的是基本定時器,基本定時器具有16位的自動重載計數器(TIM_Period)與16位的預分頻器(TIM_Prescaler)。并且自動重載計數器只能遞增計數(TIM_CounterMode_Up),發生更新事件(TIM_IT_Update)會生成中斷/DMA請求(計數器上溢)。? ? ? ?
定時時間? ? ? ??
定時時間需要三個參數決定,定時器的時鐘頻率、預分頻器的值與自動重載計數器的值。
這里我提供一下我寫的自動計算填的值代碼(這個只匹配16位的定時器哦,32位的不一定適用哦):
.\main.exe 84 1 8400這樣使用即可,第一個參數84是定時器的頻率以MHZ為單位,第二個參數是需要定時的時間,這里以秒為單位,第三個參數可以選擇填自己確定的預分配值或者自動重載值即可。這里可以得到匹配的自動重載值或者預分頻值(在正式填的時候,不要忘記減1)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>/*** @brief 獲得分頻系數或者計數器* * @param argc * @param argv * @return int */
int main(int argc, char const *argv[])
{if (argc != 4) // 需要兩個參數:時間 和 預分頻return -1;int clock = atoi(argv[1])*1000000; // 84 MHz 定時器時鐘float tim_sec = atof(argv[2]); // 期望的定時秒數int prescaler = atoi(argv[3]); // 預分頻值// 計算 ARR(自動重裝載值)int result = (int)(((float)tim_sec * clock) / ((float)prescaler));if (result > 0 && result < 65536)printf("%d\n", result);elseprintf("Out of range\n");return 0;
}
定時器時鐘確定
我的板子芯片是STM32F407ZET6,最大支持的頻率是168MHz,通過鎖相環分頻器(PLLCLOCK)可以將外部高速時鐘(HSE)的8MHz轉化為168MHz,這里我們通過時鐘樹來分析。
我們的定時器的頻率不是單純的像總線一樣有84MHz或者42MHz限制,這里面的關鍵就是。
如果設置了板子的頻率為168MHz,并且設置了非1的的分頻系數,那么定時器的時鐘頻率是總線的頻率的兩倍。
????????倍頻鎖相環被正確配置為168MHz
? ? ? ? 如果在標準庫中設置倍頻鎖相環被正確配置為168MHz。我們其實只要修改兩個地方即可,其一外部晶振的正確頻率,其一PLL_M
對應代碼處為:
我后面會寫一篇文章專門介紹為什么這樣就可以設置系統時鐘為168MHz。可以期待一波。
1.system_stm32f4xx.c文件修改PLL_M值8MHz晶振即為8b?
2.stm32f4xx.h文件修改HSE_VALUE,8MHZ晶振即為8000000
定時器的庫函數操作
代碼
本質使用了若干函數
RCC_APBxPeriphClockCmd(RCC_APBxPeriph_TIMx, ENABLE)
TIM_TimeBaseInit(TIMx, &TIM_TimeBaseInitStruct)
NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
TIM_ITConfig(TIMx, TIM_IT_Update)
TIM_Cmd(ENABLE)
這里我使用了基本定時器的TIM7,通過定時器的定時中斷操作,把led翻轉。
void TimerBaseInit(void)
{// 開啟定時器時鐘/* 開啟TIM6與TIM7基本定時器時鐘 84MHz*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);// 為每個定時器賦值/* 基本定時器TIM7 800ms 84MHz*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStruct.TIM_Period = 42000 - 1;TIM_TimeBaseInitStruct.TIM_Prescaler = 1600 - 1;TIM_TimeBaseInit(TIM7, &TIM_TimeBaseInitStruct);/* TIM7 */NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = TIM7_IRQn;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0xf;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;NVIC_Init(&NVIC_InitStruct);TIM_ITConfig(TIM7, TIM_IT_Update, ENABLE);TIM_Cmd(TIM7, ENABLE);
}
定時中斷函數:
void TIM7_IRQHandler(void)
{if (TIM_GetITStatus(TIM7, TIM_IT_Update)){TIM_ClearITPendingBit(TIM7, TIM_IT_Update);GPIO_ToggleBits(GPIOF, GPIO_Pin_10);}
}
定時器的寄存器操作
代碼
寄存器初始化:
void TimerBaseRegisterInit(void)
{//開啟定時器時鐘RCC->APB1ENR |= (0b11<<4); //TIM6、TIM7RCC->APB2ENR |= (0b11<<16); //TIM9、TIM10//配置定時器/* 配置定時器前先關閉定時器,即關閉計數器 */TIM6->CR1 &= ~(1 <<0 );/* 配置定時器參數 */TIM6->PSC = 10000-1; //分頻值 10000TIM6->ARR = 8400 - 1; // 對應分頻器10000TIM6->CNT = 0; // 清0計數器,個人認為自動重裝初值應該從0開始TIM6->CR1 |= (0b10000100);TIM6->DIER |= (0b1);//配置NVICNVIC->ISER[1] = 1u << (TIM6_DAC_IRQn %32);//配置完成定時器,開啟計數器TIM6->CR1 |= (1 << 0);
}
定時中斷函數:
void TIM7_IRQHandler(void)
{if (TIM7->SR == 1){TIM7->SR = 0;GPIO_ToggleBits(GPIOF, GPIO_Pin_10);}
}
寄存器
這里提供基本定時器使用的寄存器:
啟動定時器時鐘寄存器:
控制寄存器CR1:
寄存器配置寄存器,搭配使用
中斷使能寄存器
狀態寄存器
配置NVIC使能中斷寄存器