找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 108|回复: 4
打印 上一主题 下一主题
收起左侧

STC8H1K08 ADC不起作用

[复制链接]
跳转到指定楼层
楼主
程序如下,LED灯没有变化

#include "STC8h.h"
#include "intrins.h"
typedef unsigned char u8;
typedef unsigned int u16;
sbit led=P1^1;       
u16 num;
void GPIO_Init()
{
        P1M0 = 0xFF;
        P1M1 = 0xFF;
        P5M0 = 0x00;
        P5M1 = 0x00;
        P3M0 = 0x00;
        P3M1 = 0x04;
}

void STC8ADC_Init()
{
    P_SW2 |= 0x80;
    ADCTIM = 0x3f;                       
    P_SW2 &= 0x7f;
    ADCCFG = 0x00;            
    ADC_CONTR = 0x8A;     
}

void ADC_Isr() interrupt 5
{
   EADC = 1;                        
  ADC_CONTR |= 0x40;
  _nop_();
  _nop_();
        while(  !(ADC_CONTR&0x20) );
  ADC_CONTR&= ~0x20;
  num=(ADC_RES<<8) | ADC_RESL;              
}

void main()
{
          GPIO_Init();
    STC8ADC_Init();
          EA=1;
   led=1;
        while(1)
{
if(num>300)
led=~led;
}}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:69038 发表于 2026-6-20 00:39 | 只看该作者
1、既然用中断方式,那么EA=1总是要有的,否则中断无效,你的代码中没有?
2、“~”的作用对象至少是字节(BYTE),可你的代码是 led=~led,(P1^1),不匹配,可能会产生意料之外的结果。
3、ADC_CONTR = 0x8A ,说明用的是ADC10,不同封装ADC10对应不同IO,IO选对了?
4、是否有ADC_Vref,参考电源有接?

回复

使用道具 举报

板凳
ID:69038 发表于 2026-6-20 00:44 | 只看该作者
认真看一下代码,原来是在中断中才开EADC=1的,有才,左脚踩右脚了!
void ADC_Isr() interrupt 5
{
   EADC = 1;                        
  ADC_CONTR |= 0x40;
  _nop_();
  _nop_();
        while(  !(ADC_CONTR&0x20) );
  ADC_CONTR&= ~0x20;
  num=(ADC_RES<<8) | ADC_RESL;              
}

回复

使用道具 举报

地板
ID:69038 发表于 2026-6-20 00:54 | 只看该作者
来自STC AI助手的回答(最后附有改进后代码):

您的代码有 多个关键问题,逐一分析如下:
&#128308; 问题 1:ADC 转换从未启动(最核心问题)

ADC_CONTR = 0x8A;   // 0x8A = 1000 1010

位         名称         值         含义
Bit 7         ADC_POWER         1         ✓ ADC电源已打开
Bit 6         ADC_START         0         ✗ 从未启动转换!
Bit 5         ADC_FLAG         0         标志位
Bit 4         ADC_EPWMT         0         未使用PWM触发
Bit 3-0         ADC_CHS         1010         ADC10通道(P3.2)

后果:ADC 不上电、不开始转换,中断永远触发不了,ISR中的代码从未被执行,num始终为 0。
&#128308; 问题 2:ISR 内部逻辑严重错误(死锁风险)

void ADC_Isr() interrupt 5
{
    EADC = 1;                   // ① 在ISR中使能中断——无意义,应在主循环中设置
    ADC_CONTR |= 0x40;          // ② 启动新转换
    _nop_(); _nop_();
    while( !(ADC_CONTR&0x20) ); // ③ 轮询ADC_FLAG —— 死锁!!!
    ADC_CONTR&= ~0x20;           // ④ 清标志
    num=(ADC_RES<<8) | ADC_RESL;
}

对照手册范例(STC8H手册 21.7.2 中断方式)正确的 ISR 应为:

