这星期老师让给项目中添加一个检测输入信号频率的功能,用于矿下煤气浓度检测,于是搞了几天做成了一个样例,由于电路板的限制,用的是TIM3和TIM4。
这个程序最多支持8路不通频率信号的测量,由于有实际要求,我把测量的频率设定在1000~200Hz之间,当然测更高的频率也行,只是我没有测试过2000Hz以上的信号。
频率检测的原理
怎么编程需要看懂下面这张定时器输入捕获结构图,我只进行了最低限度的解释,要了解更多推荐看看野火的《零死角玩转STM32F103指南者》高级定时器那一章。
使用 STM32 一定要找到参考手册、数据手册或者其他参考书,不然寸步难行。
这里先说一下怎么进行信号捕获
0.TIMx一直在计数
1.信号通过 TIMx_CH1/2/3/4 流入
2.经过滤波器和边沿检测
3.信号进入捕获通道
4.经过预分频器
5.捕获寄存器在发生捕获时存储 CNT 的值,产生 CCxI 中断
首先借助于定时器的输入捕获的4个通道的边沿捕获功能,检测到上升沿后出发 CI 中断。
以 TIM3 的 CH1 为例,它检测到第一个上升沿后,我们用变量 cnt_val 记录一下 CNT(计数器) 的值。
当 CH1 遇到第二次遇到上升沿时,TIM3->CCR1 寄存器记录下此时 CNT 的值,我们用一个变量 ccr_val 记录一下。
然后退出中断, ( ccr_val - cnt_val ) 的值就是一个周期内 TIM3 计数的次数(忽略计数器更新),用变量 c_num 记录。接着用 TIM3 的计时频率 / c_num 可以得出频率的值。
从上面的图中可以看出,假如捕获信号的一个周期内定时器发生了更新,那这次采集就算失败,因为更新后CNT重新计数了。
还有一种情况是信号源突然掉线,所以需要定时进行在线检测。
目前我没想到其它的bug。
GPIO初始化
我用到了两个计时器,这里我只拿一个TIM3举例子,下面用到了很多宏定义,是为了方便我开关某些功能。
#define USE_TIM3 1
#define USE_TIM3_CH1 1
#define USE_TIM3_CH2 1
#define USE_TIM3_CH3 1
#define USE_TIM3_CH4 1
#define TIM3_IT_CCx /**/TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4/*注意要与上面的通道向对应*/
#define TIM3_GPIO_CLK RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB
static void TIM3_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE);//TIM3 端口重映射,我用不上
//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE); //禁止JTAG功能,把PB3,PB4作为普通IO口使用,我也用不上
RCC_APB2PeriphClockCmd(TIM3_GPIO_CLK,ENABLE);//开启对应GPIO的时钟,TIM3_GPIO_CLK是宏定义
#if USE_TIM3_CH1
//配置CH1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_InitStructure.GPIO_Pin = TIM3_CH1_PIN;
GPIO_Init(TIM3_CH1_PORT,&GPIO_InitStructure);
#endif
#if USE_TIM3_CH2
//配置CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = TIM3_CH2_PIN;
GPIO_Init(TIM3_CH2_PORT,&GPIO_InitStructure);
#endif
#if USE_TIM3_CH3
//配置CH3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = TIM3_CH3_PIN;
GPIO_Init(TIM3_CH3_PORT,&GPIO_InitStructure);
#endif
#if USE_TIM3_CH4
//配置CH4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = TIM3_CH4_PIN;
GPIO_Init(TIM3_CH4_PORT,&GPIO_InitStructure);
#endif
}
定时器初始化
同样,只拿一个TIM3举例
static void GENERAL_TIM3_Config(void)
{
//配置引脚,就是上面的那个函数
TIM3_GPIO_Config();
//配置时基
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
//初始化TIM3时钟72MHz
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1; //分频 72M/72 = 1MHz
TIM_TimeBaseInitStructure.TIM_Period = 0xffff-1; //计时周期65535 计时器的一个周期是(1s/1M)*65535 = 0.0656s即65ms
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //配置为1分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//配置为向上计数模式
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,TIM3没有
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure); //配置TIM3
TIM_ClearFlag(TIM3, TIM_FLAG_Update); //清除中断标志位
}
配置输入捕获
这里需要注意 TIM_ICInitStructure.TIM_Channel 不能像配置GPIO一样多个值相或,如 GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8,TIM_Channel只能一次配置一个,我踩过的坑希望其他人别去踩。
// 测量的起始边沿
#define GENERAL_TIM_STRAT_ICPolarity TIM_ICPolarity_Rising
static void TIM3_IC_Config(void)
{
TIM_ICInitTypeDef TIM_ICInitStructure;
#if USE_TIM3_CH1
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //配置捕获通道,!!!!"这个参数不支持多参数相或"!!!!!
TIM_ICInitStructure.TIM_ICFilter = 0x0f; //设置滤波,参考手册p215
TIM_ICInitStructure.TIM_ICPolarity = GENERAL_TIM_STRAT_ICPolarity;//选择IC触发边沿,上升沿还是下降沿,这里用的宏定义
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //预分频数1
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//直接输入
TIM_ICInit(TIM3, &TIM_ICInitStructure); //配置TIM3的输入捕获功能
#endif
#if USE_TIM3_CH2
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICFilter = 0x0f;
TIM_ICInitStructure.TIM_ICPolarity = GENERAL_TIM_STRAT_ICPolarity;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
#endif
#if USE_TIM3_CH3
TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;
TIM_ICInitStructure.TIM_ICFilter = 0x0f;
TIM_ICInitStructure.TIM_ICPolarity = GENERAL_TIM_STRAT_ICPolarity;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
#endif
#if USE_TIM3_CH4
TIM_ICInitStructure.TIM_Channel = TIM_Channel_4;
TIM_ICInitStructure.TIM_ICFilter = 0x0f;
TIM_ICInitStructure.TIM_ICPolarity = GENERAL_TIM_STRAT_ICPolarity;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
#endif
// 开启更新和捕获中断
TIM_ITConfig (TIM3, TIM_IT_Update | TIM3_IT_CCx, ENABLE );
TIM_ClearFlag(TIM3, TIM_FLAG_Update|TIM3_IT_CCx);//清中断标志
// 使能计数器
TIM_Cmd(TIM3, ENABLE);
}
配置 NVIC
这里还是只贴出了TIM3的
static void GENERAL_TIM_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
#if USE_TIM3
// 设置中断组为 1
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
// 设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn ;
// 设置主优先级为 1
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
// 设置抢占优先级为 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //中断通道使能
NVIC_Init(&NVIC_InitStructure);
#endif
}
自定义一个保存信息的结构体
typedef struct {
uint8_t Time_OutFlag; //捕获超时标志
uint8_t Capture_FinishFlag; // 捕获结束标志位
uint8_t Capture_StartFlag; // 捕获开始标志位
uint16_t Capture_CNTValue; // 第一次捕获边沿时计数器的值
uint16_t Capture_CcrValue; // 捕获寄存器的值
}TIM_ICUserValueType;
中断处理函数
extern TIM_ICUserValueType TIM_ICUserValue[8];
void TIM3_IRQHandler(void)
{
if ( TIM_GetITStatus ( TIM3, TIM_IT_Update) != RESET )
{//假定超时
TIM_ICUserValue[0].Time_OutFlag = 1;
TIM_ICUserValue[1].Time_OutFlag = 1;
TIM_ICUserValue[2].Time_OutFlag = 1;
TIM_ICUserValue[3].Time_OutFlag = 1;
// 采集波形的一个周期内发生了寄存器溢出,放弃本次采集
TIM_ICUserValue[0].Capture_StartFlag = 0;
TIM_ICUserValue[0].Capture_FinishFlag = 0;
TIM_ICUserValue[1].Capture_StartFlag = 0;
TIM_ICUserValue[1].Capture_FinishFlag = 0;
TIM_ICUserValue[2].Capture_StartFlag = 0;
TIM_ICUserValue[2].Capture_FinishFlag = 0;
TIM_ICUserValue[3].Capture_StartFlag = 0;
TIM_ICUserValue[3].Capture_FinishFlag = 0;
//清除中断标志
TIM_ClearITPendingBit ( TIM3, TIM_FLAG_Update );
}
#if USE_TIM3_CH1
// TIM3_CH1上升沿捕获中断
if ( TIM_GetITStatus (TIM3, TIM_IT_CC1 ) != RESET)
{
// 第一次捕获
if ( TIM_ICUserValue[0].Capture_StartFlag == 0 )
{
//记录首次捕获时计数器的值
TIM_ICUserValue[0].Capture_CNTValue = TIM3->CNT;
// 存捕获比较寄存器的值的变量的值清 0
TIM_ICUserValue[0].Capture_CcrValue = 0;
// 开始捕获标准置 1
TIM_ICUserValue[0].Capture_StartFlag = 1;
}
// 下降沿捕获中断
else { // 第二次捕获
// 获取捕获比较寄存器的值减去上一次计数器的值,这个值就是捕获到的高电平的时间的值
TIM_ICUserValue[0].Capture_CcrValue = TIM_GetCapture1(TIM3)-TIM_ICUserValue[0].Capture_CNTValue;
// 开始捕获标志清 0
TIM_ICUserValue[0].Capture_StartFlag = 0;
// 捕获完成标志置 1
TIM_ICUserValue[0].Capture_FinishFlag = 1;
//超时标志清除
TIM_ICUserValue[0].Time_OutFlag = 0;
}
TIM_ClearITPendingBit (TIM3,TIM_IT_CC1);
}
#endif
#if USE_TIM3_CH2
// TIM3_CH2上升沿捕获中断
if ( TIM_GetITStatus (TIM3, TIM_IT_CC2 ) != RESET)
{
if ( TIM_ICUserValue[1].Capture_StartFlag == 0 )
{
TIM_ICUserValue[1].Capture_CNTValue = TIM3->CNT;
TIM_ICUserValue[1].Capture_CcrValue = 0;
TIM_ICUserValue[1].Capture_StartFlag = 1;
}
else {
TIM_ICUserValue[1].Capture_CcrValue = TIM_GetCapture2(TIM3)-TIM_ICUserValue[1].Capture_CNTValue;
TIM_ICUserValue[1].Capture_StartFlag = 0;
TIM_ICUserValue[1].Capture_FinishFlag = 1;
TIM_ICUserValue[1].Time_OutFlag = 0;
}
TIM_ClearITPendingBit (TIM3,TIM_IT_CC2);
}
#endif
#if USE_TIM3_CH3
// TIM3_CH3上升沿捕获中断
if ( TIM_GetITStatus (TIM3, TIM_IT_CC3 ) != RESET)
{
if ( TIM_ICUserValue[2].Capture_StartFlag == 0 )
{
TIM_ICUserValue[2].Capture_CNTValue = TIM3->CNT;
TIM_ICUserValue[2].Capture_CcrValue = 0;
TIM_ICUserValue[2].Capture_StartFlag = 1;
}
else {
TIM_ICUserValue[2].Capture_CcrValue = TIM3->CCR3-TIM_ICUserValue[2].Capture_CNTValue;
TIM_ICUserValue[2].Capture_StartFlag = 0;
TIM_ICUserValue[2].Capture_FinishFlag = 1;
TIM_ICUserValue[2].Time_OutFlag = 0;
}
TIM_ClearITPendingBit (TIM3,TIM_IT_CC3);
}
#endif
#if USE_TIM3_CH4
// TIM3_CH4上升沿捕获中断
if ( TIM_GetITStatus (TIM3, TIM_IT_CC4 ) != RESET)
{
if ( TIM_ICUserValue[3].Capture_StartFlag == 0 )
{
TIM_ICUserValue[3].Capture_CNTValue = TIM3->CNT;
TIM_ICUserValue[3].Capture_CcrValue = 0;
TIM_ICUserValue[3].Capture_StartFlag = 1;
}
else {
TIM_ICUserValue[3].Capture_CcrValue = TIM_GetCapture4(TIM3)-TIM_ICUserValue[3].Capture_CNTValue;
TIM_ICUserValue[3].Capture_StartFlag = 0;
TIM_ICUserValue[3].Capture_FinishFlag = 1;
TIM_ICUserValue[3].Time_OutFlag = 0;
}
TIM_ClearITPendingBit (TIM3,TIM_IT_CC4);
}
#endif
}
结果处理
这个函数你可以放到 main 函数里,我只是把它的结果用串口打印出来(串口部分请自行解决,网上的例子一大堆)
void PWM_IC_Print(void)
{
double fre;
// TIM 计数器的驱动时钟
uint32_t TIM_PscCLK = 72000000 / (71+1);
int i = 0;
for(i = 0;i<8;i++)
{
//超时判断
if(TIM_ICUserValue
.Time_OutFlag == 1)
continue;
//判断是否捕获完成
if(TIM_ICUserValue.Capture_FinishFlag == 1)
{
// 计算一个周期的计数器的值
fre = (TIM_ICUserValue.Capture_CcrValue+1);
//滤波器,我这里只留下了我需要的频段,具体值可以任意修改
//下面的值的计算方法是 (f(TIM3)/目标频率)
if(fre>10000||fre<500)
continue;
// 打印频率,你想怎么处理,请自便
printf ( "rn %d号输入测得频率: %f hzrn",i,1/(fre/TIM_PscCLK));
//清除标志
TIM_ICUserValue.Capture_FinishFlag = 0;
}
}
}
下面是实际的测试结果
我是用TIM2产生了一个200hz的方波,测了一下,还可以
然后连起来,PA6(TIM3 CH1) 接 PA2(待测信号),棕色那根是GND
打开串口调试助手
测试一下多路输入,1个200hz,2个1000hz
这星期老师让给项目中添加一个检测输入信号频率的功能,用于矿下煤气浓度检测,于是搞了几天做成了一个样例,由于电路板的限制,用的是TIM3和TIM4。
这个程序最多支持8路不通频率信号的测量,由于有实际要求,我把测量的频率设定在1000~200Hz之间,当然测更高的频率也行,只是我没有测试过2000Hz以上的信号。
频率检测的原理
怎么编程需要看懂下面这张定时器输入捕获结构图,我只进行了最低限度的解释,要了解更多推荐看看野火的《零死角玩转STM32F103指南者》高级定时器那一章。
使用 STM32 一定要找到参考手册、数据手册或者其他参考书,不然寸步难行。
这里先说一下怎么进行信号捕获
0.TIMx一直在计数
1.信号通过 TIMx_CH1/2/3/4 流入
2.经过滤波器和边沿检测
3.信号进入捕获通道
4.经过预分频器
5.捕获寄存器在发生捕获时存储 CNT 的值,产生 CCxI 中断
首先借助于定时器的输入捕获的4个通道的边沿捕获功能,检测到上升沿后出发 CI 中断。
以 TIM3 的 CH1 为例,它检测到第一个上升沿后,我们用变量 cnt_val 记录一下 CNT(计数器) 的值。
当 CH1 遇到第二次遇到上升沿时,TIM3->CCR1 寄存器记录下此时 CNT 的值,我们用一个变量 ccr_val 记录一下。
然后退出中断, ( ccr_val - cnt_val ) 的值就是一个周期内 TIM3 计数的次数(忽略计数器更新),用变量 c_num 记录。接着用 TIM3 的计时频率 / c_num 可以得出频率的值。
从上面的图中可以看出,假如捕获信号的一个周期内定时器发生了更新,那这次采集就算失败,因为更新后CNT重新计数了。
还有一种情况是信号源突然掉线,所以需要定时进行在线检测。
目前我没想到其它的bug。
GPIO初始化
我用到了两个计时器,这里我只拿一个TIM3举例子,下面用到了很多宏定义,是为了方便我开关某些功能。
#define USE_TIM3 1
#define USE_TIM3_CH1 1
#define USE_TIM3_CH2 1
#define USE_TIM3_CH3 1
#define USE_TIM3_CH4 1
#define TIM3_IT_CCx /**/TIM_IT_CC1|TIM_IT_CC2|TIM_IT_CC3|TIM_IT_CC4/*注意要与上面的通道向对应*/
#define TIM3_GPIO_CLK RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB
static void TIM3_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE);//TIM3 端口重映射,我用不上
//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE); //禁止JTAG功能,把PB3,PB4作为普通IO口使用,我也用不上
RCC_APB2PeriphClockCmd(TIM3_GPIO_CLK,ENABLE);//开启对应GPIO的时钟,TIM3_GPIO_CLK是宏定义
#if USE_TIM3_CH1
//配置CH1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_InitStructure.GPIO_Pin = TIM3_CH1_PIN;
GPIO_Init(TIM3_CH1_PORT,&GPIO_InitStructure);
#endif
#if USE_TIM3_CH2
//配置CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = TIM3_CH2_PIN;
GPIO_Init(TIM3_CH2_PORT,&GPIO_InitStructure);
#endif
#if USE_TIM3_CH3
//配置CH3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = TIM3_CH3_PIN;
GPIO_Init(TIM3_CH3_PORT,&GPIO_InitStructure);
#endif
#if USE_TIM3_CH4
//配置CH4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = TIM3_CH4_PIN;
GPIO_Init(TIM3_CH4_PORT,&GPIO_InitStructure);
#endif
}
定时器初始化
同样,只拿一个TIM3举例
static void GENERAL_TIM3_Config(void)
{
//配置引脚,就是上面的那个函数
TIM3_GPIO_Config();
//配置时基
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
//初始化TIM3时钟72MHz
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1; //分频 72M/72 = 1MHz
TIM_TimeBaseInitStructure.TIM_Period = 0xffff-1; //计时周期65535 计时器的一个周期是(1s/1M)*65535 = 0.0656s即65ms
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //配置为1分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//配置为向上计数模式
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,TIM3没有
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure); //配置TIM3
TIM_ClearFlag(TIM3, TIM_FLAG_Update); //清除中断标志位
}
配置输入捕获
这里需要注意 TIM_ICInitStructure.TIM_Channel 不能像配置GPIO一样多个值相或,如 GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8,TIM_Channel只能一次配置一个,我踩过的坑希望其他人别去踩。
// 测量的起始边沿
#define GENERAL_TIM_STRAT_ICPolarity TIM_ICPolarity_Rising
static void TIM3_IC_Config(void)
{
TIM_ICInitTypeDef TIM_ICInitStructure;
#if USE_TIM3_CH1
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //配置捕获通道,!!!!"这个参数不支持多参数相或"!!!!!
TIM_ICInitStructure.TIM_ICFilter = 0x0f; //设置滤波,参考手册p215
TIM_ICInitStructure.TIM_ICPolarity = GENERAL_TIM_STRAT_ICPolarity;//选择IC触发边沿,上升沿还是下降沿,这里用的宏定义
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //预分频数1
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//直接输入
TIM_ICInit(TIM3, &TIM_ICInitStructure); //配置TIM3的输入捕获功能
#endif
#if USE_TIM3_CH2
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICFilter = 0x0f;
TIM_ICInitStructure.TIM_ICPolarity = GENERAL_TIM_STRAT_ICPolarity;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
#endif
#if USE_TIM3_CH3
TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;
TIM_ICInitStructure.TIM_ICFilter = 0x0f;
TIM_ICInitStructure.TIM_ICPolarity = GENERAL_TIM_STRAT_ICPolarity;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
#endif
#if USE_TIM3_CH4
TIM_ICInitStructure.TIM_Channel = TIM_Channel_4;
TIM_ICInitStructure.TIM_ICFilter = 0x0f;
TIM_ICInitStructure.TIM_ICPolarity = GENERAL_TIM_STRAT_ICPolarity;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
#endif
// 开启更新和捕获中断
TIM_ITConfig (TIM3, TIM_IT_Update | TIM3_IT_CCx, ENABLE );
TIM_ClearFlag(TIM3, TIM_FLAG_Update|TIM3_IT_CCx);//清中断标志
// 使能计数器
TIM_Cmd(TIM3, ENABLE);
}
配置 NVIC
这里还是只贴出了TIM3的
static void GENERAL_TIM_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
#if USE_TIM3
// 设置中断组为 1
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
// 设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn ;
// 设置主优先级为 1
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
// 设置抢占优先级为 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //中断通道使能
NVIC_Init(&NVIC_InitStructure);
#endif
}
自定义一个保存信息的结构体
typedef struct {
uint8_t Time_OutFlag; //捕获超时标志
uint8_t Capture_FinishFlag; // 捕获结束标志位
uint8_t Capture_StartFlag; // 捕获开始标志位
uint16_t Capture_CNTValue; // 第一次捕获边沿时计数器的值
uint16_t Capture_CcrValue; // 捕获寄存器的值
}TIM_ICUserValueType;
中断处理函数
extern TIM_ICUserValueType TIM_ICUserValue[8];
void TIM3_IRQHandler(void)
{
if ( TIM_GetITStatus ( TIM3, TIM_IT_Update) != RESET )
{//假定超时
TIM_ICUserValue[0].Time_OutFlag = 1;
TIM_ICUserValue[1].Time_OutFlag = 1;
TIM_ICUserValue[2].Time_OutFlag = 1;
TIM_ICUserValue[3].Time_OutFlag = 1;
// 采集波形的一个周期内发生了寄存器溢出,放弃本次采集
TIM_ICUserValue[0].Capture_StartFlag = 0;
TIM_ICUserValue[0].Capture_FinishFlag = 0;
TIM_ICUserValue[1].Capture_StartFlag = 0;
TIM_ICUserValue[1].Capture_FinishFlag = 0;
TIM_ICUserValue[2].Capture_StartFlag = 0;
TIM_ICUserValue[2].Capture_FinishFlag = 0;
TIM_ICUserValue[3].Capture_StartFlag = 0;
TIM_ICUserValue[3].Capture_FinishFlag = 0;
//清除中断标志
TIM_ClearITPendingBit ( TIM3, TIM_FLAG_Update );
}
#if USE_TIM3_CH1
// TIM3_CH1上升沿捕获中断
if ( TIM_GetITStatus (TIM3, TIM_IT_CC1 ) != RESET)
{
// 第一次捕获
if ( TIM_ICUserValue[0].Capture_StartFlag == 0 )
{
//记录首次捕获时计数器的值
TIM_ICUserValue[0].Capture_CNTValue = TIM3->CNT;
// 存捕获比较寄存器的值的变量的值清 0
TIM_ICUserValue[0].Capture_CcrValue = 0;
// 开始捕获标准置 1
TIM_ICUserValue[0].Capture_StartFlag = 1;
}
// 下降沿捕获中断
else { // 第二次捕获
// 获取捕获比较寄存器的值减去上一次计数器的值,这个值就是捕获到的高电平的时间的值
TIM_ICUserValue[0].Capture_CcrValue = TIM_GetCapture1(TIM3)-TIM_ICUserValue[0].Capture_CNTValue;
// 开始捕获标志清 0
TIM_ICUserValue[0].Capture_StartFlag = 0;
// 捕获完成标志置 1
TIM_ICUserValue[0].Capture_FinishFlag = 1;
//超时标志清除
TIM_ICUserValue[0].Time_OutFlag = 0;
}
TIM_ClearITPendingBit (TIM3,TIM_IT_CC1);
}
#endif
#if USE_TIM3_CH2
// TIM3_CH2上升沿捕获中断
if ( TIM_GetITStatus (TIM3, TIM_IT_CC2 ) != RESET)
{
if ( TIM_ICUserValue[1].Capture_StartFlag == 0 )
{
TIM_ICUserValue[1].Capture_CNTValue = TIM3->CNT;
TIM_ICUserValue[1].Capture_CcrValue = 0;
TIM_ICUserValue[1].Capture_StartFlag = 1;
}
else {
TIM_ICUserValue[1].Capture_CcrValue = TIM_GetCapture2(TIM3)-TIM_ICUserValue[1].Capture_CNTValue;
TIM_ICUserValue[1].Capture_StartFlag = 0;
TIM_ICUserValue[1].Capture_FinishFlag = 1;
TIM_ICUserValue[1].Time_OutFlag = 0;
}
TIM_ClearITPendingBit (TIM3,TIM_IT_CC2);
}
#endif
#if USE_TIM3_CH3
// TIM3_CH3上升沿捕获中断
if ( TIM_GetITStatus (TIM3, TIM_IT_CC3 ) != RESET)
{
if ( TIM_ICUserValue[2].Capture_StartFlag == 0 )
{
TIM_ICUserValue[2].Capture_CNTValue = TIM3->CNT;
TIM_ICUserValue[2].Capture_CcrValue = 0;
TIM_ICUserValue[2].Capture_StartFlag = 1;
}
else {
TIM_ICUserValue[2].Capture_CcrValue = TIM3->CCR3-TIM_ICUserValue[2].Capture_CNTValue;
TIM_ICUserValue[2].Capture_StartFlag = 0;
TIM_ICUserValue[2].Capture_FinishFlag = 1;
TIM_ICUserValue[2].Time_OutFlag = 0;
}
TIM_ClearITPendingBit (TIM3,TIM_IT_CC3);
}
#endif
#if USE_TIM3_CH4
// TIM3_CH4上升沿捕获中断
if ( TIM_GetITStatus (TIM3, TIM_IT_CC4 ) != RESET)
{
if ( TIM_ICUserValue[3].Capture_StartFlag == 0 )
{
TIM_ICUserValue[3].Capture_CNTValue = TIM3->CNT;
TIM_ICUserValue[3].Capture_CcrValue = 0;
TIM_ICUserValue[3].Capture_StartFlag = 1;
}
else {
TIM_ICUserValue[3].Capture_CcrValue = TIM_GetCapture4(TIM3)-TIM_ICUserValue[3].Capture_CNTValue;
TIM_ICUserValue[3].Capture_StartFlag = 0;
TIM_ICUserValue[3].Capture_FinishFlag = 1;
TIM_ICUserValue[3].Time_OutFlag = 0;
}
TIM_ClearITPendingBit (TIM3,TIM_IT_CC4);
}
#endif
}
结果处理
这个函数你可以放到 main 函数里,我只是把它的结果用串口打印出来(串口部分请自行解决,网上的例子一大堆)
void PWM_IC_Print(void)
{
double fre;
// TIM 计数器的驱动时钟
uint32_t TIM_PscCLK = 72000000 / (71+1);
int i = 0;
for(i = 0;i<8;i++)
{
//超时判断
if(TIM_ICUserValue
.Time_OutFlag == 1)
continue;
//判断是否捕获完成
if(TIM_ICUserValue.Capture_FinishFlag == 1)
{
// 计算一个周期的计数器的值
fre = (TIM_ICUserValue.Capture_CcrValue+1);
//滤波器,我这里只留下了我需要的频段,具体值可以任意修改
//下面的值的计算方法是 (f(TIM3)/目标频率)
if(fre>10000||fre<500)
continue;
// 打印频率,你想怎么处理,请自便
printf ( "rn %d号输入测得频率: %f hzrn",i,1/(fre/TIM_PscCLK));
//清除标志
TIM_ICUserValue.Capture_FinishFlag = 0;
}
}
}
下面是实际的测试结果
我是用TIM2产生了一个200hz的方波,测了一下,还可以
然后连起来,PA6(TIM3 CH1) 接 PA2(待测信号),棕色那根是GND
打开串口调试助手
测试一下多路输入,1个200hz,2个1000hz
举报