STM32 定時器的幾種基本使用


title: STM32——外設Timer定時器
date: 2020-05-20 14:43:52
tags:
categories: STM32學習記錄


算法


對定時器的基本認識

先來看看這種MCU有多少定時器:函數

STM32定時器分類

定時器分爲3類:工具

  1. 基本定時器的功能最少,只能充當基本的時基,甚至都沒有外部引腳。
  2. 通用定時器擁有基本定時器的所有功能,同時有輸入捕獲模式,用以接收外部的PWM,脈衝之類的信息,也有
  3. 高級定時器又有通用定時器的所有功能,又有互補輸出模式,功能最爲強大

先具體看一下夾在中間的通用定時器的官方文檔中的描述:學習

爲何須要定時器,而且須要這麼多定時器呢?這是由於STM32的處理器是一種單線程的模式,這這時若是沒有一個專門的外設,那麼在軟件定時期間就沒法處理其餘的工做。因而ST就提供了這些能夠獨立工做的定時器來完成須要的定時工做。ui

定時器的心臟:時基編碼

一、內部時鐘(CK_INT)spa

二、外部時鐘模式 1:外部輸入腳(TIx)線程

三、外部時鐘模式 2:外部觸發輸入(ETR)3d

四、內部觸發輸入(ITRx):使用 A 定時器做爲 B 定時器的預分頻器(A 爲 B 提供時鐘)。 這些時鐘,具體選擇哪一個能夠經過 TIMx_SMCR 寄存器的相關位來設置。code

定時器時基

在這裏呢,最基本的定時功能就是功能1,如上圖,基本定時器和通用定時器是掛載在APB1低速總線的,和PCLK共用來自APB1預分頻器的時鐘源,這個總線的最大頻率是36M,可是因爲預分頻器分頻倍數是1,因此在這裏就由系統自動給雙倍頻率了,所以基本定時器和通用定時器的時鐘頻率都是72MHz的。

實踐1:定時器溢出中斷

原理

這個功能就和傳統8051單片機的定時器功能同樣,計數,溢出,觸發中斷,簡單明瞭,多了個自動重裝載功能,應該是最簡單的定時器功能了,甚至基本定時器都可以完成,我就從這裏開始學習定時器吧。

和GPIO,串口,外部中斷同樣,建立並添加src文件和inc文件:

TIMER.h

#ifndef _TIMER_H
#define _TIMER_H

extern TIM_HandleTypeDef TIM3_Handler;      //定時器句柄

void TIM3_Init(u16 arr,u16 psc);
#endif

TIMER.c

首先是初始化程序,一切是那麼熟悉,從GPIO起,咱們都使用HAL庫提供的初始化結構體來初始化外設。

#include "TIMER.h"
#include "LED.h"

TIM_HandleTypeDef TIM3_Handler;      //定時器句柄 

//定時器溢出時間計算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定時器工做頻率,單位:Mhz
void TIM3_Init(uint16_t arr,uint16_t psc)		//arr:自動重裝值 psc:時鐘預分頻數
{  
    TIM3_Handler.Instance=TIM3;                          //通用定時器3
    TIM3_Handler.Init.Prescaler=psc;                     //分頻係數
    TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;    //向上計數器
    TIM3_Handler.Init.Period=arr;                        //自動裝載值
    TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//時鐘分頻因子
    HAL_TIM_Base_Init(&TIM3_Handler);			//時基初始化
    
    HAL_TIM_Base_Start_IT(&TIM3_Handler); //使能定時器3和定時器3更新中斷:TIM_IT_UPDATE 
}

追究底層原理的話,這些給結構體成員賦值的過程當中,主要就是在操做CR1,CR2控制寄存器,完成定時器最基本的設置基礎計時頻率的過程,通過初始化函數上,就完成了這些配置。

CounterMode:TIM_COUNTERMODE_UP TIM_COUNTERMODE_DOWN下

Channel:用來設置活躍通道。每一個定時器最多有四個通道可 以用來作輸出比較,輸入捕獲等功能之用。這裏的 Channel 就是用來設置活躍通道的,取值範 圍爲:HAL_TIM_ACTIVE_CHANNEL_1~ HAL_TIM_ACTIVE_CHANNEL_4。

