找回密码
 立即注册
查看: 325|回复: 16

STC8H1K08-36I-TSSOP20芯片AD1读数大部分准确,但偶发出现 0000

[复制链接]

12

主题

53

回帖

844

积分

高级会员

积分
844
发表于 2026-9-23 20:14:26 | 显示全部楼层 |阅读模式
AD1直接接到VCC引脚的,有10UF和0.1UF电容,并用示波器监测,绝对不会有瞬间到0的干扰,而且现板上没有任何别的电路,只有最小元件MCU,也没有其它外围电路影响。附件是串口回传的数据 STC8H1K08 ADC 第15通道(内部1.19V)-AD1通道 ,交替打印15通道AD1通道原始10位值结果.txt (25.83 KB, 下载次数: 52)

  1. /****************************************************
  2. 功能:STC8H1K08 ADC 第15通道(内部1.19V)测试/AD1通道 ,交替打印15通道/AD1通道原始10位值
  3.    ----------------------------------------------------------------------------
  4.   【硬件配置】与之前相同
  5.    1. 芯片: STC8H1K08-36I-TSSOP20
  6.    2. 第15通道为芯片内部固定1.19V参考源,
  7.          3. AD1为(P1.1)第20脚,配置为高阻输入
  8.    3. 串口1(P3.0/P3.1) 打印输出: 115200,8,N,1
  9.    ----------------------------------------------------------------------------
  10.   【目的】验证ADC模块本身是否稳定
  11.    测第15通道读数稳定(约 1024*1.19/VRef), 不出现0
  12.           AD1(P1.1,外接稳压供电的VCC引脚)读数大部分为 1023(满量程),但偶发出现 0000,
  13.     说明ADC模块和读时序没问题, 问题暂找不到原因
  14.   【软件功能】
  15.    1. 时钟: 内部 11.0592MHz (ISP下载软件设置, 代码中无需配置)
  16.    2. 定时器0做0.5ms节拍
  17.    3. 主循环中读第15通道, 打印,读第1通道, 识别码A1/B2交替
  18.   【读法】ADCCFG=0x0f 左对齐: 10位结果 = (ADC_RES<<2)|(ADC_RESL>>6)
  19.    ----------------------------------------------------------------------------
  20. ******************************************************/
  21. #include "reg51.h"
  22. #include "intrins.h"
  23. #define FOSC            11059200UL
  24. #define BRT             (65536 - FOSC / 115200 / 4)
  25. #define P1IE  (*(unsigned char volatile xdata *)0xfe31)
  26. sfr     AUXR        =   0x8e;
  27. sfr     ADC_CONTR   =   0xbc;
  28. sfr     ADC_RES     =   0xbd;
  29. sfr     ADC_RESL    =   0xbe;
  30. sfr     ADCCFG      =   0xde;
  31. sfr     P_SW2       =   0xba;
  32. #define ADCTIM  (*(unsigned char volatile xdata *)0xfea8)
  33. sfr     P0M1    =   0x93;
  34. sfr     P0M0    =   0x94;
  35. sfr     P1M1    =   0x91;
  36. sfr     P1M0    =   0x92;
  37. sfr     P2M1    =   0x95;
  38. sfr     P2M0    =   0x96;
  39. sfr     P3M1    =   0xb1;
  40. sfr     P3M0    =   0xb2;
  41. sfr     P4M1    =   0xb3;
  42. sfr     P4M0    =   0xb4;
  43. sfr     P5M1    =   0xc9;
  44. sfr     P5M0    =   0xca;
  45. /* 全局变量区 */
  46. bit busy;
  47. bit flag;          //T0中断标志
  48. bit tx_toggle;     //识别码翻转标志: 0发0xA1, 1发0xB2
  49. char buf[8];
  50. unsigned int adcval;   //ADC原始10位结果
  51. void UartIsr() interrupt 4
  52. {
  53.     if (TI)
  54.     {
  55.         TI = 0;
  56.         busy = 0;
  57.     }
  58.     if (RI)
  59.     {
  60.         RI = 0;
  61.     }
  62. }
  63. void UartInit()
  64. {
  65.     SCON = 0x50;
  66.     TMOD = 0x00;
  67.     TL1 = BRT;
  68.     TH1 = BRT >> 8;
  69.     TR1 = 1;
  70.     AUXR = 0x40;
  71.     busy = 0;
  72. }
  73. void UartSend(char dat)
  74. {
  75.     while (busy);
  76.     busy = 1;
  77.     SBUF = dat;
  78. }
  79. void UartSendStr(char *str)
  80. {
  81.     while (*str != '\0')
  82.     {
  83.         UartSend(*str);
  84.         str++;
  85.     }
  86. }
  87. void ADCInit()
  88. {
  89.     P_SW2 |= 0x80;
  90.     ADCTIM = 0x3f;          //设置ADC内部时序
  91.     P_SW2 &= 0x7f;
  92.     ADCCFG = 0x0f;          //设置ADC时钟为系统时钟/2/16, 左对齐
  93. }
  94. /* 读第15通道(内部1.19V)一次, 返回10位结果 */
  95. /* 读指定通道一次, 返回10位结果 */
  96. int ADCRead(unsigned char ch)
  97. {
  98.     unsigned int res;
  99.     unsigned char hi, lo;
  100.     ADC_CONTR = 0x80 | ch;          //使能ADC, 选通道ch (ch=0x01为AD1, ch=0x0F为第15通道)
  101.     ADC_CONTR |= 0x40;              //启动转换
  102.     _nop_();
  103.     _nop_();
  104.     while (!(ADC_CONTR & 0x20));    //查询完成标志
  105.     ADC_CONTR &= ~0x20;             //清完成标志
  106.     hi = ADC_RES;                   //先锁存高8位
  107.     lo = ADC_RESL;                  //再锁存低位
  108.     res = ((unsigned int)hi << 2) | ((unsigned int)lo >> 6);  //左对齐10位
  109.     return res;
  110. }
  111. void Timer0_Init(void)      //0.5毫秒@11.0592MHz
  112. {
  113.     AUXR |= 0x80;           //定时器时钟1T模式
  114.     TMOD &= 0xF0;           //设置定时器模式
  115.           TL0 = 0x66;                                      //设置定时初始值
  116.           TH0 = 0xEA;                                      //设置定时初始值
  117.     TF0 = 0;                //清除TF0标志
  118.     TR0 = 1;                //定时器0开始计时
  119.     ET0 = 1;                //使能定时器0中断
  120.     EA  = 1;                //使能总中断
  121. }
  122. void main()
  123. {
  124.     P0M0 = 0x00;  P0M1 = 0x00;
  125.     P1M0 = 0x00;  P1M1 = 0x00;   //第15通道是内部通道, 不需要配置P1口
  126.     P2M0 = 0x00;  P2M1 = 0x00;
  127.     P3M0 = 0x00;  P3M1 = 0x00;
  128.     P4M0 = 0x00;  P4M1 = 0x00;
  129.     P5M0 = 0x00;  P5M1 = 0x00;
  130.        
  131.           P_SW2 |= 0x80;          //使能访问XFR
  132.     P1IE &= ~0x02;          //关闭P1.1数字输入(作为ADC口必须关)
  133.     P_SW2 &= 0x7f;          //不访问XFR了, 可关
  134.     tx_toggle = 0;
  135.     ADCInit();
  136.     UartInit();
  137.     Timer0_Init();
  138.     ES = 1;
  139.     while (flag);  flag = 0;    //等0.5ms
  140.     while (flag);  flag = 0;    //再等0.5ms
  141.     while (1)
  142.     {
  143.             if (flag)
  144.             {
  145.                 flag = 0;
  146.                 if (tx_toggle == 0)     // 交替读两个通道
  147.                     adcval = ADCRead(0x01);     // AD1 (P1.1)
  148.                 else
  149.                     adcval = ADCRead(0x0F);     // 第15通道(内部1.19V)
  150.                 buf[0] = (adcval / 1000) + '0';
  151.                 buf[1] = (adcval / 100) % 10 + '0';
  152.                 buf[2] = (adcval / 10) % 10 + '0';
  153.                 buf[3] = adcval % 10 + '0';
  154.                 buf[4] = tx_toggle ? 0xB2 : 0xA1;   // A1=AD1, B2=第15通道
  155.                 buf[5] = '\r';
  156.                 buf[6] = '\n';
  157.                 buf[7] = '\0';
  158.                 tx_toggle = !tx_toggle;
  159.                 UartSendStr(buf);
  160.             }
  161.     }
  162. }
  163. //T0 中断-节拍器
  164. void Timer0_Isr(void) interrupt 1
  165. {
  166.     flag = 1;
  167. }
