zpzobcna 发表于 2025-4-9 14:31:44

8H1K17T 双路ADC检测 | 已解决

// 头文件
#include "STC8H.H"                              // 引入STC8系列单片机的头文件
#include "intrins.h"                            // 引入Keil的内部函数头文件


// 数据存储
#define HIGH_THRESHOLD 3000                     // 亮度变暗的上阈值
#define LOW_THRESHOLD2900                     // 亮度变亮的下阈值
#define HIGH_Voltage   819                      // 1V
#define LOW_Voltage    573                      // 0.7V
#define STABLE_COUNT   20                     // 设定连续检测 20 次才切换状态


// 端口定义
sbit LED    = P5^4;                           // LED
sbit DELAY= P3^5;                           // 继电器
sbit GM   = P3^3;                           // 定义AD光敏输入端口
sbit HP   = P1^3;                           // 电网电压
sbit PH0    = P1^0;                           // 主
sbit PH1    = P1^1;                           // 从


/*-------------------------------------------光敏探头ADC检测-----------------------------------------------------*/
// 1ms延时函数
void delay_ms(uint16 x)
{
    uint16 j, i;   
    for(j = 0; j < x; j++)   
    {
      for(i = 0; i < 1580; i++);      // 延时代码
    }
}

// ADC初始化
void ADC_INIT()
{
    ADCCFG &= ~0x0f;            // 50KSPS
    ADCCFG |= 0x02;             // SPEED(2)
    ADCTIM = 0x35;            // CSSETUP(0), CSHOLD(1), SMPDUTY(21)
    ADC_CONTR&=0xDF;            // 清AD转换完成标志

    EADC = 0;                   // 禁止ADC中断
    ADCCFG|=0x20;               // ADC转换结果12位数
    ADC_CONTR|=0x40;            //启动AD转换,ADC_START=1
}

// ADC口检测AD转换值函数
uint16 GET_ADC_DATA(void)
{
    uint16 AD_DATA = 0;         // ADC数据存储位
    ADC_CONTR&=0xDF;            // 清AD转换完成标志
    ADC_CONTR&=0xBF;            // 关闭AD转换,ADC_START=0
    // 数据高八位存RES 低2位存RESL
    AD_DATA = ADC_RES;          // 首先读取高八位的数值
    AD_DATA <<= 8;
    AD_DATA|= ADC_RESL;         // 读取低4位
    ADC_CONTR|=0x40;            //启动AD转换,ADC_START=1
    returnAD_DATA;
}

// 转换ADC电压值
void datapros()
{
    static uint16 stable_counter = 0;               // 计数器
    static bit light_on = 0;                        // 当前灯的状态

    uint16 temp = GET_ADC_DATA();
    uint16 voltage = (((float)temp / 4095) * 4095);

    // 记录状态是否需要改变
    bit new_state = light_on;

    if (!light_on && voltage > HIGH_THRESHOLD)      // 低亮度,尝试开灯
    {
      new_state = 1;
    }
    else if (light_on && voltage < LOW_THRESHOLD)   // 高亮度,尝试关灯
    {
      new_state = 0;
    }

    // 仅在状态需要改变时,进行稳定性检查
    if (new_state != light_on)
    {
      stable_counter++;                           // 状态变化时,增加稳定计数
      if (stable_counter >= STABLE_COUNT)         // 只有状态连续 N 次不变,才真正执行
      {
            light_on = new_state;

            if(light_on == 0)                     // 检测需要关闭灯
            {
                PH0 = 0;                            // 主
                PH1 = 0;                            // 从

                LED = 0;
                DELAY = 0;
            }
            else if(light_on == 1)
            {
                LED = 1;
                DELAY = 1;
            }

            stable_counter = 0;                     // 计数器归零
      }
    }
    else
    {
      stable_counter = 0;                         // 状态没有变化,重置计数
    }
}

// HP转换ADC电压值----电网电压检测
void HPdatapros()
{
    static uint16 stable_counter = 0;               // 计数器
    static bit light_on = 0;                        // 当前灯的状态   
    bit new_state = light_on;                     // 记录状态是否需要改变

    uint16 temp = GET_ADC_DATA();
    uint16 voltage = (((float)temp / 4095) * 4095);

    if(voltage > HIGH_Voltage)                      // 电网电压超过1V
    {
      PH0 = 0;                                    // 主
      PH1 = 0;                                    // 从
    }

    if (light_on && voltage > LOW_Voltage)          // 电网电压超过0.7V
    {
      new_state = 0;
    }

    // 仅在状态需要改变时,进行稳定性检查
    if (new_state != light_on)
    {
      stable_counter++;                           // 状态变化时,增加稳定计数
      if (stable_counter >= STABLE_COUNT)         // 只有状态连续 N 次不变,才真正执行
      {
            light_on = new_state;

            if(light_on == 0)                     // 检测需要关闭高压
            {
                PH0 = 0;                            // 主
                PH1 = 0;                            // 从

                delay_ms(5000);                     // 5s

                PH0 = 1;                            // 主
                PH1 = 1;                            // 从
            }
            stable_counter = 0;                     // 计数器归零
      }
    }
    else
    {
      stable_counter = 0;                         // 状态没有变化,重置计数
    }
}
/*-------------------------------------------光敏探头 电网电压ADC检测------------------------------------------------*/