這裏呢,和串口相似,在時基初始化函數中,會調用一個__WEAK弱定義過的空函數:

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance==TIM3)
	{
		__HAL_RCC_TIM3_CLK_ENABLE();            //使能TIM3時鐘
		HAL_NVIC_SetPriority(TIM3_IRQn,1,3);    //設置中斷優先級,搶佔優先級1,子優先級3
		HAL_NVIC_EnableIRQ(TIM3_IRQn);          //開啓ITM3中斷 
	}
}

這樣,初始化才正式完成,而後是中斷向量表中的中斷服務函數:

void TIM3_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&TIM3_Handler);
}

又是熟悉的感受,直接調用HAL庫的處理函數,咱們按照慣例,進去看看:

HAL庫定時器中斷處理函數

這個函數竟然有300多行,不過還好,機智的我把它摺疊起來了,若是忽略具體實現細節,這個其實很清晰了,這個函數的做用是,經過判判定時器的寄存器,判斷觸發中斷的究竟是什麼事件,是捕獲比較1234,仍是定時器更新事件,仍是輸入捕獲事件,而後調用相應的回調函數。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim==(&TIM3_Handler))
    {
		HAL_GPIO_TogglePin(LED0_PORT, LED0_PIN);
		HAL_GPIO_TogglePin(LED1_PORT, LED1_PIN);
    }
}

在這裏我寫的是一個閃燈程序(我在前面包含了LED.h頭文件)

在主程序中初始化:

#include "main.h"
#include "stm32f1xx.h" //芯片選型的功能,裏面包含了stm32f1xx_hal.c

#include "LED.h"
#include "TIMER.h"


int main(void)
{
	HAL_Init();
	SystemClock_Config();
	LED_Init();
	TIM3_Init(9999, 7199);
	while(1)
	{
	}
}

能夠看到初始化的兩個重要參數:

void TIM3_Init(uint16_t arr,uint16_t psc)		//arr:自動重裝值 psc:時鐘預分頻數
  • 自動重載值:指的是計數多少次自動重裝,16位寄存器儲存這個值,範圍是0 - 65535,這個是向上計數,就是0到arr就會觸發重裝中斷事件
  • 時鐘預分頻數:指的是相對於時鐘基礎頻率(這裏是72MHz),的分頻數,這個也很強大,也是16位寄存器儲存這個值,範圍仍然是0 - 65535。

專門有2個寄存器:自動重載寄存器 預分頻寄存器,看看官方的公式:

自動重載寄存器 預分頻寄存器

f_CK_PSC就是時鐘頻率,這裏是2倍頻後的APB1時鐘,72MHz。

再來看看這個初始化函數:

TIM3_Init(9999, 7199);

72e-6 / (7199+1) = 1e-4 (s) 就是0.1ms的計數間隔。計數範圍是0 - 9999,一共1e4一萬個計數次數,至關於1s一次,觸發中斷,自動裝填,從新運行。

實踐2:定時器輸出PWM波

### 原理

先來看看原理,怎麼輸出佔空比不一樣的,穩定頻率的PWM波(節選自正點原子):

PWM比較示意圖

假定定時器工做在向上計數 PWM 模式,且當 CNT=CCRx 時輸出 1。那麼就能夠獲得如上的 PWM 示意圖:當 CNT 值小於 CCRx 的時候,IO 輸出低電平(0),當 CNT 值大於等於 CCRx 的時候, IO 輸出高電平(1),當 CNT 達到 ARR 值的時候,從新歸零,而後從新向上計數,依次循環。 改變 CCRx 的值,就能夠改變 PWM 輸出的佔空比,改變 ARR 的值,就能夠改變 PWM 輸出的頻率,這就是 PWM 輸出的原理。

前面也看到了,只有基本定時器是沒有外部引腳的,不能做爲輸出,其餘的定時器均可以輸出PWM波,這裏以通用定時器做爲工具,操做單片機輸出PWM波。

除了實踐1中用到的寄存器,還有3種和捕獲/比較有關的寄存器:2個配置用的模式寄存器TIMx_CCMR1/2

TIMx_CCMR1/2

4個工做時用的狀態寄存器TIMx_CCR1/2/3/4,其實這裏只用到一個,由於通用定時器有4個通道,一個通道對應一個這種寄存器,列出來一個看看

TIMx_CCER

1個使能寄存器TIMx_CCER

TIMx_CCR1/2/3/4

能夠看到每兩位控制一個通道,而後隔一個沒用的保留位。

