LeoringYang 发表于 6 天前

ADC模数转换开发不解之处

官文基本示例C语言的,我编译运行正常,最后发现只需4行就能触发中断:


ADC_POWER =1; //使能ADC模块
EADC = 1;//使能ADC中断
EA = 1;
ADC_START = 1; //启动AD转换


我对应这四行加上中断,翻译成汇编还是无法触发中断,这两天身心疲惫
我再死磕两天,要是还没有结果,我得给自个放假。

晓飛飛 发表于 6 天前

再死磕几天的时间足够掌握C语言了,对于单片机这种简单的代码来说,无论C还是汇编最终都是对寄存器的操作,流程原理都是一样的。

lcwswust 发表于 6 天前

哦,看来是编译器的问题了。那得对比一下KEIL生成的机器码和你这编译器的机器码。

wulin 发表于 6 天前

官文示例有汇编的


神农鼎 发表于 6 天前

STC-ISP最新版软件,AiCube-ISP V6.96Z版内置:
STC 【AiCube】:图形化配置各功能模块外设,自动生成程序;
STC 【AI 助手】:按输入的要求,自动生成程序,
或排查您程序的错误。

STC 【AI 助手】,已加载 STC全系列MCU专业技术手册。
help.stcaimcu.com
可以帮您:
- 回答STC技术手册中的技术问题
- 自动生成您需要的通用MCU程序
- 自动高效排查您程序中的错误

所以只要有 C语言的基础,STC的MCU,你其实已经算能用了,
再用到哪,研究到哪,
让 STC 【AI 助手】帮你研究总结



神农鼎 发表于 6 天前



贴如下内容到   STC 【AI助手】,help.stcaimcu.com



STC8H1K08-TSSOP20, 工作在24MHz, 串口波特率 9600,串口在P3.0/P3.1;
收到串口命令:50H后,对ADC0进行ADC转换,将转换结果串口送出;
收到串口命令:51H后,对ADC1进行ADC转换,将转换结果串口送出;




神农鼎 发表于 6 天前

<p>好的,信息已获取完毕。现在我来编写完整的代码。</p>
<p>根据手册:</p>
<ul>
<li><strong>STC8H1K08 TSSOP20</strong> 的 P3.0/P3.1 默认就是串口1的 RXD/TXD</li>
<li><strong>ADC0</strong> 对应 P1.0,<strong>ADC1</strong> 对应 P1.1</li>
<li>串口1使用定时器1模式2(8位自动重载),1T模式,SMOD=1,24MHz下9600波特率:重载值 = 256 - (2×24000000)/(32×9600) = 256 - 156.25 ≈ 100 (0x64)</li>
<li>ADC结果格式使用右对齐(RESFMT=1),10位结果:ADC_RES高2位,ADC_RESL低8位</li>
</ul>
<p>以下是完整代码:</p>
<pre><code class="language-c">/******************************************************************************
* 文件名    : main.c
* 单片机    : STC8H1K08 (TSSOP20)
* 晶振      : 内部IRC 24MHz
* 功能说明:
*   串口1(P3.0/P3.1) 波特率9600
*   收到命令 0x50 -&gt; 对ADC0(P1.0)进行ADC转换,结果串口送出
*   收到命令 0x51 -&gt; 对ADC1(P1.1)进行ADC转换,结果串口送出
*   结果格式: &quot;ADC0:1023\r\n&quot; 或 &quot;ADC1:1023\r\n&quot;
*
* 接线说明:
*   P3.0(RxD) - 外部串口工具TXD
*   P3.1(TxD) - 外部串口工具RxD
*   P1.0(ADC0) - 模拟输入通道0
*   P1.1(ADC1) - 模拟输入通道1
*
* 注意事项:
*   1. ADC输入通道对应的I/O口必须设置为高阻输入模式
*   2. ADC上电后需等待约1ms再启动转换
*   3. 使用Keil C51编译,选择STC8H系列芯片
******************************************************************************/

