来自STC AI助手的回答(最后附有改进后代码):
您的代码有 多个关键问题,逐一分析如下:
🔴 问题 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。
🔴 问题 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。
🔴 问题 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,灯永远不亮。
🔴 问题 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。
🔴 问题 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 自动更新
}
}
📋 问题对照总结
问题 代码行 后果
❌ 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 需确认输入信号接在哪 |