void ADC_Isr() interrupt 5
{
    ADC_CONTR &= ~0x20;     // ① 先清中断标志
    num = (ADC_RES << 8) | ADC_RESL;  // ② 读结果
    ADC_CONTR |= 0x40;      // ③ 启动下一次转换
}

您的代码问题:

    第③步 while(!(ADC_CONTR&0x20)) 在 ISR 内轮询标志——此时ADC_START刚刚写入,转换刚开始,标志还没置位,会永远卡死在循环中(死锁)。
    正常流程应该是:先清标志 → 读结果 → 启动下次转换 → RETI。

&#128308; 问题 3:P1 口模式全部设为开漏,LED 无法驱动

P1M0 = 0xFF;
P1M1 = 0xFF;

对于 STC8H 系列,端口模式定义:
PnM1         PnM0         模式
0         0         准双向口
0         1         推挽输出
1         0         高阻输入
1         1         开漏

P1M0=0xFF, P1M1=0xFF → 所有 P1 引脚均为开漏模式。P1.1作为LED输出,用开漏模式无法输出高电平驱动LED,灯永远不亮。
&#128308; 问题 4:ADC 通道与引脚对应关系需确认

您的 ADC_CONTR = 0x8A(ADC_CHS=1010)选择的是 ADC10 通道。

对于 STC8H1K08 系列,查手册 21.1.1 的通道映射表:
ADC_CHS         ADC通道         对应引脚(STC8H1K08)
1010         ADC10         P3.2

而您在 GPIO_Init 中将 P3M1=0x04(即 P3.2 设为了高阻输入),这步是对的。但您的 ADC 输入信号实际接在哪个引脚上?请确认:

    如果输入信号接在 P3.2 → ADC10(CHS=1010)✅
    如果输入信号接在 P1.0 → ADC0(CHS=0000)
    如果输入信号接在 P1.1 → ADC1(CHS=0001)

另外需要注意的是,STC8H1K08 只有 ADC0/1/8/9/10/11/12/13/14 和内部1.19V 共11个通道,没有 ADC2~ADC7。
&#128308; 问题 5:ADCCFG = 0x00 设定左对齐,但读法需匹配

ADCCFG = 0x00;  // RESFMT=0 → 左对齐

STC8H1K08 是 10位ADC,左对齐时:

    ADC_RES[7:0] = 结果的高8位
    ADC_RESL[7:6] = 结果的低2位(ADC_RESL的低6位为0)

所以正确的读取方式应为:

num = (ADC_RES << 2) | (ADC_RESL >> 6);   // 10位值
// 或 num = (u16)ADC_RES * 4 + (ADC_RESL >> 6);

而您写的 (ADC_RES<<8) | ADC_RESL 会把 ADC_RES 左移8位(相当于乘以256),值会严重偏大,远超300这个阈值。
✅ 修正后的完整代码

以下是根据 STC8H 手册 21.7.2 范例修正的 中断方式 代码,假设 ADC 输入接 P3.2(ADC10),LED 接 P1.1:

#include "stc8h.h"
#include "intrins.h"

typedef unsigned char u8;
typedef unsigned int u16;

sbit led = P1^1;
u16 num;

void GPIO_Init()
{
    // P1.1 LED → 推挽输出(P1M0.1=1, P1M1.1=0)
    // P1.0/1.2等保持准双向
    P1M0 = 0x02;   // 仅 P1.1 推挽
    P1M1 = 0x00;

    // P3.2 ADC输入 → 高阻输入(P3M1.2=1, P3M0.2=0)
    P3M0 = 0x00;
    P3M1 = 0x04;   // 仅 P3.2 高阻

    // P5保持默认
    P5M0 = 0x00;
    P5M1 = 0x00;
}

void ADC_Init()
{
    P_SW2 |= 0x80;          // 使能访问 XFR
    ADCTIM = 0x3F;          // ADC 内部时序
    P_SW2 &= 0x7F;

    ADCCFG = 0x00;          // RESFMT=0 左对齐, SPEED=0000 最快速度

    ADC_CONTR = 0x80;       // ADC_POWER=1, CHS=0000(ADC0)
    // 稍后需要时再切换通道并启动
}

