单片机学习笔记---AD模数转换&DA数模转换

目录

AD模数转换

XPT2046.c

XPT2046.h

main.c

DA数模转换

main.c


上一篇博客讲了AD/DA转换的工作原理,也介绍了运算放大器的工作原理,这节开始代码演示!

AD模数转换

新创建一个工程:AD模数转换

第一个工程将用到LCD1602和Delay函数,所以首先将我们之前讲过的代码模块添加进来

然后创建主程序main.c,XPT2046.c,XPT2046.h文件。

开始代码讲解:

XPT2046.c

首先根据原理图重定义引脚:

#include <REGX52.H>
#include <INTRINS.H>

//引脚定义
sbit XPY2046_DIN=P3^4;
sbit XPY2046_CS=P3^5;
sbit XPY2046_DCLK=P3^6;
sbit XPY2046_DOUT=P3^7;

 接下来就根据上一篇博客讲过的这个时序来定义函数

将DLCK拉高有立马拉低的过程中,手册上显示这些过程基本都是纳秒级别的,而我们单片机IO口的翻转周期是1微秒这么长,足够操作这些过程了。

将命令字节发送结束后时序就完成到这了

然后由于下降沿是表示读出,所以我们要读取数据之后首先将DCLK拉高,再来一个下降沿,数据才会过来。

unsigned int XPT2046_ReadAD(unsigned char Command)
{
	unsigned char i;//发送/读取的次数
	unsigned int Data=0;//数据
	
	XPY2046_DCLK=0;//片选之前先初始化一下DCLK
	//这一步不写也没关系,因为每次任务结束之后DCLK都会拉低到低电平的。
	
	XPY2046_CS=0;//片选
	
	//反复8次发送命令字的8位数据
	for(i=0;i<8;i++)
	{
		XPY2046_DIN=Command&(0x80>>i);//将命令字8位数据从左往右依次放到DIN线上
		XPY2046_DCLK=1;//DCLK拉高时输入数据
		XPY2046_DCLK=0;//立马拉低这个过程是1us,足够发送一位数据了。
	}
	
	//发送命令字的整个时间肯定超过TACO这个时间了,
	//所以读取之前可以不延时。
	
	//反复16次读取两个字节
	for(i=0;i<16;i++)
	{
		XPY2046_DCLK=1;//下降沿是表示读出,所以首先将DCLK拉高,
		XPY2046_DCLK=0;//再来一个下降沿,数据才会过来
		
		//如果XPY2046_DOUT=1就把1赋值给Data,
		//从左往右依次判断XPY2046_DOUT的16位数据并赋值给Data
		if(XPY2046_DOUT){Data|=(0x8000>>i);}
	}
	XPY2046_CS=1;//释放CS
	if(Command&0x08)//如果命令字的第5位是1表示是在8位转换的模式下
	{
		return Data>>8;//在8位转换的模式下,要把最终的数据右移8位截取低8位的数据
	}
	else
	{
		return Data>>4;//在12位转换的模式下,要把最终的数据右移4位截取低12位的数据
	}
}

在8位转换的模式下,要把最终的数据右移8位截取低8位的数据。

在12位转换的模式下,要把最终的数据右移4位截取低12位的数据。

读取数据时,后面的四位0填充可以不读,那么读取数据时直接i<12,也可以读出来再右移。

最后我们只要调用这个函数就可以得到AD值(范围:8位为0~255,12位为0~4095)。

XPT2046.h

接下来我们看一下命令字怎么写:

XPT2046芯片手册上的介绍:

PD1还选择内部的参考电压,如果给它1就选择内部参考电压2.5V,它就对2.5V进行等分。但是我们一般不用内部参考电压,我们用外部电压,PD1=0就外部参考电压,就是原理图的上的VREF,为5V。

我们的开发板上的这个ADC模块利用的是单端模式,SER/DFR=1

A2,A1,A0的配置要看这个表

A2,A1,A0组合选择,我们只要主要看表的前半部分就可以

PS:YN是-Y,XP是+X,YP是+Y

如果说我们要测+X的话,就把A2,A1,A0置为001或者011

如果想测电池电压(电池电压并不是测电池,它也当做是一个通用的AD)就将A2,A1,A0置为010

如果我们要测辅助输入的电压,就置为110

-Y端(YN)直接接地,不用测,所以直接不用管

如果我们要测YP(+Y),就置为101

最后我们配置的控制字一般是:

S=1(起始位)

A2,A1,A0=001或者011测+X(XP),010测VBAT,110测AUXIN,101测+Y(YP)

MODE=1/0 (8位转换和12位转换都可以试一下)