// 系统初始化
void system_init()
{
    delay_ms(1000);                     // 延时1s 待市电稳定

    // 引脚定义
    P3M0 |= 0x20; P3M1 &= ~0x20;      // P35 强推挽
    P5M0 |= 0x10; P5M1 &= ~0x10;      // P54 强推挽
    P1M0 |= 0x03; P1M1 &= ~0x03;      // P10 11 强推挽

    LED = 0;                            // 关灯
    DELAY = 0;                        // 继电器打开
    PH0 = 0;                            // 主
    PH1 = 0;                            // 从

    ADC_INIT();                         // ADC初始化
}
void main()
{
    system_init();

    while(1)
    {
      ADC_CONTR = 0x8B;
      datapros();

      ADC_CONTR = 0x83;            // ADC3
      //HPdatapros();                // 电网电压实时检测
    }
}

现在的问题是,当我没有调用电网电压检测的两行代码时 光敏检测是正常的
但是一旦调用会导致LED delay 包括PH0 1四个全部都变成低电平
原来的光敏也不起作用,这个时候电网电压检测引脚是接地的 也就是0V
正常不应该会影响到

代码写的不好,大佬高抬贵手给看看如何能保证这两个检测互不干扰

国芯人工智能 发表于 2025-4-9 14:33:00

回复:关于STC8H1K17T双路ADC检测的代码分析与优化建议

1. 代码概述
该代码基于STC8H1K17T单片机,实现了双路ADC检测功能,主要用于光敏传感器和电网电压的检测。通过设定阈值和稳定计数,实现了对亮度变化和电压状态的判断,并控制LED和继电器的输出。

2. 代码分析
2.1 头文件引入
c
include "STC8H.H"// 引入STC8系列单片机的头文件
include "intrins.h"// 引入Keil的内部函数头文件
STC8H.H 是STC8系列单片机的专用头文件,包含了寄存器和外设的定义。
intrins.h 提供了Keil编译器的内部函数,如延时函数 nop() 等。

2.2 数据存储与阈值定义
c
define HIGHTHRESHOLD 3000// 亮度变暗的上阈值
define LOWTHRESHOLD2900// 亮度变亮的下阈值
define HIGHVoltage   819   // 1V
define LOWVoltage    573   // 0.7V
define STABLECOUNT   20    // 设定连续检测 20 次才切换状态
HIGHTHRESHOLD 和 LOWTHRESHOLD 用于判断光敏传感器的亮度变化。
HIGHVoltage 和 LOWVoltage 用于判断电网电压的状态。
STABLECOUNT 用于确保状态切换的稳定性,避免误判。

2.3 端口定义
c
sbit LED    = P5^4;// LED
sbit DELAY= P3^5;// 继电器
sbit GM   = P3^3;// 定义AD光敏输入端口
sbit HP   = P1^3;// 电网电压
sbit PH0    = P1^0;// 主
sbit PH1    = P1^1;// 主
LED 和 DELAY 分别控制LED和继电器的输出。
GM 和 HP 分别用于光敏传感器和电网电压的ADC输入。
PH0 和 PH1 可能用于其他功能,但未在代码中明确说明。

3. 优化建议
3.1 增加注释
建议在代码中增加更多注释,特别是对关键逻辑和寄存器配置的说明,以提高代码的可读性和可维护性。

3.2 状态机设计
可以考虑使用状态机来管理亮度变化和电压状态的切换,以提高代码的结构性和可扩展性。

3.3 参数化配置
将阈值和稳定计数等参数定义为宏或常量,方便后续调整和优化。

3.4 错误处理
增加对ADC采样结果的错误处理机制,如超时检测或异常值过滤,以提高系统的鲁棒性。

3.5 代码模块化
将ADC采样、状态判断和输出控制等功能模块化,便于代码的复用和测试。

4. 总结
该代码实现了基本的双路ADC检测功能,但在注释、结构化和错误处理等方面还有优化空间。通过上述建议,可以进一步提高代码的质量和系统的稳定性。

如需进一步的技术支持或代码优化,请随时联系。

注意:以上回复基于提供的代码片段,未涉及具体实现细节。如有更多需求或问题,欢迎进一步交流。

