初學STM32之編碼器測速以及測頻法的實現

資料來著江協科技

? ? ? ?這篇是編碼器測速,江科大的源碼在測速的時候,定時器TIM2是一直在跑的,不受其它控的,它就一直隔1S讀一次CNT的值。它也不管是否有輸入信號。源碼程序修改一下是可以實現對PWM信號以測頻法的方式讀取。

? ? ? ?筆者稍微改了一下這源碼程序,讓TIM3有信號輸入時,TIM2才開始工作計數。源碼在讀連續信號的時候還是好用的,在讀離散信號的時候可能就不怎么好用了。

比如我希望在某個IO口檢測到一段1KHZ的頻率的方波(或幾次計數),方波的持續時間達到300ms,就開啟某個功能,源碼這個方案就不太好用,因此稍微修改了下程序。讓其也滿足這個條件

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "Encoder.h"
#include "LED.h"int16_t Num;			//定義在定時器中斷里自增的變量
uint16_t i = 0;         //中斷次數指示,TIM2是1S中斷int main(void)
{/*模塊初始化*/OLED_Init();		//OLED初始化Timer_Init();		//定時中斷初始化Encoder_Init();// LED_Init();/*顯示靜態字符串*/OLED_ShowString(1, 1, "CNT:");			//1行1列顯示字符串Num:while (1){if(Num != 0)OLED_ShowSignedNum(1, 5, Num, 5);			//不斷刷新顯示Num變量}
}/*** 函    數:TIM2中斷函數* 參    數:無* 返 回 值:無* 注意事項:此函數為中斷函數,無需調用,中斷觸發后自動執行*           函數名為預留的指定名稱,可以從啟動文件復制*           請確保函數名正確,不能有任何差異,否則中斷函數將不能進入*/
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)		//判斷是否是TIM2的更新事件觸發的中斷{Num = TIM3_GetNumber();                                  //TIM3的CNT值賦值給Num,該值在1s中斷中        TIM_SetCounter( TIM3, 0);                               //TIM3的CNT清零    TIM_Cmd(TIM2, DISABLE);                                 //關閉TIM2定時器TIM_SetCounter( TIM2, 0);                               //TIM2 CNT計數清0   TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);                 //使能TM3捕獲中斷    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);             //清除TIM2更新事件的中斷標志位}
}

Encoder.c

#include "stm32f10x.h"                  // Device headervoid Encoder_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);    //開啟TIM3外設時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);   //開啟GPIOA外設時鐘GPIO_InitTypeDef GPIO_InitStruct;                   //GPIO功能設置GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU ;GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz ;GPIO_Init( GPIOA, &GPIO_InitStruct);TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;   //時基單元設置TIM_TimeBaseInitStruct.TIM_ClockDivision  = TIM_CKD_DIV1 ;//該處設置對于目前的程序信號好像不起作用,最起碼結果上不起作用因此默認為不分頻TIM_TimeBaseInitStruct.TIM_CounterMode  = TIM_CounterMode_Up  ;TIM_TimeBaseInitStruct.TIM_Period  = 65536-1  ;  //ARRTIM_TimeBaseInitStruct.TIM_Prescaler  = 1-1 ;   //Psc,設置為不分頻即1個觸發信號觸發一次TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);TIM_ICInitTypeDef   TIM_ICInitStruct;          //信號輸入捕獲設置,設置了兩個通道TIM_ICStructInit(&TIM_ICInitStruct);TIM_ICInitStruct.TIM_Channel = TIM_Channel_1  ;TIM_ICInitStruct.TIM_ICFilter = 0x0F ;TIM_ICInit(TIM3,  &TIM_ICInitStruct);TIM_ICInitStruct.TIM_Channel = TIM_Channel_2  ;TIM_ICInitStruct.TIM_ICFilter = 0x0F ;TIM_ICInit(TIM3,  &TIM_ICInitStruct);TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, //編碼器設置TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);  //TM3捕獲1中斷使能// 配置TIM3中斷NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //搶占優先級NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        //響應優先級NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);TIM_Cmd( TIM3, ENABLE);   //時鐘使能}uint16_t TIM3_GetNumber(void)
{return TIM_GetCounter(TIM3);}void TIM3_IRQHandler(void)
{if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET){             TIM_Cmd(TIM2, ENABLE);  // 使能TIM2定時器                   TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); // 清除中斷標志TIM_ITConfig(TIM3, TIM_IT_CC1, DISABLE);//TIM3關閉捕獲中斷}
}

