51单片机从零开始入门教程(模数转换篇)

本文介绍了AD/DA转换器的功能,如模拟-数字(A/D)和数字-模拟(D/A)转换,以及运算放大器在电路中的应用。重点讲解了XPT2046触摸屏控制器的4线电阻式A/D转换器,包括其特性、时序和编程指令。通过实例演示了如何利用这些技术实现液晶屏控制和模拟信号生成。
摘要由CSDN通过智能技术生成

参考教程:[16-1] AD/DA_哔哩哔哩_bilibili

温馨提示:本篇大多数原理可以不必深究,底层已经将复杂的部分封装好了,写代码的时候照着手册写就可以了。

1、AD/DA介绍:

(1)AD(Analog to Digital):模拟-数字转换,将模拟信号转换为计算机可操作的数字信号。(根据前面的学习,单片机看起来只能读取引脚当前是处于高电平还是低电平,并不能读取引脚的实际电压值,但是借助ADC模数转换器,单片机可以直接获取引脚的电压值,而不仅仅是单调的1/0)

(2)DA(Digital to Analog):数字-模拟转换,将计算机输出的数字信号转换为模拟信号。(根据前面的学习,单片机看起来只能将引脚置为高电平或是低电平,也就是说只能提供数字信号,但是借助PWM调制,单片机可以给引脚提供模拟信号,而不仅仅是单调的1/0)

(3)AD/DA转换打开了计算机与模拟信号的大门,极大的提高了计算机系统的应用范围,也为模拟信号数字化处理提供了可能。

2、硬件电路模型:

(1)AD转换通常有多个输入通道,用多路选择开关连接至AD转换器,以实现AD多路复用的目的,提高硬件利用率。

(2)AD/DA与单片机数据传送可使用并口(速度快、原理简单),也可使用串口(接线少、使用方便)。

(3)可将AD/DA模块直接集成在单片机内,这样直接写入/读出寄存器就可进行AD/DA转换,单片机的IO口可直接复用为AD/DA的通道。

3、运算放大器:

(1)运算放大器(简称“运放”)是具有很高放大倍数的放大电路单元。内部集成了差分放大器、电压放大器、功率放大器三级放大电路,是一个性能完备、功能强大的通用放大电路单元,由于其应用十分广泛,现已作为基本的电路元件出现在电路图中。

(2)运算放大器可构成的电路有:电压比较器、反相放大器、同相放大器、电压跟随器、加法器、积分器、微分器等。

(3)运算放大器电路的分析方法:虚短、虚断(负反馈条件下)。

(4)四种运放电路:

①电压比较器:

②反向放大器:

③同向放大器:

④电压跟随器:

4、XPT2046是一款4线制电阻式触摸屏控制器,内含12位分率125KHz转换速率逐步逼近型A/D转换器除XPT2046支持从1.5V到5.25V的低电压I/O接口。XPT2046能通过执行两次A/D转换查出被按的屏幕位置,除此之外,还可以测量加在触摸屏上的压力。内部自带2.5V参考电压,可以作为辅助输入、温度测量和电池监测之用,电池监测的电压范围可以从0V到6V。XPT2046片内集成有一个温度传感器。在2.7V的典型工作状态下,关闭参考电压,功耗可小于0.75mW。XPT2046采用微小的封装形式:TSSOP-16,QFN-16和VFBGA-48。工作温度范围为-40C~+85C。与ADS7846、TSC2046、AK4182A完全兼容。

5、XPT2046的时序及命令字:(重点,与写程序密切相关)

(1)给XPT2046发送命令字,XPT2046收到命令后通过DOUT将相应管脚的模拟信号转换为数字信号,然后将数字信号格式的数据一位一位传到主机,不过通过DOUT传到主机的二进制数据为了凑够两个字节,低8位或低4位(取决于分辨率)会使用0填充,软件需要用位移运算符处理一下再转换成十进制数据

(2)8位命令字的含义

(3)单端模式输入配置

(4)性能指标:

分辨率:指AD/DA数字量的精细程度,通常用位数表示。例如,对于5V电源系统来说,8位的AD可将5V等分为256份,即数字量变化最小一个单位时,模拟量变化5V/256=0.01953125V,所以,8位AD的电压分辨率为0.01953125V,AD/DA的位数越高,分辨率就越高

②转换速度:表示AD/DA的最大采样/建立频率,通常用转换频率或者转换时间来表示,对于采样/输出高速信号,应注意AD/DA的转换速度。

