【江科大STM32】TIM輸入捕獲模式PWMI模式測頻率

一、輸入捕獲測頻率

?接線圖:

測信號的輸入引腳為PA6,信號從PA6進來,待測的PWM信號也是STM32自己生成的,輸出引腳是PA0,所以接線這里直接用一根線將PA0引到PA6就可以了。?如果有信號發生器的話,也可以設置成方波信號輸出,高電平設置成3.3v,低電平0v,然后直接接到PA6,另一個就是共地。

PWM初始化模塊:

目前這里我們要借用一下之前寫過的PWM模塊代碼,以便生成待測信號,所以這里程序直接復制,之前的PWM驅動LED呼吸燈工程,在這個工程基礎上寫。然后在這個PWM模塊代碼進行改進,目前這個代碼邏輯是,初始化TIM2的通道1,產生一個PWM的波形,輸出引腳是PA0,然后通過Set_Compare函數,可以調節CCR1的寄存器的值,從而控制PWM的占空比,但是目前這個PWM頻率是在初始化已經寫好的了,是固定的,操作起來不太方便。所以我們要在最后再加一個函數,用來便捷地調節PWM頻率。

那這里如何調節PWM頻率??

通過公式,我們知道PWM頻率=更新頻率=72M/(PSC+1/(ARR+1),所以PSC和ARR都可以調節頻率,但是占空比=CCR/(ARR+1),所以通過ARR調節頻率,同時還會影響到占空比,而通過PSC調節頻率,不會影響占空比,顯然比較方便。所以這里我們直接固定ARR為100-1,通過調節PSC來改變PWM頻率,這里的ARR為100-1,CCR的數值直接就是占空比。

這里實際使用也是有技巧的,一般可以根據分辨率的要求,先確定好ARR值,比如分辨率[1/(ARR+1)],1%就足夠了。那ARR就是給100-1,這樣PSC決定頻率,CCR決定占空比。如果想要更高的分辨率,比如0.1%,那ARR就先固定1000-1,這樣頻率就是72M/預分頻/1000,占空比就是CCR/1000,這樣也好算。

然后ARR我們固定給100-1,初始化操作的PSC就先不管,后面再寫一個函數,在初始化之后單獨修改PSC。?

void PWM_SetPrescaler(uint16_t Prescaler) //配置TIMx預調度器。
{TIM_PrescalerConfig(TIM2,Prescaler,TIM_PSCReloadMode_Immediate);//Prescaler立即被加載
}

代碼詳解:?

?void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode)//配置TIMx預調度器

參數說明
TIMx其中x為1 ~ 17,選擇TIM外設
Prescaler指定預Prescaler寄存器值
TIM_PSCReloadMode指定TIM預分頻器的重裝模式。該參數可以是以下值之一:TIM_PSCReloadMode_Update:在更新事件中加載precaler;TIM_PSCReloadMode_Immediate:立即加載precaler

TIM_PSCReloadMode的兩個選擇參數說白了還是影子寄存器的預裝載問題,就是你寫入的值是立刻生效還是在更新事件中生效。

  • 立刻生效:可能在值發生改變時產生切斷波形的現象,比如PWM一個周期剛過去一半,立刻生效了,那就立刻切斷當前波形,開始新的一個周期。在頻率變化時,這里會出現一個不完整的周期。
  • 更新事件生效:就是會有一個緩存器,延遲參數的寫入時間,等一個周期結束了,在更新事件時,再統一改變參數,?保證每個周期的完整。

在這里我們選擇哪個參數都無所謂,我們要求不高,哪個都可以,那這里就選擇立刻生效吧。?

主函數這里調用之后就生成一個頻率為?1KHz,占空比為50%的信號了。

int main(void)
{OLED_Init();PWM_Init();PWM_SetPrescaler(720 - 1);  //頻率=72M/(PSC - 1)/(ARR - 1) 這里(ARR - 1)=100PWM_SetCompare1(50);		//Duty= CCR/(ARR - 1)while(1){}
}

輸入捕獲初始化:

這里因為是自己測自己,所以還要建一個輸入捕獲模塊代碼,測一下PA0口的頻率和占空比。