void ADC_Isr() interrupt 5
{
    ADC_CONTR &= ~0x20;             // 清 ADC_FLAG
    num = ((u16)ADC_RES << 2) | (ADC_RESL >> 6);   // 10位左对齐 → 数值
    led = (num > 300) ? (~led) : led;   // LED 根据结果翻转
    // 可选:启动下一次转换
    // ADC_CONTR |= 0x40;
}

void main()
{
    GPIO_Init();
    ADC_Init();

    EADC = 1;               // 使能 ADC 中断
    EA = 1;

    // 选择通道并启动第一次转换
    ADC_CONTR = 0x80 | 0x0A;    // ADC_POWER=1, CHS=1010(ADC10=P3.2)
    _nop_(); _nop_();           // 等待 ADC 电源稳定(实际需~1ms,最好加软件延时)
    ADC_CONTR |= 0x40;          // 启动第一次转换

    led = 0;                    // LED初始灭(假设低电平灭)

    while(1)
    {
        // 也可以在 main loop 中查询方式:
        // if(num > 300) led = ~led;
        // num 由 ISR 自动更新
    }
}

&#128203; 问题对照总结
问题         代码行         后果
❌ ADC_START 从未置1         ADC_CONTR = 0x8A         转换永不启动,ISR不触发,num恒为0
❌ ISR内轮询等待标志         while(!(ADC_CONTR&0x20))         死锁卡死
❌ ISR内写EADC         EADC=1         不影响,应写在main中
❌ P1全部开漏         P1M0=P1M1=0xFF         LED无法驱动
❌ 10位ADC结果读法错误         (ADC_RES<<8)|ADC_RESL         数值远大于300
⚠️ ADC通道选择         0x8A→ADC10=P3.2         需确认输入信号接在哪
回复

使用道具 举报

5#
ID:1064915 发表于 2026-6-21 08:41 | 只看该作者
1. 修正GPIO初始化
只将ADC输入脚设为高阻,LED脚设为推挽输出:

c
void GPIO_Init()
{
    P1M0 = 0x02;      // P1.1 推挽输出(bit1=1)
    P1M1 = 0x00;      // P1.1 推挽(M1=0)
    P1M0 &= ~0x04;    // P1.2 高阻输入(M0=0)
    P1M1 |= 0x04;     // P1.2 高阻(M1=1)
    // 其他端口保持默认或按需配置
}
如果您的ADC通道不是P1.2,请根据实际情况修改。

2. 完善ADC初始化
在STC8ADC_Init()中添加中断使能,并清除相关标志:

c
void STC8ADC_Init()
{
    P_SW2 |= 0x80;          // 访问特殊功能寄存器
    ADCTIM = 0x3f;          // 设置ADC时序
    P_SW2 &= 0x7f;
    ADCCFG = 0x00;          // 左对齐,12位结果
    ADC_CONTR = 0x8A;       // 开启ADC电源,选择通道10(P1.2),不启动转换
   
    // 使能ADC中断
    IE2 |= 0x20;            // STC8H中EADC位在IE2.5
    // 或者使用 EADC = 1; (如果头文件已定义)
}
3. 启动第一次转换
在main()中,开总中断后立即启动转换:

c
void main()
{
    GPIO_Init();
    STC8ADC_Init();
    EA = 1;
    led = 1;
    ADC_CONTR |= 0x40;      // 启动第一次ADC转换(ADC_START位)
   
    while(1)
    {
        if(num > 300)
        {
            led = ~led;
            // 可根据需要加入延时,避免翻转过快
        }
    }
}
4. 精简中断服务函数
移除EADC=1,确保转换完成后自动启动下一次:

c
void ADC_Isr() interrupt 5
{
    ADC_CONTR &= ~0x20;      // 清除完成标志(ADC_FLAG)
    num = (ADC_RES << 8) | ADC_RESL;
    ADC_CONTR |= 0x40;       // 启动下一次转换
}
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表