6、AD模数转换:

(1)该项目需要用到液晶屏模块的代码以及延时函数。

(2)添加缺失的代码文件,然后进行编译,按照主函数中的注释进行调试。

①XPT2046.h文件:

#ifndef __XPT2046_H__
#define __XPT2046_H__

//命令字宏定义(需要对照手册)
//全部选择单端输入方式,2位为1
//XP就是X+,YP就是Y+(对应开发板原理图)

//8位转换分辨率
#define XPT2046_XP_8    0x9C  //1001 1100(1000 1100也可以)
#define XPT2046_YP_8    0xDC  //1101 1100
#define XPT2046_VBAT_8  0xAC  //1010 1100
#define XPT2046_AUX_8   0xEC  //1110 1100
//12位转换分辨率
#define XPT2046_XP_12    0x94  //1001 0100(1000 0100也可以)
#define XPT2046_YP_12    0xD4  //1101 0100
#define XPT2046_VBAT_12  0xA4  //1010 0100
#define XPT2046_AUX_12   0xE4  //1110 0100


unsigned int XPT2046_ReadAD(unsigned char Command);

#endif

②XPT2046.c文件:

#include <REGX52.H>
#include "Delay.h"

sbit XPT2046_CS = P3^5;
sbit XPT2046_DCLK = P3^6;
sbit XPT2046_DIN = P3^4;
sbit XPT2046_DOUT = P3^7;

unsigned int XPT2046_ReadAD(unsigned char Command)
{
	unsigned char i;
	unsigned int ADVAlue = 0;
	
	XPT2046_DCLK = 0;  //初始时DCLK为0
	XPT2046_CS = 0;    //初始时CS取反为1,所以CS为0
	
	for(i = 0; i < 8; i++)  //写入8位命令
	{
		XPT2046_DIN = Command & (0x80 >> i);  //把命令一位一位地写在DIN上
		XPT2046_DCLK = 1;    //DCLK上升沿写入一位命令
		XPT2046_DCLK = 0;    //为下一个上升沿做准备
	}
	
	Delay(10);
	
	for(i = 0; i < 16; i++)  //读DOUT的16位数据
	{
		XPT2046_DCLK = 1;    //DCLK上升沿,AD往DOUT上发一位数据
		XPT2046_DCLK = 0;    //为下一个上升沿做准备
		if(XPT2046_DOUT)
		{
			ADVAlue |= (0x8000 >> i);  //把DOUT上的数据一位一位地读进来
		}
	}
	
	Delay(10);
	
	XPT2046_CS = 1;  //读过程结束,CS置为1
	
	//DOUT传来的二进制数据为了凑够两个字节,低8位或低4位会使用0填充(Zero Filled)
	//所以得到的二进制数据需要把低位的0去掉才能转换为十进制数据
	if(Command & 0x08)  //判断命令字的第三位
		return ADVAlue >> 8;  //选择8位转换分辨率的处理
	else
		return ADVAlue >> 4;  //选择12位转换分辨率的处理
}

③main.c文件:

#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
#include "XPT2046.h"

unsigned int ADValue = 0;

void main()
{
	LCD_Init();
	LCD_ShowString(1,1,"ADJ  NTC  RG");
	while(1)
	{
		/*
		ADValue = XPT2046_ReadAD(XPT2046_XP_12); //读取X+的电压
		//转动AD/DA模块的电位器
		//理论上选择8位转换分辨率最大值为0255,12位转换分辨率最大值为4095
		LCD_ShowNum(2,1,ADValue,4);
		*/
		
		ADValue = XPT2046_ReadAD(XPT2046_XP_8); //读取X+的电压
		LCD_ShowNum(2,1,ADValue,3);
		
		ADValue = XPT2046_ReadAD(XPT2046_YP_8); //读取Y+的电压
		LCD_ShowNum(2,6,ADValue,3);
		
		ADValue = XPT2046_ReadAD(XPT2046_VBAT_8); //读取VABT的电压
		LCD_ShowNum(2,11,ADValue,3);
		
		Delay(10);
	}
}

7、DA数模转换:

(1)该项目就是之前实现过的呼吸灯,需要延时函数,然后在main.c文件中添加如下代码,进行编译。

#include <REGX52.H>
#include "Timer0.h"
#include "Delay.h"

sbit DA = P2^1;   //LED2接收模拟信号
unsigned char Counter, Compare, i;

