Ai8H单片机ADC采样,定时器触发,DMA的问题求助
我的问题如下:单片机是Ai8H8K64U,工作频率内部时钟11.0592MHz。目标是想通过ADC采样,然后计算获取交流电的有效电压值。
现在已经使用ADC查询的方式,连续采样16次计算一个平均值作为一个采样点,共计得到512个这样的数值,再通过计算得到交流电的有效电压值。
从验证的结果看,这个方式的误差基本上可以保证在2%以内,但是比较耗费单片机的CPU的时间。
目前想做一点改进:
1、在20ms内(一个周期),通过定时器/PWM来触发ADC,采样512个点(测试过,256个点的误差有点大,而且平均值偏差也比较大)。
2、采样的数据点,通过DMA的方式传输到缓存(XDATA的一个数组)。
3、512个点采样结束后,给出一个结束标志位(中断标志,或者是自定义的标志位都可以),然后根据结束标志位启动计算过程。
我自己也曾经尝试过用DMA来传输数据,不过失败了,得到的结果总是一个固定数值。所以才来这里求助。
无论是官方的高手(比如梁工)还是路过的大神,帮我提供一下思路,我去尝试下。
尊敬的用户:
您好!感谢您对STC8H系列单片机的深入使用与探索。针对您提出的问题,我们将从ADC采样、定时器触发、DMA传输及数据处理等方面进行专业分析,并提供可行的解决方案。
一、系统概述
您所使用的单片机为STC8H8K64U,其内部时钟频率为11.0592MHz(注意:您提到的是“110592MHz”,应为11.0592MHz)。目标是通过ADC采集交流电的有效电压值,目前采用查询方式完成16次采样取平均值,共采集512个点,最终计算有效值。当前方法精度满足要求,但CPU占用较高,因此希望优化为定时器触发ADC+DMA传输的方式,以提升效率并减少CPU负担。
二、问题分析与建议
1. 定时器触发ADC采样
您希望在20ms内(即一个工频周期)完成512次采样,这意味着每点间隔约为 39.0625μs。对于STC8H系列单片机而言,若使用内部时钟11.0592MHz,其机器周期为 1/11.0592MHz × 12 = 1.087μs,因此可以配置定时器实现精确的采样间隔。
推荐方式:使用定时器中断或PWM输出作为ADC触发源。
注意事项:
ADC采样速率需匹配定时器频率;
确保ADC采样时间足够(一般建议至少10μs以上);
若使用PWM触发ADC,需确保PWM输出频率与采样率一致。
2. DMA传输ADC数据
您尝试使用DMA传输ADC数据失败,结果固定不变,可能原因如下:
DMA通道未正确配置:ADC数据寄存器地址与目标缓冲区地址未设置正确;
ADC未启用DMA请求:需在ADC控制寄存器中开启DMA使能;
DMA优先级冲突:其他外设可能抢占DMA资源;
缓存区未初始化:XDATA数组未正确分配空间或未清空;
ADC未进入正常工作模式:如未开启ADC、未选择正确的输入通道等。
建议步骤:
1. 配置ADC为连续采样模式,并启用DMA请求;
2. 设置DMA通道,将ADC数据寄存器(如ADCDATA)映射到XDATA数组;
3. 启动ADC和DMA后,等待DMA传输完成标志;
4. 检查DMA状态寄存器,确认是否发生错误或未完成。
3. 采样完成后触发计算
当DMA完成512个数据的传输后,可通过以下方式触发计算:
DMA传输完成中断:在DMA中断服务程序中设置标志位;
DMA传输完成标志位:通过读取DMA状态寄存器判断是否完成;
软件轮询:在主循环中检测标志位,触发计算过程。
建议使用DMA中断方式,避免主循环频繁轮询,提高系统响应性。
三、参考代码结构(伪代码)
c
// 定义ADC与DMA相关变量
define ADCSAMPLECOUNT 512
unsigned int adcbuffer; // XDATA数组
volatile unsigned char dmacompleteflag = 0;
// 初始化ADC
void ADCInit(void) {
ADCEN = 1; // ADC使能
ADCCON1 |= 0x08; // 选择通道0
ADCCON1 |= 0x01; // 选择连续采样模式
ADCCON1 |= 0x40; // 开启DMA请求
}
// 初始化DMA
void DMAInit(void) {
DMACON = 0x00; // 清除DMA配置
DMACON |= 0x01; // 选择DMA通道0
DMASRC0 = (unsigned int)&ADCDATA; // ADC数据寄存器地址
DMADST0 = (unsigned int)adcbuffer; // 缓冲区地址
DMALN0 = ADCSAMPLECOUNT2; // 数据长度(16位)
DMACON |= 0x80; // 启动DMA
}
// 定时器初始化(用于触发ADC)
void TimerInit(void) {
TMOD = 0x01; // 定时器0模式1
TH0 = 0x00;
TL0 = 0x00;
ET0 = 1; // 开启定时器0中断
TR0 = 1; // 启动定时器
}
// 定时器中断服务函数
void Timer0ISR(void) interrupt 1 {
// 触发一次ADC采样(可配合PWM或直接触发)
ADCCON1 |= 0x80; // 触发ADC转换
}
// DMA中断服务函数
void DMAISR(void) interrupt 15 {
if (DMACON & 0x01) { // 判断DMA通道0是否完成
dmacompleteflag = 1;
DMACON &= ~0x01; // 清除DMA标志
}
}
// 主循环
void main(void) {
ADCInit();
DMAInit();
TimerInit();
while(1) {
if (dmacompleteflag) {
// 执行有效值计算
float Vrms = CalculateVrms(adcbuffer, ADCSAMPLECOUNT);
// ...后续处理
dmacompleteflag = 0;
}
}
}
四、常见问题排查建议
| 问题 | 原因 | 解决方案 |
|------|------|----------|
| DMA传输数据始终为0 | ADC未正确配置或未启动 | 检查ADC使能、通道选择、采样模式 |
| DMA未触发 | DMA配置错误或未启用 | 检查DMA通道、源地址、目标地址、长度 |
| 计算误差大 | 采样点不足或采样不准确 | 增加采样点数或调整采样频率 |
| CPU占用高 | 查询方式效率低 | 改用DMA+中断方式 |
五、总结
通过定时器触发ADC与DMA传输数据相结合的方式,可以显著降低CPU负载,提高系统效率。建议按上述步骤进行配置,并结合实际测试不断调试参数。如仍有疑问,欢迎进一步交流。
祝您项目顺利!
——国芯人工智能团队
[本文内容由国芯人工智能辅助生成,仅供参考] 你可以搜搜本站关于交流电采样计算电压、电流有效值的帖子。
我做过不少交流互感器、霍尔互感器(交直流兼容)、直接采样的方式,采样计算交流电压有效值、电流有效值、有功功率、视在功率、功率因数,得益于STC的低噪声、高精度的12位ADC,测量结果非常满意!
ADC采样交流电计算有效值,采样速度为一个周期20ms采样4的倍数次,次数越多越好,但是次数太多会很占CPU时间,2个通道不超过20KHz(一个周期采样400次,可以连续采样4个周期1600次),6个通道不超过10KHz(一个周期采样200次,可以连续采样10个周期1600次)。
如果只测量一路交流电压或电流,则使用PWMA自动触发ADC、ADC中断读取数据并计算平方和,是最优方案。
如果是多路ADC,则使用ADC DMA,但ADC DMA不能设置采样率,所以要用定时器中断来启动ADC DMA。定时器中断设置为最高优先级。
比如使用ADC DMA采样6个通道(三相交流电压、电流),定时器中断触发ADC DMA,DMA中断读取结果。通道安排顺序为:电压1、电流1、电压2、电流2、电压3、电流3,这样同一相电压和电流相位差不超过0.15度。
ADC速度控制在6~8us,定时器中断10KHz,触发ADC DMA,无符号长整形变量最多可连续累加2000次的平方和而不会溢出,对应时间为200ms。
国芯人工智能 发表于 2025-7-18 17:23
尊敬的用户:
您好!感谢您对STC8H系列单片机的深入使用与探索。针对您提出的问题,我们将从ADC采样、定时 ...
非常感谢你的帮助,我按照你的思路去试试。 梁工 发表于 2025-7-18 18:15
你可以搜搜本站关于交流电采样计算电压、电流有效值的帖子。
我做过不少交流互感器、霍尔互感器(交直流兼 ...
好的,也非常感谢你的回复。
页:
[1]