而後前面咱們瞭解到除了基本定時器,其餘的定時器都是有對應引腳的,這就是重映射覆用功能:

TIM3複用功能重映射

PWM.h

#ifndef __PWM_H
#define __PWM_H

#include "stm32f1xx.h"
#include "stm32f1xx_hal.h"

extern TIM_HandleTypeDef TIM3_Handler;      //定時器句柄 

void TIM3_PWM_Init(uint16_t arr,uint16_t psc);
void TIM_SetTIM3Compare2(uint32_t compare);

#endif

PWM.c

#include "PWM.h"
#include "LED.h"

TIM_HandleTypeDef 	TIM3_Handler;	  	//定時器句柄 
TIM_OC_InitTypeDef 	TIM3_CH2Handler;	//定時器3通道2句柄

void TIM3_PWM_Init(uint16_t arr,uint16_t psc)
{  
	TIM3_Handler.Instance=TIM3;							//定時器3
	TIM3_Handler.Init.Prescaler=psc;					//定時器分頻
	TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;	//向上計數模式
	TIM3_Handler.Init.Period=arr;		  				//自動重裝載值
	TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
	
	HAL_TIM_PWM_Init(&TIM3_Handler);					//初始化PWM
	
	TIM3_CH2Handler.OCMode=TIM_OCMODE_PWM1;				//模式選擇PWM1
	TIM3_CH2Handler.Pulse=arr/100;						//設置比較值,此值用來肯定佔空比,默認比較值爲自動重裝載值的一半,即佔空比爲50%
	TIM3_CH2Handler.OCPolarity=TIM_OCPOLARITY_LOW;		//輸出比較極性爲低
	
	HAL_TIM_PWM_ConfigChannel(&TIM3_Handler,&TIM3_CH2Handler,TIM_CHANNEL_2);	//配置TIM3通道2
	
	HAL_TIM_PWM_Start(&TIM3_Handler,TIM_CHANNEL_2);		//開啓PWM通道2
	 	   
}

實踐3:PWM輸出用於驅動直流有刷減速電機

這一篇屬於PWM的應用方面:節選自個人另外一篇博客

MOTOR.h

#ifndef __MOTOR_H
#define __MOTOR_H
#include <sys.h> //包含引腳轉換的宏函數,便於編寫代碼

#define PWMA TIM1->CCR1 //用於控制A電機的PWM佔空比調節
#define AIN2 PBout(15)
#define AIN1 PBout(14)

#define PWMB TIM1->CCR4 //用於控制B電機的PWM佔空比調節
#define BIN1 PBout(13)
#define BIN2 PBout(12)

void MiniBalance_PWM_Init(u16 arr,u16 psc);
void MiniBalance_Motor_Init(void);
#endif

這裏幹了幾件事:

  • 電機控制引腳宏定義

  • PWM佔空比改變宏定義

  • 兩個初始化函數宏定義

MOTOR.c

首先是對4個控制引腳的初始化,高速下拉推輓輸出:

#include "MOTOR.h"

TIM_HandleTypeDef 	TIM1_Handler = {0};	  	//定時器1句柄 
TIM_OC_InitTypeDef 	TIM1_CH14Handler = {0};	//定時器1通道14句柄


void MiniBalance_Motor_Init(void)		//完成4個控制引腳的初始化
{
	GPIO_InitTypeDef GPIO_Initure = {0};	//聲明初始化結構體
	__HAL_RCC_GPIOB_CLK_ENABLE();			//開啓GPIOB時鐘

	GPIO_Initure.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;	//端口配置
	GPIO_Initure.Pull = GPIO_NOPULL;
	GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP;	  						//推輓輸出
	GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH;					 //50M
	
	HAL_GPIO_Init(GPIOB, &GPIO_Initure);				  //根據設定參數初始化GPIOB 
}

而後是對PWM外設的初始化,這裏用定時器1的一、4通道,映射到PA8和PA11兩個引腳用來控制兩個電機的轉速:

