TIM定時器(第一部分)
- TIM(Timer)定時器
- 定時器可以對輸入的時鐘進行計數,并在計數值達到設定值時觸發中斷
- 16位計數器、預分頻器、自動重裝寄存器的時基單元,在72MHz計數時鐘下可以實現最大59.65s的定時
- 不僅具備基本的定時中斷功能,而且還包含內外時鐘源選擇、輸入捕獲、輸出比較、編碼器接口、主從觸發模式等多種功能
- 根據復雜度和應用場景分為了高級定時器、通用定時器、基本定時器三種類型
定時器類型
STM32F103C8T6定時器資源:TIM1、TIM2、TIM3、TIM4
定時器結構圖
基本定時器
內部時鐘一般為72Mhz, 預分頻器就是把時鐘頻率分頻,最高可以65536, 比如預分配器是2,那么時鐘頻率為24Mhz,計時器等于自動重裝載寄存器的時候,就是計時時間到了,那它就會產生中斷信號,并且清零計數器,計數器自動開始下一次的計數計時。
有黑色陰影的就是帶有影子寄存器的緩沖機制。
通用定時器
先初始化TIM3,然后使用主模式把它的更新事件映射到TRGO上,接著再初始化TIM2,這里選擇ITR2,對應的就是TIM3的TRGO,然后后面再選擇時鐘為外部時鐘模式1,這樣TIM3的更新事件就可以驅動TIM2的時基單元,也就實現了定時器的級聯。
高級定時器
原本的通用定時器最高時間為59秒多,現在有了重復計數計數器后,就還需要再乘65536,提升了定時時間。
右邊的輸出引腳,由原來的一個變為了兩個互補的輸出,可以輸出一對互補的PWM波,這些電路為了驅動三相無刷電機。
DTG寄存器就是高級定時器對輸出比較模塊的升級。
DTG(Dead Time Generate)死區生成電路:在開關切換的瞬間,由于器件的不理想,造成短暫的直通現象,所以才加入DTG, 在開關切換的瞬間,產生一定時長的死區,讓橋臂的上下管全部關斷,防止直通現象。
定時中斷基本結構
預分頻器時序
計數器計數頻率:CK_CNT = CK_PSC / (PSC + 1)
?
計數器時序
計數器溢出頻率:CK_CNT_OV = CK_CNT / (ARR + 1)
??????????????????? 把計數頻率帶入得?? = CK_PSC / (PSC + 1) / (ARR + 1)
計時器有/無預裝時序
?ARPE就是控制有/無預裝,0代表沒有,1代表有
RCC時鐘樹
時鐘樹,STM32中用來產生和配置時鐘,并且把配置好的時鐘發送到各個外設的系統,時鐘是所有外設運行的基礎。
輸出比較簡介(第二部分)
- OC(Output Compare)輸出比較
- 輸出比較可以通過比較CNT與CCR寄存器值的關系,來對輸出電平進行置1、置0或翻轉的操作,用于輸出一定頻率和占空比的PWM波形
- 每個高級定時器和通用定時器都擁有4個輸出比較通道
- 高級定時器的前3個通道額外擁有死區生成和互補輸出的功能
?CCR(捕獲比較寄存器)
PWM
- PWM(Pulse Width Modulation)脈沖寬度調制
- 在具有慣性的系統中,可以通過對一系列脈沖的寬度進行調制,來等效地獲得所需要的模擬參量,常應用于電機控速等領域
- PWM參數:頻率 = 1 / TS??????????? 占空比 = TON / TS?????????? 分辨率 = 占空比變化步距
輸出比較通道
通用
高級
?OC1和OC1N是兩個互補的輸出端口,分別控制上管和下管的導通和關閉。
輸出比較模式
PWM基本結構
參數計算
PWM頻率:?? ???? Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM占空比:?? ?Duty = CCR / (ARR + 1)
PWM分辨率:?? ?Reso = 1 / (ARR + 1)
舵機簡介
- 舵機是一種根據輸入PWM信號占空比來控制輸出角度的裝置
- 輸入PWM信號要求:周期為20ms,高電平寬度為0.5ms~2.5ms
直流電機及驅動簡介
- 直流電機是一種將電能轉換為機械能的裝置,有兩個電極,當電極正接時,電機正轉,當電極反接時,電機反轉
- 直流電機屬于大功率器件,GPIO口無法直接驅動,需要配合電機驅動電路來操作
- TB6612是一款雙路H橋型的直流電機驅動芯片,可以驅動兩個直流電機并且控制其轉速和方向
硬件驅動電路
輸入捕獲簡介(第三部分)
- IC(Input Capture)輸入捕獲
- 輸入捕獲模式下,當通道輸入引腳出現指定電平跳變時,當前CNT的值將被鎖存到CCR中,可用于測量PWM波形的頻率、占空比、脈沖間隔、電平持續時間等參數
- 每個高級定時器和通用定時器都擁有4個輸入捕獲通道
- 可配置為PWMI模式,同時測量頻率和占空比
- 可配合主從觸發模式,實現硬件全自動測量
- 對于輸入捕獲和輸出比較寄存器,只能使用其中一個,不能同時使用
頻率測量
測頻法適合測量高頻信號、測周法適合測量低頻信號
主從觸發模式
主模式可以將定時器內部的信號,映射到TRGO引腳,用于觸發別的外設。
從模式是接收其他外設或者自身外設的一些信號,用于控制自身定時器的運行,也就是被別的信號控制。
觸發源選擇指定的一個信號得到TRGI,TRGI去觸發從模式,從模式可以選擇一項操作來自動執行。
輸入捕獲基本結構
?輸入捕獲一般來捕獲頻率。
PWMI基本結構
TI1FP1配置上升沿觸發,觸發捕獲和清零CNT,正常捕獲周期 ,然后再把TI1FP2配置為下降沿觸發,通過交叉通道,去觸發通道2的捕獲單元。
PWMI一般來捕獲頻率和占空比。
TIM編碼器接口(第四部分)
- Encoder Interface 編碼器接口
- 編碼器接口可接收增量(正交)編碼器的信號,根據編碼器旋轉產生的正交信號脈沖,自動控制CNT自增或自減,從而指示編碼器的位置、旋轉方向和旋轉速度
- 每個高級定時器和通用定時器都擁有1個編碼器接口
- 兩個輸入引腳借用了輸入捕獲的通道1和通道2
編碼器測速一般應用在電機控制的項目上,使用PWM驅動電機,再使用編碼器測量電機的速度,然后再用PID算法進行閉環控制。
一個編碼器有兩個輸出,一個是A相,一個是B相,然后接入到STM32,定時器的編碼器接口,編碼器接口自動控制定時器時基單元中的CNT計數器進行自增或自減,比如初始化后,CNT初始化為0,然后編碼器右轉,CNT++,右轉產生一個脈沖,CNT就加一次,比如右轉產生10個脈沖后,停下來,那么這個過程CNT就由0自增到10,比如編碼器再左轉產生5個脈沖,CNT就會在原本的10的基礎上自減5。
正交編碼器
編碼器接口基本結構
工作模式
正轉的狀態都向上計數,反轉的狀態都向下計數。
一般情況下用第三種工作模式。
舉例(均不反向)
第一個狀態,TI1上升沿,TI2低電平,查詢上面表,上升沿并且低電平,就是向上計數,這就是正轉。
毛刺展示的就是正交編碼器抗噪聲原理,TI2沒有變化,但是TI1連續跳變,這不符合正交編碼器的信號規律,正常情況下是兩個輸出交替變化。 所以出現了一個引腳不變,另一個引腳連續跳變多次的毛刺信號,計數器就會加減加減來回擺動,實現了抗噪聲。
舉例(TI1反向)
如果使用TIM_ICPolarity_Rising,那么就是均不反向;如果使用TIM_ICPolarity_Falling,那么就是反向。
這里的舉例是TI1反向,可以根據極性來選擇反不反向。
比如我原本想要正速度,但是是負速度,就可以選擇這個反向,也可以交換兩個引腳,交換極性。
代碼部分
定時器中斷配置代碼部分
#include "Timer.h"extern uint16_t Num;void Timer_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 1.時鐘使能TIM_InternalClockConfig(TIM2); // 2.選擇時鐘模式// TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F); // 配置外部時鐘2TIM_TimeBaseInitTypeDef TIM_TimeBaseInitSturcture; // 3.TIM初始化TIM_TimeBaseInitSturcture.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitSturcture.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitSturcture.TIM_Period = 10000 - 1;TIM_TimeBaseInitSturcture.TIM_Prescaler = 7200 - 1; // 計算公式: 72Mz/(PSC+1)/(ARR+1)TIM_TimeBaseInitSturcture.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitSturcture); TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 手動更新中斷標志位清除(因為單片機一上電就會中斷,先清楚標志位)TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 4.配置時鐘中斷NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 5.設置NVIC優先級分組NVIC_InitTypeDef NVIC_InitStructure; // 6.配置NVICNVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_Init(&NVIC_InitStructure);TIM_Cmd(TIM2, ENABLE); // 7.使能TIM2
}/* TIM2中斷函數(用戶自定函數) */
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) // 檢查TIM2中斷是否發生{Num++;TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清楚TIM2中斷標志位}
}
PWM配置代碼部分
#include "Bsp_PWM.h"void PWM_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 1.TIM2時鐘使能RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 2.GPIOA時鐘使能// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 重映射設置// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); // 重新映射TIM2// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); // 如果需要使用重映射,但它是一個調試接口,則需要同時編寫這兩個接口。GPIO_InitTypeDef GPIO_InitStructure; // 3.配置GPIOGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; // 4.配置TIMTIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; // ARR值(重裝載值) TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; // PSC(預分頻系數)TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_OCInitTypeDef TIM_OCInitStructure; // 5.TIM輸出比較通道初始化TIM_OCStructInit(&TIM_OCInitStructure); // 結構指定初始值TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_Pulse = 90; // CCR(捕獲/比較寄存器)TIM_OC1Init(TIM2, &TIM_OCInitStructure);TIM_Cmd(TIM2, ENABLE); // 6.TIM2使能}void PWM_SetCompare1(uint16_t Compare)
{TIM_SetCompare1(TIM2, Compare); // 設置TIM2 Capture Compare1寄存器值
}
輸入捕獲配置代碼部分
#include "IC.h"void IC_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 1.開啟時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);GPIO_InitTypeDef GPIO_InitStructure; // 2.GPIO配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);TIM_InternalClockConfig(TIM3); // 3.選擇時鐘模式TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; // 4.時基單元配置TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);TIM_ICInitTypeDef TIM_ICInitStructure; // 5.輸入捕獲配置TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;TIM_ICInitStructure.TIM_ICFilter = 0xF;TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); // 6.觸發源選擇TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); // 7.配置從模式TIM_Cmd(TIM3, ENABLE); // 8.開啟TIM3時鐘
}uint32_t IC_GetFreq(void)
{return 1000000 / (TIM_GetCapture1(TIM3) + 1); // fx = fc / N fc=72M/(PSC+1)=1M N(讀取CCR的值)
}uint32_t IC_GetDuty(void)
{return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1); // Duty = CCR2 / CCR1 因為要顯示整數,乘100。返回的值范圍為0~100,對應占空比為0%~100%
}
編碼器配置代碼部分
#include "Bsp_EncoderSpeed.h"void EncoderSpeed_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 1.開啟GPIOA時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 2.開啟TIM3時鐘GPIO_InitTypeDef GPIO_InitStructure; // 3.GPIO配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉輸入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);TIM_InternalClockConfig(TIM3); // 4.時鐘模式選擇(這里選擇內部時鐘)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; // 5.TIM0配置TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 分頻系數TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 計數模式TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重復計數次數(當設為1時,需要進入2次中斷,中斷代碼才生效)TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);TIM_ICInitTypeDef TIM_ICInitStructure; // 6.輸入捕獲配置TIM_ICStructInit(&TIM_ICInitStructure);TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; // TIM頻道1TIM_ICInitStructure.TIM_ICFilter = 0xF; // 輸入捕獲濾波器TIM_ICInit(TIM3, &TIM_ICInitStructure);TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;TIM_ICInit(TIM3, &TIM_ICInitStructure);TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); // 7.編碼器配置庫函數TIM_Cmd(TIM3, ENABLE); // 8.使能TIM3 }/* 獲得編碼器速度 */
int16_t Encoder_GetSpeed(void)
{int16_t Temp;Temp = TIM_GetCounter(TIM3);TIM_SetCounter(TIM3, 0);return Temp;
}/* 獲得編碼器的CNT */
int16_t Encoder_GetCNT(void)
{return TIM_GetCounter(TIM3);
}
不知道為什么我注釋代碼在VS code里面時排列整齊,到這就不整齊了,逼死強迫癥患者。