- 2025-01-13
-
回复了主题帖:
多通道输入捕获干扰问题,诉求大佬们帮忙解决,感谢!
tagetage 发表于 2025-1-3 12:31
以上都是编程的细节问题,你应该多想一想,这个项目软硬件的总体架构我认为都没有问题,剩下的就是细节了。 ...
非常感谢大佬您的耐心指导,前段时间忙别的去了,这两天花时间按照您指导的方式,由简单入手,先搞定一个风扇的各种情况,然后再慢慢增加风扇,优化了代码,特别是您提到的切换通道时要关闭采集,同时也需要足够的延时,这个问题终于解决了!真的是太感激了,编程还有很多细节需要跟大佬们多多学习!万分感激
- 2025-01-03
-
回复了主题帖:
多通道输入捕获干扰问题,诉求大佬们帮忙解决,感谢!
tagetage 发表于 2025-1-3 10:27
我不断更改代码,但是就是解决不了问题,于是实在是没招,只能跟您请教下,实在是不好意思打扰到您了。代码 ...
我是按照这个流程去弄的,先调试的一个风扇,后面才慢慢增加风扇的,其实不增加风扇,只要将其余监测通道打开,我就发现数据就会不准,总感觉输入捕获回来的值就会偶尔出现错误!我输入捕获采用的主从模式,总感觉是捕获时CCR寄存器的值不是当前风扇的转速,之前三个风扇一起测试时,我都是转速一样测试的,现在我将有的风扇转速快,有的风扇转速慢,现在问题就出来了
-
回复了主题帖:
多通道输入捕获干扰问题,诉求大佬们帮忙解决,感谢!
tagetage 发表于 2024-12-23 11:45
很高兴你采纳了我的建议并取得了很好的效果。
来进行监测stm32的IO口是否一直处于高电平啊?----用软 ...
对了,stm32芯片采用的是:STM32F103RCT6
-
回复了主题帖:
多通道输入捕获干扰问题,诉求大佬们帮忙解决,感谢!
tagetage 发表于 2024-12-23 11:45
很高兴你采纳了我的建议并取得了很好的效果。
来进行监测stm32的IO口是否一直处于高电平啊?----用软 ...
上午好,冒昧打扰到您了!还是上次那个问题,我采用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,手动增加一些转换的间隔时间
}
}
还请劳烦您费心看下,不甚感激!
- 2024-12-23
-
回复了主题帖:
大佬们上午好:请问有什么好的方法检测IO口是否一直处于高电平?
秦天qintian0303 发表于 2024-12-23 17:15
IO口得读取也可以,需要设计好IO读取得周期
我仔细思考了下,综合考虑下决定还是采用定时器定时监测IO引脚电平是否变化来做!非常感谢您的耐心指导。
-
回复了主题帖:
大佬们上午好:请问有什么好的方法检测IO口是否一直处于高电平?
maychang 发表于 2024-12-23 17:06
其实,成本最低的办法就是定时检测若干I/O引脚的状态,与上次检测到的状态比较,看是否有变化。这个方法 ...
我仔细思考了下,综合考虑下决定还是采用定时器定时监测IO引脚电平是否变化来做!非常感谢您的耐心指导。
-
回复了主题帖:
大佬们上午好:请问有什么好的方法检测IO口是否一直处于高电平?
秦天qintian0303 发表于 2024-12-23 16:06
没有的时候就一直处于高电平状态,这个是外部电路的的状态吗?如果是的话其实可以用外部中断进行实时检测, ...
没有的时候,我用逻辑分析仪抓了下波形,就是一直处于高电平状态!我现在就是想通过外部中断来监测,但是一直没能成功。一直没想明白需要怎么去判定一定周期没有来外部中断的方法。还请指导下需要怎么去判定到底有没有来中断?
我尝试了用库函数:EXTI_GetITStatus和EXTI_GetFlagStatus都不行。这些函数都必须在中断服务程序里执行,如果没有中断,中断服务程序是不会执行的。
-
回复了主题帖:
大佬们上午好:请问有什么好的方法检测IO口是否一直处于高电平?
maychang 发表于 2024-12-23 15:54
当年在另一个论坛,有位网友提出单片机的一个I/O口控制一个设备。但是该I/O口并不是联接到这个设备,而是 ...
谢谢,我了解下倍压整流电路!
-
回复了主题帖:
大佬们上午好:请问有什么好的方法检测IO口是否一直处于高电平?
beyond_笑谈 发表于 2024-12-23 15:25
对,就是这个意思。而且反相器的驱动能力大一点,可以直接驱动LED显示状态。
谢谢!
-
回复了主题帖:
大佬们上午好:请问有什么好的方法检测IO口是否一直处于高电平?
wangerxian 发表于 2024-12-23 15:13
引脚设置成输入捕获,可能还是可以直接读引脚的状态,因为直接读的是寄存器,对捕获模式没有什么影响。
感谢指导!
-
回复了主题帖:
大佬们上午好:请问有什么好的方法检测IO口是否一直处于高电平?
常见泽1 发表于 2024-12-23 13:59
低电平捕获 获取这个脚不是低电平的状态吧 应该可以吧
我也想着实在不行就采用轮询的方式来读取引脚电平是不是一直低电平状态。谢谢
-
回复了主题帖:
大佬们上午好:请问有什么好的方法检测IO口是否一直处于高电平?
令狐大侠 发表于 2024-12-23 13:50
中断?
我尝试了下中断,没搞定!设定了有中断标志位置1,没有置0,然后测试发现这个标志位一直为1。刚接触编程,还很多不懂
void EXTI9_5_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line7) == SET) //判断是否是外部中断7号线触发的中断
{
TIM3CH_CAPTURE_Flag = 1; //置接收标志位变量为1
EXTI_ClearITPendingBit(EXTI_Line7); //清除外部中断7号线的中断标志位
}
else if (EXTI_GetITStatus(EXTI_Line7) != SET) //判断是否是外部中断7号线触发的中断
{
TIM3CH_CAPTURE_Flag = 0; //置接收标志位变量为1
}
}
-
回复了主题帖:
大佬们上午好:请问有什么好的方法检测IO口是否一直处于高电平?
dige 发表于 2024-12-23 13:34
如果不够精确,加一个频率电压转换电路,比如用LM331等器件制作。如果价格因素,可以采用你之前的捕获比 ...
好的,非常感谢
-
回复了主题帖:
大佬们上午好:请问有什么好的方法检测IO口是否一直处于高电平?
beyond_笑谈 发表于 2024-12-23 13:24
在MCU的信号输入捕捉通道接反相器,采用MCU另一个引脚读取这个信号状态,同时反相器也能驱动LED
  ...
