在嵌入式系统开发中,ADC(模数转换器) 是一个非常常见且重要的功能。它可以将模拟信号转换为数字信号,从而使得微控制器能够处理来自外部传感器等设备的模拟数据。在本文中,我们将使用PIC16F1719微控制器,并详细介绍如何配置和使用其内置的ADC模块来读取模拟信号。
ADC(Analog-to-Digital Converter,模数转换器) 是一种将模拟信号转换为数字信号的设备。模拟信号是连续的电压信号,而数字信号则是离散的,通常为0和1组成的二进制数据。在许多应用中,传感器产生的信号往往是模拟信号,因此使用ADC可以将这些信号转换为微控制器可以处理的数字形式。
在这个示例中,我们将使用PIC16F1719的ADC模块,通过读取A0引脚上的模拟电压信号,将其转换为数字值并存储在变量中。通过循环采样,每秒更新一次转换结果。
在使用ADC之前,首先需要了解PIC16F1719的ADC模块支持的输入电压范围。在这个示例中,ADC的参考电压Vref+是Vdd,即微控制器的供电电压,Vref-为接地电压Vss。这意味着ADC可以将0V到Vdd之间的模拟信号转换为数字值。
PIC16F1719的ADC模块有多个可配置的选项,比如参考电压、输入通道、转换时钟和结果的对齐方式等。下面我们逐步讲解如何进行配置。
以下是配置ADC并进行模数转换的示例代码:
c#include <xc.h>
#define _XTAL_FREQ 32000000 // 设置系统时钟为32MHz
void main(void) {
// 系统初始化
OSCCON = 0xF0; // 设置内部振荡器为8MHz,并启用4倍PLL,系统时钟达到32MHz
ANSELA = 0x00; // 将A端口全部设置为数字模式
ANSELAbits.ANSA0 = 1; // 将A0设置为模拟输入
TRISAbits.TRISA0 = 1; // 将A0引脚设置为输入
// ADC配置
ADCON1bits.ADCS = 0b010; // 设置ADC时钟为Fosc/32
ADCON1bits.ADFM = 1; // 设置结果右对齐
ADCON1bits.ADNREF = 0; // Vref-为Vss
ADCON1bits.ADPREF = 0b00; // Vref+为Vdd
ADCON0bits.CHS = 0b00000; // 选择通道AN0(对应A0引脚)
// 清空ADRESH和ADRESL寄存器
ADRESL = 0;
ADRESH = 0;
// 开始ADC操作
while (1) {
ADCON0bits.ADON = 1; // 开启ADC模块
__delay_us(10); // 等待采样时间
ADCON0bits.GO = 1; // 启动ADC转换
while (ADCON0bits.GO_nDONE); // 等待转换完成
int result = ((ADRESH << 8) + ADRESL); // 获取10位ADC结果
__delay_ms(1000); // 每秒采样一次
}
}
首先,我们需要配置系统的时钟源。通过配置OSCCON寄存器,我们设置了内部振荡器为8MHz,并启用了4倍锁相环(PLL),将系统时钟频率提升至32MHz。高频率时钟能保证ADC转换的快速响应。
cOSCCON = 0xF0; // 设置内部振荡器为8MHz,使用4x PLL
在PIC16F1719中,A0引脚(AN0通道) 将作为模拟输入。为了启用ADC功能,必须将相应的引脚配置为模拟模式。
cANSELA = 0x00; // 将A端口全部设置为数字模式
ANSELAbits.ANSA0 = 1; // 设置A0为模拟输入
TRISAbits.TRISA0 = 1; // 设置A0为输入
通过ADCON1和ADCON0寄存器,我们可以配置ADC的各项参数:
cADCON1bits.ADCS = 0b010; // 设置ADC时钟为Fosc/32
ADCON1bits.ADFM = 1; // 结果右对齐
ADCON1bits.ADNREF = 0; // Vref- 为 Vss
ADCON1bits.ADPREF = 0b00; // Vref+ 为 Vdd
ADCON0bits.CHS = 0b00000; // 选择通道AN0
每次ADC操作前,我们需要先开启ADC模块,之后通过设置GO位启动转换,并等待转换完成。转换结果存储在两个寄存器ADRESH和ADRESL中,将其组合即可得到完整的10位ADC结果。
cADCON0bits.ADON = 1; // 开启ADC模块
__delay_us(10); // 等待采样时间
ADCON0bits.GO = 1; // 启动ADC转换
while (ADCON0bits.GO_nDONE); // 等待转换完成
int result = ((ADRESH << 8) + ADRESL); // 获取10位结果
ADC完成转换后,结果存储在ADRESH和ADRESL寄存器中。由于我们选择了右对齐,最终结果是一个10位的数字。我们通过将高8位左移8位,然后与低8位相加,得到最终的转换结果。
cint result = ((ADRESH << 8) + ADRESL); // 获取10位结果
为了避免频繁采样,我们在每次ADC读取后延迟1秒钟。这个延迟可以根据实际应用进行调整。
c__delay_ms(1000); // 每秒采样一次
通过上述步骤,我们成功使用了PIC16F1719的ADC模块,从模拟输入引脚读取电压值,并将其转换为数字信号。ADC的使用非常广泛,例如读取温度传感器、电位计等模拟设备的信号。掌握了ADC的配置与使用,能够帮助开发者在嵌入式系统中实现丰富的功能。
c/* PIC16F1719 配置位设置 */
#pragma config FOSC = INTOSC /* 振荡器选择位 (INTOSC 振荡器: CLKIN 引脚上的 I/O 功能) */
#pragma config WDTE = OFF /* 看门狗定时器使能 (WDT 禁用) */
#pragma config PWRTE = OFF /* 上电定时器使能 (PWRT 禁用) */
#pragma config MCLRE = ON /* MCLR 引脚功能选择 (MCLR/VPP 引脚功能为 MCLR) */
#pragma config CP = OFF /* 闪存程序存储器代码保护 (程序存储器代码保护禁用) */
#pragma config BOREN = OFF /* 低电压复位使能 (低电压复位禁用) */
#pragma config CLKOUTEN = OFF /* 时钟输出使能 (CLKOUT 功能禁用。CLKOUT 引脚上的 I/O 或振荡器功能) */
#pragma config IESO = ON /* 内外部切换模式 (内外部切换模式使能) */
#pragma config FCMEN = ON /* 故障安全时钟监控器使能 (故障安全时钟监控器使能) */
#pragma config WRT = OFF /* 闪存自写保护 (写保护关闭) */
#pragma config PPS1WAY = OFF /* 外设引脚选择单向控制 (PPSLOCK 位可以通过软件反复设置和清除) */
#pragma config ZCDDIS = ON /* 零交叉检测禁用 (ZCD 禁用。ZCD 可以通过设置 ZCDCON 的 ZCDSEN 位来启用) */
#pragma config PLLEN = ON /* 锁相环使能 (4x PLL 使能) */
#pragma config STVREN = ON /* 栈溢出/下溢复位使能 (栈溢出或下溢将导致复位) */
#pragma config BORV = LO /* 低电压复位电压选择 (低电压复位电压 (Vbor),选择低触发点。) */
#pragma config LPBOR = OFF /* 低功率低电压复位 (低功率低电压复位禁用) */
#pragma config LVP = ON /* 低电压编程使能 (低电压编程使能) */
/* 定义系统振荡器频率 */
#define _XTAL_FREQ (32000000ul)
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//counter variable
int count = 0;
void main(void) {
unsigned char index = 0;
/* 程序初始化设置 */
OSCCON = 0xF0; /* 设置 INTOSC 为 8MHz,并使用 4xPLL,使系统振荡器达到 32MHz */
INTCON = 0; /* 禁用所有中断源 */
PIE1 = 0;
PIE2 = 0;
PIE3 = 0;
// Allow PLL startup time ~2 ms
__delay_ms(10);
ANSELA = 0x00; // 将所有A端口引脚设置为数字模式
ANSELB = 0x00; // 将所有B端口引脚设置为数字模式
ANSELC = 0x00; // 将所有C端口引脚设置为数字模式
// Set PIN D1 as output
TRISDbits.TRISD1 = 0;
// Turn off LED
LATDbits.LATD1 = 0;
// Setup PORTD
TRISD = 0;
ANSELD = 0;
TRISBbits.TRISB2 = 0;
ANSELBbits.ANSB2 = 0;
/////////////////////
// Setup EUSART
////////////////////
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 0x00; // unlock PPS
RB2PPSbits.RB2PPS = 0x14; //RB2->EUSART:TX;
RXPPSbits.RXPPS = 0x0B; //RB3->EUSART:RX;
PPSLOCK = 0x55;
PPSLOCK = 0xAA;
PPSLOCKbits.PPSLOCKED = 0x01; // lock PPS
////////////////////
// Configure ADC
///////////////////
// Set A0 as input
TRISAbits.TRISA0 = 1;
// Set A0 as analog
ANSELAbits.ANSA0 = 1;
// Fosc/32 ADC conversion time is 1.0 us
ADCON1bits.ADCS = 0b010;
// Right justified
ADCON1bits.ADFM = 1;
// Vref- is Vss
ADCON1bits.ADNREF = 0;
// Vref+ is Vdd
ADCON1bits.ADPREF = 0b00;
// Set input channel to AN0
ADCON0bits.CHS = 0b00000;
// Zero ADRESL and ADRESH
ADRESL = 0;
ADRESH = 0;
// enable peripheral interrupt
INTCONbits.PEIE = 1;
INTCONbits.GIE = 1; // 使能全局中断
// variable to store conversion result
int result;
while (1) {
// Turn ADC on
ADCON0bits.ADON = 1;
// Sample CH0
__delay_us(10);
ADCON0bits.GO = 1;
while (ADCON0bits.GO_nDONE);
// Store ADC result
result = ((ADRESH << 8) + ADRESL);
__delay_ms(1000);
}
}
希望这篇文章能够帮助你理解如何在PIC微控制器上使用ADC模块!
本文作者:Dong
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC。本作品采用《知识共享署名-非商业性使用 4.0 国际许可协议》进行许可。您可以在非商业用途下自由转载和修改,但必须注明出处并提供原作者链接。 许可协议。转载请注明出处!