1、接近开关说明
接近开关有PNP型和NPN型,还分常开与常闭。接近开关允许的电压范围在6~36V,本项目中给接近开关接入的为12V电压。PNP型,感应到金属时信号端与正极输出端接通输出高电平,信号输出端输出信号电压和正极输出端接入的电压相等,即为12V。由于单片机IO口能承受的最高电压为3.3V,所以需要给接近开关信号电压降压。降压电路如下图所示,然而实际测得无信号输出的时候,即接近开关未感应到金属时,OUT端输出电压为2.5V左右,有信号输出时为3.3V左右,电平边沿跨度不大,无法被输入捕获。于是,改进了该电路,去除了3.3V的电压,结果为无信号输入时为0V,有信号输入时为3.3V左右,可以被捕获,可以测得转速。
由于项目中电机转速高,需要采样频率高得接近开关,于是选择了齿轮转速传感器(实际上也是接近开关,不过采样频率能达到10KHz)。该传感器为NPN常开型,无输出信号时悬空(啥也没),有输出信号时输出端与负极输出端接通输出低电平,故NPN与PNP型的接近开关信号端电路又不一样。最后电路为3.3V电压串联10K上拉电阻再接上接近开关的信号端,最后接入GND形成闭合电路。整体电路在未感应到金属时输出高电平,有感应时输出低电平。
2、程序
程序主要有主程序、串口、时钟配置和中断服务程序5部分。
串口和时钟配置程序完全移植之前的代码,不需要特别的改动。
中断服务程序将测量脉宽的程序改成测量频率,即捕获两次上升沿。
主程序就是根据频率计算转速。
程序:
mian.c
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_GeneralTim.h"
#include "bsp_led.h"
int main(void)
{
float n1,n2;
// int i,temp=0;
// float transmisson_radio[3]={1,2,3};
float time1,time2;
// TIM 计数器的驱动时钟
float TIM2_PscCLK = 72000000 / (GENERAL_TIM2_PSC+1);
float TIM3_PscCLK = 72000000 / (GENERAL_TIM3_PSC+1);
/* 串口初始化 */
USART_Config();
/* 定时器初始化 */
GENERAL_TIM_Init();
LED_GPIO_Config();
printf ( "\r\n STM32 输入捕获实验\r\n" );
printf ( "\r\n 电机转速测量\r\n" );
while ( 1 )
{
if(TIM2_ICUserValueStructure.Capture_FinishFlag == 1)
{
// 计算高电平时间的计数器的值
time1 = TIM2_ICUserValueStructure.Capture_Period * (GENERAL_TIM2_PERIOD+1) +
(TIM2_ICUserValueStructure.Capture_CcrValue+1);
n1=60*TIM2_PscCLK/time1;
// 打印高电平脉宽时间
printf ( "\r\n测得电机1转速:%0.4f r/min\r\n",n1);
TIM2_ICUserValueStructure.Capture_FinishFlag = 0;
}
if(TIM3_ICUserValueStructure.Capture_FinishFlag == 1)
{
// 计算高电平时间的计数器的值
time2 = TIM3_ICUserValueStructure.Capture_Period * (GENERAL_TIM3_PERIOD+1) +
(TIM3_ICUserValueStructure.Capture_CcrValue+1);
n2=60*TIM3_PscCLK/time2;
// 打印高电平脉宽时间
printf ( "\r\n测得电机2转速:%0.4f r/min\r\n",n2);
TIM3_ICUserValueStructure.Capture_FinishFlag = 0;
}
// Delay(0xfff);
if(TIM2_FLAG==1 && TIM3_FLAG==1)
{
if(n1>=n2)
{
printf("传动比:%0.5f \r\n",n1/n2);
if((n1/n2<2.47 && n1/n2>2.421)||(n1/n2<1.3635 && n1/n2>1.3365))
{
GPIO_ResetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
// printf("判断1\r\n");
}
else
{
// printf("判断2\r\n");
GPIO_ResetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
}
}
else
{
printf("传动比:%0.5f \r\n",n2/n1);
if((n2/n1<2.47 && n2/n1>2.421)||(n2/n1<1.3635 && n2/n1>1.3365))
{
GPIO_ResetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
// printf("判断1\r\n");
}
else
{
// printf("判断2\r\n");
GPIO_ResetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
}
}
TIM2_FLAG=0;
TIM3_FLAG=0;
}
}
bsp_GeneralTim.c
#include "bsp_GeneralTim.h"
// 定时器2输入捕获用户自定义变量结构体定义
TIM2_ICUserValueTypeDef TIM2_ICUserValueStructure = {0,0,0,0};
// 定时器3输入捕获用户自定义变量结构体定义
TIM3_ICUserValueTypeDef TIM3_ICUserValueStructure = {0,0,0,0};
uint8_t TIM2_FLAG=0;
uint8_t TIM3_FLAG=0;
// TIM2中断优先级配置
static void GENERAL_TIM2_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 设置中断组为0
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
// 设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIM2_IRQ ;
// 设置主优先级为 0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
// 设置抢占优先级为3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
// TIM3中断优先级配置
static void GENERAL_TIM3_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 设置中断组为0
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
// 设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = GENERAL_TIM3_IRQ ;
// 设置主优先级为 0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
// 设置抢占优先级为3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
static void GENERAL_TIM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// TIM2输入捕获通道 GPIO 初始化
RCC_APB2PeriphClockCmd(GENERAL_TIM2_CH1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = GENERAL_TIM2_CH1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GENERAL_TIM2_CH1_PORT, &GPIO_InitStructure);
// TIM3输入捕获通道 GPIO 初始化
RCC_APB2PeriphClockCmd(GENERAL_TIM3_CH1_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = GENERAL_TIM3_CH1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GENERAL_TIM3_CH1_PORT, &GPIO_InitStructure);
}
///*
// * 注意:TIM_TimeBaseInitTypeDef结构体里面有5个成员,TIM6和TIM7的寄存器里面只有
// * TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的时候只需初始化这两个成员即可,
// * 另外三个成员是通用定时器和高级定时器才有.
// *-----------------------------------------------------------------------------
// *typedef struct
// *{ TIM_Prescaler 都有
// * TIM_CounterMode TIMx,x[6,7]没有,其他都有
// * TIM_Period 都有
// * TIM_ClockDivision TIMx,x[6,7]没有,其他都有
// * TIM_RepetitionCounter TIMx,x[1,8,15,16,17]才有
// *}TIM_TimeBaseInitTypeDef;
// *-----------------------------------------------------------------------------
// */
/* ---------------- PWM信号 周期和占空比的计算--------------- */
// ARR :自动重装载寄存器的值
// CLK_cnt:计数器的时钟,等于 Fck_int / (psc+1) = 72M/(psc+1)
// PWM 信号的周期 T = ARR * (1/CLK_cnt) = ARR*(PSC+1) / 72M
// 占空比P=CCR/(ARR+1)
//TIM2
static void GENERAL_TIM2_Mode_Config(void)
{
// 开启定时器时钟,即内部时钟CK_INT=72M
GENERAL_TIM2_APBxClock_FUN(GENERAL_TIM2_CLK,ENABLE);
/*--------------------时基结构体初始化-------------------------*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM2_PERIOD;
// 驱动CNT计数器的时钟 = Fck_int/(psc+1)
TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM2_PSC;
// 时钟分频因子 ,配置死区时间时需要用到
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,设置为向上计数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,没用到不用管
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定时器
TIM_TimeBaseInit(GENERAL_TIM2, &TIM_TimeBaseStructure);
/*--------------------输入捕获结构体初始化-------------------*/
TIM_ICInitTypeDef TIM_ICInitStructure;
// 配置输入捕获的通道,需要根据具体的GPIO来配置
TIM_ICInitStructure.TIM_Channel = GENERAL_TIM2_CHANNEL_x;
// 输入捕获信号的极性配置
TIM_ICInitStructure.TIM_ICPolarity = GENERAL_TIM_END_ICPolarity;
// 输入通道和捕获通道的映射关系,有直连和非直连两种
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
// 输入的需要被捕获的信号的分频系数
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
// 输入的需要被捕获的信号的滤波系数
TIM_ICInitStructure.TIM_ICFilter = 0;
// 定时器输入捕获初始化
TIM_ICInit(GENERAL_TIM2, &TIM_ICInitStructure);
// 清除更新和捕获中断标志位
TIM_ClearFlag(GENERAL_TIM2, TIM_FLAG_Update|GENERAL_TIM2_IT_CCx);
// 开启更新和捕获中断
TIM_ITConfig (GENERAL_TIM2, TIM_IT_Update | GENERAL_TIM2_IT_CCx, ENABLE );
// 使能计数器
TIM_Cmd(GENERAL_TIM2, ENABLE);
}
//TIM3
static void GENERAL_TIM3_Mode_Config(void)
{
// 开启定时器时钟,即内部时钟CK_INT=72M
GENERAL_TIM3_APBxClock_FUN(GENERAL_TIM3_CLK,ENABLE);
/*--------------------时基结构体初始化-------------------------*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Period=GENERAL_TIM3_PERIOD;
// 驱动CNT计数器的时钟 = Fck_int/(psc+1)
TIM_TimeBaseStructure.TIM_Prescaler= GENERAL_TIM3_PSC;
// 时钟分频因子 ,配置死区时间时需要用到
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,设置为向上计数
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,没用到不用管
TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定时器
TIM_TimeBaseInit(GENERAL_TIM3, &TIM_TimeBaseStructure);
/*--------------------输入捕获结构体初始化-------------------*/
TIM_ICInitTypeDef TIM_ICInitStructure;
// 配置输入捕获的通道,需要根据具体的GPIO来配置
TIM_ICInitStructure.TIM_Channel = GENERAL_TIM3_CHANNEL_x;
// 输入捕获信号的极性配置
TIM_ICInitStructure.TIM_ICPolarity = GENERAL_TIM_END_ICPolarity;
// 输入通道和捕获通道的映射关系,有直连和非直连两种
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
// 输入的需要被捕获的信号的分频系数
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
// 输入的需要被捕获的信号的滤波系数
TIM_ICInitStructure.TIM_ICFilter = 0;
// 定时器输入捕获初始化
TIM_ICInit(GENERAL_TIM3, &TIM_ICInitStructure);
// 清除更新和捕获中断标志位
TIM_ClearFlag(GENERAL_TIM3, TIM_FLAG_Update|GENERAL_TIM3_IT_CCx);
// 开启更新和捕获中断
TIM_ITConfig (GENERAL_TIM3, TIM_IT_Update | GENERAL_TIM3_IT_CCx, ENABLE );
// 使能计数器
TIM_Cmd(GENERAL_TIM3, ENABLE);
}
void GENERAL_TIM_Init(void)
{
GENERAL_TIM2_NVIC_Config();
GENERAL_TIM3_NVIC_Config();
GENERAL_TIM_GPIO_Config();
GENERAL_TIM2_Mode_Config();
GENERAL_TIM3_Mode_Config();
}
bsp_GeneralTim.h
#ifndef __BSP_GENERALTIME_H
#define __BSP_GENERALTIME_H
#include "stm32f10x.h"
/************通用定时器TIM参数定义,只限TIM2、3、4、5************/
// 当使用不同的定时器的时候,对应的GPIO是不一样的,这点要注意
//TIM2时基相关宏定义
#define GENERAL_TIM2 TIM2
#define GENERAL_TIM2_APBxClock_FUN RCC_APB1PeriphClockCmd
#define GENERAL_TIM2_CLK RCC_APB1Periph_TIM2
#define GENERAL_TIM2_PERIOD 0XFFFF
#define GENERAL_TIM2_PSC (72-1)
//TIM2时基相关宏定义
#define GENERAL_TIM3 TIM3
#define GENERAL_TIM3_APBxClock_FUN RCC_APB1PeriphClockCmd
#define GENERAL_TIM3_CLK RCC_APB1Periph_TIM3
#define GENERAL_TIM3_PERIOD 0XFFFF
#define GENERAL_TIM3_PSC (72-1)
// TIM2 输入捕获通道GPIO相关宏定义
#define GENERAL_TIM2_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define GENERAL_TIM2_CH1_PORT GPIOA
#define GENERAL_TIM2_CH1_PIN GPIO_Pin_0
#define GENERAL_TIM2_CHANNEL_x TIM_Channel_1
// TIM3 输入捕获通道 GPIO相关宏定义
#define GENERAL_TIM3_CH1_GPIO_CLK RCC_APB2Periph_GPIOA
#define GENERAL_TIM3_CH1_PORT GPIOA
#define GENERAL_TIM3_CH1_PIN GPIO_Pin_6
#define GENERAL_TIM3_CHANNEL_x TIM_Channel_1
// TIM2中断相关宏定义
#define GENERAL_TIM2_IT_CCx TIM_IT_CC1
#define GENERAL_TIM2_IRQ TIM2_IRQn
#define GENERAL_TIM2_INT_FUN TIM2_IRQHandler
// TIM3中断相关宏定义
#define GENERAL_TIM3_IT_CCx TIM_IT_CC1
#define GENERAL_TIM3_IRQ TIM3_IRQn
#define GENERAL_TIM3_INT_FUN TIM3_IRQHandler
// 获取捕获寄存器值函数宏定义
#define GENERAL_TIM_GetCapturex_FUN TIM_GetCapture1
// 捕获信号极性函数宏定义
#define GENERAL_TIM_OCxPolarityConfig_FUN TIM_OC1PolarityConfig
// 测量的起始边沿
#define GENERAL_TIM_STRAT_ICPolarity TIM_ICPolarity_Rising
// 测量的结束边沿
#define GENERAL_TIM_END_ICPolarity TIM_ICPolarity_Falling
// 定时器2输入捕获用户自定义变量结构体声明
typedef struct
{
uint8_t Capture_FinishFlag; // 捕获结束标志位
uint8_t Capture_StartFlag; // 捕获开始标志位
uint16_t Capture_CcrValue; // 捕获寄存器的值
uint16_t Capture_Period; // 自动重装载寄存器更新标志
}TIM2_ICUserValueTypeDef;
extern TIM2_ICUserValueTypeDef TIM2_ICUserValueStructure;
// 定时器3输入捕获用户自定义变量结构体声明
typedef struct
{
uint8_t Capture_FinishFlag; // 捕获结束标志位
uint8_t Capture_StartFlag; // 捕获开始标志位
uint16_t Capture_CcrValue; // 捕获寄存器的值
uint16_t Capture_Period; // 自动重装载寄存器更新标志
}TIM3_ICUserValueTypeDef;
extern TIM3_ICUserValueTypeDef TIM3_ICUserValueStructure;
/**************************函数声明********************************/
extern uint8_t TIM2_FLAG,TIM3_FLAG;
void GENERAL_TIM_Init(void);
#endif /* __BSP_GENERALTIME_H */
stm32f10x_it.c
#include "stm32f10x_it.h"
#include "bsp_GeneralTim.h"
void GENERAL_TIM2_INT_FUN(void)
{
// 当要被捕获的信号的周期大于定时器的最长定时时,定时器就会溢出,产生更新中断
// 这个时候我们需要把这个最长的定时周期加到捕获信号的时间里面去
if ( TIM_GetITStatus ( GENERAL_TIM2, TIM_IT_Update) != RESET )
{
TIM2_ICUserValueStructure.Capture_Period ++;
TIM_ClearITPendingBit ( GENERAL_TIM2, TIM_FLAG_Update );
}
// 上升沿捕获中断
if ( TIM_GetITStatus (GENERAL_TIM2, GENERAL_TIM2_IT_CCx ) != RESET)
{
// 第一次捕获
if ( TIM2_ICUserValueStructure.Capture_StartFlag == 0 )
{
// 计数器清0
TIM_SetCounter ( GENERAL_TIM2, 0 );
// 自动重装载寄存器更新标志清0
TIM2_ICUserValueStructure.Capture_Period = 0;
// 存捕获比较寄存器的值的变量的值清0
TIM2_ICUserValueStructure.Capture_CcrValue = 0;
// 当第一次捕获到上升沿之后,就把捕获边沿配置为下降沿
// GENERAL_TIM_OCxPolarityConfig_FUN(GENERAL_TIM, TIM_ICPolarity_Falling);
// 开始捕获标准置1
TIM2_ICUserValueStructure.Capture_StartFlag = 1;
}
// 下降沿捕获中断
else // 第二次捕获
{
// 获取捕获比较寄存器的值,这个值就是捕获到的高电平的时间的值
TIM2_ICUserValueStructure.Capture_CcrValue =
GENERAL_TIM_GetCapturex_FUN (GENERAL_TIM2);
// 当第二次捕获到下降沿之后,就把捕获边沿配置为上升沿,好开启新的一轮捕获
// GENERAL_TIM_OCxPolarityConfig_FUN(GENERAL_TIM, TIM_ICPolarity_Rising);
// 开始捕获标志清0
TIM2_ICUserValueStructure.Capture_StartFlag = 0;
// 捕获完成标志置1
TIM2_ICUserValueStructure.Capture_FinishFlag = 1;
TIM2_FLAG = 1;
}
TIM_ClearITPendingBit (GENERAL_TIM2,GENERAL_TIM2_IT_CCx);
}
}
void GENERAL_TIM3_INT_FUN(void)
{
// 当要被捕获的信号的周期大于定时器的最长定时时,定时器就会溢出,产生更新中断
// 这个时候我们需要把这个最长的定时周期加到捕获信号的时间里面去
if ( TIM_GetITStatus ( GENERAL_TIM3, TIM_IT_Update) != RESET )
{
TIM3_ICUserValueStructure.Capture_Period ++;
TIM_ClearITPendingBit ( GENERAL_TIM3, TIM_FLAG_Update );
}
// 上升沿捕获中断
if ( TIM_GetITStatus (GENERAL_TIM3, GENERAL_TIM3_IT_CCx ) != RESET)
{
// 第一次捕获
if ( TIM3_ICUserValueStructure.Capture_StartFlag == 0 )
{
// 计数器清0
TIM_SetCounter ( GENERAL_TIM3, 0 );
// 自动重装载寄存器更新标志清0
TIM3_ICUserValueStructure.Capture_Period = 0;
// 存捕获比较寄存器的值的变量的值清0
TIM3_ICUserValueStructure.Capture_CcrValue = 0;
// 当第一次捕获到上升沿之后,就把捕获边沿配置为下降沿
// GENERAL_TIM_OCxPolarityConfig_FUN(GENERAL_TIM, TIM_ICPolarity_Falling);
// 开始捕获标准置1
TIM3_ICUserValueStructure.Capture_StartFlag = 1;
}
// 下降沿捕获中断
else // 第二次捕获
{
// 获取捕获比较寄存器的值,这个值就是捕获到的高电平的时间的值
TIM3_ICUserValueStructure.Capture_CcrValue =
GENERAL_TIM_GetCapturex_FUN (GENERAL_TIM3);
// 当第二次捕获到下降沿之后,就把捕获边沿配置为上升沿,好开启新的一轮捕获
// GENERAL_TIM_OCxPolarityConfig_FUN(GENERAL_TIM, TIM_ICPolarity_Rising);
// 开始捕获标志清0
TIM3_ICUserValueStructure.Capture_StartFlag = 0;
// 捕获完成标志置1
TIM3_ICUserValueStructure.Capture_FinishFlag = 1;
TIM3_FLAG = 1;
}
TIM_ClearITPendingBit (GENERAL_TIM3,GENERAL_TIM3_IT_CCx);
}
}