?

步驟:?

①RCC開啟時鐘(把要用的TIM外設和GPIO外設時鐘都打開)?

②GPIO初始化,把GPIO配置為輸入模式?,一般選擇上拉或者浮空輸入模式

③配置時基單元,讓CNT計數器在內部時鐘驅動下自增運行?

④配置輸入捕獲單元,包括濾波器、極性選擇、直連通道還是交叉通道、分頻器這些參數,用一個結構體就可以配置

⑤選擇從模式的觸發源觸發源為TF1FP1,這里調用一個庫函數給一個參數即可

⑥選擇觸發之后執行的操作

⑦開啟定時器?

?代碼:

?這里的時鐘選擇,要選擇TIM3,也是APB1外設時鐘,因為PWM模塊還需要TIM2輸出PWM,所以要開啟TIM3時鐘。GPIO初始化需要查看引腳定義表,TIM3通道1對應的是PA6引腳,如果選擇其它定時器或者其它通道,那這個引腳就需要根據引腳定義表改變

?

這里的TIM_Prescaler值決定了測周法的標準頻率Fc,72M/預分頻就是就計數器自增的頻率,就是計數器標準頻率,這個需要根據你信號頻率的分布范圍來調整,這里暫時先給72-1,這樣標準頻率就是72M/72=1MHz,這樣方便計算。?

?void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct)//根據指定初始化TIM外設TIM_ICInitStruct中的參數

?輸入捕獲初始化結構定義
參數說明
TIM_Channel指定TIM通道(1~4通道)
TIM_ICPolarity極性選擇,指定輸入信號的活動邊緣(上升、下降、雙邊沿觸發)
TIM_ICSelection指定輸入(直連或者交叉輸入)
TIM_ICPrescaler指定輸入捕獲預分頻(1、2、4、8分頻,1分頻就是不分頻)
TIM_ICFilter指定輸入捕獲過濾器,取值范圍為0x0 ~ 0xF之間的數字,數值越大,濾波效果越好
濾波器和分頻器的區別:?

雖然兩個都是計次的東西,但是濾波器計次,并不會改變信號的原有頻率,一般濾波器的采樣頻率都會遠高于信號頻率,所以它只會濾除高頻噪聲,使信號更加平滑,1KHz濾波之后仍然是1KHz,信號頻率不會發生變化。而分頻器就是對信號本身進行計次,會改變頻率,1KHz,二分頻之后就是500Hz,四分頻之后就是250Hz。

對應步驟⑤ :

void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource)//選擇輸入觸發器源?

參數說明
TIMx其中x可以是1、2、3、4、5、8、9、12或15來選擇TIM外設。
TIM_InputTriggerSource輸入觸發器源。

?TIM_InputTriggerSource參數選擇:下面兩圖對應

對應步驟⑥?

?void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode)//選擇TIMx從模式

參數說明
TIMx其中x可以是1、2、3、4、5、8、9、12或15來選擇TIM外設。
TIM_SlaveMode指定定時器從模式。

TIM_SlaveMode參數選擇:?

最后,啟動定時器后,CNT就會在內部時鐘的驅動下不斷自增,即使信號沒有過來它也會自增。一直自增也沒有關系,因為在有信號過來的時候,它就會在從模式下自動清零,不會影響測量。初始化完成后,整個電路就能實現全自動測量了。?

void IC_Init(void)
{/*開啟時鐘*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//開啟TIM3的時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//開啟GPIOA的時鐘/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;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);							//將PA6引腳初始化為上拉輸入/*配置時鐘源*/TIM_InternalClockConfig(TIM3);		//選擇TIM3為內部時鐘,若不調用此函數,TIM默認也為內部時鐘/*時基單元初始化*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定義結構體變量TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //時鐘分頻,選擇不分頻,此參數用于配置濾波器時鐘,不影響時基單元功能TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //計數器模式,選擇向上計數TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;               //計數周期,即ARR的值TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;               //預分頻器,即PSC的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重復計數器,高級定時器才會用到TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);             //將結構體變量交給TIM_TimeBaseInit,配置TIM3的時基單元/*輸入捕獲初始化*/TIM_ICInitTypeDef TIM_ICInitStructure;							//定義結構體變量TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				//選擇配置定時器通道1TIM_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_ICInit(TIM3, &TIM_ICInitStructure);							//將結構體變量交給TIM_ICInit,配置TIM3的輸入捕獲通道/*選擇觸發源及從模式*/TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);					//觸發源選擇TI1FP1TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);					//從模式選擇復位//即TI1產生上升沿時,會觸發CNT歸零/*TIM使能*/TIM_Cmd(TIM3, ENABLE);			//使能TIM3,定時器開始運行
}