修改后的代碼功能:

? ? ? ?TIM3通道1檢測到輸入捕獲的時候會進入中斷使能定時器2,定時器2開始計時,當定時器2溢出時進入定時器2的中斷讀取TIM3 CNT里的值并且清0,(顯然如果TIM3的輸入信號頻率過快的話會產生一點延時)并關閉定時器2,使能定時器3捕獲中斷,退出TIM2中斷如果編碼器還在轉動又會馬上進入TIM3捕獲中斷開啟定時器2,讓其再開啟定時功能。


? ? ? 以此為基礎重寫了之前的測頻法程序。

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "ICfeq.h"uint16_t Num;			//定義在定時器中斷里自增的變量
uint16_t i = 0;         //中斷次數指示,TIM2是1S中斷int main(void)
{/*模塊初始化*/OLED_Init();		//OLED初始化Timer_Init();		//定時中斷初始化IC_Init();/*顯示靜態字符串*/OLED_ShowString(1, 1, "CNT:");			//1行1列顯示字符串Num:while (1){if(Num != 0)OLED_ShowNum(1, 5, Num, 5);			//不斷刷新顯示Num變量}
}/*** 函    數:TIM2中斷函數* 參    數:無* 返 回 值:無* 注意事項:此函數為中斷函數,無需調用,中斷觸發后自動執行*           函數名為預留的指定名稱,可以從啟動文件復制*           請確保函數名正確,不能有任何差異,否則中斷函數將不能進入*/
void TIM2_IRQHandler(void)
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)		//判斷是否是TIM2的更新事件觸發的中斷{//Delay_ms( 10);Num = TIM3_GetNumber();                                  //TIM3的CNT值賦值給Num,該值在1s中斷中        // Delay_ms( 100);TIM_SetCounter( TIM3, 0);                               //TIM3的CNT清零    TIM_Cmd(TIM2, DISABLE);                                 //關閉TIM2定時器TIM_SetCounter( TIM2, 0);                               //TIM2 CNT計數清0 TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);                 //使能TM3捕獲中斷   TIM_ClearITPendingBit(TIM2, TIM_IT_Update);             //清除TIM2更新事件的中斷標志位}
}

ICfeq.c

#include "stm32f10x.h"                  // Device headervoid IC_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);    //開啟TIM3外設時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);   //開啟GPIOA外設時鐘GPIO_InitTypeDef GPIO_InitStruct;                   //GPIO功能設置GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU ;GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_6 ;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz ;GPIO_Init( GPIOA, &GPIO_InitStruct);TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;   //時基單元設置TIM_TimeBaseInitStruct.TIM_ClockDivision  = TIM_CKD_DIV1 ;//該處設置對于目前的程序信號好像不起作用,最起碼結果上不起作用因此默認為不分頻TIM_TimeBaseInitStruct.TIM_CounterMode  = TIM_CounterMode_Up  ;TIM_TimeBaseInitStruct.TIM_Period  = 65536-1  ;  //ARRTIM_TimeBaseInitStruct.TIM_Prescaler  = 1-1 ;   //Psc,設置為不分頻即1個觸發信號觸發一次TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);TIM_ICInitTypeDef   TIM_ICInitStruct;          //信號輸入捕獲設置TIM_ICStructInit(&TIM_ICInitStruct);TIM_ICInitStruct.TIM_Channel = TIM_Channel_1  ;TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising   ;TIM_ICInitStruct.TIM_ICFilter = 0x0F ;TIM_ICInit(TIM3,  &TIM_ICInitStruct);
//   TIM_SelectInputTrigger( TIM3, TIM_TS_TI1FP1);TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_External1);TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);  //TM3捕獲1中斷使能// 配置TIM3中斷NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //搶占優先級NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;        //響應優先級NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);TIM_Cmd( TIM3, ENABLE);   //時鐘使能}uint16_t TIM3_GetNumber(void)
{return TIM_GetCounter(TIM3);}void TIM3_IRQHandler(void)
{if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET){             TIM_Cmd(TIM2, ENABLE);  // 使能TIM2定時器                   TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); // 清除中斷標志TIM_ITConfig(TIM3, TIM_IT_CC1, DISABLE);//TIM3關閉捕獲中斷}
}

