单片机学习笔记---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

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

cpp 复制代码
#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拉高,再来一个下降沿,数据才会过来。

cpp 复制代码
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位数据组合起来的命令字加一个宏定义,要用的时候可以直接调用就好了(声明为外部可调用)

cpp 复制代码
#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液晶屏上

cpp 复制代码
#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口的定义

cpp 复制代码
#include <REGX52.H>
#include "Delay.h"
#include "Timer0.h"

sbit DA=P2^1;

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

cpp 复制代码
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数模转换

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

相关推荐
集芯微电科技有限公司4 分钟前
AD536A高性能真有效值直流转换电路替代PC2909
人工智能·单片机·嵌入式硬件·神经网络·生成对抗网络
sensen_kiss4 分钟前
CPT306 Principles of Computer Games Design 电脑游戏设计原理 Pt.3 实时图形
学习·计算机视觉
2501_926978335 分钟前
物理学原理和人工智能领域的底层一致性
人工智能·经验分享·笔记·ai写作
可乐鸡翅好好吃7 分钟前
关于频率(HZ)与周期(ms)的转换
单片机·嵌入式硬件
努力的lpp8 分钟前
2024小迪安全课程第三节复习笔记
笔记·安全
冥王丁B17 分钟前
第31章 Prompt 与聊天模型笔记
笔记·python·prompt
进击的横打20 分钟前
【车载开发系列】RH850常用的定时器
单片机·嵌入式硬件·rh850
東雪木25 分钟前
编程算法学习——栈与队列算法
学习·算法·排序算法
左左右右左右摇晃26 分钟前
Java笔记——包装类(自动拆装箱)
java·笔记·python
ADHD多动联盟40 分钟前
什么是儿童ADHD的运动干预方案?主要有怎样的应对分心走神的疗法?
学习·学习方法·玩游戏