注意:????

void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);?

這里輸出比較和輸入捕獲都有四個通道,OCInit四個通道,每個通道占用一個函數,而ICInit是四個通道共用一個函數的,在結構體會有額外一個參數選擇哪個通道。

  1. void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
  2. void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
  3. void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
  4. void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);?
  5. uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);//獲取TIMx輸入捕獲1值。
  6. uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);//獲取TIMx輸入捕獲2值。
  7. uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);//獲取TIMx輸入捕獲3值。
  8. uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx); //獲取TIMx輸入捕獲4值。

?這8個函數都是讀取各個通道的CCR值,是相對應的,輸出比較模式下,CCR是只寫的,用TIM_SetCompare寫入;輸入捕獲模式下,CCR是只讀的,用TIM_GetCapture讀出。

返回值:各Capture Compare 寄存器值。

查看頻率讀取CCR(頻率)

?這里需要執行公式Fx=Fc/N,之前說Fc=72M/(PSC+1),PSC=72-1,所以72M/72=1MHz,然后除以N,N就是讀取CCR的值,用uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx)函數讀取

uint32_t IC_GetFreq(void)
{return 1000000 / (TIM_GetCapture1(TIM3) + 1);		//測周法得到頻率fx = fc / N,這里不執行+1的操作也可
}

主函數:?

int main(void)
{OLED_Init();PWM_Init();IC_Init();OLED_ShowString(1,1,"Freq:00000Hz");/*使用PWM模塊提供輸入捕獲的測試信號:將待測信號傳給PA0,PA0又通過導線輸入到PA6,PA6是TIM3的通道1,通道1通過輸入捕獲模塊,測得頻率*/PWM_SetPrescaler(720 - 1);  //頻率=72M/(PSC - 1)/(ARR - 1) 這里(ARR - 1)=100PWM_SetCompare1(50);		//Duty= CCR/(ARR - 1)while(1){OLED_ShowNum(1,6,IC_GetFreq(),5);//不斷刷新顯示輸入捕獲測得的頻率}
}

二、PWMI模式測頻率&占空比

這里輸入捕獲代碼部分直接對上一個代碼進行升級就可以了。配置成兩個通道同時捕獲一個引腳

?

這里輸入捕獲有兩種配置方法:效果一樣?

1、直接復制多一份通道初始化

2、 使用ST公司封裝好的函數

void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct)//根據指定配置TIM外設TIM_ICInitStruct中的參數來測量外部PWM信號

?作用:這個函數你只要傳入一個通道的參數,在函數里它會自動把剩下一個通道初始化成相反的配置,比如這里傳入通道1,直連,上升沿,函數就會順帶配置通道2,交叉,下降;傳入通道2,直連,上升沿,函數就會順帶配置通道1,交叉,下降沿。這個函數只能傳入通道1和通道2,不能傳通道3和通道4。

?獲取占空比函數:

?根據上節課PWMI分析【STM32】TIM輸入捕獲-學習筆記-CSDN博客,高電平的計數值存在CCR2里,整個周期的計數值存在CCR1里,用CCR2/CCR1就可以得到占空比了。

/*** 函    數:獲取輸入捕獲的占空比* 參    數:無* 返 回 值:捕獲得到的占空比*/
uint32_t IC_GetDuty(void)
{return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);	//占空比Duty = CCR2 / CCR1 * 100,這里不執行+1的操作也可
}

主函數:

int main(void)
{/*模塊初始化*/OLED_Init();		//OLED初始化PWM_Init();			//PWM初始化IC_Init();			//輸入捕獲初始化/*顯示靜態字符串*/OLED_ShowString(1, 1, "Freq:00000Hz");		//1行1列顯示字符串Freq:00000HzOLED_ShowString(2, 1, "Duty:00%");			//2行1列顯示字符串Duty:00%/*使用PWM模塊提供輸入捕獲的測試信號*/PWM_SetPrescaler(720 - 1);					//PWM頻率Freq = 72M / (PSC + 1) / 100PWM_SetCompare1(50);						//PWM占空比Duty = CCR / 100while (1){OLED_ShowNum(1, 6, IC_GetFreq(), 5);	//不斷刷新顯示輸入捕獲測得的頻率OLED_ShowNum(2, 6, IC_GetDuty(), 2);	//不斷刷新顯示輸入捕獲測得的占空比}
}

?測頻率性能:32:43

誤差分析:34:45?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/72677.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/72677.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/72677.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

湖倉一體化及冷、熱、實時三級存儲

一、湖倉一體化(Lakehouse) 湖倉一體化(Lakehouse)是數據湖(Data Lake)與數據倉庫(Data Warehouse)的結合,旨在解決傳統數據架構中數據孤島、存儲冗余、計算性能不足等問…

go切片定義和初始化

1.簡介 切片是數組的一個引用,因此切片是引用類型,在進行傳遞時,遵守引用傳遞的機制。切片的使用和數組類似,遍歷切片、訪問切片的元素和切片的長度都一樣。。切片的長度是可以變化的,因此切片是一個可以動態變化的數…

游戲引擎學習第138天

倉庫:https://gitee.com/mrxiao_com/2d_game_3 資產:game_hero_test_assets_003.zip 發布 我們的目標是展示游戲運行時的完整過程,從像素渲染到不使用GPU的方式,我們自己編寫了渲染器并完成了所有的工作。今天我們開始了一些新的內容&#…

畢業項目推薦:基于yolov8/yolov5/yolo11的暴力行為檢測識別系統(python+卷積神經網絡)

文章目錄 概要一、整體資源介紹技術要點功能展示:功能1 支持單張圖片識別功能2 支持遍歷文件夾識別功能3 支持識別視頻文件功能4 支持攝像頭識別功能5 支持結果文件導出(xls格式)功能6 支持切換檢測到的目標查看 二、數據集三、算法介紹1. YO…

docker中kibana啟動后,通過瀏覽器訪問,出現server is not ready yet

問題:當我在瀏覽器訪問kibana時,瀏覽器給我報了server is not ready yet. 在網上試了很多方法,都未能解決,下面是我的方法: 查看kibana日志: docker logs -f kibana從控制臺打印的日志可以發現&#xff…

在 Docker 中,無法直接將外部多個端口映射到容器內部的同一個端口

Docker 的端口映射是一對一的,即一個外部端口只能映射到容器內部的一個端口。 1. 為什么不能多對一映射? 端口沖突: 如果外部多個端口映射到容器內部的同一個端口,Docker 無法區分外部請求應該轉發到哪個內部端口,會…

游戲引擎學習第120天

倉庫:https://gitee.com/mrxiao_com/2d_game_3 上次回顧:周期計數代碼 我們正在進行一個項目的代碼優化工作,目標是提高性能。當前正在優化某個特定的代碼片段,已經將其執行周期減少到48個周期。為了實現這一目標,我們設計了一個…

C++中的.h文件一般是干什么的?

在C中,.h 文件通常是 頭文件(Header File),它們的主要作用是聲明類、函數、常量、宏以及其他在多個源文件(.cpp文件)之間共享的元素。頭文件提供了一個接口,使得不同的源文件能夠訪問這些共享的…

基礎算法總結

基礎算法總結 1、模擬1.1 什么是模擬算法1.2 算法題1.2.1 多項式輸出1.2.2 蛇形方陣 2 高精度算法2.1 什么是高精度算法2.2 算法題2.2.1 高精度加法 2.2.2 高精度乘法 3 普通枚舉3.1 算法題3.1.1 鋪地毯 3.1.2 回文日期 4 前綴和算法4.1 什么是前綴和4.2 算法題4.2.1 最大子段和…