? ? ?程序的其它組件部分參考江科大的文件,TIM2的定時是1S,輸入信號是1KHZ的PWM,輸入端口是PA6,現在的程序與筆者之前的測頻法比較。現在的程序更合理點,如果在主函數中斷賦值語句前插入延時語句

Delay_ms( 10);Num = TIM3_GetNumber();                                  //TIM3的CNT

,那么最終得到的CNT值是會變大,前一個測頻法程序是不會的,前一個測頻法只能測試連續輸入PWM。如果信號比較離散的話,它測試結果會變的不準確。


? ? ? 分享一下學習過程中發生的錯誤,之前這個代碼一直有個BUG,編碼器旋鈕你隨便轉的話可能會導致程序死機卡死。后面想了不少時間,找的問題是中斷優先級照成的。

TIM3的中斷控制著TIM2的中斷,并且進入中斷會使自身中斷關閉。TIM2中斷會關閉自身的定時器并使能TIM3中斷,如過有中斷嵌套的話,位置不對就會死機。筆者之前的中斷優先級設置的不合理,就出現死機了。

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

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

相關文章

oracle怎么查看是否走了索引

SELECT * FROM CRM_STATION_APPEAL_RESULT WHERE COMPLAINT_ID ce1a1d8f-e2a2-4126-8cb7-14384cb24468; 這是查詢語句,怎么看這個查詢是否走了索引呢 EXPLAIN PLAN FOR SELECT * FROM CRM_STATION_APPEAL_RESULT WHERE COMPLAINT_ID ce1a1d8f-e2a2-4126-8cb7-14…

C++進階——C++11_{ }初始化_lambda_包裝器

目錄 1、{ }初始化 1.1 C98的{ } 1.2 C11的{ } 1.3 C11中的std::initializer_list 總結一下: 2、lambda 2.1 lambda的語法 2.2 捕捉列表 2.3 lambda的應用 2.4 lambda的原理 3、包裝器 3.1 function 3.2 bind 1、{ }初始化 1.1 C98的{ } C98中一般數組…

【微知】Mellanox網卡網線插入后驅動的幾個日志?(Cable plugged;IPv6 ... link becomes ready)

概要 本文是一個簡單的信息記錄。記錄的是當服務器網卡的光模塊插入后內核的日志打印。通過這種日志打印,可以在定位分析問題的時候,知道進行過一次模塊插拔。 日志 截圖版: 文字版: [32704.121294] mlx5_core 0000:01:00.0…

單片機Day05---靜態數碼管

目錄 一、原理圖:?編輯 二、思路梳理: 三:一些說明: 1.點亮方式: 2.數組: 3.數字與段碼對應: 四:程序實現: 一、原理圖: 二、思路梳理: …

Cesium.js(6):Cesium相機系統

Camera表示觀察場景的視角。通過操作攝像機,可以控制視圖的位置、方向和角度。 幫助文檔:Camera - Cesium Documentation 1 setView setView 方法允許你指定相機的目標位置和姿態。你可以通過 Cartesian3 對象來指定目標位置,并通過 orien…

【Python技術生態全景:十大核心應用領域深度解析】

目錄 前言:Python的統治力一、基礎應用領域1. Web開發數據科學 二、前沿技術領域機器學習深度學習 三、行業解決方案量化金融生物信息 四、創新應用方向物聯網開發區塊鏈開發 五、效率工具生態自動化運維游戲開發 結語:Python的邊界與突破技術局限未來演…

leetcode 2787. Ways to Express an Integer as Sum of Powers

題目描述 這道題是0-1背包問題。可以理解為,有一個最大容量是n的背包,有n個物品,第i個物品的重量是i^x,問裝滿背包有多少種裝法。題目要求必須是互不相同的數的x次冪的和等于n,那就表示每個數只能用一次,也…

面試經驗分享 | 成都滲透測試工程師二面面經分享

可以看看我的置頂文章和專欄找我哦 概況 面試過程 面試官的問題 問題1、你覺得當前OAuth2.0下的攻擊手段有哪些?結合具體案例詳細講講 問題2、php/java反序列化漏洞的原理?程序員/運維如何避免此類漏洞或如何防御? 問題3、如果一臺服務器被入侵后,你會如何做應急…

模仿axios的封裝效果來封裝fetch,實現baseurl超時等