#include &quot;STC8H.h&quot;
#include &lt;stdio.h&gt;

#define FOSC      24000000UL      // 系统主时钟 24MHz
#define BRT         9600            // 串口波特率

// 定时器1重载值计算 (模式2, 1T, SMOD=1)
// 波特率 = (2^SMOD * SYSclk) / (32 * (256 - 重载值))
// 重载值 = 256 - (2 * SYSclk) / (32 * 波特率)
#define T1RL_VALUE(256 - (unsigned char)((2UL * FOSC) / (32UL * BRT)))

// 串口发送缓冲区
unsigned char tx_buf;

// 函数声明
void UART1_Init(void);
void UART1_SendByte(unsigned char dat);
void UART1_SendString(unsigned char *str);
void ADC_Init(void);
unsigned int ADC_Convert(unsigned char channel);
void Delay1ms(void);
void DelayXms(unsigned int ms);

/**********************************************
* 主函数
**********************************************/
void main(void)
{
    unsigned char cmd;
    unsigned int adc_value;

    // 设置内部IRC频率为24MHz (出厂时已校准,此处仅做示例)
    // 实际使用中若已通过ISP下载设置好频率,可省略此步

    // 初始化串口1
    UART1_Init();

    // 初始化ADC
    ADC_Init();

    // 打开总中断
    EA = 1;

    // 发送启动提示
    UART1_SendString(&quot;STC8H1K08 ADC Test Ready\r\n&quot;);

    while (1)
    {
      // 查询方式接收串口数据
      if (RI)                     // 接收到数据
      {
            RI = 0;               // 清除接收中断标志
            cmd = SBUF;             // 读取接收到的数据

            if (cmd == 0x50)      // 命令0x50: 转换ADC0
            {
                adc_value = ADC_Convert(0);   // ADC0通道
                sprintf((char *)tx_buf, &quot;ADC0:%u\r\n&quot;, adc_value);
                UART1_SendString(tx_buf);
            }
            else if (cmd == 0x51)   // 命令0x51: 转换ADC1
            {
                adc_value = ADC_Convert(1);   // ADC1通道
                sprintf((char *)tx_buf, &quot;ADC1:%u\r\n&quot;, adc_value);
                UART1_SendString(tx_buf);
            }
      }
    }
}

/**********************************************
* 串口1初始化
* 模式1(8位UART, 可变波特率)
* 定时器1模式2(8位自动重载)作为波特率发生器
* 1T模式, SMOD=1
**********************************************/
void UART1_Init(void)
{
    // 设置定时器1为波特率发生器
    // 定时器1模式2: 8位自动重装载
    TMOD &amp;= 0x0F;                   // 清除T1的M1M0位
    TMOD |= 0x20;                   // 设置T1为模式2 (8位自动重载)

    // 设置定时器1为1T模式 (12T/1T选择)
    // AUXR寄存器的T1x12位: 0=12T, 1=1T
    AUXR |= 0x40;                   // T1x12 = 1, 1T模式

    // 设置SMOD = 1 (波特率加倍)
    PCON |= 0x80;                   // SMOD = 1

    // 设置定时器1重载值
    TH1 = T1RL_VALUE;
    TL1 = T1RL_VALUE;

    // 启动定时器1
    TR1 = 1;                        // T1_RUN = 1

    // 配置串口1
    SCON = 0x50;                  // SM0=0, SM1=1 (模式1), REN=1 (允许接收)
    // SM2=0, TB8=0, RB8=0, TI=0, RI=0

    // 使能串口1中断 (本程序使用查询方式,可不使能)
    // ES = 1;
}

/**********************************************
* 串口1发送一个字节
**********************************************/
void UART1_SendByte(unsigned char dat)
{
    SBUF = dat;                     // 写入发送缓冲区
    while (!TI);                  // 等待发送完成
    TI = 0;                         // 清除发送中断标志
}

