在之前的帖子分享了AI8051在串口通讯中,使用DMA的方式进行数据的交互,今天和大家分享使用DMA的方式实现ADC的数据读取。
使用AI8051U的DMA进行ADC数据采集,可以极大地减轻CPU的负担,实现高效、自动化的多通道模拟信号采集。下面我将为你详细解析其核心原理、关键寄存器配置,并提供实践步骤和代码示例。
一:核心原理:数据的“自动化处理”功能
类似于其他的32位单片机一样,DMA方式类似于一个智能的数据搬运工一样,可以将数据从内核到外设,或者是外设到内核的之间的数据交互,具体的工作流程如下所示:
1:启动DMA功能,单片机需要告知DMA控制器,此时立即开始工作,这个时候单片机就可以去处理其他任务了。
2:数据转换:ADC的通道按照初始化配置的时序,通道,自动对指定的模拟通道进行采集,转换等。
3:数据搬运:当ADC转换完成后,数据会被DMA控制器主动的放到内存(XDATA)中的配置的缓冲区内,此过程不经过mcu的干预。
4:产生中断:当DMA传输的数据个数到达设定次数的时候,DMA控制器会产生中断,告知CPU,数据接收完成可以进行处理
二:配置参数
2.1 传输次数与循环模式
你需要告诉DMA总共要传输多少次ADC结果。这通过配置 DMA_ADC_AMT 寄存器来实现。例如,如果你想采集8个通道,每个通道转换4次,那么总传输次数就是32次。
此外,你还需要决定传输完成后的行为。如果设置为单次模式,传输32次后DMA就会停止;如果设置为循环模式,DMA会在完成32次传输后,自动从头开始新一轮的采集,周而复始。
2.2 转换间隔时间
这是AI8051U DMA-ADC功能的一个亮点。你可以通过 DMA_ADC_ITVH 和 DMA_ADC_ITVL 寄存器来设置两次ADC转换启动之间的间隔时间。这个功能让你能够精确控制采样率,而无需依赖定时器中断。
2.3 多通道与多次采样
你可以设置DMA依次扫描多个模拟通道。通过配置 DMA_ADC_CFG2 等寄存器,可以指定每个通道连续转换的次数。这使得“一轮”采集可以包含对多个通道的多次采样,非常灵活。
三:软件代码:
3.1 初始化系统与IO口
设置系统时钟,使能访问XFR(扩展特殊功能寄存器)的开关(如 P_SW2 |= 0x80)。
将用作ADC输入的IO口(如P1.0)设置为高阻输入模式。
#define ADC_SPEED 15 /* 0~15, ADC转换时间(CPU时钟数) = (n+1)*32 ADCCFG */ #define RES_FMT (1<<5) /* ADC结果格式 0: 左对齐, ADC_RES: D11 D10 D9 D8 D7 D6 D5 D4, ADC_RESL: D3 D2 D1 D0 0 0 0 0 */ /* ADCCFG 1: 右对齐, ADC_RES: 0 0 0 0 D11 D10 D9 D8, ADC_RESL: D7 D6 D5 D4 D3 D2 D1 D0 */ #define ADC_CH 16 /* ADC转换通道数 */ #define ADC_TIMES 4 /* 每个通道ADC转换数据次数 */
3.2 配置DMA功能
设置源地址:将DMA的外设地址指向ADC的结果寄存器(ADC_RES 和 ADC_RESL)。
设置目的地址:在XDATA空间定义一个数组作为缓冲区,并将DMA的目的地址设置为该数组的首地址。
设置传输计数:向 DMA_ADC_AMT 寄存器写入你要传输的总次数。例如,要采集8通道各4次,共32次。
设置间隔时间:根据需要,向 DMA_ADC_ITVH/L 寄存器写入值,以控制采样率。
配置工作模式:设置DMA为循环模式或单次模式,并使能DMA传输完成中断(可选)。
void DMA_Config(void)
{
DMA_ADC_STA = 0x00;
DMA_ADC_CFG = 0x80; //bit7 1:Enable Interrupt
DMA_ADC_RXAH = (u8)((u16)&DmaBuffer >> 8); //ADC转换数据存储地址
DMA_ADC_RXAL = (u8)((u16)&DmaBuffer);
DMA_ADC_CFG2 = CVTIMESEL; //每个通道ADC转换次数
DMA_ADC_CHSW0 = 0xff; //ADC通道使能寄存器 ADC7~ADC0
DMA_ADC_CHSW1 = 0xff; //ADC通道使能寄存器 ADC15~ADC8
DMA_ADC_CR = 0xc0; //bit7 1:Enable ADC_DMA, bit6 1:Start ADC_DMA
}3.3 启动DMA中断服务函数
启动DMA传输。通常是通过向DMA控制寄存器的“启动”位写入1来完成。
编写DMA传输完成中断服务函数。在函数中,清除中断标志,并对缓冲区中的ADC数据进行处理(如取平均值、转换电压值等)。
void ADC_DMA_Interrupt(void) interrupt 13
{
DMA_ADC_STA = 0;
DmaFlag = 1;
}3.4 主函数:
实现ADC通道的数据上传功能:
if(DmaFlag) //判断ADC DMA是否转换完成
{
DmaFlag = 0; //清除ADC DMA转换完成标志
for(i=0; i<ADC_CH; i++)
{
printf("ADC%02d = ",DmaBuffer[i].Channel); //串口打印ADC通道
for(n=0; n<ADC_TIMES; n++)
{
printf("0x%04x ",DmaBuffer[i].Data[n]); //串口打印当前通道的每次采样数据
}
printf(", AVER:0x%04x\r\n",DmaBuffer[i].average); //串口打印平均值
}
DMA_ADC_CR = 0xc0; //bit7 1:Enable ADC_DMA, bit6 1:Start ADC_DMA
}四:测试结果如下:

可见,16个通道的ADC数据全部上传到串口了,
我要赚赏金
