目录

江科大51单片机笔记16ADDA转换下

江科大51单片机笔记【16】AD/DA转换(下)

写在前言

此为博主自学江科大51单片机(B站)的笔记,方便后续重温知识

在后面的章节中,为了防止篇幅过长和易于查找,我把一个小节分成两部分来发,上章节主要是关于本节课的硬件介绍、电路图、原理图等理论知识,主要是为下章节的代码部分打基础。

我的单片机是24年12月在tb普中买的,型号是STC89C52,在原视频中引脚或接口不对应的我都会改正,保证在我的机子上能运行才发上来的,还有一些文字部分是我的理解,并非照搬,所以可能有理解不到位的现象。

如有误或交流,敬请指点提问

如果上一节的运放、电路图、结构图看不懂没关系,只需要看懂下面这个时序图就可以写代码实现功能了

一、AD模数转换

先导入之前写好的Delay和LCD1602模块,然后新建XPT2046点c和点h文件

接下来就是对照这个时序把数据读出来

https://i-blog.csdnimg.cn/direct/06b619dfa47047cca755f165e6b27eea.png

1.先定义引脚

https://i-blog.csdnimg.cn/direct/d957c6a023b94d13ae6c16f2fa1aadd5.png

#include <REGX52.H>

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

2.定义函数

给个参数(控制字),用于选择通道

并且定义一个变量ADAvalue,最后返回出来

https://i-blog.csdnimg.cn/direct/1cbab0b47ee54d67804d12b635e1a8b4.png

https://i-blog.csdnimg.cn/direct/9135c2d770b34c39acfb9989814fc49a.png

然后是根据上图对每个引脚先后赋值

首先把DCLK置0(初始化),CS置0,DIN赋值参数控制字的最高位

然后再给DCLK置1(上升沿),DIN就把最高位发出去,然后置0(下降沿),查看手册发现上下时间是ns级,所以不用延时

接下来就是同理依次把8个位发出去,用for循环实现

发出去之后就可以读数据了

https://i-blog.csdnimg.cn/direct/e980546c99444418ab62e839da24b6b3.png

接下来就是到图中这个位置,先给DCLK一个上升沿,一个下降沿,数据才会过来

进行一个判断,如果Dout为1,就让ADAValue把数据读出来(因为是16位所以或上0x8000)

然后对该过程循环16位,用for循环实现,因为只有12位是有数值的,后面都是零填充的,所以我们要在最后返回ADAValue的时候向右移位(如果选择8位读就移8位,12位读就移4位)

最后再把CS置高,结束这个时序,到这里就写完这个函数了

//XPT2046.c

#include <REGX52.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;
	XPT2046_CS=0;
	
	for(i=0;i<8;i++)
	{
		XPT2046_DIN=Command&(0x80>>i);
		XPT2046_DCLK=1;
		XPT2046_DCLK=0;
	}
	for(i=0;i<16;i++)
	{
		XPT2046_DCLK=0;
		XPT2046_DCLK=1;
		if(XPT2046_DOUT){ADValue|=(0x8000>>i);}
	}
	XPT2046_CS=1;
	if(Command&0x08)
	{
		return ADValue>>8;
	}
	else
	{
		return ADValue>>4;
	}
}
}

这里再讲一下 命令字怎么写

https://i-blog.csdnimg.cn/direct/3ed7c13984184cbdbff57802f31ca76f.png

S就是起始位,给1;A2A1A0是地址,选择通道;MODE就是模式选择位,=0是12位模式,=1是8位模式;SER/DFR也是选择模式,=1是单端模式,=0是差分模式,我们在这用单端模式;PD0和PD1表示掉电和内部参考电压配置的关系,若为11,处于供电状态,若为00,处于低功率模式(当PD1为0时关闭内部参考电压源,为1时打开,内部参考源作用即假设原本测量值是从0-5V,接到这就是2.5-5V)