复制代码
与AI互动的记录:

STC8H1K08 ADC 偶发读 0 问题 实验记录
芯片型号:STC8H1K08-36I-TSSOP20
时钟:内部 11.0592MHz(ISP 下载软件设置)
串口:串口1(P3.0/P3.1),115200, 8, N, 1
测试通道:AD1(P1.1 外部引脚,接 VCC,10μF+0.1μF 滤波);对比通道:ADC 第15通道(内部固定 1.19V)
VRef+(第9脚):接 VCC(5V)
日期:本次实验记录

一、问题现象
AD1(P1.1,接稳压 VCC)读数大部分为 1023(满量程),但偶发出现 0000,也出现过 0511 / 0032 / 0010 等小值。
现象可稳定复现,且随代码版本变化(从偶发到连续)。
识别码约定:A1 = AD1(P1.1),B2 = 第15通道(内部1.19V)。
二、关键实验与结果
实验1:原始代码(只读 AD1,P1.1 接 VCC)
结果:稳定 1023,偶发 0000 / 0511 / 0032 / 0010。

实验2:加 ADC_CONTR &= ~0x20;(启动前清 ADC_FLAG)
结果:仍偶发 0000 → ADC_FLAG 残留不是成因。

实验3:读结果移到清标志之前 + 加超时保护
结果:仍偶发 0000 → 读值时序不是成因。

