工程代碼可見Github<傳送門>
1個留待補(bǔ)充的程序:
避免干擾,嘗試用開關(guān)總中斷的方法
一、輸入捕獲測量原理
之前我們做的是PWM波的輸出,現(xiàn)在再來看PWM的輸入,即測量PWM波頻率和占空比的測量。
理解一副重要的示意圖:
圖中紫色部分為我們輸入的方波信號,首先我們剛開始時要把捕獲通道設(shè)置為上升沿觸發(fā),假如我們捕獲到了一個上升沿,也就是到達(dá)了A點(diǎn),那么我們的定時器就開始計數(shù),隨后我們還要吧捕獲通道設(shè)置成為下降沿觸發(fā)。當(dāng)我們捕獲了一個下降沿時,也就是到達(dá)了B點(diǎn),那么我們獲取一個計數(shù)值,定時器還得繼續(xù)計數(shù),同時重新把捕獲通道設(shè)置為上升沿觸發(fā)。直到再次捕獲到了一個上升沿,那么就代表已經(jīng)到達(dá)C點(diǎn)了,我們再獲取一次計數(shù)值。
那么我們通過獲取的這兩個計數(shù)值,就很容易的算出頻率和占空比。<參考eavane大神博客>
頻率 = 1000000 / 第二次捕獲值(72分頻的前提)
占空比 = 第一次捕獲值 / 第二次捕獲值 * 100
知道了原理,再來看看具體實(shí)現(xiàn)吧。
二、實(shí)現(xiàn)代碼
main.c
/*******************************************************************************
* 文件名:main.c
* 描 述:
* 作 者:CLAY
* 版本號:v1.0.0
* 日 期: 2019年1月26日
* 備 注:TIM3_CH1-PA6, TIM3_CH2-PA7配置為輸入捕獲,
* 測量TIM4的CH1-PB6和PB7的頻率以及占空比
*******************************************************************************
*/
#include "config.h"
#include "led.h"
#include "key.h"
#include "timer.h"
#include "beep.h"
#include "lcd.h"
#include "stdio.h"
#include "usart.h"
#include "i2c.h"
#include "eeprom.h"
#include "PWMMode_Advance.h"
#include "PWM_Compare.h"
#include "PWM_Capture.h"
u8 string[20];
int main(void)
{
STM3210B_LCD_Init();
LCD_Clear(Blue);
LCD_SetBackColor(Blue);
LCD_SetTextColor(White);
LEDInit();
KeyInit();
BeepInit();
TIM2Init(2000, 72);//定時2ms
TIM1_PWMInit(2000, 40, 80);//頻率2K CH2占空比40% CH3占空比80%
TIM4_PWMCompare(2000, 1000, 40, 80);//CH1占空比2K 40% CH2 1K占空比80%
TIM3_CaptureInit();
while(1)
{
KeyDriver();
if(TIM3_CH1_CAPTURE_MODE == 3)
{
sprintf((char*)string,"ch1_fre:%d ",1000000 / TIM3_CH1_CAPTURE_HL);
LCD_DisplayStringLine(Line3, string);
sprintf((char*)string,"ch1_duty:%d ",TIM3_CH1_CAPTURE_H * 100/TIM3_CH1_CAPTURE_HL);
LCD_DisplayStringLine(Line4, string);
TIM3_CH1_CAPTURE_MODE = 0;
}
if(TIM3_CH2_CAPTURE_MODE == 3)
{
sprintf((char*)string,"ch2_fre:%d ",1000000 / TIM3_CH2_CAPTURE_HL);
LCD_DisplayStringLine(Line7, string);
sprintf((char*)string,"ch2_duty:%d ",TIM3_CH2_CAPTURE_H * 100/TIM3_CH2_CAPTURE_HL);
LCD_DisplayStringLine(Line8, string);
TIM3_CH2_CAPTURE_MODE = 0;
}
}
}
void KeyAction(int code)
{
if(code == 1)//按下B1,切換燈狀態(tài),蜂鳴器鳴叫0.1s
{
GPIOC->ODR ^= (1<<8);//PC8不斷取反
GPIOD->ODR |= (1<<2);//PD2置1,使能573鎖存器
GPIOD->ODR &= ~(1<<2);//PD2清0,關(guān)閉573鎖存器
Beep(100);
}
else if(code == 2)
{
Beep(-1);
}
else if(code == 3)
{
Beep(0);
}
else if(code == 4)
{
}
}
PWM_Capture.c
#include "PWM_Capture.h"
u8 TIM3_CH1_CAPTURE_MODE = 0;
u32 TIM3_CH1_CAPTURE_H = 0, TIM3_CH1_CAPTURE_HL = 0;
u8 TIM3_CH2_CAPTURE_MODE = 0;
u32 TIM3_CH2_CAPTURE_H = 0, TIM3_CH2_CAPTURE_HL = 0;
u8 CAPTURE_MODE;
void TIM3_IOInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//***注意這里是APB2***
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA時鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //TIM3通道1-PA6 TIM3通道2-PA7
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//***輸入捕獲使用IO模式為上拉輸入或者浮空輸入
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//輸入就所謂輸出速度了,這句可省略。
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIO
}
void NVIC_TIM3Enable(void)
{
NVIC_InitTypeDef NVIC_initstructure;
NVIC_initstructure.NVIC_IRQChannel = TIM3_IRQn; //選擇TIM4中斷通道
NVIC_initstructure.NVIC_IRQChannelCmd = ENABLE; //使能中斷通道
NVIC_initstructure.NVIC_IRQChannelPreemptionPriority = 0; //設(shè)定搶占優(yōu)先級為0
NVIC_initstructure.NVIC_IRQChannelSubPriority = 0; //設(shè)定響應(yīng)優(yōu)先級為0
NVIC_Init(&NVIC_initstructure);
}
void TIM3Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
//***注意這里是APB1***
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定時器3時鐘
TIM_TimeBaseStructure.TIM_Period = 0xFFFF; //***設(shè)置為0xFFFF
TIM_TimeBaseStructure.TIM_Prescaler = 72-1; //設(shè)置預(yù)分頻值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設(shè)置時鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上計數(shù)模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //初始化 TIM3
}
void TIM3_CaptureInit(void)
{
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM3_IOInit();//TIM3通道1-PA6 TIM3通道2-PA7配置
TIM3Init();//TIM3定時器配置
NVIC_TIM3Enable();//中斷向量配置
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//選擇通道
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//上升沿觸發(fā)
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//必須選擇指向TI寄存器
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//不分頻
TIM_ICInitStructure.TIM_ICFilter = 0;//不濾波
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ITConfig(TIM3, TIM_IT_CC1|TIM_IT_CC2, ENABLE);
TIM_Cmd(TIM3, ENABLE);
}
PWM_Capture.h
#ifndef _PWM_CAPTURE_H
#define _PWM_CAPTURE_H
#include "config.h"
void TIM3_CaptureInit(void);
extern u8 CAPTURE_MODE;
extern u8 TIM3_CH1_CAPTURE_MODE;
extern u32 TIM3_CH1_CAPTURE_H, TIM3_CH1_CAPTURE_HL;
extern u8 TIM3_CH2_CAPTURE_MODE;
extern u32 TIM3_CH2_CAPTURE_H, TIM3_CH2_CAPTURE_HL;
#endif
stm32f10x_it.c
void TIM2_IRQHandler(void)
{
static u16 tmr500ms = 0;
if(TIM_GetITStatus(TIM2, TIM_FLAG_Update))
{
TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update);
tmr500ms++;
KeyScan();
BeepScan(2);//2ms掃描
if(tmr500ms >= 250)
{
tmr500ms = 0;
CAPTURE_MODE ^= 1;
}
}
}
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_CC1) == 1)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
if(CAPTURE_MODE)
{
switch(TIM3_CH1_CAPTURE_MODE)
{
case 0:
TIM3_CH1_CAPTURE_H = 0;//
TIM3_CH1_CAPTURE_HL = 0;
TIM3_CH1_CAPTURE_MODE = 1;
TIM_SetCounter(TIM3, 0);//計數(shù)值清0
TIM_OC1PolarityConfig(TIM3, TIM_ICPolarity_Falling);//設(shè)置為下降沿觸發(fā)
break;
case 1:
TIM3_CH1_CAPTURE_H = TIM_GetCounter(TIM3);//第一次獲取計數(shù)值
TIM3_CH1_CAPTURE_MODE = 2;
TIM_OC1PolarityConfig(TIM3, TIM_ICPolarity_Rising);//設(shè)置為上升沿觸發(fā)
break;
case 2:
TIM3_CH1_CAPTURE_HL = TIM_GetCounter(TIM3);//第二次獲取計數(shù)值
TIM3_CH1_CAPTURE_MODE = 3;
TIM_OC1PolarityConfig( TIM3,TIM_ICPolarity_Rising);//設(shè)置為上升沿觸發(fā)
break;
default:
break;
}
}
else
{
TIM3_CH1_CAPTURE_MODE = 0;
}
}
if(TIM_GetITStatus(TIM3, TIM_IT_CC2) == 1)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
if(!CAPTURE_MODE)
{
switch(TIM3_CH2_CAPTURE_MODE)
{
case 0:
TIM3_CH2_CAPTURE_H = 0;
TIM3_CH2_CAPTURE_HL = 0;
TIM3_CH2_CAPTURE_MODE = 1;
TIM_SetCounter(TIM3, 0);
TIM_OC2PolarityConfig( TIM3,TIM_ICPolarity_Falling);
break;
case 1:
TIM3_CH2_CAPTURE_H = TIM_GetCounter(TIM3);
TIM3_CH2_CAPTURE_MODE = 2;
TIM_OC2PolarityConfig( TIM3,TIM_ICPolarity_Rising);
break;
case 2:
TIM3_CH2_CAPTURE_HL = TIM_GetCounter(TIM3);
TIM3_CH2_CAPTURE_MODE = 3;
TIM_OC2PolarityConfig( TIM3,TIM_ICPolarity_Rising);
break;
default:
break;
}
}
else
{
TIM3_CH2_CAPTURE_MODE = 0;
}
}
}
二、需要注意的地方
1、實(shí)驗(yàn)結(jié)果
2、由于我是直接用之前的工程,之前TIM3做了通用定時器的PWM模式輸出實(shí)驗(yàn),而現(xiàn)在要配置為輸入捕獲,兩個同時存在會出現(xiàn)如下錯誤:
所以要刪除pwmmode
相關(guān)文件,包括以下幾個:
3、程序中的變量釋義
TIM3_CH1_CAPTURE_MODE
用來記錄當(dāng)前捕獲在哪個位置 TIM3_CH1_CAPTURE_H
用來記錄第一次捕獲的時間 TIM3_CH1_CAPTURE_HL
用來記錄第二次捕獲的時間
4、CAPTURE_MODE
的優(yōu)秀所在
兩個通道肯定不能同時進(jìn)行捕獲,首先兩者同時捕獲,同時記錄這個無所謂,關(guān)鍵是它們還都涉及計算。計算最怕被中斷打斷,這樣可能導(dǎo)致結(jié)果不正確。這里目前有兩種解決方案,一種是分時復(fù)用,就像上面程序中寫的那樣,另外一種解決辦法就像我們51操作步進(jìn)電機(jī)計算節(jié)拍或者就像控制DHT11時序一樣,開關(guān)總中斷,這個方法稍后再做調(diào)試,會一并掛上來。
5、中斷函數(shù)里總的處理思想
當(dāng)TIM3_CH1_CAPTURE_MODE=0時,如果發(fā)生了中斷,那么就代表我們捕獲到了上升沿,當(dāng)前在上面簡圖的A點(diǎn),我們把定時器計數(shù)值清0,讓定時器重新計數(shù),同時還要把觸發(fā)方式設(shè)置為下降沿觸發(fā)。
當(dāng)TIM3_CH1_CAPTURE_MODE=1時候,發(fā)生了中斷,那么就代表我們捕獲到了下降沿,當(dāng)前的位置在上面簡圖的B點(diǎn),我們獲取一次定時器的計數(shù)值,同時還得把我們觸發(fā)方式設(shè)置為上升沿觸發(fā)。
當(dāng)TIM3_CH1_CAPTURE_MODE=2時候,發(fā)生中斷,那么就代表我們一個周期已經(jīng)捕獲完畢了,當(dāng)前位置在上面簡圖的C點(diǎn),我們再獲取一次定時器的計數(shù)值,隨后就可以根據(jù)我們兩次獲取的計數(shù)值,算出我們方波的頻率和周期。
當(dāng)TIM3_CH1_CAPTURE_MODE=3時,就意味著我們可以開始計算周期和頻率,當(dāng)我們處理好數(shù)據(jù)后,我們就可以把TIM3_CH1_CAPTURE_MODE清0,準(zhǔn)備開始下一次捕獲。
然后在主函數(shù)中,進(jìn)行TIM3_CH1_CAPTURE_MODE狀態(tài)判斷,進(jìn)而計算出占空比和頻率的值,然后顯示到LCD上,隨后再開啟下一次捕獲。