//TIM1 PWM初始化 
//arr:自動重裝值。
//psc:時鐘預分頻數
//定時器溢出時間計算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定時器工做頻率,單位:Mhz
void MiniBalance_PWM_Init(u16 arr,u16 psc)		//完成兩個PWM輸出引腳的初始化
{
	TIM1_Handler.Instance=TIM1;							//定時器1
	TIM1_Handler.Init.Prescaler=psc;					//定時器分頻
	TIM1_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;	//向上計數模式
	TIM1_Handler.Init.Period=arr;		 				//自動重裝載值
	TIM1_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
	HAL_TIM_PWM_Init(&TIM1_Handler);	  			 //初始化PWM
	
	TIM1_CH14Handler.OCMode=TIM_OCMODE_PWM1;		 	//模式選擇PWM1
	TIM1_CH14Handler.Pulse= 0;						//設置比較值,初始佔空比設爲0
	TIM1_CH14Handler.OCPolarity=TIM_OCPOLARITY_HIGH; 	//輸出極性 高電平有效 
	HAL_TIM_PWM_ConfigChannel(&TIM1_Handler,&TIM1_CH14Handler,TIM_CHANNEL_1);//初始化TIM1,PWM通道一、4
	HAL_TIM_PWM_ConfigChannel(&TIM1_Handler,&TIM1_CH14Handler,TIM_CHANNEL_4);//初始化TIM1,PWM通道一、4
	
	HAL_TIM_PWM_Start(&TIM1_Handler,TIM_CHANNEL_1);	//開啓PWM通道一、4
	HAL_TIM_PWM_Start(&TIM1_Handler,TIM_CHANNEL_4);	//開啓PWM通道一、4
}

在HAL_TIM_PWM_Init()中會調用端口初始化MSP函數,須要用戶重定義覆蓋HAL庫的若定義函數:

//定時器底層驅動,時鐘使能,引腳配置
//此函數會被HAL_TIM_PWM_Init()調用
//htim:定時器句柄
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
	GPIO_InitTypeDef GPIO_Initure;
	
    if(htim->Instance==TIM1)
	{
		__HAL_RCC_TIM1_CLK_ENABLE();			//使能TIM1時鐘
		__HAL_AFIO_REMAP_TIM1_PARTIAL();		//TIM1通道引腳部分重映射使能
		__HAL_RCC_GPIOA_CLK_ENABLE();			//開啓GPIOA時鐘
		
		GPIO_Initure.Pin=GPIO_PIN_8|GPIO_PIN_11;           	//PA8 PA11
		GPIO_Initure.Mode=GPIO_MODE_AF_PP;  	//複用推輓輸出,用於輸出PWM
		GPIO_Initure.Pull=GPIO_PULLDOWN;          //下拉
		GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//高速
		HAL_GPIO_Init(GPIOA,&GPIO_Initure); 	
	}
}

而後是中斷服務函數,等等,咱們設置了自動重裝填,不須要中斷控制,OK結束。

實踐4:定時器的輸入捕獲模式(結合超聲波模塊)

定時器初始化

us015timer.h

#ifndef _US015TIMER_H
#define _US015TIMER_H
#include "sys.h"

void TIM3_CH3_Cap_Init(u32 arr,u16 psc);

#endif

us015timer.c

#include "us015timer.h"

// PC8---Echo TIM3 CH3 


//TIM3 捕獲初始化
TIM_HandleTypeDef TIM3_Handler;         //定時器3句柄

//定時器2通道1輸入捕獲配置
//arr:自動重裝值(TIM3是16位的!!)
//psc:時鐘預分頻數
void TIM3_CH3_Cap_Init(u32 arr,u16 psc)
{  
    TIM_IC_InitTypeDef TIM3_CH3Config;  
    
    TIM3_Handler.Instance=TIM3;                          //通用定時器3
    TIM3_Handler.Init.Prescaler=psc;                     //分頻係數
    TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;    //向上計數器
    TIM3_Handler.Init.Period=arr;                        //自動裝載值
    TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//時鐘分頻因子
   	TIM3_Handler.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;//使能自動重載
    HAL_TIM_IC_Init(&TIM3_Handler);						//初始化輸入捕獲時基參數
    
    TIM3_CH3Config.ICPolarity=TIM_ICPOLARITY_RISING;    //上升沿捕獲
    TIM3_CH3Config.ICSelection=TIM_ICSELECTION_DIRECTTI;//映射到TI1上
    TIM3_CH3Config.ICPrescaler=TIM_ICPSC_DIV1;          //配置輸入分頻,不分頻
    TIM3_CH3Config.ICFilter=0;                          //配置輸入濾波器,不濾波
    HAL_TIM_IC_ConfigChannel(&TIM3_Handler,&TIM3_CH3Config,TIM_CHANNEL_3);//配置TIM3通道3
	
    HAL_TIM_IC_Start_IT(&TIM3_Handler,TIM_CHANNEL_3);   //開啓TIM3的捕獲通道3,而且開啓捕獲中斷
    __HAL_TIM_ENABLE_IT(&TIM3_Handler,TIM_IT_UPDATE);   //使能更新中斷
	
	HAL_NVIC_SetPriority(TIM3_IRQn,2,0);    //設置中斷優先級,搶佔優先級2,子優先級0
    HAL_NVIC_EnableIRQ(TIM3_IRQn);          //開啓ITM3中斷通道 
}