/**********************************************
* 串口1发送字符串
**********************************************/
void UART1_SendString(unsigned char *str)
{
    while (*str != '\0')
    {
      UART1_SendByte(*str++);
    }
}

/**********************************************
* ADC初始化
* 使用ADC_VRef+引脚作为参考电压 (接VCC)
* 结果右对齐: ADC_RES + ADC_RESL = 10位
* ADC时钟 = SYSclk / 2 / (SPEED+1)
* 设置SPEED=15, ADC时钟 = 24MHz/2/16 = 750kHz
**********************************************/
void ADC_Init(void)
{
    // 开启ADC电源
    ADC_CONTR = 0x80;               // ADC_POWER = 1

    // 配置ADC结果格式和时钟
    // RESFMT = 1 (右对齐), SPEED = 15 (ADC时钟约750kHz)
    ADCCFG = 0x2F;                  // 0x2F = 0010 1111
                                    // bit5: RESFMT=1 (右对齐)
                                    // bit3-0: SPEED=1111

    // 配置ADC时序 (使用默认值即可)
    // ADCTIM 默认: CSSETUP=0, CSHOLD=01, SMPDUTY=01010
    // 如需调整可修改:
    // P_SW2 |= 0x80;               // 使能访问XFR
    // ADCTIM = 0x2A;               // 默认值

    // 等待ADC电源稳定 (约1ms)
    DelayXms(1);
}

/**********************************************
* ADC转换函数
* channel: ADC通道号 (0~15)
* 返回: 10位ADC转换结果 (0~1023)
**********************************************/
unsigned int ADC_Convert(unsigned char channel)
{
    unsigned int result;

    // 设置ADC输入通道对应的I/O口为高阻输入模式
    // ADC0 -&gt; P1.0, ADC1 -&gt; P1.1
    if (channel == 0)
    {
      P1M0 &amp;= ~0x01;            // P1.0: M0=0
      P1M1 |= 0x01;               // P1.1: M1=1 -&gt; 高阻输入
    }
    else if (channel == 1)
    {
      P1M0 &amp;= ~0x02;            // P1.1: M0=0
      P1M1 |= 0x02;               // P1.1: M1=1 -&gt; 高阻输入
    }

    // 选择ADC通道
    ADC_CONTR &amp;= 0xF0;            // 清除ADC_CHS位
    ADC_CONTR |= (channel &amp; 0x0F);// 设置通道号

    // 启动ADC转换
    ADC_CONTR |= 0x40;            // ADC_START = 1

    // 等待转换完成
    while (!(ADC_CONTR &amp; 0x20));    // 等待ADC_FLAG置1

    // 清除ADC转换完成标志
    ADC_CONTR &amp;= ~0x20;             // ADC_FLAG = 0

    // 读取转换结果 (右对齐模式)
    // ADC_RES 为高2位, ADC_RESL 为低8位
    result = ((unsigned int)ADC_RES &lt;&lt; 8) | ADC_RESL;

    return result;
}

/**********************************************
* 延时1ms (软件延时, 24MHz下粗略延时)
**********************************************/
void Delay1ms(void)
{
    unsigned char i, j;

    i = 32;
    j = 40;
    do
    {
      while (--j);
    } while (--i);
}

