上午好,冒昧打扰到您了!还是上次那个问题,我采用stm32的一个输入捕获通道通过您推荐的SN74HC251芯片来读取风扇转速,现在遇到一个问题,搞了两天一直不知道哪里出了问题?
现象是这样的:
1、我现在用一颗SN74HC251去控制了六个风扇分别采用输入捕获通道读取转速,然后转速获取到之后,在OLED上面进行转速显示,我总共连接了五个风扇,其中第二个风扇我拔掉了,没链接,验证风扇出故障情况下是否会显示0转速。
其中理论转速:第一个风扇转速应该是:3600转,第二个拔掉了应该是:0转,第三个应该是3600转,第四个、第五个、第六个应该是:2400转
但是实际显示出来的结果是:第一个风扇转速是:2400转,第二个拔掉了是:0转,第三个是3600转,第四个是:3600转,第五个、第六个是:2400转
经过查看发现,显示错位了。我感觉是输入捕获捕获的还是上一次的值。
2、于是我采用单步调试进行验证看是否数据错位,但是神奇的是我单步调试的时候,显示完全OK,没有错位,我不断更改代码,但是就是解决不了问题,于是实在是没招,只能跟您请教下,实在是不好意思打扰到您了。
代码如下:
SN74HC251控制程序:
#include "stm32f10x.h" // Device header
//IO定义
#define GPIO_Clk_A RCC_APB2Periph_GPIOA
#define GPIO_Port_EN GPIOA
#define GPIO_Pin_EN GPIO_Pin_4
#define GPIO_Pin_A GPIO_Pin_0
#define GPIO_Pin_B GPIO_Pin_1
#define GPIO_Pin_C GPIO_Pin_2
/**
* 函 数:HC251多路复用器引脚初始化
* 参 数:无
* 返 回 值:无
*/
void HC251_Init (void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(GPIO_Clk_A, ENABLE); //开启GPIOB的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,赋值为推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_A | GPIO_Pin_B | GPIO_Pin_C | GPIO_Pin_EN; //GPIO引脚,赋值为第12、13、14、15号引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,赋值为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数
//函数内部会自动根据结构体的参数配置相应寄存器
//实现GPIOB的初始化
}
/**
* 函 数:HC251多路复用器失能
* 参 数:无
* 返 回 值:无
*/
void HC251_Disable(void)
{
GPIO_SetBits(GPIO_Port_EN, GPIO_Pin_EN);
}
/**
* 函 数:HC251多路复用器使能
* 参 数:无
* 返 回 值:无
*/
void HC251_Enable(void)
{
GPIO_ResetBits(GPIO_Port_EN, GPIO_Pin_EN);
}
/**
* 函 数:HC251多路复用器通道选择
* 参 数:Channel 通道 范围:0 ~ 7
* 返 回 值:无
*/
void HC251_SetChannel(uint8_t Channel)
{
switch (Channel)
{
case 0:
GPIO_ResetBits(GPIOA, GPIO_Pin_A);
GPIO_ResetBits(GPIOA, GPIO_Pin_B);
GPIO_ResetBits(GPIOA, GPIO_Pin_C);
break;
case 1:
GPIO_SetBits(GPIOA, GPIO_Pin_A);
GPIO_ResetBits(GPIOA, GPIO_Pin_B);
GPIO_ResetBits(GPIOA, GPIO_Pin_C);
break;
case 2:
GPIO_ResetBits(GPIOA, GPIO_Pin_A);
GPIO_SetBits(GPIOA, GPIO_Pin_B);
GPIO_ResetBits(GPIOA, GPIO_Pin_C);
break;
case 3:
GPIO_SetBits(GPIOA, GPIO_Pin_A);
GPIO_SetBits(GPIOA, GPIO_Pin_B);
GPIO_ResetBits(GPIOA, GPIO_Pin_C);
break;
case 4:
GPIO_ResetBits(GPIOA, GPIO_Pin_A);
GPIO_ResetBits(GPIOA, GPIO_Pin_B);
GPIO_SetBits(GPIOA, GPIO_Pin_C);
break;
case 5:
GPIO_SetBits(GPIOA, GPIO_Pin_A);
GPIO_ResetBits(GPIOA, GPIO_Pin_B);
GPIO_SetBits(GPIOA, GPIO_Pin_C);
break;
case 6:
GPIO_ResetBits(GPIOA, GPIO_Pin_A);
GPIO_SetBits(GPIOA, GPIO_Pin_B);
GPIO_SetBits(GPIOA, GPIO_Pin_C);
break;
case 7:
GPIO_SetBits(GPIOA, GPIO_Pin_A);
GPIO_SetBits(GPIOA, GPIO_Pin_B);
GPIO_SetBits(GPIOA, GPIO_Pin_C);
break;
}
}
风扇是否停转、堵转监测程序:
#include "stm32f10x.h" // Device header
#include "Delay.h"
uint8_t Data[4] = {0}; //FG信号输入IO口电平状态数组
uint8_t IOState_Flag = 0;
/**
* 函 数:计数传感器初始化
* 参 数:无
* 返 回 值:无
*/
void IO_State_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //将PB0引脚初始化为上拉输入
}
/**
* 函 数:获取状态标志位
* 参 数:无
* 返 回 值:IOState_Flag 取值:0/1
*/
uint8_t IO_GetState(void)
{
uint8_t i = 0;
for(i=0; i<4; i++)//扫描4个通道是否为0
{
Data[i] = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0);
Delay_ms(4);
}
if (Data[0]==Data[1] && Data[1]==Data[2] && Data[2]==Data[3]) //如果均为0
{
IOState_Flag = 1; //则返回1
}
else
{
IOState_Flag = 0; //则返回0
}
return IOState_Flag;
}
输入捕获获取转速值程序:
#include "stm32f10x.h" // Device header
#include "RpmGet_Delay.h"
#include "IO_State.h"
#include "HC251.h"
uint16_t Rpm[6] = {0}; //定义转速数组
uint8_t i = 0;
/**
* 函 数:输入捕获初始化
* 参 数:无
* 返 回 值:无
*/
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_IPD;
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; //选择配置定时器通道1
TIM_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); //触发源选择TI1FP1
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); //从模式选择复位
//即TI1产生上升沿时,会触发CNT归零
/*TIM使能*/
TIM_Cmd(TIM3, ENABLE); //使能TIM3,定时器开始运行
}
/**
* 函 数:转速获取初始化
* 参 数:无
* 返 回 值:无
*/
void RpmGet_Init(void)
{
Timer7_Init();
IC_Init();
HC251_Init(); //风扇转速捕获初始化
IO_State_Init();
}
/**
* 函 数:获取风扇的实际转速
* 参 数:无
* 返 回 值:无
*/
void RpmGet_Value(void)
{
//Flag是标志位,是通过TIM7定时器中断产生的,定时器1ms产生依次中断,然后设置计数值为1000
//1000减至0,将Flag置1,通过这种方式达到1秒换一个通道捕获一次转速
if(Flag == 1 && i<6)
{
HC251_Disable();
HC251_SetChannel(i);
HC251_Enable(); //这里我也尝试加了延时,最多5ms,但是没用
//输入捕获获取到的值经过公式转换为转速值,再赋值给数Rpm数组
Rpm[i] = 30 * (1000000 / (TIM_GetCapture1(TIM3) + 1));
if(IO_GetState() == 1) //该代码是监测风扇是否堵转,停转
{
Rpm[i] = 0;
}
i++;
Flag = 0;
if(i == 6)
{
i = 0;
}
}
}
主程序如下:
#include "stm32f10x.h" // Device header
#include "PowerSupply_Control.h"
#include "MyDelay.h"
#include "IO_State.h"
#include "Delay.h"
#include "PWM.h"
#include "OLED.h"
#include "Key.h"
#include "Timer.h"
#include "AD.h"
#include "RpmGet.h"
#include "NTC.h"
uint8_t KeyNum; //定义用于接收按键码的变量
uint16_t SpeedA,SpeedB; //定义速度变量
//float A0, A1; //定义AD值变量
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
PWM_Init(); //PWM初始化
Key_Init(); //按键初始化
PowerSupply_Control_Init(); //供电IO口初始化
Timer_Init(); //定时器初始化
RpmGet_Init();
/*显示静态字符串*/
OLED_ShowString(1,1,"SD:000&000&000"); //1行1列显示字符串Speed:
OLED_ShowString(2,1,"A:0000&0000&0000"); //2行1列显示字符串RpmA:
OLED_ShowString(3,1,"B:0000&0000&0000"); //2行1列显示字符串RpmB:
OLED_ShowString(4,1,"C:0000&0000&0000"); //2行1列显示字符串RpmC:
// OLED_ShowString(2, 10, "A0:00.0");
// OLED_ShowString(3, 10, "A1:00.0");
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC中断分组
while(1)
{
OLED_ShowNum(3,13,IO_GetState(),2);
KeyNum = Key_GetNum(); //获取按键键码
if(KeyNum == 2) //按键1按下
{
SpeedA += 20; //速度变量自增20
SpeedB += 20;
if(SpeedA > 100) //速度变量超过100后
{
SpeedA = 0; //速度变量变为0
}
if(SpeedB > 100) //速度变量超过100后
{
SpeedB = 0; //速度变量变为0
}
}
PWM_SetCompare1(SpeedA); //设置直流风扇的速度为速度变量
PWM_SetCompare2(SpeedB);
OLED_ShowNum(1,4,SpeedA,3); //OLED显示速度变量
OLED_ShowNum(1,8,SpeedB,3);
if(Capture_Flag[3] == 1) //标志位置1,代表一次延时完成
{
PowerSupply_Control_NG(); //风扇供电电压翻转
Capture_Flag[3] = 0; //标志位置0
}
RpmGet_Value();
OLED_ShowNum(2,3,Rpm[0],4); //2行3列显示第一个风扇实际转速
OLED_ShowNum(2,8,Rpm[1],4); //2行8列显示第二个风扇实际转速
OLED_ShowNum(3,3,Rpm[2],4); //3行3列显示第三个风扇实际转速
OLED_ShowNum(3,8,Rpm[3],4); //3行8列显示第四个风扇实际转速
OLED_ShowNum(4,3,Rpm[4],4); //4行3列显示第五个风扇实际转速
OLED_ShowNum(4,8,Rpm[5],4); //4行8列显示第六个风扇实际转速
// A0 = read_battery_ntc(AD_Value[0]);
// A1 = read_battery_ntc(AD_Value[1]);
// OLED_ShowNum(2, 13, (uint16_t)A0, 2); //显示转换结果第0个数据
// OLED_ShowNum(2, 16, (uint16_t)(A0 * 10) %10, 1); //显示转换结果第1个数据
// OLED_ShowNum(3, 13, (uint16_t)A1, 2); //显示转换结果第0个数据
// OLED_ShowNum(3, 16, (uint16_t)(A1 * 10) %10, 1); //显示转换结果第1个数据
//
// Delay_ms(100); //延时100ms,手动增加一些转换的间隔时间
}
}
还请劳烦您费心看下,不甚感激!