//定時器2底層驅動,時鐘使能,引腳配置
//此函數會被HAL_TIM_IC_Init()調用
//htim:定時器2句柄
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
    GPIO_InitTypeDef GPIO_Initure;
    __HAL_RCC_TIM3_CLK_ENABLE();            //使能TIM3時鐘
    __HAL_AFIO_REMAP_TIM3_ENABLE();	
	  __HAL_RCC_GPIOC_CLK_ENABLE();			//開啓GPIOC時鐘
	
    GPIO_Initure.Pin=GPIO_PIN_8;            //PC8
    GPIO_Initure.Mode=GPIO_MODE_AF_INPUT; 	//複用推輓輸入
    GPIO_Initure.Pull=GPIO_PULLDOWN;        //下拉
    GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;     //高速
    HAL_GPIO_Init(GPIOC,&GPIO_Initure);

    HAL_NVIC_SetPriority(TIM3_IRQn,2,0);    //設置中斷優先級,搶佔優先級2,子優先級0
    HAL_NVIC_EnableIRQ(TIM3_IRQn);          //開啓ITM3中斷通道 
}

//捕獲狀態
//[7]:0,沒有成功的捕獲;1,成功捕獲到一次.
//[6]:0,還沒捕獲到低電平;1,已經捕獲到低電平了.
//[5:0]:捕獲低電平後溢出的次數
u8  TIM3CH3_CAPTURE_STA=0;							//輸入捕獲狀態 
u16	TIM3CH3_CAPTURE_VAL;							  //輸入捕獲值(TIM3是16位)

//定時器2中斷服務函數
void TIM3_IRQHandler(void)
{
	HAL_TIM_IRQHandler(&TIM3_Handler);				//定時器共用處理函數
}
 
//定時器更新中斷(計數溢出)中斷處理回調函數, 該函數在HAL_TIM_IRQHandler中會被調用,實現多個計數器滿裝載的計數
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//更新中斷(溢出)發生時執行
{
	if((TIM3CH3_CAPTURE_STA&0X80)==0)				//還未成功捕獲上升沿
	{
		if(TIM3CH3_CAPTURE_STA&0X40)				//已經捕獲到高電平了
		{
			if((TIM3CH3_CAPTURE_STA&0X3F)==0X3F)	//高電平太長了
			{
				TIM3CH3_CAPTURE_STA|=0X80;			//標記成功捕獲了一次
				TIM3CH3_CAPTURE_VAL=0XFFFF;
			}else TIM3CH3_CAPTURE_STA++;
		}	 
	}		
}

//定時器輸入捕獲中斷處理回調函數,該函數在HAL_TIM_IRQHandler中會被調用
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//捕獲中斷髮生時執行
{
	if((TIM3CH3_CAPTURE_STA&0X80)==0)				//還未成功捕獲
	{
		if(TIM3CH3_CAPTURE_STA&0X40)				//捕獲到一個降低沿 
		{	  			
			TIM3CH3_CAPTURE_STA|=0X80;				//標記成功捕獲到一次高電平脈寬
            TIM3CH3_CAPTURE_VAL=HAL_TIM_ReadCapturedValue(&TIM3_Handler,TIM_CHANNEL_3);//獲取當前的捕獲值.
			TIM_RESET_CAPTUREPOLARITY(&TIM3_Handler,TIM_CHANNEL_3);   //必定要先清除原來的設置!!
            TIM_SET_CAPTUREPOLARITY(&TIM3_Handler,TIM_CHANNEL_3,TIM_ICPOLARITY_RISING);//配置TIM3通道3上升沿捕獲
		}else  										//還未開始,第一次捕獲上升沿
		{
			TIM3CH3_CAPTURE_STA=0;					//清空
			TIM3CH3_CAPTURE_VAL=0;
			TIM3CH3_CAPTURE_STA|=0X40;				//標記捕獲到了上升沿
			__HAL_TIM_DISABLE(&TIM3_Handler);      	//關閉定時器3
			__HAL_TIM_SET_COUNTER(&TIM3_Handler,0);
			TIM_RESET_CAPTUREPOLARITY(&TIM3_Handler,TIM_CHANNEL_3);   //必定要先清除原來的設置!!
			TIM_SET_CAPTUREPOLARITY(&TIM3_Handler,TIM_CHANNEL_3,TIM_ICPOLARITY_FALLING);//定時器3通道3設置爲降低沿捕獲
			__HAL_TIM_ENABLE(&TIM3_Handler);		//使能定時器3
		}		    
	}		
}