/**********************************************
* 延时N毫秒
**********************************************/
void DelayXms(unsigned int ms)
{
    unsigned int i;
    for (i = 0; i &lt; ms; i++)
    {
      Delay1ms();
    }
}
</code></pre>
<hr />
<h2>代码说明</h2>
<h3>1. 串口配置</h3>
<ul>
<li>
<p><strong>串口1</strong> 工作于 <strong>模式1</strong>(8位UART,可变波特率)</p>
</li>
<li>
<p><strong>定时器1</strong> 工作于 <strong>模式2</strong>(8位自动重装载),<strong>1T模式</strong></p>
</li>
<li>
<p><strong>SMOD=1</strong>(波特率加倍),波特率计算公式:</p>
<div class="language-math">波特率 = \frac{2^{SMOD} \times SYSclk}{32 \times (256 - 重载值)}</div>
<p>代入 <span class="language-math">​SYSclk=24MHz</span>,<span class="language-math">​波特率=9600</span>,得重载值 ≈ 100(0x64)</p>
</li>
</ul>
<h3>2. ADC配置</h3>
<ul>
<li><strong>ADC0</strong> 对应 <strong>P1.0</strong>,<strong>ADC1</strong> 对应 <strong>P1.1</strong>(STC8H1K08系列)</li>
<li>ADC结果格式:<strong>右对齐</strong>(RESFMT=1),<code>ADC_RES</code> 为高2位,<code>ADC_RESL</code> 为低8位,组合为10位结果(0~1023)</li>
<li>ADC时钟:<span class="language-math">​F_{ADC} = SYSclk / 2 / (SPEED+1) = 24MHz / 2 / 16 = 750kHz</span></li>
<li>ADC输入通道对应的I/O口必须设置为 <strong>高阻输入模式</strong></li>
</ul>
<h3>3. 命令处理</h3>
<ul>
<li>收到 <strong>0x50</strong> → 选择ADC0通道 → 启动转换 → 发送 <code>&quot;ADC0:xxx\r\n&quot;</code></li>
<li>收到 <strong>0x51</strong> → 选择ADC1通道 → 启动转换 → 发送 <code>&quot;ADC1:xxx\r\n&quot;</code></li>
</ul>
<h3>4. 使用说明</h3>
<ol>
<li>使用 <strong>Keil C51</strong> 编译,芯片选择 <strong>STC8H1K08</strong> 系列</li>
<li>通过 <strong>STC-ISP</strong> 下载软件,选择内部IRC时钟 <strong>24MHz</strong></li>
<li>P1.0接模拟信号(0~VCC),P1.1接另一路模拟信号</li>
<li>串口助手发送 <strong>0x50</strong> 或 <strong>0x51</strong>(十六进制发送),即可收到ADC转换结果</li>
</ol>
<hr />
<p><a href="https://www.stcaimcu.com/plugin.php?id=twpx_xunsearch&amp;mod=forum&amp;q=STC8H1K08-TSSOP20%20%E5%B7%A5%E4%BD%9C%E5%9C%A824MHz%20%E4%B8%B2%E5%8F%A3%E6%B3%A2%E7%89%B9%E7%8E%87%209600&amp;f=_all&amp;m=yes&amp;syn=yes&amp;s=relevance&amp;fid=0">🔍 搜索 &quot;STC8H1K08-TSSOP20 工作在24MHz 串口波特率 9600&quot;</a></p>
<p>⏱️ 查找用时 7.0s | 总回答用时 34.2s</p>

梁工 发表于 6 天前

你用C语言编译,再看汇编。
ADC中断读取结果是基本操作,大量使用,不需查询等待,可以节省时间。

LeoringYang 发表于 6 天前

多谢各位提出的意见和方案。经过一下午的折腾,现在确认ADC的确产生中断,也运行到了中断程序。但它触发的频率上百k,导致用LED根本看不出来;另外出现一个特神奇的现象,在定时器中P6=P6 ^ 0x01,不知道是硬件故障还是什么原因,P6的值会一直为0x00,P6=P6 (+)(-)(|)(&) 0x01都是正常的。这两个问题困了我一天。P6=P6 ^ 0x01这个需要仿真调试,用keil调试不出来。
估计你们可能会疑惑,为什么就我会出现这些奇怪的问题?这与我所做的事有关。我现在的做的事并不是STC32G12K128上的项目功能开发,我在做YxraSDK工具与STC32G12K128图形化对接。YxraSDK是建立在Yxra语言上的,而Yxra语言面向对象,不依赖任何第三方。我的目标是做一个比keil更方便快捷,且不需要C语言的线国产化的平台。
感谢各位!
页: [1]
查看完整版本: ADC模数转换开发不解之处