SER/DFR=1(单端模式)

PD1=0(外部参考电压)

PD0=0(低功耗模式)

为了方便,我们直接将这8位数据组合起来的命令字加一个宏定义,要用的时候可以直接调用就好了(声明为外部可调用)

#ifndef __XPT2046_H__
#define __XPT2046_H__

//选择8位转换模式
#define XPT2046_VBAT_8	    0xAC
#define XPT2046_AUX_8		0xEC
#define XPT2046_XP_8		0x9C	//0xBC
#define XPT2046_YP_8		0xDC

//选择12位转换模式
#define XPT2046_VBAT_12	      0xA4
#define XPT2046_AUX_12		  0xE4
#define XPT2046_XP_12		  0x94	//0xB4
#define XPT2046_YP_12		  0xD4

unsigned int XPT2046_ReadAD(unsigned char Command);

#endif

main.c

接下来就分别测电位器,热敏电阻和光敏电阻

PS:电位器就是可调电阻

我们将定义好的命令字对应传给我们XPT2046_ReadAD()函数,然后它结果显示在LCD1602液晶屏上

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

unsigned int ADValue;

void main(void)
{
	LCD_Init();
	LCD_ShowString(1,1,"ADJ  NTC  GR");
	//第一行显示"ADJ  NTC  GR"分别表示可调电阻,热敏电阻,光敏电阻
	while(1)
	{
		ADValue=XPT2046_ReadAD(XPT2046_XP_12);		//读取AIN0,可调电阻
		LCD_ShowNum(2,1,ADValue,4);				//显示AIN0
		ADValue=XPT2046_ReadAD(XPT2046_YP_12);		//读取AIN1,热敏电阻
		LCD_ShowNum(2,6,ADValue,4);				//显示AIN1
		ADValue=XPT2046_ReadAD(XPT2046_VBAT_12);	//读取AIN2,光敏电阻
		LCD_ShowNum(2,11,ADValue,4);			//显示AIN2
		Delay(100);
		//由于光敏电阻和热敏电阻非常敏感,
		//所以阻值变化非常快,读出来的数字电压变化也非常快
		//延迟是为了读出来的数据能够显示的时间长一点点,
		//这样我们就能看清数据,要不然数据变换更新太快我们都看不清楚。
	}
}

效果请看视频:

AD模数转换

以上就是AD数模转换的代码演示!

DA数模转换

接下来开始演示第二个代码,

新创建一个工程:DA数模转换

DA转换可以直接用PWM来实现,直接把之前写电机那一篇的程序添加进来修改一下。

main.c

首先是根据DA的原理图改IO口的定义

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

sbit DA=P2^1;

然后将所有Motor都改成DA,我们实现一个呼吸灯的效果就好了,所以就不用独立键盘相关的配置了,全部删掉:

unsigned char Counter,Compare;	//计数值和比较值,用于输出PWM
unsigned char i;

void main()
{
	Timer0_Init();
	while(1)
	{
		for(i=0;i<100;i++)
		{
			Compare=i;			//设置比较值,改变PWM占空比
			Delay(10);
		}
		for(i=100;i>0;i--)
		{
			Compare=i;			//设置比较值,改变PWM占空比
			Delay(10);
		}
	}
}

void Timer0_Routine() interrupt 1
{
	TL0 = 0x9C;		//设置定时初值
	TH0 = 0xFF;		//设置定时初值
	Counter++;
	Counter%=100;	//计数值变化范围限制在0~99
	if(Counter<Compare)	//计数值小于比较值
	{
		DA=1;		//输出1
	}
	else				//计数值大于比较值
	{
		DA=0;		//输出0
	}
}

 如果看不懂这里的话可以去看一下我这两篇博客,里面讲了PWM的原理和代码演示:

单片机学习笔记---直流电机驱动(PWM)-CSDN博客

单片机学习笔记---LED呼吸灯&直流电机调速-CSDN博客

 最终效果请看视频:(PS:手机录像的效果会有明显的屏闪)

DA数模转换

以上就是本篇的内容,源码会放在评论区,如有问题可评论区留言!

​​​​​​​

声明:

本专栏是本人跟着B站江科大的视频的学习过程中记录下来的笔记,部分内容借鉴了江科大的视频资料。我之所以记录下来是为了方便自己日后复习。如果你也是跟着江科大的视频学习的,可以配套本专栏食用,如有问题可以留言交流,同时也可以私信我拉进微信群聊。

单片机数模转换程序 将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的数据相互转换
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Vera工程师养成记

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

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

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

打赏作者

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

抵扣说明:

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

余额充值