实验4:只读第15通道(内部1.19V,ADC_CHS=1111)
结果:

读数稳定 240(理论值 ≈ 1024×1.19/5 ≈ 244,实测 240 合理,含内部1.19V ±1% 误差);
偶尔跳 241/242;
全程从未出现 0000。
实验5:交替读 AD1(A1)与第15通道(B2)
结果:

B2(第15通道)全程稳定 240~243,从未出 0;
A1(AD1)仍偶发 0000;
A1 掉 0 的那一帧,紧邻的 B2 完全正常。
三、已确认的结论(由实验4、5 证明)
ADC 模块本身正常(第15通道稳定);
读值代码、串口打印、时序、通道切换全部正常(第15通道用同一套代码,稳定);
电源干扰被排除(第15通道测的就是 VCC 相关,它不跳);
满量程边界误差被排除(第15通道在 240,非边界,也正常);
问题 100% 锁定在"AD1(P1.1)这个外部引脚通道"本身。
四、已排除的假设(均经实测或推理否定)
序号        假设        排除依据
1        ADC 电源未等 1ms        电源自始至终未关,中段才出错
2        同通道需丢首次        通道固定,且第15通道不丢也正常
3        ADC_FLAG 残留        加清标志后仍出 0(实验2)
4        C51 类型截断        第15通道用同一读法,正常(实验4)
5        采样电容充不到位        撤回;P1.1 为稳压直供
6        满量程边界 + 误差        0 是数量级错误,误差解释不了
7        电源干扰        第15通道不跳(实验4/5)
8        通道切换后首次脏        交替读时 B2 仍正常(实验5)
注:"P1IE 关闭数字输入" 与 "P1.1 输出锁存器被清0" 两项尚未最终确认,见第六节。

五、当前最可疑的原因(待验证)
P1.1 输出锁存器(P1 端口 B1)可能被清 0:

若 P1 的 B1 = 0,则 P1.1 的内部输出驱动会试图把引脚拉低,与外部 VCC 顶牛;
采样瞬间被内部下拉拉向 0 → 读到 0;
第15通道是内部通道,没有输出锁存器,所以不受影响。

实验6:确认是否已加 P1IE(已加)
实验5 的数据是否在加了以下两行之后测的?(已加)
#define P1IE  (*(unsigned char volatile xdata *)0xfe31)   // 文件开头声明
...
P_SW2 |= 0x80;      // 使能访问XFR
P1IE &= ~0x02;      // 关闭P1.1数字输入
P_SW2 &= 0x7f;
📋
若已加仍出0 → P1IE 假设排除;已加
实验7:检查代码是否写过 P1 / P1.1
搜索代码里有没有 P1 = / P1 &= / P1 |= 之类;
若有,先删除,让 P1.1 锁存器保持复位默认值 1。
实验8:明确把 P1.1 锁存器置 1
在 main() 中 P1M1=0x02 之后加:

    P1M0 = 0x00;
    P1M1 = 0x02;        //P1.1高阻输入(ADC口)
    P1 |= 0x02;         //★明确把P1.1锁存器置1, 防止内部下拉
📋
若加后 0000 消失 → 根因确认:P1.1 输出锁存器被清0,内部下拉顶牛外部VCC。
七、当前可用的测试代码骨架(交替读 AD1 / 第15通道)
/* ADCRead 必须带通道参数 */
int ADCRead(unsigned char ch)
{
    unsigned int res;
    unsigned char hi, lo;

    ADC_CONTR = 0x80 | ch;          //ch=0x01为AD1(P1.1), ch=0x0F为第15通道
    ADC_CONTR |= 0x40;              //启动转换
    _nop_(); _nop_();
    while (!(ADC_CONTR & 0x20));    //查询完成标志
    ADC_CONTR &= ~0x20;             //清完成标志

    hi = ADC_RES;                   //先锁存
    lo = ADC_RESL;
    res = ((unsigned int)hi << 2) | ((unsigned int)lo >> 6);   //左对齐10位
    return res;
}
📋
主循环:

if (tx_toggle == 0) adcval = ADCRead(0x01);   // AD1 (P1.1) → 识别码A1
else                adcval = ADCRead(0x0F);   // 第15通道   → 识别码B2
📋
八、关键参数备忘(避免明天重算)
项        值        说明
ADCTIM        0x3F        CSSETUP=0, CSHOLD=1, SMPDUTY=31(手册推荐值)
ADCCFG        0x0F        SPEED=1111(SYSclk/2/16), RESFMT=0 左对齐
10位结果读法        (ADC_RES<<2) | (ADC_RESL>>6)        左对齐
第15通道        ADC_CHS=1111, ADC_CONTR=0x8F        内部1.19V
第15通道理论值        ≈244        1024×1.19/5
P1M1        0x02        P1.1 高阻输入
P1IE        地址 FE31H, B1=P11IE        XFR区,需 P_SW2|=0x80
识别码        A1=AD1, B2=第15通道       
九、文档信息出处
手册《STC8H系列单片机》第「ADC模数转换,内部1.19V参考信号源」章节:ADC_CONTR/ADCCFG/ADCTIM 位定义、速度公式、第15通道、ADC_POWER 注意事项。
手册《STC8H系列单片机》第「I/O口」章节:PxM0/PxM1 模式表、PxIE(P1IE 地址 FE31H,B1=P11IE,复位值 1111,1111)。
手册《STC8H系列单片机》第「存储器」章节:EAXFR 使用说明(P_SW2 |= 0x80;)。
论坛《复现到了ADC出现0值的问题》等帖:官方"读到0=采样到0电压"的结论(供参考)。

回复

使用道具 举报 送花

已绑定手机

11

主题

484

回帖

1362

积分

金牌会员

积分
1362
发表于 2026-9-23 21:21:56 | 显示全部楼层
连续取样3次,前两次取样结果抛弃或连续取样10次,丢弃1个最高值和1个最低值,取剩余8个值的平均值。
回复

使用道具 举报 送花

12

主题

53

回帖

844

积分