因為要在cocos游戲項目里面發送網絡請求獲取數據,并且還有可能用到websocket發送請求,所以這里封裝一個fetch放便使用: // fetch封裝// 基礎配置 const BASE_URL 你的url const TIMEOUT 5000// 請求封裝 const http async (url: string, …

小米運維面試題及參考答案(80道面試題)

請講解一下 linux top 后進程的狀態 在 Linux 系統中,使用top命令可以查看系統中正在運行的進程的相關信息,進程通常有以下幾種狀態: 運行(R):表示進程正在 CPU 上運行或者正在運行隊列中等待運行。處于運行狀態的進程正在積極地使用 CPU 資源來執行其任務。睡眠(S):進…

a sort.py demo

這份代碼展示了如何使用 sort.py。注意,此處,我將文件名改為 my_sort.py。 你并不能直接 copy 使用,因為環境,包,還有模型。 此處使用 SSD-MobileNetv2 進行物體檢測,將結果傳入以 np 數組的形式傳入sort…

使用Redis解決:集群的Session共享問題

使用Redis解決:集群的Session共享問題 session共享問題:多臺Tomcat并不共享session存儲空間,當請求切換到不同的tomcat服務時導致數據丟失的問題。 問題背景 ?無狀態HTTP協議:HTTP協議本身是無狀態的,服務器無法直接識…

Linux 內核知識體系[1]

1 Linux內核知識體系 2.Linux內核學習路線 2.1基礎知識準備 操作系統基礎:了解操作系統的概念和基本原理,包括進程管理、內存管理、文件系統、輸入輸出等。 書籍:《操作系統:設計與實現》(Andrew S. Tanenbaum&…

KiActivateWaiterQueue函數和Queue->Header.WaitListHead隊列等待列表的關系

第一部分&#xff1a; if (Thread->ApcState.KernelApcPending && (Thread->SpecialApcDisable 0) && (Thread->WaitIrql < APC_LEVEL)) { } else { // // Insert wait block in ob…

讓DeepSeek API支持聯網搜索

引子 DeepSeek官網注冊的API token是不支持聯網搜索的&#xff0c;這導致它無法輔助分析一些最新的情況或是幫忙查一下互聯網上的資料。本文從實戰角度提供一種穩定可靠的方法使得DeepSeek R1支持聯網搜索分析。 正文 首先登錄火山方舟控制臺&#xff0c;https://www.volcen…

生物信息Rust-01

前言-為什么想學Rust&#xff1f; 一直想多學一門編譯語言&#xff0c;主要有幾個原因吧&#xff08;1. 看到一位老師實驗室要求需要掌握一門編譯語言&#xff1b;2. 自己享想試著開發一些實用的生信工具&#xff0c;感覺自己現在相比于數據分析&#xff0c;探索生物學層面的意…

字符串與相應函數(上)

字符串處理函數分類 求字符串長度&#xff1a;strlen長度不受限制的字符串函數&#xff1a;strcpy,strcat,strcmp長度受限制的字符串函數:strncpy,strncat,strncmp字符串查找&#xff1a;strstr,strtok錯誤信息報告&#xff1a;strerror字符操作&#xff0c;內存操作函數&…

asm匯編源代碼之文件操作相關

提供7個子程序:   1. 關閉文件 FCLOSE   2. 打開文件 FOPEN   3. 文件大小 FSIZE   4. 讀文件 FREAD   5. 寫文件 FWRITE   6. 建立文件 FCREATE   7. 讀取或設置文件指針 FPOS 具體功能及參數描述如下 ; ---------------------------- FCLOSE PROC  FAR ; IN…

[Dify] 使用 Docker 本地部署 Dify 并集成 Ollama 模型的詳細指南

在 AI 應用快速發展的今天&#xff0c;開源項目如 Dify 正成為構建本地化 AI 應用的利器。通過 Dify&#xff0c;你可以輕松地集成不同的大語言模型&#xff08;LLM&#xff09;&#xff0c;如 Ollama&#xff0c;并快速創建可交互的 AI 應用。本篇文章將帶你一步步通過 Docker…

Spring Boot 測試詳解,包含maven引入依賴、測試業務層類、REST風格測試和Mock測試

Spring Boot 測試詳解 1. 測試依賴引入 Spring Boot 默認通過以下 Maven 依賴引入測試工具&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</s…