[本文内容由国芯人工智能辅助生成,仅供参考]

zpzobcna 发表于 2025-4-9 15:23:18

这个代码主要是为了,先执行电网电压检测,只有低于0.7V的时候才正常工作光敏的,然后高于0.7V就停5s再开,电网电压超过1V整个设备才关机这样子

原来是为了控制高压发生器,一旦短路到了0.7V,我就把高压发生器停掉,但是灯和继电器不停

然后超过1V说明设备异常 整个设备才关机

DebugLab 发表于 2025-4-9 16:49:41


给static变量赋值,每次进入这个函数都会被赋值(程序中是清零),那static就没有意义了
你的程序要去掉=0,如需要初始化清零,这个变量放在data,用startup.a51初始化(放在xdata要修改startup.a51)
或声明为全局变量,在函数外初始化仅赋值1次,注意不能声明时赋值
另外你的程序缺少打开EAXFR,初始化就打开,不要再关闭
https://www.stcaimcu.com/forum.php?mod=viewthread&tid=16535



梁工 发表于 2025-4-9 17:30:45

你的程序中,启动ADC后没有等待检测到ADC完成就读取数据,这是不对的。
初始化时,不要启动ADC转换。
// ADC初始化
void ADC_INIT()
{
    ADCCFG &= ~0x0f;            // 50KSPS
    ADCCFG |= 0x02;             // SPEED(2)
    ADCTIM = 0x35;            // CSSETUP(0), CSHOLD(1), SMPDUTY(21)
    ADC_CONTR&=0xDF;            // 清AD转换完成标志

    EADC = 0;                   // 禁止ADC中断
    ADCCFG|=0x20;               // ADC转换结果12位数
//    ADC_CONTR|=0x40;       //不要启动AD转换!!!!!!!!!!!!!
}

在读取函数启动ADC转换并等待完成,再读数据。
// ADC口检测AD转换值函数
uint16 GET_ADC_DATA(void)
{
    uint16 AD_DATA;         // ADC数据存储位

    ADC_CONTR|=0x40;       //启动AD转换!
    NOP(5);   //加5个NOP
    while((ADC_CONTR & 0x20) == 0);   //等待转换完成
    ADC_CONTR&=0xDF;            // 清AD转换完成标志
//ADC_CONTR&=0xBF;            // 不用清ADC_START=0, 自动清0的
    return(((uint16)ADC_RES << 8) + ADC_RESL);//读取并返回结果
}

zpzobcna 发表于 2025-4-9 23:59:07

已解决,转换问题导致的,一个通道没发现问题,两个通道就出事了

springvirus 发表于 2025-4-10 05:55:11

感谢楼主分享,学习一下

梁工 发表于 2025-4-10 10:24:36

zpzobcna 发表于 2025-4-9 23:59
已解决,转换问题导致的,一个通道没发现问题,两个通道就出事了

你的程序中,启动ADC后没有等待ADC完成就读取数据,才会导致错误,请参考我上面5楼的贴。

zpzobcna 发表于 2025-4-10 17:40:05

// 头文件
#include "STC8H.H"                                  // 引入STC8系列单片机的头文件
#include "intrins.h"                              // 引入Keil的内部函数头文件


// 宏定义
// 数据存储
#define HIGH_THRESHOLD 4000                         // 亮度变暗的上阈值
#define LOW_THRESHOLD3900                         // 亮度变亮的下阈值
#define HIGH_HP 1000                              // 1V
#define LOW_HP700                                 // 0.7V
#define STABLE_COUNT   20                           // 设定连续检测 20 次才切换状态
#define ADC_CHANNEL_GM 11                           // 光敏 ADC 通道
#define ADC_CHANNEL_HP 3                            // 电网 ADC 通道
// 数据类型
#defineuint32   unsigned long                     // 定义32位无符号整数类型
#defineuint16   unsigned int                      // 定义16位无符号整数类型
#defineuint8    unsigned char                     // 定义8位无符号整数类型


// 端口定义
sbit LED    = P5^4;                                 // LED
sbit DELAY= P3^5;                                 // 继电器
sbit GM   = P3^3;                                 // 定义AD光敏输入端口
sbit HP   = P1^3;                                 // 电网电压
sbit PH0    = P1^0;                                 // 主
sbit PH1    = P1^1;                                 // 从


// 1ms延时函数
void delay_ms(uint16 x)
{
    uint16 j, i;   
    for(j = 0; j < x; j++)   
    {
      for(i = 0; i < 1580; i++);                  // 延时代码
    }
}