密碼學(哈希函數)

4.1 Hash函數與數據完整性 數據完整性: 檢測傳輸消息(加密或未加密)的修改。 密碼學Hash函數: 構建某些數據的簡短“指紋”;如果數據被篡改,則該指紋(以高概率)不再有效。Hash函數…

游戲引擎學習第135天

倉庫:https://gitee.com/mrxiao_com/2d_game_3 回顧 game_asset.cpp 的創建 在開發過程中,不使用任何現成的游戲引擎或第三方庫,而是直接基于 Windows 進行開發,因為 Windows 目前仍然是游戲的標準平臺,因此首先在這個環境中進行…

Linux:文件描述符與重定向

目錄 一、文件描述符 1.文件內核對象 2.文件描述符分配原則 二、文件重定向 1.重定向的現象 輸出重定向 輸入重定向 dup2 2.重定向的使用 三、標準輸出和標準錯誤 繼上篇文章中,我們了解了fd打印的值為文件描述符,那么它還有什么作用呢&…

白盒測試(3):PCB阻抗測試方法

PCB阻抗測試是確保信號完整性的關鍵,通過測量走線的特性阻抗,驗證其是否符合設計目標。常用方法包括時域反射法(TDR)、網絡分析儀法和仿真軟件法。TDR通過分析反射信號定位阻抗異常,網絡分析儀通過S參數計算阻抗&#…

CentOS 7 安裝Nginx-1.26.3

無論安裝啥工具、首先認準了就是官網。Nginx Nginx官網下載安裝包 Windows下載: http://nginx.org/download/nginx-1.26.3.zipLinxu下載 wget http://nginx.org/download/nginx-1.26.3.tar.gzLinux安裝Nginx-1.26.3 安裝之前先安裝Nginx依賴包、自行選擇 yum -y i…

筆記:如何使用XAML Styler以及在不同的開發環境中使用一致

一、目的:分享如何使用XAML Styler以及在不同的開發環境中使用一致 XAML Styler 是一個 Visual Studio 擴展,用于自動格式化和整理 XAML 文件。它可以幫助開發者保持一致的代碼風格,提高代碼的可讀性和可維護性。以下是如何在 Visual Studio …

分布式存儲學習——HBase概述

1.1 HBase概述 1.1.1 理解大數據背景 1.1.2 HBase是什么 1.1.3 HBase與Hadoop的關系 1.1.4 HBase的核心功能模塊 1.1.5 HBase的應用場景和經典案例 1.1.6 小結 本文參考于學校《HBase應用于開發》教材 1.1 HBase概述 本節將介紹大數據背景和HBase的基本概念&#xff0c…

交叉編譯openssl及curl

操作環境:Ubuntu20.04 IDE工具:Clion2020.2 curl下載地址:https://curl.se/download/ openssl下載地址:https://openssl-library.org/source/old/index.html 直接交叉編譯curl會報錯找不到openssl,所以需要先交叉編…

MDM 如何徹底改變醫療設備的遠程管理

在現代醫療行業迅速發展的格局中,醫院和診所越來越依賴諸如醫療平板和移動工作站等移動設備。這些設備在提高工作效率和提供卓越的患者護理方面發揮著關鍵作用。然而,隨著它們的廣泛使用,也帶來了一系列挑戰,例如在不同地點確保數…

零基礎C語言學習日志22(自定義類型:聯合和枚舉)

目錄 聯合體 聯合體類型的聲明 聯合體的特點 相同成員聯合體和結構體的對比 聯合體大小的計算 例子 枚舉類型 枚舉類型的聲明 枚舉類型的優點 枚舉類型的使用 聯合體 聯合體類型的聲明 像結構體一樣,聯合體也是由一個或者多個成員構成,這些成…

天津大學02-深度解讀DeepSeek:部署、使用、安全【文末附下載鏈接】

大模型風險與不當用例——價值觀錯位 大模型與人類價值觀、期望之間的不一致而導致的安全問題,包含:? 社會偏見(Social Bias)LLM在生成文本時強化對特定社會群體的刻板印象,例如將穆斯林與恐怖主義關聯,或…