您的意思是不是用另一个引脚去轮询这个输入捕获引脚的状态?如果轮循到状态长时间不变,就是风扇不转了。
-
回复了主题帖:
多通道输入捕获干扰问题,诉求大佬们帮忙解决,感谢!
tagetage 发表于 2024-12-23 12:16
IO轮询的话,我考虑的是:一直轮询监测是否是高电平的话,风扇正常运转的话,可能也会刚好多次轮循到高电 ...
是的,刚接触编程没多久!所以需要多请教!我先尝试看看中断能不能搞定,如果实在不行,就按照您说的这样去轮询
-
回复了主题帖:
多通道输入捕获干扰问题,诉求大佬们帮忙解决,感谢!
tagetage 发表于 2024-12-23 12:25
按理说没有上升沿或者下降沿它是不会有中断的。-------是的,但是我上面也说了,,在软件里判断,有中断则 ...
了解啦!非常感谢
-
回复了主题帖:
大佬们上午好:请问有什么好的方法检测IO口是否一直处于高电平?
dige 发表于 2024-12-23 13:15
那就增加一个RC,将PWM滤波成直流量,用ADC进行采集,通过ADC的值来判断
感谢指导,我用这种方式来试试!
-
回复了主题帖:
多通道输入捕获干扰问题,诉求大佬们帮忙解决,感谢!
tagetage 发表于 2024-12-23 12:19
不知道是不是我监测中断的方式不对?按理说没有上升沿或者下降沿它是不会有中断的。
非常感谢您的指导! ...
是的,我画个流程图,再重新试试,非常感谢!
-
回复了主题帖:
多通道输入捕获干扰问题,诉求大佬们帮忙解决,感谢!
tagetage 发表于 2024-12-23 11:48
就是74HC251的反向输出端用上,在接一个IO做查询,或中断处理。我想你也能明白。。
我现在的做法就是将反向端用上了;
IO轮询的话,我考虑的是:一直轮询监测是否是高电平的话,风扇正常运转的话,可能也会刚好多次轮循到高电平的状态,这样会出现误判断
利用中断最好,但是我不知道怎么去判断中断有没有?我尝试了下去监测中断标志位,但是没有达到效果。不知道是不是我监测中断的方式不对?按理说没有上升沿或者下降沿它是不会有中断的。
非常感谢您的指导!
-
回复了主题帖:
大佬们上午好:请问有什么好的方法检测IO口是否一直处于高电平?
beyond_笑谈 发表于 2024-12-23 11:30
已经采用单片机检测了,是需要再额外监测显示?那就在端口并联上拉电阻和LED
是这样的:我现在用一个输入捕获通道通过多路复用器分别获取四个直流风扇的FG信号的方波,读取出风扇的转速,但是我发现在风扇不转时,转速值还是有的不是0。
但是实际风扇不转时,FG信号一直处于低电平,我就想通过监测不转时的低电平信号,来修改显示的转速值为0。
这样还可以监测风扇的好坏,风扇坏了不转时可以实时发现。
感谢您的回复!