// ADC初始化
void ADC_INIT()
{
    ADC_CONTR |= 0x80;                              // 打开ADC电源
    ADCCFG &= ~0x0f;                              // 清除速度选择位
        ADCCFG |= 0x02;                                                // 设置采样速率为 SPEED=2,即 50KSPS
        ADCTIM = 0x35;                                                // CSSETUP(0), CSHOLD(1), SMPDUTY(21)
    ADC_CONTR &= 0xDF;                              // 清AD转换完成标志

    EADC = 0;                                       // 禁止ADC中断
    ADCCFG |= 0x20;                                 // ADC转换结果12位数
}

// ADC口检测AD转换值函数
uint16 ADC_ReadChannel(uint8 channel)
{
    uint16 result;

    ADC_CONTR &= 0xF0;                              // 清通道选择位
    ADC_CONTR |= (channel & 0x0F);                  // 设置通道
    ADC_CONTR &= ~0x20;                           // 清完成标志
    ADC_CONTR |= 0x40;                              // 启动转换

    while (!(ADC_CONTR & 0x20));                  // 等待完成
    ADC_CONTR &= ~0x20;                           // 清完成标志

    result = ADC_RES;                               // 首先读取高八位的数值
    result <<= 8;
    result|= ADC_RESL;                                  // 读取低4位
    return result;
}

// 光敏
void process_gm(uint8 channel)
{
    static uint16 gm_stable_counter = 0;
    static bit gm_light_on = 0;

    uint16 temp = ADC_ReadChannel(channel);// 用传入的通道号
    uint16 voltage = (uint32)temp * 5000 / 4095;

    bit new_state = gm_light_on;

    if (!gm_light_on && voltage > HIGH_THRESHOLD)
    {
      new_state = 1;
    }
    else if (gm_light_on && voltage < LOW_THRESHOLD)
    {
      new_state = 0;
    }

    if (new_state != gm_light_on)
    {
      gm_stable_counter++;
      if (gm_stable_counter >= STABLE_COUNT)
      {
            gm_light_on = new_state;
            LED = gm_light_on;
            DELAY = gm_light_on;
            gm_stable_counter = 0;
      }
    }
    else
    {
      gm_stable_counter = 0;
    }
}

void process_hp(uint8 channel)
{
    static uint16 hp_stable_counter = 0;
    static bit hp_light_on = 0;

    uint16 temp = ADC_ReadChannel(channel);// 用传入的通道号
    uint16 voltage = (uint32)temp * 5000 / 4095;

    bit new_state = hp_light_on;

    if (!hp_light_on && voltage > HIGH_HP)            // 超过1V
    {
      new_state = 1;
    }
    else if (hp_light_on && voltage < LOW_HP)         // 超过0.7V
    {
      new_state = 0;
    }

    if (new_state != hp_light_on)
    {
      hp_stable_counter++;
      if (hp_stable_counter >= STABLE_COUNT)
      {
            hp_light_on = new_state;
            if(hp_light_on == 1)      // 超过1V
            {
                mode = 2;
               
                LED = 0;
                DELAY = 0;
                PWMA_ENO &= ~0x03;
                PH0 = 0;
                PH1 = 0;
            }
            else if(hp_light_on == 0)       // 超过0.7V
            {
                PWMA_ENO &= ~0x03;
                PH0 = 0;
                PH1 = 0;

                delay_ms(5000);

                PWMA_ENO |= 0x03;
            }
            hp_stable_counter = 0;
      }
    }
    else
    {
      hp_stable_counter = 0;
    }
}


// 系统初始化
void system_init()
{
    delay_ms(1000);                                     // 延时1s 待市电稳定

    // 引脚定义
    P3M0 |= 0x20; P3M1 &= ~0x20;                        // P35 强推挽
    P5M0 |= 0x10; P5M1 &= ~0x10;                        // P54 强推挽
    P1M0 |= 0x03; P1M1 &= ~0x03;                        // P10 11 强推挽

        LED = 0;                                          // 关灯
    DELAY = 0;                                          // 继电器打开
    PH0 = 0;                                          // 主
    PH1 = 0;                                          // 从

    ADC_INIT();                                       // ADC初始化
}


// 主函数
void main()
{
    system_init();

    while (1)
    {
      process_gm(ADC_CHANNEL_GM);// 光敏通道
      process_hp(ADC_CHANNEL_HP);// 电网通道
    }
}



这是我的写法 一个通道对应一个数据处理函数 然后可以直接用数字传入通道 并且通道检测不会相互冲突

zpzobcna 发表于 2025-4-10 17:40:54

zpzobcna 发表于 2025-4-10 17:40
// 头文件
#include "STC8H.H"                                  // 引入STC8系列单片机的头文件
#include ...

这样就可以保证双路以及多路检测都可以运行了
页: [1]
查看完整版本: 8H1K17T 双路ADC检测 | 已解决