ADC模数转换开发不解之处
官文基本示例C语言的,我编译运行正常,最后发现只需4行就能触发中断:ADC_POWER =1; //使能ADC模块
EADC = 1;//使能ADC中断
EA = 1;
ADC_START = 1; //启动AD转换
我对应这四行加上中断,翻译成汇编还是无法触发中断,这两天身心疲惫
我再死磕两天,要是还没有结果,我得给自个放假。
再死磕几天的时间足够掌握C语言了,对于单片机这种简单的代码来说,无论C还是汇编最终都是对寄存器的操作,流程原理都是一样的。 哦,看来是编译器的问题了。那得对比一下KEIL生成的机器码和你这编译器的机器码。 官文示例有汇编的
STC-ISP最新版软件,AiCube-ISP V6.96Z版内置:
STC 【AiCube】:图形化配置各功能模块外设,自动生成程序;
STC 【AI 助手】:按输入的要求,自动生成程序,
或排查您程序的错误。
STC 【AI 助手】,已加载 STC全系列MCU专业技术手册。
help.stcaimcu.com
可以帮您:
- 回答STC技术手册中的技术问题
- 自动生成您需要的通用MCU程序
- 自动高效排查您程序中的错误
所以只要有 C语言的基础,STC的MCU,你其实已经算能用了,
再用到哪,研究到哪,
让 STC 【AI 助手】帮你研究总结
贴如下内容到 STC 【AI助手】,help.stcaimcu.com
STC8H1K08-TSSOP20, 工作在24MHz, 串口波特率 9600,串口在P3.0/P3.1;
收到串口命令:50H后,对ADC0进行ADC转换,将转换结果串口送出;
收到串口命令:51H后,对ADC1进行ADC转换,将转换结果串口送出;
<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 -> 对ADC0(P1.0)进行ADC转换,结果串口送出
* 收到命令 0x51 -> 对ADC1(P1.1)进行ADC转换,结果串口送出
* 结果格式: "ADC0:1023\r\n" 或 "ADC1:1023\r\n"
*
* 接线说明:
* 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 "STC8H.h"
#include <stdio.h>
#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("STC8H1K08 ADC Test Ready\r\n");
while (1)
{
// 查询方式接收串口数据
if (RI) // 接收到数据
{
RI = 0; // 清除接收中断标志
cmd = SBUF; // 读取接收到的数据
if (cmd == 0x50) // 命令0x50: 转换ADC0
{
adc_value = ADC_Convert(0); // ADC0通道
sprintf((char *)tx_buf, "ADC0:%u\r\n", adc_value);
UART1_SendString(tx_buf);
}
else if (cmd == 0x51) // 命令0x51: 转换ADC1
{
adc_value = ADC_Convert(1); // ADC1通道
sprintf((char *)tx_buf, "ADC1:%u\r\n", 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 &= 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 -> P1.0, ADC1 -> P1.1
if (channel == 0)
{
P1M0 &= ~0x01; // P1.0: M0=0
P1M1 |= 0x01; // P1.1: M1=1 -> 高阻输入
}
else if (channel == 1)
{
P1M0 &= ~0x02; // P1.1: M0=0
P1M1 |= 0x02; // P1.1: M1=1 -> 高阻输入
}
// 选择ADC通道
ADC_CONTR &= 0xF0; // 清除ADC_CHS位
ADC_CONTR |= (channel & 0x0F);// 设置通道号
// 启动ADC转换
ADC_CONTR |= 0x40; // ADC_START = 1
// 等待转换完成
while (!(ADC_CONTR & 0x20)); // 等待ADC_FLAG置1
// 清除ADC转换完成标志
ADC_CONTR &= ~0x20; // ADC_FLAG = 0
// 读取转换结果 (右对齐模式)
// ADC_RES 为高2位, ADC_RESL 为低8位
result = ((unsigned int)ADC_RES << 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 < 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>"ADC0:xxx\r\n"</code></li>
<li>收到 <strong>0x51</strong> → 选择ADC1通道 → 启动转换 → 发送 <code>"ADC1:xxx\r\n"</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&mod=forum&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&f=_all&m=yes&syn=yes&s=relevance&fid=0">🔍 搜索 "STC8H1K08-TSSOP20 工作在24MHz 串口波特率 9600"</a></p>
<p>⏱️ 查找用时 7.0s | 总回答用时 34.2s</p>
你用C语言编译,再看汇编。
ADC中断读取结果是基本操作,大量使用,不需查询等待,可以节省时间。 多谢各位提出的意见和方案。经过一下午的折腾,现在确认ADC的确产生中断,也运行到了中断程序。但它触发的频率上百k,导致用LED根本看不出来;另外出现一个特神奇的现象,在定时器中P6=P6 ^ 0x01,不知道是硬件故障还是什么原因,P6的值会一直为0x00,P6=P6 (+)(-)(|)(&) 0x01都是正常的。这两个问题困了我一天。P6=P6 ^ 0x01这个需要仿真调试,用keil调试不出来。
估计你们可能会疑惑,为什么就我会出现这些奇怪的问题?这与我所做的事有关。我现在的做的事并不是STC32G12K128上的项目功能开发,我在做YxraSDK工具与STC32G12K128图形化对接。YxraSDK是建立在Yxra语言上的,而Yxra语言面向对象,不依赖任何第三方。我的目标是做一个比keil更方便快捷,且不需要C语言的线国产化的平台。
感谢各位!
页:
[1]