本文最后更新于31 天前,其中的信息可能已经过时,如有错误请发送邮件到tudougin@163.com
1. 12-bit 模/数转换器
ADC 模块可将模拟输入信号转换成 12-bit 的数字信号。ADC 可在不同的时钟速度下运行,并且在高达2MHz 的时钟速度下 (即 100 kHz 的采样率,10 μs/采样) 仍具有 11-bit 精度。
模拟输入信号可选择为 7 个 I/O (ANx) 通道之一、内部运放输出(OP0OUT)或 1 个内部通道 (1/4VDD)。ADC 由指令、I/O (PA4 / PB3) 或 PWM 触发。在触发和 ADC 采样之间可增加延时或前沿消隐(Leading Edge Blanking, LEB)。
2. ADC 转换步骤
2.1 ADC准备
- 设置 ADCEN = 1,打开 ADC 模块时钟。
- 配置端口:
- 设置 TRISx = 1,禁止引脚输出驱动。
- 设置 ANSELAx = 1,关闭数字输入、弱上拉和弱下拉功能。
- 配置 ADC 模块:
- 选择 ADC 转换时钟源(SysClk/N;N = 2, 4, 8, 16, 32, 64 / LIRC 256 kHz 或 32 kHz)。
- 选择 ADC 参考电压(内部参考电压为 0.5V,2.0V,3.0V / VREF+可选 VDD/PA1,VREF−可选 GND/PA0)。
- 选择 ADC 触发条件:软件、PA4/PB3-ADC_ETR 或 PWM,有或无 LEB。
- 选择转换结果格式(左对齐或右对齐)。
- 使能阈值比较(可选)。
- 配置 ADC 中断 (可选):
- 使能 ADC 转换完成中断。
- 使能外设总中断。
- 关闭全局中断(如需执行中断服务程序则使能)。
- 打开 ADC 模块。然后等待所需稳定时间 TST (~15 µs),当 VADC-REF选择内部参考电压时,则需等待内部参考电压的稳定时间 TVRINT (参阅” TVRINT”,章节 16.7) 和 TST时间的较长者,即 max(TVRINT,TST)。
软件触发(ADEX = 0),GO/DONE 由指令置位后立即启动 A/D 转换。如果由 PA4/PB2 或 PWM 触发,则有一定的延迟时间。在使能 LEB 前,需先设置 ADEX 和 ADON 寄存器。如果 LEBEN=1,则将忽略 ETGSEL,触发源即为 LEB 的触发源。此时由 LEB 定时器溢出触发 ADC自动转换。
2.1.1 软件触发
ANSELA=0B00000001; //控制IO的数模输入,1:对应的IO为模拟管脚,0:对应的IO为数字IO,设置AN0为模拟管脚
ADCON0=0B00000000;
//Bit[6:4]:000-选择模拟通道AN0
//Bit2:当软件设定GO/DONE位时启动AD转换(即不用外部触发源)
ADCON1=0B11100100;
//Bit7: 1-ADC转换结果右对齐,即装入转换结果时,ADRESH的高4位被设置为0
//Bit[6:4]:110-ADC转换时钟设置为Fosc/64
//Bit[3:2]:01-负参考电压-GND
//Bit[1:0]:00-正参考电压-内部参考电压
ADCON2=0B01000000;
//Bit[7:6]:01-ADC内部参考电压2V
ADCON3=0B00000000;
ADDLY=0B00000000; //外部触发延时
ADCMPH=0B00000000; //ADC比较阈值,用于ADC结果高8位比较
ADON=1; //使能ADC
DelayUs(200); //打开ADC模块后,需等待ADC稳定时间Tst(~15us);当选择内部参考电压时需等待内部参考电压的稳定时间Tvrint(~450us)
DelayUs(200);
DelayUs(50);
2.1.2 PA4/PB3-ADC_ETR触发
ANSELA=0B00000001; //控制IO的数模输入,1:对应的IO为模拟管脚,0:对应的IO为数字IO,设置AN0为模拟管脚
ADCON0=0B00000100;
//Bit[6:4]:000-选择模拟通道AN0
//Bit2:1-当外部触发源触发时启动AD转换
ADCON1=0B11100100;
//Bit7: 1-ADC转换结果右对齐,即装入转换结果时,ADRESH的高4位被设置为0
//Bit[6:4]:110-ADC转换时钟设置为Fosc/64
//Bit[3:2]:01-负参考电压-GND
//Bit[1:0]:00-正参考电压-内部参考电压
ADCON2=0B01010111;
//Bit[7:6]:01-ADC内部参考电压2V
//Bit[5:4]:01-上升沿触发
//Bit[3]:00-LEB计数器第8位
//Bit[2:0]:111-触发源选ADC_ETR
ADCON3=0B00000000;
//Bit[3]:0-禁止LEB结束时触发ADC转换
LEBCON = 0B00000000;
//Bit[7]:0-禁止LEB
//Bit[6:5]:00-LEB信号源,TIM1_CH1
//Bit[3]:0-上升沿触发LEB
ADDLY=0B00000000; //外部触发延时
ADCMPH=0B00000000; //ADC比较阈值,用于ADC结果高8位比较
ADON=1; //使能ADC
DelayUs(200); //打开ADC模块后,需等待ADC稳定时间Tst(~15us);当选择内部参考电压时需等待内部参考电压的稳定时间Tvrint(~450us)
DelayUs(200);
DelayUs(50);
2.1.3 定时器触发
ANSELA=0B00000001; //控制IO的数模输入,1:对应的IO为模拟管脚,0:对应的IO为数字IO,设置AN0为模拟管脚
ADCON0=0B00000100;
//Bit[6:4]:000-选择模拟通道AN0
//Bit2:1-当外部触发源触发时启动AD转换
ADCON1=0B11100100;
//Bit7: 1-ADC转换结果右对齐,即装入转换结果时,ADRESH的高4位被设置为0
//Bit[6:4]:110-ADC转换时钟设置为Fosc/64
//Bit[3:2]:01-负参考电压-GND
//Bit[1:0]:00-正参考电压-内部参考电压
ADCON2= 0B01000000;//0B01010000;
//Bit[7:6]:01-ADC内部参考电压2V
//Bit[5:4]:01-上升沿触发
//Bit[3]:00-LEB计数器第8位
//Bit[2:0]:000-触发源选TIM1_CH1
ADCON3=0B00001000;
//Bit[3]:1 LEB结束时触发ADC转换
LEBCON = 0B10000000;
//Bit[7]:0-使能LEB
//Bit[6:5]:00-LEB信号源,TIM1_CH1
//Bit[3]:0-上升沿触发LEB
ADDLY=0B00000000; //外部触发延时
ADCMPH=0B00000000; //ADC比较阈值,用于ADC结果高8位比较
ADON=1; //使能ADC
DelayUs(200); //打开ADC模块后,需等待ADC稳定时间Tst(~15us);当选择内部参考电压时需等待内部参考电压的稳定时间Tvrint(~450us)
DelayUs(200);
DelayUs(50);
TCKSRC = 0B00000000; //系统时钟
PCKEN |= 0B00000010; //打开TIMER1时钟
TIM1CR1 = 0B00000000; //向上计数
TIM1CR2 = 0B00000000;
TIM1SMCR = 0B00000000;
TIM1ETR = 0B00000000;
TIM1IER = 0B00000000;
TIM1CCMR1 = 0B01100000; //PWM1模式
TIM1CCER1 = 0B00000001; //OC1输出到相应引脚,高电平有效
TIM1CNTRL = 0;
TIM1CNTRH = 0;
TIM1PSCRL = 31; //预分频L
TIM1PSCRH = 0; //预分频H
TIM1ARRL = 255;
TIM1ARRH = 0; //周期寄存器
TIM1CCR1L = 128;
TIM1CCR1H = 0; //占空比寄存器
TIM1CR1 |= 0B00000001;//使能计数器
TIM1EGR = 0B00000001; //产生更新事件
TIM1BKR = 0B10000000;//pwm输出使能
2.2 读取通道ADC值
uint GET_ADC_DATA(uchar adcChannel)
{
ADCON0&=0B00001111;
ADCON0|=adcChannel<<4;
DelayUs(2); //TACQ延时2us,外部串联电阻小于21kΩ
//TACQ延时4us,外部串联电阻43kΩ
//TACQ时间:必做,通道切换到GO/DONE置1的时间,保证内部 ADC 输入电容充满。
//TACQ > 0.09*(R+1)us;R为外部串联电阻(kΩ),串联电阻越小越好,最大不要超过50kΩ
// ADCIF = 1;
GO = 1; //软件读取时,GO置1,芯片立即读取;外部触发(PA4/PB3-ADC_ETR 或 PWM)时只能依靠ADCIF标志位判断,这里换成ADCIF置1清零
NOP(); //采样保持时间0~1TAD
NOP();
//DelayUs(12); //采样保持时间(ADDLY +6) *TAD
// while(ADCIF == 0); //ADC 转换完成标志位
while(GO);
//从GO = 1 ---> 从GO = 0,转换过程需要16TAD
//TAD(us)与转换时钟Fosc/ADCS[2:0]有关
return (uint)(ADRESH<<8|ADRESL);
}
3. 示例代码
ADC配置为软件触发模式,正参考电压源为内部基准电压(VADC_REF 2V),以确保稳定的采样基准。系统通过轮询方式监测按键输入状态,每次检测到按键被有效按下时,触发 ADC 启动转换流程。转换完成后,读取 ADC 结果并进行串口输出显示。
void main(void){
DelayMs(10);
sysInit();
gpioInit();
adcInit();
usartInit();
DelayMs(100);
while (1) {
uint32_t milliVolt = 0;
uint16_t adcValuex = 0;
if (DemoPortIn == 0) { // 检测按键按下
DelayMs(10); // 消抖处理
if (DemoPortIn == 0) {
// 等待按键释放
while (DemoPortIn == 0);
milliVolt=(uint32_t)adcValuex*2000/4096;
DemoPortOut = ~DemoPortOut;
}
uint8_t Str[50] = {0};
sprintf(Str,"当前引脚电压为%d mV\r\n",milliVolt);
UART_SendString(Str);
}
DelayMs(50); // 稍作延时避免过度轮询
}
}
uint16_t GET_ADC_DATA(uint8_t adcChannel){
ADCON0&=0B00001111;
ADCON0|=adcChannel<<4;
DelayUs(4); //TACQ延时2us,外部串联电阻小于21kΩ
GO = 1;
NOP();
NOP();
while(GO);
return (uint16_t)(ADRESH<<8|ADRESL);
}
void adcInit(void){
ANSELA=0B00011000; //控制IO的数模输入,1:对应的IO为模拟管脚,0:对应的IO为数字IO,设置AN0为模拟管脚
ADCON0=0B00000000;
//Bit2:当软件设定GO/DONE位时启动AD转换(即不用外部触发源)
ADCON1=0B11100100;
//Bit7: 1-ADC转换结果右对齐,即装入转换结果时,ADRESH的高4位被设置为0
//Bit[6:4]:110-ADC转换时钟设置为Fosc/64
//Bit[3:2]:01-负参考电压-GND
//Bit[1:0]:01-正参考电压-内部 VADC-REF
ADCON2=0B01000000;
ADCON3=0B00000000;
ADDLY=0B00000000; //外部触发延时
ADCMPH=0B00000000; //ADC比较阈值,用于ADC结果高8位比较
ADON=1; //使能ADC
DelayUs(200); //打开ADC模块后,需等待ADC稳定时间Tst(~15us);当选择内部参考电压时需等待内部参考电压的稳定时间Tvrint(~450us)
DelayUs(200);
DelayUs(50);
}