目录
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数模转换
以上就是本篇的内容,源码会放在评论区,如有问题可评论区留言!