高级会员

积分
844
 楼主| 发表于 2026-9-24 07:47:38 | 显示全部楼层
wul*** 发表于 2026-9-23 21:21
连续取样3次,前两次取样结果抛弃或连续取样10次,丢弃1个最高值和1个最低值,取剩余8个值的平均值。 ...

前4次取样丢弃都试过了,没用。丢高和丢低邓平均,这种方法只能提高精准度,可解决不了为0的问题,10个中,出现2个0呢?或者有一个比0稍大的呢?(现在是0.5MS测一次才只有0,再提高频率,会有更多的0和比0大的数,靠平均解决不了。
必须要找到测量为0的原因。
因为是同时测内部15通道 的1.19V和外部通道 1,15通道这个就非常稳定,电压也准,不会出现0的问题,但同时测的1通道就会有0。不要怀疑AD1外部干扰,因为它是直接有电线焊到VCC脚外的10UF和0.1UF电容脚上的。而且线非常短。不可能有大的干扰,就一块小板子,现在其它的元件全部都没焊,只有这个IC和2个滤波电容,一条短接线,一个烧录排针。
更用示波器看着的,没有纹波干扰。且如果真有干扰到0V,芯片的低压复位也应启动复位了(有个10UF电容的,不可能在瞬间降到0,超出物理现象了)
我现在不是要精度高,因为有点波动可以取平均和设一个合理的阀值,但会测出一个不存在的电压,这个在量产时就会很麻烦,因为你不知在外界各种干扰下,那个是正常电压,那个是异常电压。无法起到监测的作用。
我主要是想知道,是芯片AD检测,都会有这种0,还是代码在处理这种太频繁的AD检测上还有漏洞没解决?但我想0.5MS检测一次不算高吧?规格书上标称10位ADC最高可以每秒500K次转换,只有几us,非常快的。
回复

使用道具 举报 送花

11

主题

148

回帖

1445

积分

金牌会员

积分
1445
发表于 2026-9-24 08:59:55 | 显示全部楼层
ADCRead(unsigned char ch)这个函数里面,为什么不先等待完成呢,这样的话,如果重复2次调用间隔时间很短,应该会出问题吧
微信qsssuv
回复

使用道具 举报 送花

已绑定手机

123

主题

5133

回帖

1万

积分

超级版主

无情的代码机器

积分
11465
发表于 2026-9-24 09:10:31 | 显示全部楼层

有人遇到过,串电阻,并小电容,可改善
截图202609240909334346.jpg
三天不学习,赶不上刘少奇~
回复

使用道具 举报 送花

已绑定手机

5

主题

92

回帖

1306

积分

金牌会员

积分
1306
发表于 2026-9-24 09:11:56 | 显示全部楼层
全代码输入检查STCAI,试试
我已查清手册,这里有一个非常关键的严重问题必须先指出,它正是您代码里 AD1 通道偶发读到 0 的根本原因。

一、先诊断您代码里的 Bug
问题 1(致命):P1.1 没有配置成高阻输入
手册明确要求:

被选择为ADC输入通道的I/O口,必须设置PxM0/PxM1寄存器将I/O口模式设置为高阻输入模式。

而您的代码里:

P1M0 = 0x00;  P1M1 = 0x00;   //第15通道是内部通道, 不需要配置P1口
📋
注释写着"第15通道不需要配置P1口",但您同时还要读 AD1(P1.1)!P1.1 被留在了准双向口模式(P1M1.1=0, P1M0.1=0)。准双向口的强上拉/弱上拉驱动,会与外部模拟信号"打架",导致 ADC 采样保持电容充放电异常,读回 0 或跳变——这正是您 AD1 偶发 0000 的直接原因。

AD1 用 P1.1,必须设为高阻输入:P1M1 |= 0x02; P1M0 &= ~0x02;

问题 2:STC8H1K08 的 RESFMT 在 B5,不是您以为的"B3/B2"
您注释写"ADCCFG=0x0f 左对齐",但按手册:STC8H1K08 系列

B7        B6        B5        B4        B3        B2        B1        B0
-        -        RESFMT        -        SPEED[3:0]                       
ADCCFG = 0x0f = 0000 1111B,其中 B5(RESFMT)=0 确实是左对齐,这一项您写对了,且 SPEED=1111 → SYSclk/2/16,也符合手册例程。✅ 但注意手册例程给的是 0x2f(RESFMT=1 右对齐, SPEED=1111),
如果左对齐,10位结果在 8位ADC_RESL 里只占高2位(因为 STC8H1K08 是 10 位 ADC,左对齐时 RESL 低2位有效,放在高2位)。

⚠️ 这里要纠正您代码的理解:STC8H1K08 左对齐时是"高8位在 RES,低2位在 RESL",不是低6位。所以:

res = ((unsigned int)hi << 2) | ((unsigned int)lo >> 6);  // ❌ 错误
📋
应该是低 2 位:lo >> 6 取到的是 bit7~bit6 —— 对左对齐10位而言,低2位确实在高2位,>>6 是对的。但若您想更稳妥,建议直接用右对齐(ADCCFG=0x2f),res=(RES<<2)|(RESL>>6) 就废了,右对齐应写成 res = (hi<<8)|lo。

问题 3:ADC 上电后没有等待约 1ms
手册特别强调:

给MCU的内部ADC模块电源打开后,需等待约1ms,等MCU内部的ADC电源稳定后再让ADC工作。

您的 ADCInit() 里 ADC_CONTR = 0x80 | ch 一开电源就立刻转换,未等待。这也是读数漂移的诱因。建议初始化时先开电源,延后约 1ms 再启动转换。

问题 4:数字输入未关闭(省电/抗干扰)
您已写 P1IE &= ~0x02; 关 P1.1 数字输入,这一项是对的 ✅。但要注意 P1IE 在 FE31H(XFR 区),访问前要 P_SW2 |= 0x80,您已做 ✅。

二、修正后的完整代码(STC8H1K08)
/****************************************************
功能:STC8H1K08 ADC 第15通道(内部1.19V) / AD1(P1.1) 交替打印原始10位值
硬件:STC8H1K08-36I-TSSOP20;串口1(P3.0/P3.1) 115200,8,N,1
时钟:内部 11.0592MHz(ISP下载软件设置,代码无需配置)
说明:AD1 用 P1.1,必须配置为高阻输入,并关闭其数字输入
******************************************************/
#include "stc8h.h"
#include "intrins.h"

#define FOSC    11059200UL
#define BRT     (65536 - (FOSC / 115200 + 2) / 4)

bit busy;
bit flag;
bit tx_toggle;              // 0 发 0xA1(AD1), 1 发 0xB2(第15通道)

char buf[8];
unsigned int adcval;

void UartIsr() interrupt 4
{
    if (TI)
    {
        TI = 0;
        busy = 0;
    }
    if (RI)
    {
        RI = 0;
    }
}

void UartInit()
{
    SCON = 0x50;
    TMOD = 0x00;
    TL1 = BRT;
    TH1 = BRT >> 8;
    TR1 = 1;
    AUXR = 0x40;
    busy = 0;
}

void UartSend(char dat)
{
    while (busy);
    busy = 1;
    SBUF = dat;
}

void UartSendStr(char *str)
{
    while (*str != '\0')
    {
        UartSend(*str);
        str++;
    }
}

/* 延时约 ms 毫秒 @11.0592MHz, 简单软件延时 */
void Delay_ms(unsigned int ms)
{
    unsigned int i;
    unsigned char j;
    while (ms--)
    {
        i = 200;
        while (i--)
        {
            j = 250;
            while (j--);
        }
    }
}

void ADCInit()
{
    P_SW2 |= 0x80;              // 使能访问 XFR
    ADCTIM = 0x3f;              // 设置ADC内部时序
    P_SW2 &= 0x7f;

    ADCCFG = 0x0f;              // 左对齐, SYSclk/2/16 (STC8H1K08: RESFMT在B5)
    ADC_CONTR = 0x80 | 0x0f;    // 打开ADC电源, 先选第15通道
    Delay_ms(2);                // 手册要求: ADC电源打开后等约1ms再工作
}

/* 读指定通道一次, 返回10位结果(左对齐: RES高8位 + RESL高2位) */
unsigned int ADCRead(unsigned char ch)
{
    unsigned int res;
    unsigned char hi, lo;

    ADC_CONTR = 0x80 | ch;          // 使能ADC, 选通道ch (0x01=AD1, 0x0F=第15通道)
    ADC_CONTR |= 0x40;              // 启动转换
    _nop_();
    _nop_();
    while (!(ADC_CONTR & 0x20));    // 等待转换完成
    ADC_CONTR &= ~0x20;             // 清完成标志

    hi = ADC_RES;                   // 先读高8位
    lo = ADC_RESL;                  // 再读低位
    res = ((unsigned int)hi << 2) | ((unsigned int)lo >> 6);  // 左对齐10位
    return res;
}

void Timer0_Init(void)              // 0.5ms @11.0592MHz
{
    AUXR |= 0x80;                   // 定时器时钟 1T
    TMOD &= 0xF0;
    TL0 = 0x66;
    TH0 = 0xEA;
    TF0 = 0;
    TR0 = 1;
    ET0 = 1;
    EA  = 1;
}

void main()
{
    P0M0 = 0x00;  P0M1 = 0x00;
    P1M0 = 0x00;  P1M1 = 0x00;
    P2M0 = 0x00;  P2M1 = 0x00;
    P3M0 = 0x00;  P3M1 = 0x00;
    P4M0 = 0x00;  P4M1 = 0x00;
    P5M0 = 0x00;  P5M1 = 0x00;

    /* ---- AD1 = P1.1: 必须设为高阻输入, 并关闭数字输入 ---- */
    P1M1 |= 0x02;                   // P1.1 高阻输入
    P1M0 &= ~0x02;

    P_SW2 |= 0x80;                  // 使能访问 XFR
    P1IE &= ~0x02;                  // 关闭 P1.1 数字输入(做ADC口必须关)
    P_SW2 &= 0x7f;

    tx_toggle = 0;
    ADCInit();
    UartInit();
    Timer0_Init();
    ES = 1;

    while (flag);  flag = 0;
    while (flag);  flag = 0;

    while (1)
    {
        if (flag)
        {
            flag = 0;

            if (tx_toggle == 0)
                adcval = ADCRead(0x01);     // AD1 (P1.1)
            else
                adcval = ADCRead(0x0F);     // 第15通道(内部1.19V)

            buf[0] = (adcval / 1000) + '0';
            buf[1] = (adcval / 100) % 10 + '0';
            buf[2] = (adcval / 10) % 10 + '0';
            buf[3] = adcval % 10 + '0';
            buf[4] = tx_toggle ? 0xB2 : 0xA1;   // A1=AD1, B2=第15通道
            buf[5] = '\r';
            buf[6] = '\n';
            buf[7] = '\0';
            tx_toggle = !tx_toggle;

            UartSendStr(buf);
        }
    }
}

void Timer0_Isr(void) interrupt 1
{
    flag = 1;
}
📋
三、关于您"读 AD1 偶发 0000"的排查建议
按手册依据,逐条核对:

P1.1 高阻输入(本次已修正)——最大嫌疑,务必先改。
ADC 电源稳定等待 ≥1ms(本次已加)。
"两个数据建议丢弃":手册例程注释明确写 //前两个数据建议丢弃。ADC 模块刚使能时前几次转换值不可信,建议在正式打印前预读 2 次并丢弃。
采样时间:手册强调"适当加长对外部信号的采样时间"。您 ADCTIM=0x3f 为 0011 1111B,SMPDUTY=11111(最大32个时钟),CSSETUP/CSHOLD 也为最大档,已较充分;若仍跳,可再确认外部信号源内阻不要过大。
buf[0]=(adcval/1000)+'0':adcval 最大 1023,这个写法没问题;但若真读到 1024(边界)会串位,虽概率极低,可用 %10 保险。
最可能的原因就是第 1 条。 请先改 P1.1 高阻输入 + 加 1ms 等待再测。

如果改完仍有偶发 0,请告诉我:AD1 接的信号内阻多大、是否与 ADC 同一 VCC 供电,我再帮您进一步分析。

信息来源:STC8H 手册「ADC模数转换,内部1.19V参考信号源」章、「I/O口」章的端口模式配置寄存器(PxM0/PxM1)与 I/O 口允许数字信号输入使能控制寄存器(PxIE)说明。
回复

使用道具 举报 送花

12

主题

53

回帖

844

积分

高级会员

积分
844
 楼主| 发表于 2026-9-24 10:25:34 | 显示全部楼层
Ai_St*** 发表于 2026-9-24 09:11
全代码输入检查STCAI,试试
我已查清手册,这里有一个非常关键的严重问题必须先指出,它正是您代码里 AD1  ...

全代码输入检查STCAI,贴过N回了,这版就是AI改过的,看我头部注释,之前是设置成高阻输入的,有问题后加入了通道15的测试,AI将它变更了,我还没注意到。
但这个上拉测其它电压会出问题,但现在我测的是VCC电压,也就是直接用电线焊到VCC脚的,等于是这个内部弱上拉我是直接用0R短接的,但它还是会有偶发的0,所以搞不清楚那里出的问题。
至于另外说的ADC电源没稳定----如果是这个问题,那应该15通道也会出现这种情况,但15通道 不会有0。且也应是前面的数据有问题,不会在后面和中间出现。(ADC打开后,没有代码关闭过),另外丢弃前2次,4次均试过,没什么作用(因为之前是一直只测AD1,一直是5V稳压,所以丢不丢没关系(例程建议丢,是因为如果测的是变化的信号,怕内部电容没充够电出现测量误差,),在AD1输入口加电容是同一道理。
我现在就是在研究,到底是那里导致的原因。同一段测量代码测内部1.19电压正确,基本就排除了AD模块和代码上有太大的问题(如果只是波动我也认了,但这个是0)。
下一步我想布一个全包围的地,加电容,再这样测看它会怎么样。
回复

使用道具 举报 送花

12

主题

53

回帖

844

积分

高级会员

积分
844
 楼主| 发表于 2026-9-24 10:30:13 | 显示全部楼层
erci*** 发表于 2026-9-24 09:10
有人遇到过,串电阻,并小电容,可改善

这样能改善,那就说明AD模块和代码都应没问题,铁定是芯片受到外部干扰了,我就是要找出是怎么受的干扰,最好能复现。这样在设计中才能避免同样错误。
如果能找出规律就完美了
回复

使用道具 举报 送花

已绑定手机

7

主题

445

回帖

6692

积分

论坛元老

积分
6692
发表于 2026-9-24 10:38:43 | 显示全部楼层
有没有原理图、PCB?
建议在靠近MCU的地方对ADC脚加RC滤波,ADC连续采样3次取中位值(排序,去掉最小值、最大值,取位于中间的值)。
信号里有没有方波?或陡峭的边沿?
我曾用STC8H1K08做BLDC的相电压采集,也曾遇到ADC莫名其妙出现0值,加了滤波也未解决,
由于只有一次采样的机会,所以未作中位值处理,最后是换成了STC15W的单片机才行的。

(个人经验:STC15W的ADC是最稳定的,STC8G、STC8H都容易受干扰出现0值,AI8051U、STC32G不太了解)
回复

使用道具 举报 送花

已绑定手机

5

主题

92

回帖

1306

积分

金牌会员

积分
1306
发表于 2026-9-24 10:40:28 | 显示全部楼层
丝瓜*** 发表于 2026-9-24 10:25
全代码输入检查STCAI,贴过N回了,这版就是AI改过的,看我头部注释,之前是设置成高阻输入的,有问题后加 ...

以前用8051U测试,一直很稳定。8H1K没用过,确实要仔细排查,
回复

使用道具 举报 送花

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

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-11-8 01:07 , Processed in 0.081749 second(s), 89 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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