這裏因爲篇幅因素,只展現定時器的設置,端口設置另外一個下拉高速推輓輸出,實現對超聲波模塊的觸發便可。

這個要比輸出PWM要複雜一點,主要多了中斷服務函數,以及中斷回調函數上,由於超聲波模塊要求在接收到一個上升沿的回聲數據以後馬上進行數據的處理,便於在主循環或者其餘中斷中實現相應的避障操做。

實踐5:定時器的編碼器模式

對於編碼電機,之前用51這種功能簡單的單片機,只能使用定時器,配合特定的編碼器解算算法,才能獲得相對精確的電機碼盤信息,可是STM32的通用定時器和高級定時器,自帶編碼器模式,簡直是太方便了,如圖:

編碼器接口模式

經過描述,能夠看到,咱們能夠經過配置通用定時器的一個通道,實現單編碼器的讀取,也能夠經過設置一個定時器的12兩個通道,至關於硬件四倍頻技術,實現高精度的編碼器讀取。

編碼器模式

編碼器模式的配置

這一次我使用cubeMX進行配置,使配置過程簡單明瞭:

編碼器cubeMX配置

  1. GPIO引腳複用爲高級定時器TIM1的兩個通道CH1,CH2
  2. 選擇定時器1模式爲:編碼器模式
  3. 分頻:不分頻
  4. 計數值:0xFFFF,取最大,減少循環偏差(我認爲是這樣的)
  5. 很重要,使用12雙通道,進行AB相正交編碼器的硬件四倍頻。

生成代碼(記得勾選生成獨立的h文件和C文件):

tim.h

#ifndef __tim_H
#define __tim_H
#ifdef __cplusplus
 extern "C" {
#endif

#include "main.h"

extern TIM_HandleTypeDef htim1;

void MX_TIM1_Init(void);

#ifdef __cplusplus
}
#endif
#endif

tim.c

我把原來cubeMX的註釋刪掉了,省略了MSP的失能函數,在重要的語句後面寫了本身的註釋:

/* Includes ------------------------------------------------------------------*/
#include "tim.h"

TIM_HandleTypeDef htim1;

void MX_TIM1_Init(void)
{
  TIM_Encoder_InitTypeDef sConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 0;										//計數頻率:不分頻
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;					//向上計數
  htim1.Init.Period = 65535;									//計數值0xFFFF
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;			//時鐘預分頻器:不分頻
  htim1.Init.RepetitionCounter = 0;								//計數初值?
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;//重裝失能
  sConfig.EncoderMode = TIM_ENCODERMODE_TI12;					//通道12,硬件四倍頻
 //通道1
  sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;					//捕獲極性:高
  sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC1Prescaler = TIM_ICPSC_DIV1;						//通道時鐘分頻:不分頻
  sConfig.IC1Filter = 0;										//硬件濾波:不濾波
 //通道2
  sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;					
  sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  sConfig.IC2Filter = 0;
  
  if (HAL_TIM_Encoder_Init(&htim1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

}
//MSP初始化函數:被HAL_TIM_Encoder_Init調用,用於配置對應的引腳映射
void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef* tim_encoderHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(tim_encoderHandle->Instance==TIM1)
  {
    __HAL_RCC_TIM1_CLK_ENABLE();					//定時器1時鐘使能
    __HAL_RCC_GPIOA_CLK_ENABLE();					//GPIOA時鐘使能

    GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;	//8,9引腳
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;			//輸入模式
    GPIO_InitStruct.Pull = GPIO_NOPULL;				//無上下拉
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);			//GPIO初始化
  }
}
相關文章
相關標籤/搜索