void main()
{
	Timer0_Init();
	Compare = 0;
	while(1)
	{
		//本例就是之前实现过的呼吸灯
//在主函数中不断改变自设的比较值,这样定时器0写出来的方波脉冲占空比会不断变化,从而实现PWM调制(数模转换),产生模拟信号
		for(i=0;i<100;i++)
		{
			Compare = i;
			Delay(10);
		}
		for(i=100;i>0;i--)
		{
			Compare = i;
			Delay(10);
		}
	}
}

void Timer0_Routine()  interrupt 1  //CPU响应中断后执行的函数
{
	Counter++;
	Counter %= 100;
	
	if(Counter < Compare)  //计数器和自设比较值比较
	{
		DA = 1;  //计数器低于比较值,输出高电平
	}
	else
	{
		DA = 0;  //计数器高于比较值,输出低电平
	}
	
	//每次中断结束都要重置计数单元
	TL0 = 0xA4;		//设置定时初值
	TH0 = 0xFF;		//设置定时初值
}

(2)将生成的.hex文件下载到开发板中,观察第二盏LED灯的现象。

  • 23
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
单片机数模转换程序 将da#include //52系列单片机头文件 #include #define uchar unsigned char #define uint unsigned int sbit dula=P2^6; //申明U1锁存器的锁存端 sbit wela=P2^7; //申明U2锁存器的锁存端 sbit adwr=P3^6; //定义AD的WR端口 sbit adrd=P3^7; //定义AD的RD端口 sbit led=P2^5; sbit DAC0832_CS = P3^2; sbit DAC0832_WR = P3^6; sbit AD_CS=P0^7; uchar code table[]={ 0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71}; uchar weima[] = {0xff,0xfe,0xfd,0xfb,0xf7,0xef,0xdf}; void delayms(uint xms) { uint i,j; for(i=xms;i>0;i--) //i=xms即延时约xms毫秒 for(j=110;j>0;j--); } void display(uchar bai,uchar shi,uchar ge) //显示子函数 { dula=1; P0=table[bai]|0x80; //送段选数据 dula=0; P0=0xff; //送位选数据前关闭所有显示,防止打开位选锁存时 wela=1; //原来段选数据通过位选锁存器造成混乱 P0=0x7e; //送位选数据 wela=0; delayms(1); //延时 dula=1; P0=table[shi]; dula=0; P0=0xff; wela=1; P0=0x7d; wela=0; delayms(1); dula=1; P0=table[ge]; dula=0; P0=0xff; wela=1; P0=0x7b; wela=0; delayms(1); } /*void displays(uchar a ,uchar b,uchar c) //显示子函数 { dula=1; P0=table[a]; //送段选数据 dula=0; P0=0xff; //送位选数据前关闭所有显示,防止打开位选锁存时 wela=1; //原来段选数据通过位选锁存器造成混乱 P0=0x77; //送位选数据 wela=0; delayms(1); //延时 dula=1; P0=table[b]; dula=0; P0=0xff; wela=1; P0=0x6f; wela=0; delayms(1); dula=1; P0=table[c]; dula=0; P0=0xff; wela=1; P0=0x5f; wela=0; delayms(1); } */ void displays(uchar shuzi,uchar weizhi,bit dp) { dula=1; if(dp) P0 = table[shuzi]|0x80; else P0 = table[shuzi]; dula=0; wela=1; P0 = weima[weizhi]; wela=0; } void main() // 主程序 { uint ad; uchar A1,A2,A3,adval; AD_CS=1; //置CSAD为0,选通ADCS 以后不必再管ADCS DAC0832_CS = 0; DAC0832_WR = 0; while(1) { wela=1; P0=0x7f; wela=0; adwr=1; _nop_(); adwr=0; //启动AD转换 _nop_(); adwr=1; P1=0xff; //读取P1口之前先给其写全1 adrd=1; //选通ADCS _nop_(); adrd=0; //AD读使能 _nop_(); adval=P1; ad=adval*1.941; //AD数据读取赋给P1口 adrd=1; A1=ad/100; //分出百,十,和个位 A2=ad0/10; A3=ad; P1=(~adval); P0=adval; displays(adval/100,4,0); delayms(1); displays(adval0/10,5,0); delayms(1); displays(adval,6,0); delayms(1); display(A1,A2,A3); } } ad的数据相互转换
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zevalin爱灰灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值