第5章 模数和数模转换器 A/D转换器简写为ADC(Analog to Digital Converter),是一种能把模拟量转换为相应的数字量的电子器件。D/A转换器简写为DAC(Digital To Analog Converter),与A/D转换器相反,它能把数字量转换为相应的模拟量。在单片机控制系统中,经常需要用到A/D和D/A转换器。它们的功能及其在实时控制系统中的地位如图51所示。由图可见,被控实体的过程信号可以是电量(如电流、电压等),也可以是非电量(如温度、压力、传速等),其数值是随时间连续变化的。各种模拟量都可以通过变送器或传感器 转换为相应的数字量送给单片机。单片机对过程信息进行运算和处理,把过程信息进行当地显示或打印等,同时将处理后的数字量送给D/A转换器,转换为相应的模拟量去对被控系统进行控制和调整,使系统处于最佳工作状态。 图51单片机实时控制系统示意图 上述分析表明: A/D转换器在单片机控制系统中主要用于数据采集,向单片机提供被控对象的各种实时参数,以便单片机对被控对象进行监视和控制决策; D/A转换器用于模拟控制,通过机械或电气手段对被控对象进行调整和控制。因此,A/D、D/A转换器是架设在单片机和被控实体之间的桥梁,在单片机控制系统占有极其重要的地位。 C8051F020是混合信号型单片机,在片内集成了模数(A/D)和数模(D/A)转换电路。下面分别进行叙述。 5.1模数转换器 A/D是将模拟量转换为数字量的器件。模拟量可以是电压、电流等电信号,也可以是声、光、压力、湿度、温度等随时间连续变化的非电的物理量。非电的模拟量可通过合适的传感器(如光电传感器、压力传感器、温度传感器)转换为电信号。 C8051F020片内包含一个9通道的12位的模数转换器ADC0和8通道8位的模数转换器ADC1。 5.1.1模数转换原理和性能指标 1. 转换原理 A/D转换器的种类很多,根据转换原理可以分计数式、并行式、双积分式、逐次逼近式等。计数式A/D转换器结构简单,但转换速度也很慢,所以很少采用。并行A/D转换器的转换速度最快,但因结构复杂而造价较高,只用于那些转换速度极高的场合。双积分式A/D转换器抗干扰能力强,转换精度也很高,但速度不够理想,常用于数字式测量仪表中。计算机中广泛采用逐次逼近式A/D转换器作为接口电路,它的结构不太复杂,转换速度也较高。下面仅对逐次逼近式和双积分式A/D转换器的转换原理进行简单介绍。 图52逐次逼近式A/D原理框图 1) 逐次逼近式A/D转换器 逐次逼近式A/D也称逐次比较法A/D。它由结果寄存器、D/A、比较器和置位控制逻辑等部件组成,原理框图如图52所示。 这种A/D采用对分搜索法逐次比较、逐步逼近的原理来转换,整个转换过程是个“试探”过程。控制逻辑先置1结果寄存器最高位Dn-1然后经D/A转换得到一个占整个量程一半的模拟电压Vs,比较器将此Vs和模拟输入电压Vx比较,若Vx>Vs则保留此位Dn-1(为1),否则清0Dn-1位。然后控制逻辑置1结果寄存器次高位Dn-2,连同Dn-1一起送D/A转换,得到的Vs再和Vx比较,以决定Dn-2位保留为1还是清0,以此类推。最后,控制逻辑置1结果寄存器最低位D0,然后将Dn-1、Dn-2、……、D0一起送D/A转换。转换得到的结果Vs和Vx比较,决定D0位保留为1还是清0。 至此,结果寄存器的状态便是与输入的模拟量Vx对应的数字量。 2) 双积分式的A/D转换器 双积分式也称二重积分式,其实质是测量和比较两个积分的时间(它的工作原理见图53): 一个是对模拟输入电压积分的时间T0,此时间往往是固定的; 另一个是以充电后的电压为初值,对参考电源Vref反向积分,积分电容被放电至零所需的时间Ti(Vref与Vi符号相反)。反向积分的斜率是固定的。模拟输入电压Vi与参考电压Vref之比,等于上述两个时间之比。由于Vref、T0固定,而放电时间Ti可以测出,因而可计算出模拟输入电压的大小。 图53双积分式A/D转换器工作原理图 由于T0、Vref为已知的固定常数,因此反向积分时间Ti与输入模拟电压Vi在T0时间内的平均值成正比。输入电压Vi愈高,VA愈大,Ti就愈长。在Ti开始时刻,控制逻辑同时打开计数器的控制门开始计数,直到积分器恢复到零电平时,计数停止。则计数器所计出的数字即正比于输入电压Vi在T0时间内的平均值,于是完成了一次A/D转换。 由于双积分型A/D转换是测量输入电压Vi在T0时间内的平均值,因此对常态干扰(串模干扰)有很强的抑制作用,尤其对正负波形对称的干扰信号,抑制效果更好。 双积分型的A/D转换器具有电路简单、抗干扰能力强、精度高等优点。但转换速度比较慢,常用的A/D转换芯片的转换时间为毫秒级。例如12位的积分型A/D芯片ADCET12BC,其转换时间为1ms。因此适用于模拟信号变化缓慢,采样速率要求较低,而对精度要求较高,或现场干扰较严重的场合。例如常在数字电压表中采用该芯片进行A/D采样。 2. 性能指标 衡量A/D性能的主要参数是: 1) 分辨率 分辨率(Resolution)是指输出的数字量变化一个相邻的值所对应的输入模拟量的变化值; 取决于输出数字量的二进制位数。一个n位的A/D转换器所能分辨的最小输入模拟增量定义为满量程值的2-n倍。例如,满量程为10V的8位A/D芯片的分辨率为10V×2-8=39mV; 而16位的A/D是10V×2-16=153μV。 2) 满刻度误差 满刻度误差(Full Scale Error)也称增益误差,即输出全1时输入电压与理想输入量之差。 3) 转换速率 转换速率(Conversion Rate)是指完成一次A/D转换所需时间的倒数,是一个很重要的指标。A/D转换器型号不同,转换速率差别很大。选用A/D转换器型号视应用需求而定,在被控系统的时间允许的情况下,应尽量选用便宜的逐次比较型A/D转换器。 4) 转换精度 A/D转换器的转换精度(Conversion Accuracy)由模拟误差和数字误差组成。模拟误差是比较器、解码网络中的电阻值以及基准电压波动等引起的误差,数字误差主要包括丢失码误差和量化误差,前者属于非固定误差,由器件质量决定,后者和A/D输出数字量的位数有关,位数越多,误差越小。 5.1.2C8051F020的ADC0功能结构 C8051F020的ADC0子系统就是一个100ksps、12位分辨率的逐次逼近寄存器型ADC。ADC0的最高转换速度为100ksps,其转换时钟来源于系统时钟分频,分频值保存在寄存器ADC0CF的ADCSC位。C8051F020的ADC0子系统功能框图如图54所示,它包括一个9通道的可编程模拟多路选择器(AMUX0)、一个可编程增益放大器(PGA0)和一个100ksps、12位分辨率的逐次逼近寄存器型ADC,ADC中集成了跟踪保持电路和可编程窗口检测器。AMUX0、PGA0、数据转换方式及窗口检测器都可用软件通过特殊功能寄存器来控制。ADC0所使用的电压基准将在5.3节专门介绍。只有当ADC0控制寄存器中的AD0EN位被置1时ADC0子系统(ADC0、跟踪保持器和PGA0)才被允许工作。当AD0EN位为0时,AD0C子系统处于低功耗关断方式。 图54ADC0子系统功能框图 从ADC0的功能框图可以看出,ADC0的运行主要与图上标的10个SFR有关。8个外部输入的模拟量可以通过配置寄存器AMX0CF设定为单端输入或双端输入; 8个外部输入的模拟量和一个内部温度传感器量通过通道选择寄存器AMX0SL设定在某一时刻通过多路选择器; 从多路选择器出来的模拟量通过配置寄存器ADC0CF设定ADC转换速度和对模拟量的放大倍数; 由控制寄存器ADC0CN对ADC进行模拟量转换的启动、启动方式、采样保持、转换结束、数字量格式等进行设定; 12位的转换好的数字量存放在数据字寄存器ADC0H、ADC0L中; ADC0中提供了可编程窗口检测器,通过上下限寄存器ADC0GTH、ADC0GTL、ADC0LTH、ADC0LTL设定所需要的比较极限值。 在进行模拟量转换前设定好以上SFR,CPU就按设定好的模式在模拟量转换好时用指令读出数据寄存器中的数字量或在中断服务程序中读取数字量,然后再进行下一次的转换。ADC0的电气特性见附录C。 5.1.3模拟多路选择器和PGA 模拟多路选择器(Analog Multiplexer,AMUX)中的8个通道用于外部测量,而第9通道在内部被接到片内温度传感器。这9个模拟通道通过通道选择寄存器AMX0SL和配置寄存器AMX0CF进行选择和配置,可以将AMUX输入对编程为工作在差分或单端方式。这就允许用户对每个通道选择最佳的测量技术,甚至可以在测量过程中改变方式。在系统复位后AMUX的默认方式为单端输入。表51给出了每种配置下各通道的功能。 配置寄存器AMX0CF的格式如下: 其中,各位的含义如下: 位7~4——未使用。读=0000b; 写=忽略。 位3(AIN67IC)——AIN6、AIN7输入对配置位。 0: AIN6和AIN7为独立的单端输入。 1: AIN6和AIN7为(分别为)+、-差分输入对。 位2(AIN45IC)——AIN4、AIN5输入对配置位。 0: AIN4和AIN5为独立的单端输入。 1: AIN4、AIN5为(分别为)+、-差分输入对。 位1(AIN23IC)——AIN2、AIN3输入对配置位。 0: AIN2和AIN3为独立的单端输入。 1: AIN2、AIN3为(分别为)+、-差分输入对。 位0(AIN01IC)——AIN0、AIN1输入对配置位。 0: AIN0和AIN1为独立的单端输入。 1: AIN0、AIN1为(分别为)+、-差分输入对。 注: 对于被配置成差分输入的通道,ADC0数据字格式为2的补码。 通道选择寄存器AMX0SL的格式如下: 其中,各位的含义如下: 位7~4——未使用。读=0000b; 写=忽略。 位3~0(AMX0AD3~0)——AMUX0地址位。 0000~1111,根据表51选择ADC输入的通道。 表51模拟通道配置 AMX0SL位3~0 000000010010001101000101011001111xxx AMX0CF 位3~0 0000AIN0AIN1AIN2AIN3AIN4AIN5AIN6AIN7温度传感器 0001+ (AIN0) - (AIN1)AIN2AIN3AIN4AIN5AIN6AIN7温度传感器 0010AIN0AIN1+ (AIN2) - (AIN3)AIN4AIN5AIN6AIN7温度传感器 0011+ (AIN0) - (AIN1)+ (AIN2) - (AIN3) AIN4AIN5AIN6AIN7温度传感器 0100AIN0AIN1AIN2AIN3+ (AIN4) - (AIN5)AIN6AIN7温度传感器 0101+ (AIN0) - (AIN1)AIN2AIN3+ (AIN4) - (AIN5)AIN6AIN7温度传感器 0110AIN0AIN1+ (AIN2) - (AIN3)+ (AIN4) - (AIN5)AIN6AIN7温度传感器 0111+ (AIN0) - (AIN1)+ (AIN2) - (AIN3)+ (AIN4) - (AIN5)AIN6AIN7温度传感器 1000AIN0AIN1AIN2AIN3AIN4AIN5+ (AIN6) - (AIN7)温度传感器 1001+ (AIN0) - (AIN1)AIN2AIN3AIN4AIN5+ (AIN6) - (AIN7)温度传感器 1010AIN0AIN1+ (AIN2) - (AIN3)AIN4AIN5+ (AIN6) - (AIN7)温度传感器 1011+ (AIN0) - (AIN1)+ (AIN2) - (AIN3)AIN4AIN5+ (AIN6) - (AIN7)温度传感器 1100AIN0AIN1AIN2AIN3+ (AIN4) - (AIN5)+ (AIN6) - (AIN7)温度传感器 1101+ (AIN0) - (AIN1)AIN2AIN3+ (AIN4) - (AIN5)+ (AIN6) - (AIN7)温度传感器 1110AIN0AIN1+ (AIN2) - (AIN3)+ (AIN4) - (AIN5)+ (AIN6) - (AIN7)温度传感器 1111+ (AIN0) - (AIN1)+ (AIN2) - (AIN3)+ (AIN4) - (AIN5)+ (AIN6) - (AIN7)温度传感器 在表51中可看出,从多路选择器出来的哪一个通道和单端或差分输入由通道选择寄存器AMX0SL和配置寄存器AMX0CF进行选择和配置,表左边的垂直方向表示配置寄存器AMX0CF低4位值,指出各通道的单端还是差分输入,表上边的水平方向表示通道选择寄存器AMX0SL的低4位,选择9路输入的中的某一路。 PGA(Programmable Gain Amplifier)即可编程增益放大器,它对AMUX输出信号的放大倍数由ADC0配置寄存器ADC0CF中的AMP0GN2~0确定。PGA增益可以用软件编程为0.5、1、2、4、8或16,复位后的默认增益为1。注意,PGA0的增益对温度传感器也起作用。 配置寄存器ADC0CF的格式如下: 其中,各位的含义如下: 位7~3(AD0SC4~0)——ADC0 SAR转换时钟周期控制位。 SAR转换时钟来源于系统时钟,由下面的方程给出: AD0SC=SYSCLKCLKSAR0-1 其中,AD0SC表示AD0SC4~0中保持的数值,CLKSAR0表示所需要的ADC0 SAR时钟(注: ADC0 SAR时钟应小于或等于2.5MHz)。 位2~0(AMP0GN2~0)——ADC0内部放大器增益(PGA)。 000: 增益=1 001: 增益=2 010: 增益=4 011: 增益=8 10x: 增益=16 11x: 增益=0.5 5.1.4ADC的工作方式 1. 转换过程 ADC0的转换过程由控制寄存器ADC0CN来设置和控制。 控制寄存器ADC0CN的格式如下: 其中,各位的含义如下: 位7(AD0EN)——ADC0使能位。 0: ADC0禁止。ADC0处于低耗停机状态。 1: ADC0使能。ADC0处于活动状态,并准备转换数据。 位6(AD0TM)——ADC0跟踪方式位。 0: 当ADC0被使能时,除了转换期间之外一直处于跟踪方式。 1: 由AD0CM1~0定义跟踪方式。 位5(AD0INT)——ADC0转换结束中断标志。该标志必须用软件清0。 0: 从最后一次将该位清0后,ADC0还没有完成一次数据转换。 1: ADC0完成了一次数据转换。 位4(AD0BUSY)——ADC0忙标志位。 读: 0: ADC0转换结束或当前没有正在进行的数据转换。AD0INT在AD0BUSY的下降沿被置1。 1: ADC0正在进行转换。 写: 0: 无作用。 1: 若AD0CM1~0=00b,则启动ADC0转换。 位3和位2(AD0CM1~0)——ADC0转换启动方式选择位。 如果AD0TM=0,则: 00: 向AD0BUSY写1启动ADC0转换。 01: 定时器3溢出启动ADC0转换。 10: CNVSTR上升沿启动ADC0转换。 11: 定时器2溢出启动ADC0转换。 如果AD0TM=1,则: 00: 向AD0BUSY写1时启动跟踪,持续3个SAR时钟,然后进行转换。 01: 定时器3溢出启动跟踪,持续3个SAR时钟,然后进行转换。 10: 只有当CNVSTR输入为逻辑低电平时ADC0跟踪,在CNVSTR的上升沿开始转换。 11: 定时器2溢出启动跟踪,持续3个SAR时钟,然后进行转换。 位1(AD0WINT)——ADC0窗口比较中断标志。该位必须用软件清0。 0: 自该标志被清除后未发生过ADC0窗口比较匹配。 1: 发生了ADC0窗口比较匹配。 位0(AD0LJST)——ADC0数据左对齐选择位。 0: ADC0H: ADC0L寄存器数据右对齐。 1: ADC0H: ADC0L寄存器数据左对齐。 C8051F020单片机的ADC0有4种转换启动方式,由ADC0CN中的ADC0启动转换方式位(AD0CM1和AD0CM0)的状态决定。转换触发源有: (1) 向ADC0CN的AD0BUSY位写1; (2) 定时器3溢出(即定时的连续转换); (3) 外部ADC转换启动信号的上升沿,CNVSTR; (4) 定时器2溢出(即定时的连续转换)。 AD0BUSY位在转换期间被置1,转换结束后复0。AD0BUSY位的下降沿触发一个中断(当被允许时)并将中断标志AD0INT(ADC0CN.5)置1。转换数据被保存在ADC数据字的MSB和LSB寄存器: ADC0H和ADC0L。转换数据在寄存器对ADC0H: ADC0L中的存储方式可以是左对齐或右对齐的,由ADC0CN寄存器中AD0LJST位的编程状态决定。当通过向AD0BUSY写1启动数据转换时,应查询AD0INT位以确定转换何时结束(也可以使用ADC0中断)。建议的查询步骤如下: (1) 写0到AD0INT; (2) 向AD0BUSY写1; (3) 查询并等待AD0INT变为1; (4) 处理ADC0数据。 2. 跟踪方式 ADC0CN中的AD0TM位控制ADC0的跟踪保持方式。在默认状态下,除了转换期间外,ADC0输入被连续跟踪。当AD0TM位为逻辑1时,ADC0工作在低功耗跟踪保持方式。在该方式下,每次转换之前都有3个SAR时钟的跟踪周期(在启动转换信号有效之后)。当CNVSTR信号用于在低功耗跟踪保持方式启动转换时,ADC0只在CNVSTR为低电平时跟踪; 在CNVSTR的上升沿开始转换(见图55)。当整个芯片处于低功耗待机或休眠方式时,跟踪可以被禁止(关断)。当AMUX或PGA的设置频繁改变时,低功耗跟踪保持方式也非常有用,可以保证建立时间需求得到满足。 图5512位ADC跟踪和转换时序 3. 建立时间要求 当ADC0输入配置发生改变时(AMUX或PGA的选择发生变化),在进行一次精确的转换之前需要有一个最小的跟踪时间。该跟踪时间由ADC0模拟多路器的电阻、ADC0采样电容、外部信号源阻抗及所要求的转换精度决定。图56给出了单端和差分方式下等效的ADC0输入电路。注意,这两种等效电路的时间常数完全相同。对于一个给定的建立精度(SA),所需要的ADC0建立时间可以用方程估算: t=ln2nSA×RTOTALCSAMPLE 图56ADC0等效输入电路 其中: SA是建立精度,用一个LSB的分数表示(例如,建立精度0.25对应1/4 LSB); t为所需要的建立时间,以秒为单位; RTOTAL为ADC0模拟多路器电阻与外部信号源电阻之和; n为ADC0的分辨率,用比特表示。 当测量温度传感器的输出时,RTOTAL等于RMUX。 注意: 在低功耗跟踪方式,每次转换需要用3个SAR时钟跟踪。对于大多数应用,3个SAR时钟可以满足跟踪需要。 4. 转换结果格式 12位的转换好的数字量存放在数据字寄存器ADC0H、ADC0L中。 数据字寄存器ADC0H的格式如下: 其中,各位的含义如下: 位7~0——ADC0数据字高字节。 当AD0LJST=0: 即寄存器数据右对齐,位7~4为位3的符号扩展位。位3~0是12位ADC0数据字的高4位。 当AD0LJST=1: 即寄存器数据左对齐,位7~0为12位ADC0数据字的高8位。 数据字寄存器ADC0L的格式如下: 其中,各位的含义如下: 位7~0——ADC0数据字低字节。 当AD0LJST=0: 即寄存器数据右对齐,位7~0为12位ADC0数据字的低8位。 当AD0LJST=1: 即寄存器数据左对齐,位7~4为12位ADC0数据字的低4位。位3~0总是0。 表52列出了输入信号与转换结果代码及不同的方式之间的对应关系。 表52ADC数据字转换表 AIN0为单端输入方式: (AMX0CF=0x00,AMX0SL=0x00) AIN0-AGND(伏)ADC0H: ADC0L(AD0LJST=0)ADC0H: ADC0L(AD0LJST=1) VREF×(4095/4096)0x0FFF0xFFF0 VREF/20x08000x8000 VREF×(2047/4096)0x07FF0x7FF0 00x00000x0000 AIN0-AIN1为差分输入对: (AMX0CF=0x01,AMX0SL=0x00) AIN0-AIN1(伏)ADC0H: ADC0L(AD0LJST=0)ADC0H: ADC0L(AD0LJST=1) VREF×(2047/2048)0x07FF0x7FF0 VREF/20x04000x4000 VREF×(1/2048)0x00010x0010 00x00000x0000 -VREF×(1/2048)0xFFFF(-1d)0xFFF0 -VREF/20xFC00(-1024d)0xC000 -VREF0xF800(-2048d)0x8000 5.1.5ADC0可编程窗口检测器 ADC0可编程窗口检测器提供一个中断,当ADC0转换值在ADC0下限(大于)寄存器ADC0GTH: ADC0GTL和ADC0上限(小于)寄存器ADC0LTH: ADC0LTL范围之内,并且中断开启时,引发相应中断。ADC0可编程窗口检测器可应用于节能场合,例如让系统处于空闲方式,当ADC0输入信号(例如温度)在窗口检测器预设值的范围内时,引发中断,唤醒CIP51进行相应的处理,既节能,又达到了监控的目的,同时也节省代码空间和CPU带宽。窗口检测器中断标志(ADC0CN中的AD0WINT位)也可用于查询方式。 窗口设定值的高字节和低字节被装入ADC0下限(大于)和ADC0上限(小于)寄存器(ADC0GTH、ADC0GTL、ADC0LTH和ADC0LTL)。注意,窗口检测器标志既可以在测量数据位于用户编程的极限值以内时有效,也可以在测量数据位于用户编程的极限值以外时有效,这取决于ADC0GTx和ADC0LTx寄存器的编程值。 在默认情况下,ADC0GTH: ADC0GTL=0xFFFF; ADC0LTH: ADC0LTL=0x0000。所以使得电平在全范围内均不会引发监控中断。 在ADC0LTH: ADC0LTL>ADC0GTH: ADC0GTL情况下,窗口检测中断条件为: ADC0LTH:ADC0LTL <ADC0转换值<ADC0GTH:ADC0GTL 在ADC0LTH: ADC0LTL<ADC0GTH: ADC0GTL情况下,窗口检测中断条件为: ADC0转换值>ADC0GTH:ADC0GTL 或 ADC0转换值<ADC0LTH:ADC0LTL 图57中说明一个右对齐数据格式下,单端输入窗口检测器设置例子。 图57ADC0右对齐的单端数据窗口中断示例 在图57的左边: ADC0LTH:ADC0LTL=0x0200,ADC0GTH:ADC0GTL=0x0100 则当0x0100<ADC0转换值<0x0200时,AD0WINT自动置1。若中断开启,则引发相应的中断(中断号为8,与ADC0不是同一个中断)。AD0WINT需要软件清0。 在图57的右边: ADC0LTH: ADC0LTL=0x0100,ADC0GTH: ADC0GTL=0x0200 右边的与左边的范围刚好相反。则当ADC0转换值>0x0200或ADC0转换值<0x0100时,窗口检测条件,产生AD0WINT自动置1。 图58中说明一个右对齐数据格式下,差动输入窗口检测器设置例子。在差动模式下,右对齐数据格式与int相当,0x8000~0xFFFF表示负数,0xFFFF为-1,0xF800为转换最小值(即负电平最大值)。 图58ADC0右对齐的差分数据窗口中断示例 在图58的左边: ADC0LTH: ADC0LTL=0x0100,ADC0GTH:ADC0GTL=0xFFFF 则当0xFFFF(即-1)<ADC0转换值<0x0100时,AD0WINT自动置1。 在图58的右边: ADC0LTH: ADC0LTL=0xFFFF,ADC0GTH: ADC0GTL=0x0100 右边的与左边的范围刚好相反。则当ADC0转换值>0x0100或ADC0转换值<0xFFFF时,窗口检测条件,产生AD0WINT自动置1。 至于左对齐数据格式相当于将转换值乘以16(左移4位),其他有关窗口检测的规则与右对齐相似ADC0左对齐的单端数据输入的窗口检测中断示例见图59,差分数据输入的窗口检测中断示例见图510,请读者自行分析。 图59ADC0左对齐的单端数据窗口中断示例 图510ADC0左对齐的差分数据窗口中断示例 5.1.6ADC1(8位ADC) C8051F020还有一个ADC1子系统,包括一个8通道的可配置模拟多路开关(AMUX1)、一个可编程增益放大器(PGA1)和一个500ksps、8位分辨率的逐次逼近寄存器型ADC,该ADC中集成了跟踪保持电路。ADC1的原理框图如图511所示。AMUX1、PGA1及数据转换方式都可用软件通过特殊功能寄存器 图511ADC1原理框图 来配置。与ADC1工作有关的SFR有ADC1配置寄存器ADC1CF、AMUX配置寄存器AMX1SL、ADC1控制寄存器ADC1CN、ADC1数据寄存器ADC1。只有当ADC1控制寄存器ADC1CN中的AD1EN位被置1时ADC1子系统(8位ADC、跟踪保持器和PGA)才被使能。当AD1EN位为0时,ADC1子系统处于低功耗关断方式。 1. 模拟多路开关和PGA ADC1有8个通道用于测量,用寄存器AMX1SL选择通道。PGA对AMUX输出信号的放大倍数由ADC1配置寄存器ADC1CF中的AMP1GN1~0确定。PGA增益可以用软件编程为0.5、1、2、4。复位时的默认增益为0.5。 注意: AIN1引脚也作为端口1的I/O引脚,当用作ADC1输入时必须被配置为模拟输入。为了将AIN1的某个引脚配置为模拟输入,要将寄存器P1MDIN中的对应位设置为0。被选作模拟输入的端口1引脚不进入数字I/O交叉开关。 配置寄存器ADC1CF的格式如下: 其中,各位的含义如下: 位7~3: (AD1SC4~0)——ADC1 SAR转换时钟周期控制位。 SAR转换时钟频率由下面的方程计算: AD1SC=SYSCLKCLKSAR1-1 其中: SYSCLK为系统时钟,AD1SC为AD1SC4~0中的5位数值(注意,ADC1SAR转换时钟应小于或等于6MHz)。 位2——未使用。读=0b; 写=忽略。 位1和位0(AMP1GN1~0)——ADC1内部放大器增益(PGA)。 00: 增益=0.5 01: 增益=1 10: 增益=2 11: 增益=4 AMUX配置寄存器AMX1SL的格式为: 其中,各位的含义如下: 位7~3——未使用。读=00000b; 写=忽略。 位2~0——(AMX1AD2~0)——AMX1地址位。 000~111: ADC1输入选择如下: 000——选择AIN1.0 001——选择AIN1.1 010——选择AIN1.2 011——选择AIN1.3 100——选择AIN1.4 101——选择AIN1.5 110——选择AIN1.6 111——选择AIN1.7 2. ADC1的工作方式 ADC1的最高转换速度为500ksps。ADC1的转换时钟(SAR1时钟)来源于系统时钟分频。由ADC1CF寄存器的AD1SC位决定(系统时钟/(AD1SC+1),0≤AD1SC≤31)。ADC1转换时钟频率最大为6MHz。ADC1的转换过程主要由控制寄存器ADC1CN的设置来控制。ADC1控制寄存器ADC1CN的格式为: 其中,各位的含义如下: 位7(AD1EN)——ADC1使能位。 0: ADC1禁止。ADC1处于低功耗停机状态。 1: ADC1使能。ADC1处于活动状态,并准备转换数据。 位6(AD1TM)——ADC1跟踪方式位。 0: 一般跟踪方式。当ADC1被使能时,除了转换期间之外一直处于跟踪方式。 1: 低功耗跟踪方式。由AD1CM2~0定义跟踪方式。 位5(AD1INT)——ADC1转换结束中断标志,该标志必须用软件清0。 0: 从最后一次将该位清0后,ADC1还没有完成一次数据转换。 1: ADC1完成一次数据转换。 位4(AD1BUSY)——ADC1忙标志位。 读: 0: ADC1转换结束或不在进行数据转换。AD1INT在AD1BUSY的下降沿被置1。 1: ADC1正在进行转换。 写: 0: 无效。 1: 若AD1CM2~0=000b,则启动ADC1转换。 位3~1(AD1CM2~0)——ADC1转换启动方式选择。 AD1TM=0: 000——向AD1BUSY写1启动ADC1转换。 001——定时器3溢出启动ADC1转换。 010——CNVSTR上升沿启动ADC1转换。 011——定时器2溢出启动ADC1转换。 1xx——向AD0BUSY写1启动ADC1转换(与ADC0软件命令转换同步)。 AD1TM=1: 000——向AD1BUSY写1时启动跟踪并持续3个SAR1时钟,然后进行转换。 001——定时器3溢出启动跟踪并持续3个SAR1时钟,然后进行转换。 010——只有当CNVSTR输入为逻辑低电平时才启动ADC1跟踪,在CNVSTR上升沿开始转换。 011——定时器2溢出启动跟踪并持续3个SAR1时钟,然后进行转换。 1xx——向AD0BUSY写1启动跟踪并持续3个SAR1时钟,然后进行转换。 位0——未使用。读=0b; 写=忽略。 ADC1有5种A/D转换启动方式,由ADC1CN中的ADC1启动转换方式位(AD1CM2~0)的编程状态决定。转换启动源有: (1) 向ADC1CN的AD1BUSY位写1; (2) 定时器3溢出(即定时的连续转换); (3) 外部ADC转换启动信号CNVSTR的上升沿; (4) 定时器2溢出(即定时的连续转换)。 (5) 向ADC0CN的AD0BUSY位写1(用一个软件命令启动ADC1和ADC0)。 AD1BUSY位在转换期间被置1,转换结束后清0。AD1BUSY位的下降沿触发一个中断(当被允许时)并将ADC1CN中的中断标志置1。转换结果保存在ADC1的数据字ADC1中。当采用向AD1BUSY位写1这一启动方式时,建议通过查询AD1INT来确定转换何时完成。查询步骤同ADC0。 3. 跟踪方式 ADC1CN中的AD1TM位控制ADC1的跟踪保持方式。在默认状态下,ADC1输入被连续跟踪(转换期间除外)。当AD1TM位被设置为逻辑1时,ADC1工作在低功耗跟踪方式。在该方式下,每次转换之前都要有3个SAR时钟的跟踪周期(在启动转换信号之后)。当在低功耗跟踪方式下用CNVSTR信号作为转换启动源时,只在CNVSTR为低电平时跟踪,从CNVSTR的上升沿开始转换。当整个芯片处于低功耗停机或休眠方式时,跟踪被禁止。由于需要有建立时间,因此低功耗跟踪保持方式在需要频繁改变AMUX和PGA的场合也是非常有用的。ADC1跟踪和转换时序的举例见图512。 图512ADC1跟踪和转换时序举例 5.1.7模数转换举例 1. 片内温度传感器数据采集 C8051F020的ADC0中有一个片内温度传感器,在图51中已标出。温度传感器产生一个与器件内部温度成正比的电压,该电压作为一个单端输入提供给ADC(模数转换器)的多路选择器。当选择温度传感器作为ADC的输入并且ADC启动一次转换后,可以通过简单的数学运算将ADC的输出结果转换为用度数表示的温度。温度转换器的典型应用有系统环境检测、系统过热测试和在基于热电偶的应用中测量冷端温度。 为了能使用温度传感器,它首先必须被允许,ADC及其相关的偏置电路也必须被允许。ADC可以使用内部电压基准,也可以使用外部电压基准。本例子使用内部电压基准。ADC转换的结果代码可以选择为左对齐或右对齐。本例子使用左对齐,这样可使代码的权值与ADC的位数(12或10)无关。有关ADC转换步骤为: (1) 通过将TEMPE(REF0CN.2)设置为1来允许温度传感器工作。模拟偏置发生器和内部电压基准的允许位位于REF0CN中(分别为REF0CN.1和REF0CN.0)。例如: movREF0CN,#07 h;允许温度传感器、模拟偏置发生器和电压基准 (2) 选择温度传感器作为ADC的输入。这可以通过写AMX0SL来完成,例如: movAMX0SL,#08 h ;选择温度传感器作为ADC输入 AM0CF的取值如何以及AMUX配置寄存器选择ADC是单端输入还是差分输入并不影响温度传感器工作。 (3) 设置位于ADC0CF中的ADCSAR时钟分频系数,特别是ADC转换时钟的周期至少应为500ns。 (4) 选择ADC的增益。在单端方式下,ADC能够接受的最大直流输入电压等于VREF。如果使用内部电压基准,则该值大约为2.4V。温度传感器所能产生的最大电压值稍大于1V。因此,可以安全地将ADC的增益设置为2,以提高温度分辨率。设置ADC增益的配置位在ADC0CF中。所以有: movADC0CF, #41h;设置ADC的时钟为SYSCLK/8、ADC增益为2 其余的ADC配置位在ADC0CN中。这是一个可以位寻址的特殊功能寄存器。可以选择任何一种有效的转换启动源: 定时器2或定时器3溢出、向AD0BUSY写1或使用外部CNVSTR。后面的软件示例使用定时器3溢出作为转换启动源。这里采用向AD0BUSY写1的方式。 (5) 通过写入下面的控制字,将ADC配置为低功耗跟踪方式,采用向AD0BUSY写1作为转换启动信号,输出数据采用左对齐格式: movADC0CN,#C1H ;允许ADC; 允许低功耗跟踪方式 ;清除转换完成中断 ;选择AD0BUSY作为转换启动源 ;清除窗口比较中断 ;设置输出数据格式为左对齐 (6) 至此,可以通过将AD0BUSY写1来启动一次转换: setbAD0BUSY ;启动转换 (7) 用查询或中断方式(ADC0CN的AD0INT位)等待转换完成,ADC输出寄存器(即ADC0H和ADC0L中的16位数值)中的值就是与器件内部的绝对温度成正比的代码。下面说明如何通过这一代码得到温度的摄氏度数值。 温度传感器产生一个与器件内部绝对温度成正比的电压输出。温度传感器的传输特性如图513所示。式(51)的方程给出这一电压与温度的摄氏度数值之间的关系: Vtemp=(2.86mV/℃)×Temp+776mV(51) 其中: Vtemp——温度传感器的输出电压; Temp——器件内部的摄氏温度值。 图513温度传感器的传输特性 温度传感器的电压不能直接在器件外部测量,它出现在ADC多路选择器的输入端,允许ADC测量该电压值并产生一个与电压值成正比的输出代码。ADC在左对齐、单端方式下产生的输出代码与输入电压成正比,见式(52)。 CODE=Vin×GainVREF×216(52) 其中: CODE——左对齐的ADC输出代码; Gain——PGA的增益; VREF——电压基准的电压值,如果使用内部VREF,则大约为2.43V。 把式(51)代入式(52),并假设Gain=2和VREF=2.43V,解方程得到输出温度值为式(53): Temp=(CODE-41857)154(53) 其中: Temp——温度的摄氏度数值; CODE——左对齐的ADC输出代码。 温度传感器测量的是器件的内部温度。如果希望测量环境温度,则必须考虑器件的自热效应。由于器件功率消耗的原因,测量值很可能比环境温度值高几度,为了得到环境温度,应从结果中减去因自热产生的温度增加值。这一温度值可以通过计算或测量得到。 有很多因素影响器件的自热效应。其中最主要的是电源电压、工作频率、封装的热耗散特性、器件在PCB中的安装方式以及封装外壳周围的空气流通情况。温度增加值可以通过将器件的功率消耗乘以封装的热耗散常数(通常称为θJA)来计算。在用这一常数时假定采用标准的PCB安装方式,所有的引脚都焊到电路板上,封装周围没有气流通过。 例如,一个工作在11.0592MHz、采用3.3V电源电压的C8051F005单片机,其功率消耗大致为35mW。对于64引脚的TQFP封装,其θJA值是39.5℃/W。这等价于39.5×35e-3的自热温度值,大约相当于1.4℃。 因自热而导致的温度增加可以用几种方法测量。一种方法是在器件上电之后立即启动一次转换,得到一个“冷”温度值; 然后再工作大约1min之后再测量一次,得到一个“热”温度值。这两个测量值的差就是因自热而产生的温度增加值。 另一种方法是让器件从一个低的SYSCLK频率开始工作,进行一次温度测量,然后再让器件工作在较高频率进行一次温度测量,取两者之差。在时钟频率更低时自热值是可忽略的,因为此时器件的功耗很低。 片内温度传感器的测量方法可以采用查询法或中断法。 (1) 查询法程序。 //此程序示范了ADC0的查询操作模式,ADC0配置为写AD0BUSY作为转换的开始信号,测 //量片内温度传感器,温度传感器的输出转换为摄氏度由UART0传输出去。可以通过PC //超级终端来观察温度采样值。超级终端使用方法为: 以Windows 2000 系统为例,选择 //"开始"→"程序"→"附件"→"通信"→"超级终端"命令进入超级终端(HyperTerminal) //应用程序界面,新建一个通信终端,取名为temp。单击"确定"按钮。选择终端的连接的 //串口(如串行口1) //设置对应于单片通信机程序的通信的格式和协议(如波特率、每字节的位数等) //假设在XTAL1和XTAL2之间连接22.1184MHz晶体 //系统时钟频率存储在全局常量SYSCLK,目标器件UART波特率存储在全局常量BAUDRATE //目标器件: C8051F020 //链接工具: KEIL C51 6.03 / KEIL EVAL C51 //------------------------------------------- //包含文件 //------------------------------------------- #include //SFR 声明 #include //------------------------------------------- //C8051F02X的16位SFR定义 //------------------------------------------- sfr16 DP = 0x82;   //数据指针 sfr16 TMR3RL = 0x92;   //定时器3重装值 sfr16 TMR3 = 0x94; //定时器3计数器 sfr16 ADC0 = 0xbe;   //ADC0数据 sfr16 ADC0GT = 0xc4;   //ADC0大于窗口 sfr16 ADC0LT = 0xc6;   //ADC0小于窗口 sfr16 RCAP2 = 0xca;   //定时器2捕捉/重装 sfr16 T2 = 0xcc;   //定时器2 sfr16 RCAP4 = 0xe4;   //定时器4捕捉/重装 sfr16 T4 = 0xf4;   //定时器4 sfr16 DAC0 = 0xd2;   //DAC0数据 sfr16 DAC1 = 0xd5;   //DAC1数据 //------------------------------------------- //全局常量 //------------------------------------------- #define SYSCLK 22118400   //系统时钟频率(Hz) #define BAUDRATE 9600   //UART波特率(b/s) //------------------------------------------- //函数原型 //------------------------------------------- void SYSCLK_Init(void); void PORT_Init(void); void UART0_Init(void); void ADC0_Init(void); //------------------------------------------- //主程序 //------------------------------------------- void main(void) { long temperature;   //温度百分之一的精度 int temp_int, temp_frac;   //温度的整数和小数部分 WDTCN = 0xde;   //禁止看门狗定时器 WDTCN = 0xad; SYSCLK_Init();   //初始化振荡器 PORT_Init();   //初始化数据交叉开关和通用I/O端口 UART0_Init();   //初始化UART0 ADC0_Init();   //初始化和使能ADC while (1) { AD0INT = 0;   //清除转换结束标记 AD0BUSY = 1;   //开始转换 while (AD0INT ==0);   //等待转换结束 temperature = ADC0;   //读ADC0数据 //计算温度精度为百分之一度 temperature = temperature - 41857;   //减去偏移量,使之对应0℃的值 temperature = (temperature * 100L) / 154;  //计算出对应的温度值(2.86mV/℃) temp_int = temperature / 100;   //得到温度值的整数部分 temp_frac = temperature - (temp_int * 100);  //得到温度值的小数部分 printf("Temperature is %+02d.%02d\n", temp_int, temp_frac);//从串口输出 } } //------------------------------------------- //系统时钟初始化 //------------------------------------------- //此程序初始化系统时钟使用22.1184MHz晶体作为时钟源 void SYSCLK_Init(void) { int i;   //延时计数器 OSCXCN = 0x67;   //启动外部振荡器22.1184MHz晶体 for (i=0; i < 256; i++);   //等待振荡器启动 (>1ms) while (!(OSCXCN & 0x80));   //等待晶体振荡器稳定 OSCICN = 0x88;   //选择外部振荡器作为系统时钟源并使能丢失时钟检测器 } //------------------------------------------- //I/O端口初始化 //------------------------------------------- //配置数据交叉开关和通用I/O端口 void PORT_Init(void) { XBR0 = 0x04;   //使能UART0 XBR1 = 0x00; XBR2 = 0x40;   //使能数据交叉开关和弱上拉 P0MDOUT |= 0x01;   //允许TX0为推挽输出 } //------------------------------------------- //UART0初始化 //------------------------------------------- //配置UART0 使用定时器1产生波特率 void UART0_Init(void) { SCON0 = 0x50;   //SCON0: 模式1,8位UART,允许RX TMOD = 0x20;   //TMOD: 1定时器, 模式2, 8位重装 TH1 = -(SYSCLK/BAUDRATE/16); //按波特率设置定时器1重装值 TR1 = 1;   //启动定时器1 CKCON |= 0x10;   //定时器1使用系统时钟为时基 PCON |= 0x80;   //SMOD=1 TI0 = 1;   //表示就绪 } //------------------------------------------- //ADC0初始化 //------------------------------------------- //配置ADC0 使用AD0BUSY作为转换源, 使用左对齐输出模式 //使用正常跟踪模式, 测量片内温度传感器输出 //禁止ADC0转换结束中断和ADC0窗口比较器中断 void ADC0_Init(void) { ADC0CN = 0x81;   //ADC0使能;正常跟踪模式 //当写AD0BUSY时ADC0转换开始,ADC0数据左对齐 REF0CN = 0x07;   //使能温度传感器片内VREF和VREF输出缓冲器 AMX0SL = 0x0f;   //选择温度传感器作为ADC多路模拟转换器输出 ADC0CF = (SYSCLK/2500000) << 3; //ADC转换时钟=2.5MHz ADC0CF |= 0x01;   //PGA增益=2 EIE2 &= ~0x02;   //禁止ADC0 EOC中断 EIE1 &= ~0x04;   //禁止ADC0窗口比较器中断 } //------------------------------------------- (2) 中断法程序。 //此程序是ADC0应用例程在中断模式使用定时器3溢出作为转换开始信号,测量片内温度传感 //器输出 //ADC0结果经简单的均值滤波处理,均值滤波计数值由常量INT_DEC给出 //ADC结果经计算得出温度从UART0传输 //假设在XTAL1和XTAL2之间连接22.1184MHz晶体 //系统时钟频率存储在全局常量SYSCLK,目标UART波特率存储在全局常量BAUDRATE //ADC0采样率存储在全局常量SAMPLERATE0 //目标器件: C8051F020 //链接工具: KEIL C51 6.03 / KEIL EVAL C51 //------------------------------------------- //包含文件 //------------------------------------------- #include //SFR声明 #include //------------------------------------------- //C8051F02X的16位SFR定义 //------------------------------------------- sfr16 DP = 0x82;   //数据指针 sfr16 TMR3RL = 0x92;   //定时器3重装值 sfr16 TMR3 = 0x94;   //定时器3计数器 sfr16 ADC0 = 0xbe;   //ADC0数据 sfr16 ADC0GT = 0xc4;   //ADC0大于窗口 sfr16 ADC0LT = 0xc6;   //ADC0小于窗口 sfr16 RCAP2 = 0xca;   //定时器2捕捉/重装 sfr16 T2 = 0xcc;   //定时器2 sfr16 RCAP4 = 0xe4;   //定时器4捕捉/重装 sfr16 T4 = 0xf4;   //定时器4 sfr16 DAC0 = 0xd2;   //DAC0数据 sfr16 DAC1 = 0xd5;   //DAC1数据 //------------------------------------------- //全局常量 //------------------------------------------- #define SYSCLK 22118400   //系统时钟频率(Hz) #define BAUDRATE 9600   //UART波特率(b/s) #define SAMPLERATE0 50000   //ADC0 采样频率(Hz) #define INT_DEC 256   //均值滤波计数值 //------------------------------------------- //函数原型 //------------------------------------------- void SYSCLK_Init(void); void PORT_Init(void); void UART0_Init(void); void ADC0_Init(void); void Timer3_Init(int counts); void ADC0_ISR(void); //------------------------------------------- //全局变量 //------------------------------------------- long result;   //放置经数字滤波后的结果 //------------------------------------------- //主程序 //------------------------------------------- void main(void) { long temperature;   //精度为百分之一的温度(℃) int temp_int, temp_frac;   //温度的整数和小数部分 WDTCN = 0xde;   //禁止看门狗定时器 WDTCN = 0xad; SYSCLK_Init();   //初始化振荡器 PORT_Init();   //初始化数据交叉开关和通用I/O端口 UART0_Init();   //初始化UART0 Timer3_Init(SYSCLK/SAMPLERATE0); //初始化定时器3溢出为采样速率 ADC0_Init();   //初始化ADC AD0EN = 1;   //使能ADC EA = 1;   //使能所有中断 while (1) { EA = 0;   //禁止中断 temperature = result; EA = 1;   //重使能中断 //计算温度百分之一精度 temperature = temperature - 41758; temperature = (temperature * 100L) / 154; temp_int = temperature / 100; temp_frac = temperature - (temp_int * 100); printf ("Temperature is %+02d.%02d\n", temp_int, temp_frac); } } //------------------------------------------- //系统时钟初始化 //------------------------------------------- //此程序初始化系统时钟使用22.1184MHz晶体作为系统时钟源 void SYSCLK_Init(void) { int i;   //延时计数器 OSCXCN = 0x67;   //启动外部振荡器22.1184MHz晶体 for (i=0; i < 256; i++);   //等待振荡器启动(>1ms) while (!(OSCXCN & 0x80));   //等待晶体振荡器稳定 OSCICN = 0x88;   //选择外部振荡器作为系统时钟源并允许丢失时钟检测器 } //------------------------------------------- //I/O端口初始化 //------------------------------------------- //配置数据交叉开关和通用I/O端口 void PORT_Init(void) { XBR0 = 0x04;   //使能UART0 XBR1 = 0x00; XBR2 = 0x40;   //使能数据交叉开关和弱上拉 P0MDOUT |= 0x01;   //使能TX0推挽输出 } //------------------------------------------- //UART0初始化 //------------------------------------------- //配置UART0 使用定时器1作为波特率发生器 void UART0_Init(void) { SCON0 = 0x50;   //SCON0: 模式1, 8位UART, 使能RX TMOD = 0x20;   //TMOD: 定时器1, 模式2, 8位重装 TH1 = -(SYSCLK/BAUDRATE/16);   //按波特率设置T1重装值 TR1 = 1;   //启动定时器1 CKCON |= 0x10;   //定时器1使用系统时钟作为时基 PCON |= 0x80;   //SMOD00=1 TI0 = 1;   //表示TX0就绪 } //------------------------------------------- //ADC0初始化 //------------------------------------------- //配置ADC0 使用定时器3溢出作为转换源, 转换结束产生中断 //使用左对齐输出模式允许ADC转换结束中断不使用时禁止ADC void ADC0_Init(void) { ADC0CN = 0x05; //ADC0禁止; 正常跟踪 mode; 定时器3溢出ADC0转换开始 //ADC0数据是左对齐 REF0CN = 0x07;   //允许温度传感器片内VREF和VREF输出缓冲器 AMX0SL = 0x0f;   //选择温度传感器作为ADC多路模拟转换输出 ADC0CF = (SYSCLK/2500000) << 3; //ADC转换时钟=2.5MHz ADC0CF |= 0x01;   //PGA增益=2 EIE2 |= 0x02;   //允许ADC中断 } //------------------------------------------- //定时器3初始化 //------------------------------------------- //配置定时器3,自动重装间隔由counts指定,不产生中断,使用系统时钟为时基 void Timer3_Init(int counts) { TMR3CN = 0x02;   //停止定时器3; 清除 TF3; //使用系统时钟为时基 TMR3RL = -counts;   //初始化重装值 TMR3 = 0xffff;   //设置为立即重装 EIE2 &= ~0x01;   //禁止定时器3中断 TMR3CN |= 0x04;   //启动定时器3 } //------------------------------------------- //ADC0中断服务程序 //------------------------------------------- //得到ADC0采样值, 将它加到运行总数中 //数字滤波计数器 减1,当为0时, 在全局变量放置经数字滤波后 //的结果 void ADC0_ISR(void) interrupt 15 { static unsigned int_dec=INT_DEC;   //数字滤波计数器 //当int_dec = 0时重设新值 static long accumulator=0L; AD0INT = 0;   //清除ADC转换结束标志 accumulator += ADC0;//读ADC值并加到运行总数中 int_dec--;   //更新数字滤波计数器 if (int_dec == 0) {   //如果为0记入结果 int_dec = INT_DEC;   //重设计数器 result = accumulator >> 8;//除以256,求平均值(数字滤波) accumulator = 0L;   //复位accumulator } } 2. 多通道数据采集 //此程序为ADC0的应用例程在中断模式使用定时器3溢出作为开始转换信号 //测量AIN0到AIN7的电压和温度传感器 //转换结果经过计算所得电压从UART0传输 //假设在XTAL1和XTAL2之间接22.1184MHz晶体 //系统时钟频率存储在全局常量SYSCLK,目标UART波特率存储在全局常量BAUDRATE //ADC0采样频率存储在全局常量SAMPLERATE0,电压参考值存储在VREF0 //目标器件: C8051F020 //链接工具: KEIL C51 6.03 / KEIL EVAL C51 //------------------------------------------- //包含文件 //------------------------------------------- #include //SFR声明 #include //------------------------------------------- //C8051F02X的16位SFR定义 //------------------------------------------- sfr16 DP = 0x82;   //数据指针 sfr16 TMR3RL = 0x92;   //定时器3重装值 sfr16 TMR3 = 0x94;   //定时器3计数器 sfr16 ADC0 = 0xbe;   //ADC0数据 sfr16 ADC0GT = 0xc4;   //ADC0大于窗口 sfr16 ADC0LT = 0xc6;   //ADC0小于窗口 sfr16 RCAP2 = 0xca;   //定时器2捕捉/重装 sfr16 T2 = 0xcc;   //定时器2 sfr16 RCAP4 = 0xe4;   //定时器4捕捉/重装 sfr16 T4 = 0xf4;   //定时器4 sfr16 DAC0 = 0xd2;   //DAC0数据 sfr16 DAC1 = 0xd5;   //DAC1数据 //------------------------------------------- //全局常量 //------------------------------------------- #define SYSCLK 22118400   //系统时钟频率(Hz) #define BAUDRATE 9600   //UART波特率(b/s) #define SAMPLERATE0 50000   //ADC0采样频率(Hz) #define VREF0 2430   //VREF 参考电平(mV) //------------------------------------------- //函数原型 //------------------------------------------- void SYSCLK_Init(void); void PORT_Init(void); void UART0_Init(void); void ADC0_Init(void); void Timer3_Init(int counts); void ADC0_ISR(void); //------------------------------------------- //全局变量 //------------------------------------------- long result[9];   //AIN0-7和温度传感器输出结果 //------------------------------------------- //主程序 //------------------------------------------- void main(void) { long voltage;   //电压以mV为单位 int i;   //循环计数器 WDTCN = 0xde;   //禁止看门狗定时器 WDTCN = 0xad; SYSCLK_Init();   //初始化振荡器 PORT_Init();   //初始化数据交叉开关和通用I/O UART0_Init();   //初始化UART0 Timer3_Init(SYSCLK/SAMPLERATE0); //初始化定时器3溢出作为采样率 ADC0_Init();   //初始化ADC AD0EN = 1;   //允许ADC EA = 1;   //允许所有中断 while (1) { for (i = 0; i < 9; i++) { EA = 0;   //禁止中断 voltage = result[i];   //从全局变量取得ADC值 EA = 1;   //重新使能中断 //计算电压(mV) voltage = voltage * VREF0; voltage = voltage >> 16; printf("Channel '%d' voltage is %ldmV\n", i, voltage); } } } //------------------------------------------- //系统时钟初始化 //------------------------------------------- //此程序初始化系统时钟使用22.1184MHz晶体作为系统时钟 void SYSCLK_Init(void) { int i;   //延时计数器 OSCXCN = 0x67;   //启动外部振荡器22.1184MHz晶体 for (i=0; i < 256; i++);   //等待振荡器启动 (>1ms) while (!(OSCXCN & 0x80));   //等待晶体振荡器稳定 OSCICN = 0x88;   //选择外部振荡器作为系统时钟源并允许丢失时钟检测器 } //------------------------------------------- //I/O端口初始化 //------------------------------------------- //配置数据交叉开关和通用I/O端口 void PORT_Init(void) { XBR0 = 0x04;   //使能UART0 XBR1 = 0x00; XBR2 = 0x40;   //使能数据交叉开关和弱上拉 } //------------------------------------------- //UART0初始化 //------------------------------------------- //配置UART0 使用定时1为波特率发生器 void UART0_Init(void) { SCON0 = 0x50;   //SCON0: 模式1, 8位UART, 允许RX TMOD = 0x20;   //TMOD: 定时器1, 模式2, 8位重装 TH1 = -(SYSCLK/BAUDRATE/16); //按波特率设置定时器1重装值 TR1 = 1;   //启动定时器1 CKCON |= 0x10;   //定时器1使用系统时钟为时基 PCON |= 0x80;   //SMOD00 = 1 TI0 = 1;   //表示TX0就绪 } //------------------------------------------- //ADC0初始化 //------------------------------------------- //配置ADC0 使用定时器3 溢出作为转换源, 转换结束产生中断使用左对齐输出模式 //使能ADC转换结束中断禁止ADC //注意: 使能低功率跟踪模式保证当改变通道时的跟踪次数最少 void ADC0_Init(void) { ADC0CN = 0x45;   //ADC0 禁止; 低功率跟踪模式 //当定时器3溢出时ADC0转换开始; ADC0数据左对齐 REF0CN = 0x07;   //使能温度传感器片内VREF和VREF输出缓冲器 AMX0SL = 0x00;   //选择AIN0为ADC多路模拟输出 ADC0CF = (SYSCLK/2500000) << 3; //ADC转换时钟=2.5MHz ADC0CF &= ~0x07;   //PGA增益=1 EIE2 |= 0x02;   //允许ADC中断 } //------------------------------------------- //定时器3初始化 //------------------------------------------- //配置定时器3 自动重装载时间间隔由指定(不产生中断) //使用系统时钟作为时基 void Timer3_Init(int counts) { TMR3CN = 0x02;   //停止定时器3; 清除TF3; //使用系统时钟作为时基 TMR3RL = -counts;   //初始化重装值 TMR3 = 0xffff;   //设置为立即重装 EIE2 &= ~0x01;   //禁止定时器3中断 TMR3CN |= 0x04;   //启动定时器3 } //------------------------------------------- //ADC0中断服务程序 //------------------------------------------- //ADC0转换结束中断服务程序 //读取ADC0采样值并存储在全局数组 //同时选择下一个通道转换 void ADC0_ISR(void) interrupt 15 { static unsigned char channel = 0;   //ADC多路模拟通道(0-8) AD0INT = 0;   //清除ADC转换结束标志 result[channel] = ADC0;   //读ADC值 channel++; //改变通道 if (channel == 9) { channel = 0; } AMX0SL = channel;   //设置多路模拟转换器到下一个通道 } 5.2数模转换器 5.2.1数模转换原理及性能指标 1. 转换原理 D/A转换器的原理很简单,可以总结为“按权展开,然后相加”几个字。即将要转换的数字量中每一位都按其权值分别转换为模拟量,并通过运算放大器求和相加,因此D/A转换器内部必须有一个解码网络,以实现按权值分别进行D/A转换。 解码网络通常有两种: 二进制加权电阻网络和T型电阻网络。在二进制加权电阻网络中,每位二进制位的D/A转换是通过相应位加权电阻实现的,这必然导致加权电阻阻值差别极大,尤其在D/A转换器位数较大时更不能容忍。例如,若某D/A转换器有12位,则最高位加权电阻为10kΩ时的最低位加权电阻应当是10kΩ×211=20MΩ。这么大的电阻在VLSI技术中很难制造出来,即便制造出来,其精度也很难符合要求。因此现代D/A转换器几乎毫不例外地采用T型电阻网络进行解码活动。 为了说明原理,现以4位D/A转换器为例介绍,它的原理框图如图514所示。在图中的虚线框内是T型电阻网络(桥上电阻为R,桥臂电阻为2R); OA为运算放大器,A点为虚拟地(接近0V); VREF为参考电压,由稳压电源提供; S3~S0为电子开关,受4位DAC寄存器中b3b2b1b0的控制。为了分析问题,设b3b2b1b0全为1,故S3S2S1S0全部和1端相连。由于A点为虚地,B点到A点和B点到地线的电阻一样都为2R,即I0=IL0,根据基尔霍夫电流定律: IL1=IL0+I0 ,分析C点到A点的电阻及到地线的电阻可得I1=IL1,则I0=1/2I1,同理可推得I1=1/2I2、I2=1/2I3,所以可得如下关系: I3=VREF2R=23×VREF24×R I2=I32=22×VREF24×R I1=I22=21×VREF24×R I0=I12=20×VREF24×R 图514T型电阻网络D/A转换原理框图 事实上,S3~S0的状态是受b3b2b1b0控制的,并不一定是全1,所以流入A点的电流应该是: IOUT1=b3I3+b2I2+b1I1+b0I0=(b323+b222+b121+b020)VREF24R(54) 选取Rf=R,并考虑A点为虚地,则有IRf=-Iout1。 因此,可以得到式(55): VOUT=IRfRf=-(b323+b222+b121+b020)VREF24RRf=-BVREF16(55) 对于n位T型电阻网络,式(55)可变为: VOUT=-(bn-12n-1+bn-22n-2+…+b121+b020)VREF2nRRf=-BVREF2n(56) 式中B为一个二进制数,T型电阻网络的D/A转换输出电压量绝对值与该二进制数的大小成正比。 2. 性能指标 D/A性能指标是衡量芯片质量的重要参数,主要的性能指标有4条。 1) 分辨率 分辨率(Resolution)是指D/A转换器能分辨的最小输出模拟增量,取决于输入数字量的二进制位数。一个n位的D/A转换器所能分辨的最小电压增量定义为满量程值的2-n倍。例如,满量程为10V的8位D/A 芯片的分辨率为10V×2-8=39mV; 而16位的D/A芯片的分辨率为10V×2-16=153μV。 2) 转换精度 转换精度(Conversion Accuracy)与分辨率是两个不同的概念。转换精度是指满量程时D/A的实际模拟输出值和理论值的接近程度。对T型电阻网络的D/A转换器,其转换精度与参考电压VREF、电阻值和电子开关的误差有关。例如,满量程时理论输出值为10V,实际输出值是在9.99到10.01之间,则其转换精度为±10mV。通常D/A转换器的转换精度为分辨率的一半,即为LSB/2。LSB(Least Significant Bit)是最低有效位,指最低1位数字变化引起输出电压幅度的变化量。 3) 偏移量误差 偏移量误差(Offset Error)是指输入数字量为零时,输出模拟量对零的偏移值。这种误差通常可以通过D/A转换器的外接VREF和电位器加以调整。 4) 线性度 线性度(Linearity)是指D/A转换器的实际转换特性曲线和理想直线之间的最大偏差。通常线性度不应超出±1/2LSB。 除此以外,指标还有转换速度、温度灵敏度等,通常这些参数都很小,一般不予考虑。 5.2.2C8051F020的DAC功能 C8051F020单片机有两个片内12位电压方式DAC。每个DAC的输出摆幅均为0V到(VREF1LSB),对应的输入码范围是0x000~0xFFF。可以用对应的控制寄存器DAC0CN和DAC1CN使能/禁止DAC0和DAC1。在被禁止时,DAC的输出保持在高阻状态,DAC的供电电流降到1μA或更小。DAC的功能框图如图515所示。每个DAC的电压基准在VREFD引脚提供。如果使用内部电压基准,为了使DAC输出有效,该基准必须被使能。有关配置DAC电压基准的详细信息将在5.3节介绍。 图515DAC功能框图 控制DAC工作的主要是控制寄存器DAC0CN和DAC1CN,两个SFR分别控制DAC0和DAC1,以DAC0CN为例来说明。控制寄存器DAC0CN的格式为: 其中,各位的含义如下: 位7(DAC0EN)——DAC0使能位。 0: DAC0禁止。DAC0输出引脚被禁止,DAC0处于低功耗关断方式。 1: DAC0使能。DAC0正常输出; DAC0处于工作状态。 位6和位5——未用。读=0000b; 写=忽略。 位4和位3(DAC0MD1-0)——DAC0方式位。 00: DAC输出更新发生在写DAC0H时。 01: DAC输出更新发生在定时器3溢出时。 10: DAC输出更新发生在定时器4溢出时。 11: DAC输出更新发生在定时器2溢出时。 位2~0(DAC0DF2~0): DAC0数据格式位。 5.2.3DAC输出更新 每个DAC都具有灵活的输出更新机制,允许全量程内平滑变化并支持无抖动输出更新,适合于波形发生器应用。下面的描述都是以DAC0为例,DAC1的操作与DAC0完全相同。注意,读DAC0L返回预锁存数据,所读值是最后写入该寄存器中的数据,而不是DAC0L锁存器中的值。但读DAC0H总是返回DAC0H锁存器中的值。 1. 根据软件命令更新输出 在默认方式下(DAC0CN.[4:3]=00),DAC0的输出在写DAC0数据寄存器高字节(DAC0H)时更新。注意,写DAC0L时数据被保持,对DAC0输出没有影响,直到对DAC0H的写操作发生。如果向DAC数据寄存器写入一个12位字,则12位的数据字被写到低字节(DAC0L)和高字节(DAC0H)数据寄存器。在写DAC0H寄存器后数据被锁存到DAC0。因此,如果需要12位分辨率,应在写入DAC0L之后写DAC0H。DAC可被用于8位方式,这种情况是将DAC0L初始化一个所希望的数值(通常为0x00),将数据只写入DAC0H。 2. 基于定时器溢出的输出更新 在前面介绍的ADC转换操作中,ADC转换可以由定时器溢出启动,不用处理器干预。与之类似,DAC的输出更新也可以用定时器溢出事件触发。这一特点在用DAC产生一个固定采样频率的波形时尤其有用,可以消除中断响应时间不同和指令执行时间不同对DAC输出时序的影响。当DAC0MD位(DAC0CN.[4:3])被设置为01、10或11时(分别为定时器3、定时器4或定时器2),对DAC数据寄存器的写操作被保持,直到相应的定时器溢出事件发生时DAC0H: DAC0L的内容才被复制到DAC输入锁存器,允许DAC数据改变为新值。 5.2.4DAC输出定标/调整 在某些情况下,对DAC0进行写入操作之前应对输入数据移位,以正确调整DAC输入寄存器中的数据。这种操作一般需要一个或多个装入和移位指令,因而增加软件开销和降低DAC的数据通过率。为了减少这方面的负担,数据格式化功能为用户提供了一种能对数据寄存器DAC0H和DAC0L中的数据格式编程的手段。3个DAC0DF位(DAC0CN.[2:0])允许用户在5种数据字格式指定一种,具体见DAC0CN寄存器定义。 DAC1的功能与上述DAC0的功能完全相同。 5.2.5数模转换举例 D/A转换器的编程相对A/D转换器要简单,按照要求设置好输出更新的条件,将要转换的数值量送到DAC数据寄存器就行。下面是产生锯齿波和阶梯波的示例。将DAC0设置成输出更新发生在写DAC0H时,即直接更新。DAC1设置成输出更新发生在定时器2溢出时。 1. 产生阶梯波 DAC0用程序更新输出,产生一个阶梯波形。 //--------------------------------------------- //DAC转换程序 //--------------------------------------------- //--------------------------------------------- //INCLUDES //--------------------------------------------- #include //寄存器定义文件 //C8051F02X的16位SFR定义 //--------------------------------------------- sfr16 DAC0 = 0xd2;   //DAC0数据寄存器 //--------------------------------------------- #define UP 0x010 #define T 1000 void d1ms(int count);   //延时程序 void config(void);   //配置程序 void main(void) { int i; config(); for(i=0;i<=4095;i+UP)   //形成阶梯波形 { DAC0=i;   //送数字量到DAC0直接更新输出 d1ms(T); } } void d1ms(int count) { int j; while(count--!=0) { for (j=0;j<100;j++); } } //--------------------------------------------- //配置程序 //--------------------------------------------- void config(void) { //Local Variable Definitions int n = 0; WDTCN = 0x07;   //看门狗控制寄存器 WDTCN = 0xDE;   //禁止看门狗定时器 WDTCN = 0xAD; //--------------------------------------------- //Oscillator Configuration //--------------------------------------------- OSCXCN = 0x67;   //外部振荡器寄存器,采用11.0952MHz for (n = 0; n < 255; n++);   //等待振荡器启动 while ((OSCXCN & 0x80) == 0); //等待晶振稳定 //--------------------------------------------- //Reference Control Register Configuration //--------------------------------------------- REF0CN = 0x02;   //内部偏压发生器工作 //--------------------------------------------- //--------------------------------------------- //DAC Configuration //--------------------------------------------- DAC0CN = 0x80;   //允许DAC0,程序直接更新输出,数据右对齐 DAC0L = 0x00;   //DAC1数据寄存器初值 DAC0H = 0x00; //--------------------------------------------- } 2. 产生锯齿波 用DAC1产生锯齿波,T2定时中断更新输出。 //--------------------------------------------- //DAC转换程序 //--------------------------------------------- //--------------------------------------------- //INCLUDES //--------------------------------------------- #include   //寄存器定义文件 //C8051F02X的16位SFR定义 //--------------------------------------------- sfr16 T2 = 0xcc;   //定时器2 sfr16 DAC1 = 0xd5;   //DAC1数据寄存器 //--------------------------------------------- void T2_ISR();   //T2中断服务程序 void config(void);   //配置系统 void main(void) { config();   //配置 EA=1;   //开中断 while(1); } void T2_ISR() interrupt 5 { TF2=0;   //清中断标志 DAC1++; //因为是T2溢出更新DAC1输出 //所以可以对SFR16操作,此时并不立即更新 if(DAC1>=0x1000) DAC1=0;   //形成锯齿波 } //--------------------------------------------- //Config Routine //--------------------------------------------- void config(void) { //Local Variable Definitions int n = 0; WDTCN = 0x07;   //看门狗控制寄存器 WDTCN = 0xDE;   //禁止看门狗定时器 WDTCN = 0xAD; //--------------------------------------------- //Oscillator Configuration //--------------------------------------------- OSCXCN = 0x67;   //外部振荡器寄存器,采用11.0952MHz for (n = 0; n < 255; n++); //等待振荡器启动 while ((OSCXCN & 0x80) == 0); //等待晶振稳定 //--------------------------------------------- //Reference Control Register Configuration //--------------------------------------------- REF0CN = 0x02;   //内部偏压发生器工作 //--------------------------------------------- //--------------------------------------------- //DAC Configuration //--------------------------------------------- DAC1CN = 0x98;   //允许DAC1,T2溢出中断更新输出,数据右对齐 DAC1L = 0x00;   //DAC1数据寄存器初值 DAC1H = 0x00; //--------------------------------------------- //--------------------------------------------- //Timer Configuration //--------------------------------------------- RCAP2H = 0x05;   //重新装入的时间常数 RCAP2L = 0x00; TH2 = 0x05;   //初始值 TL2 = 0x00;    T2CON = 0x04;   //启动T2 //--------------------------------------------- //--------------------------------------------- //Interrupt Configuration //--------------------------------------------- IE = 0x20;   //T2中断允许 } 5.3电 压 基 准 电压基准电路为控制ADC和DAC模块工作提供了灵活性。有3个电压基准输入引脚,允许两个ADC和两个DAC使用外部电压基准或片内电压基准输出。通过配置VREF模拟开关,ADC0还可以使用DAC0的输出作为内部基准,ADC1可以使用模拟电源电压作为基准,电压基准的功能框图如图516所示。内部电压基准电路由一个1.2V、15ppm/℃(典型值)的带隙电压基准发生器和一个两倍增益的输出缓冲放大器组成。内部基准电压可以通过VREF引脚连到应用系统中的外部器件或图516所示的电压基准输入引脚。建议在VREF引脚与AGND之间接入0.1μF和4.7μF的旁路电容。电压基准的设置由基准电压控制寄存器REF0CN来完成,它可使能/禁止内部基准发生器和选择ADC0、ADC1的基准输入。REF0CN中的BIASE位使能片内电压基准发生器,而REFBE位使能驱动VREF引脚的缓冲放大器。当被禁止时,带隙基准和缓冲放大器消耗的电流小于1μA(典型值),缓冲放大器的输出进入高阻状态。如果要使用内部带隙基准作为基准电压发生器,则BIASE和REFBE位必须被置1。如果不使用内部基准,则REFBE位可以被清0。 图516电压基准的功能框图 注意: 如果使用ADC或DAC,则不管电压基准取自片内还是片外,BIASE位必须被置为逻辑1。如果既不使用ADC也不使用DAC,则这两位都应被清0以节省功耗。AD0VRS和AD1VRS位分别用于选择ADC0和ADC1的电压基准源。基准电压控制寄存器REF0CN的格式如下: 其中,各位的含义如下: 位7~5——未用。读=000b,写=忽略。 位4(AD0VRS)——ADC0电压基准选择位。 0: ADC0电压基准取自VREF0引脚。 1: ADC0电压基准取自DAC0输出。 位3(AD1VRS)——ADC1电压基准选择位。 0: ADC1电压基准取自VREF1引脚。 1: ADC1电压基准取自AV+。 位2(TEMPE)——温度传感器使能位。 0: 内部温度传感器关闭。 1: 内部温度传感器工作。 位1(BIASE)——ADC/DAC偏压发生器使能位(使用ADC和DAC时该位必须为1)。 0: 内部偏压发生器关闭。 1: 内部偏压发生器工作。 位0(REFBE)——内部电压基准缓冲器使能位。 0: 内部电压基准缓冲器关闭。 1: 内部电压基准缓冲器工作。内部电压基准提供从VREF引脚输出。 电压基准的电气特性可参见附录C。温度传感器接在ADC0输入多路开关的最后一个输入端,REF0CN中的TEMPE位用于使能和禁止温度传感器。当被禁止时,温度传感器为默认的高阻状态,此时对温度传感器的任何A/D测量结果都是无意义的。 5.4比较器 C8051F020单片机内部有两个比较器,原理图及功能框图分别如图517和图518所示。CIP51内核与每个比较器之间的数据和控制接口都通过特殊功能寄存器实现,它可以将任何一个比较器置于低功耗关断方式。 图517比较器原理框图 图518比较器功能框图 每个比较器都有两个输入引脚,可以承受-0.25V~(AV+) +0.25V的外部驱动电压而不至损坏或发生工作错误。比较器的输出都可以经I/O交叉开关连到外部I/O引脚上。当被分配了外部引脚时,每个比较器的输出都可以被编程工作在漏极开路或者推挽方式,关于交叉开关和端口初始化的详细信息,请参见2.4节。 比较器的回差电压可以用软件通过比较器的控制寄存器进行编程,并且每个比较器都可以在上升沿、下降沿或在两个边沿产生中断。这些中断能将CIP51内核从休眠方式唤醒,而比较器的输出状态可以用软件进行查询。两个比较器原理和使用方法类似,下面主要以比较器0为例介绍。 比较器0的回差电压编程的方法是通过程序修改对应的比较器0控制寄存器CPT0CN的位3~0: 负向回差电压值由CP0HYN位的设置决定,正向回差电压值由CP0HYP位决定。用户既可以选择对回差电压值(指输入电压)编程,也可以选择对门限电压两侧的正向和负向回差对称度编程。比较器回差电压曲线如图519所示,有关回差电压指标见附录C中的比较器电气特性。比较器0控制寄存器CPT0CN各位的定义如下: 图519比较器回差电压曲线 其中,各位的含义如下: 位7(CP0EN)——比较器0使能位。 0: 比较器0禁止。 1: 比较器0使能。 位6(CP0OUT)——比较器0输出状态标志。 0: 电压值CP0+ < CP0-。 1: 电压值CP0+ > CP0-。 位5(CP0RIF)——比较器0上升沿中断标志。 0: 自该标志位被清除后,没有发生过比较器0上升沿中断。 1: 自该标志位被清除后,发生了比较器0上升沿中断。 位4(CP0FIF)——比较器0下降沿中断标志。 0: 自该标志位被清除后,没有发生过比较器0下降沿中断。 1: 自该标志位被清除后,发生了比较器0下降沿中断。 位3和位2(CP0HYP1-0)——比较器0正向回差电压控制位。 00: 禁止正向回差电压。 01: 正向回差电压=2mV。 10: 正向回差电压=4mV。 11: 正向回差电压=10mV。 位1和位0(CP0HYN1-0)——比较器0负向回差电压控制位。 00: 禁止负向回差电压。 01: 负向回差电压=2mV。 10: 负向回差电压=4mV。 11: 负向回差电压=10mV。 比较器的输出可以采用软件查询,也可以作为中断源来触发中断。在比较器输出的上升沿和/或下将沿都可以产生中断(有关中断允许和优先级控制的内容见2.3节)。比较器0的下降沿中断置1CP0FIF标志,比较器0的上升沿中断置1CP0RIF标志。这些位一旦被置1,将一直保持1状态直到被软件清除,可以在任意时刻通过读取CP0OUT位得到比较器0的输出状态。注意,在上电后直到比较器能稳定工作之前应忽略比较器的输出和中断。 每个比较器可以被单独使能或禁止(关断),通过置1,CP0EN位使能比较器0,通过清除该位禁止比较器0。如果比较器被禁止,而比较器的输出已通过交叉开关分配到I/O端口引脚上,则对应的引脚默认值为逻辑低电平,它的中断能力被停止,电源电流降到小于1μA。 另外,比较器0还可被配置为复位源,详见2.6节中的“3.比较器0复位”的介绍。 比较器1的操作与比较器0完全相同,只是比较器1不能被配置为复位源,而比较器1受CPT1CN寄存器控制,寄存器CPT1CN各位的定义如下: 其中,各位的含义如下: 位7(CP1EN)——比较器1使能位。 0: 比较器1禁止。 1: 比较器1使能。 位6(CP1OUT)——比较器1输出状态位。 0: 电压值CP1+ < CP1-。 1: 电压值CP1+ > CP1-。 位5(CP1RIF)——比较器1上升沿中断标志。 0: 自该标志位被清除后,没有发生比较器1上升沿中断。 1: 自该标志位被清除后,发生了比较器1上升沿中断。 位4(CP1FIF)——比较器1下降沿中断标志。 0: 自该标志位被清除后,没有发生过比较器1下降沿中断。 1: 自该标志位被清除后,发生了比较器1下降沿中断。 位3和位2(CP1HYP1-0)——比较器1正向回差电压控制位。 00: 禁止正向回差电压。 01: 正向回差电压=2mV。 10: 正向回差电压=4mV。 11: 正向回差电压=10mV。 位1和位0(CP1HYN1-0)——比较器1负向回差电压控制位。 00: 禁止负向回差电压。 01: 负向回差电压=2mV。 10: 负向回差电压=4mV。 11: 负向回差电压=10mV。 习题5 1. A/D转换器的作用是什么?D/A转换器的作用是什么?各在什么场合下使用? 2. A/D转换器在转换原理上有哪些类型?各有什么特点?C8051F系列单片机采用什么类型的A/D转换器? 3. D/A转换器一般为什么类型?为什么? 4. 衡量D/A转换器的技术指标有哪些? 5. 试用C51编程语言写出程序,分别用查询法和中断法(外部信号CNVSTR有效中断)对通道AIN0.0~AIN0.3进行采样,将采样数据通过UART0发送出去,通过PC的超级终端观察结果。 6. 如用C8051F020的ADC0进行16位数据的采集,用什么方法来实现?试用此方法对片内温度进行采集(查询法),将采集到的值转换为摄氏温度值通过PC的超级终端观察。如要得到器件周围的环境温度则如何计算? 7. 使用C8051F020的DAC1产生方波和锯齿波,使用定时器T4溢出更新输出。试用C51编程语言写出程序。 8. C8051F020单片机有几个比较器?如何配置使用?思考在实际的项目开发中什么时候会用到C8051F020单片机的比较器。