在这里我们给1 A2 A1 A0 1 1 0 0,A2A1A0的选择要看下图

下图只看前面部分,A2A1A0控制测哪的电压

https://i-blog.csdnimg.cn/direct/1b4a4dd5e9a34a769987ddbe68788542.png

为了方便写代码,我们把上面这些值给宏定义,这里要根据开发板的原理图对应上图,以及前面教的命令字写出来,因为是要外部调用,所以写在.h文件里

https://i-blog.csdnimg.cn/direct/75ad2f959dc44de28270e0ccbea09f82.png

我们定义不同的命令字,一个是8位,一个是12位,方便后面选择

8位是0-255,12位是0-4096,更精细

#ifndef __XPT0246_H_
#define __XPT0246_H_

#define XPT2046_XP_8       0X9C
#define XPT2046_YP_8        0XDC
#define XPT2046_VBAT_8        0XAC
#define XPT2046_AUX_8        0XEC

#define XPT2046_XP_12       0X94
#define XPT2046_YP_12       0XD4
#define XPT2046_VBAT_12       0XA4
#define XPT2046_AUX_12       0XE4


unsigned int XPT2046_ReadAD(unsigned char Command);


#endif

因为AUX接到了一个接口上,所以这里我们不读,只读其他三个

//main.c

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

unsigned int ADValue;

void main()
{
	LCD_Init();
	LCD_ShowString(1,1,"ADJ NTC RG");
	while(1)
	{
 		ADValue=XPT2046_ReadAD(XPT2046_XP_8);
		LCD_ShowNum(2,1,ADValue,3);
 		ADValue=XPT2046_ReadAD(XPT2046_YP_8);
		LCD_ShowNum(2,5,ADValue,3);
 		ADValue=XPT2046_ReadAD(XPT2046_VBAT_8);
		LCD_ShowNum(2,9,ADValue,3);
		Delay(10);
	}
}

这里我需要把DS18B20拔掉才能显示正常,暂时还未找到原因

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

unsigned int ADValue;

void main()
{
	LCD_Init();
	LCD_ShowString(1,1,"ADJ  NTC  RG");
	while(1)
	{
 		ADValue=XPT2046_ReadAD(XPT2046_XP_8);
		LCD_ShowNum(2,1,ADValue,3);
 		ADValue=XPT2046_ReadAD(XPT2046_YP_8);
		LCD_ShowNum(2,6,ADValue,3);
 		ADValue=XPT2046_ReadAD(XPT2046_VBAT_8);
		LCD_ShowNum(2,11,ADValue,3);
		Delay(10);
	}
}

这样就可以调节我们电路板上的电阻,然后会把阻值通过AD转换后输出在LCD上

GR是光敏电阻,NTC是热敏电阻,AD1(ADJ)是可调电阻

https://i-blog.csdnimg.cn/direct/7f3f1c628d274eb986fe5530e5aa68dc.png

二、 DA数模转换

接下是PWM,因为跟之前写过的直流电机调速原理是一样的,所以我们复制过来创建一个新的工程

首先是更改接口

sbit DA=P2^1;

https://i-blog.csdnimg.cn/direct/6d47e7fd0fc84dff9d5f64016b46e0f1.png

然后是更改命名,可以点这里一键替代

https://i-blog.csdnimg.cn/direct/8f9c599961f4446b9c0d84c14d2ba53a.png

然后把关于按键的部分删掉,在主函数里实现一个呼吸灯的效果

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

sbit DA=P2^1;

unsigned char Counter,Compare;
unsigned char i;

void main()
{

	Timer0_Init();
	while(1)
	{
		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
{
	Key_Loop();
	TL0 = 0xA4;		//设置定时初值
	TH0 = 0xFF;		//设置定时初值
	Counter++;
	Counter%=100;
	if(Counter<Compare)
	{
		DA=1;
	}
	else
	{
		DA=0;
	}
}

其他部分都不需要改,这样就实现了呼吸灯的效果。