【单片机】15-AD和DA转换

news2024/11/26 9:55:46

1.AD转换及其相关背景知识

1.基本概念

1.什么是AD转换?

A(A,analog,模拟的,D,digital,数字的)

现实世界是模拟的,连续分布的,无法被分成有限份;

计算机世界是数字的,离散分布的,可以被分成有限份的

AD转换就是把一个物理量从模拟的转换成数字的。

2.AD转换的意义

想要计算机来实现现实世界

3.什么情况下需要AD转换

CPU是数字的【要准确的0V或者5V】

2.AD转换的原理

1.比较器

将差一点的电压转换为准确的二进制

所有的AD转换芯片内部都是用比较器来实现的。

2.和十进制转二进制有点像

使用除法

3.AD转换中的主要概念

1.位数

AD转换后转出来的二进制数由几位二进制来表示。【实际结果是一样大】

位数越多,越细腻。【精度越高】

2.量程

AD转换器可以接受的模拟量的范围

3.精度

精度==准确度

简单理解就是转出来有多准

【精度越小可靠率越高】

4.分辨率

AD转换器转出来的二进制数,每一格表示多少

5.转换速率(转换时间)

时间越短,速率越大

6.例子

输入电压范围:0-5V,AD转换输出位数是10,精度是0.01V

量程:0-5V

分辨率:(5-0)/2exp(10)=0.00488V

比如一次AD转换后得到数据为:【1010101010】,对应电压值:1010101010-->十进制:682,电压=682*0.00488=3.328V,考虑精度后为=3.33V

4.AD转换在系统中存在的方式

1.CPU外部扩展专用AD芯片

2.CPU内部集成AD模块(内部外设)

2.原理图和数据手册

https://www.semiee.com/file/ETEK/ETEK-ET2046.pdf

1.原理图

2.数据手册

 AIN0-AIN3:不能同时采集,同一时间只能采集一路

3.SPI接线

CLK接P1.0

CS接P1.1

DI接P1.2

DO接P1.3

4.3种模拟电压变化原理

AIN0靠滑动变阻器控制电压变化

AIN1靠热敏电阻NTC

AIN2靠光敏电阻

5.ET2046控制字

bit7:起始位【高表示开始传输】:1

bit6-bit4:决定采样哪一路(AIN1,AIN0,AINT2,AINT3):

AIN0:001/011         X+

AIN1:101                Y+

AIN2:010                VBAT

AIN3:110                VBAT

bit3:设置ADC精度:【1:使用bit8位】【0:使用bit12位--一般使用这个】:0

bit2:【1:表示用单端模式】【0:表示差分模式(触摸屏)】:1

bit1:power down模式使能,00表示使能

 读AIN0:0b 1001 0100=0x94

读AIN1:0b 1101 0100=0xd4

读AIN2;0b 1010 0100=0xa4
读AIN3:0b 1110 0100=0xe4

AIN0:001/011         X+

AIN1:101                Y+

AIN2:010                VBAT

AIN3:110                VBAT

3.分析时序

1.时序图

1.SPI变种

回顾SPI知识点:
【单片机】13-实时时钟DS1302-CSDN博客

有CS,DCLK,I/O

2.上升沿写入下降沿读出

之前DS1302(SPI)的时候也是这样

上升沿写入:当CLK为上升沿的时候,数据通过DI从SPI主设备写入到SPI从设备

下降沿读出:当CLK为下降沿的时候,数据通过DO从SPI从设备读入到SPI主设备

3.读写都是高位在前

之前DS1302(SPI)的时候是低位在前

4.注意写和读的交界点

2.官方例程分析

1.ET2046写数据

/*******************************************************************************
* 函 数 名       : xpt2046_wirte_data
* 函数功能		 : XPT2046写数据
* 输    入       : dat:写入的数据
* 输    出    	 : 无
*******************************************************************************/
void xpt2046_wirte_data(u8 dat)
{
	u8 i;

	CLK = 0;//可以忽略的
	_nop_();
	for(i=0;i<8;i++)//循环8次,每次传输一位,共一个字节
	{
		//先准备好数据,在置CLK=0
		DIN = dat >> 7;//先传高位再传低位
		dat <<= 1;//将低位移到高位
		CLK = 0;//CLK由低到高产生一个上升沿,从而写入数据
		_nop_();	
		CLK = 1;
		_nop_();
	}
}

2.ET2046读数据

/*******************************************************************************
* 函 数 名       : xpt2046_read_data
* 函数功能		 : XPT2046读数据
* 输    入       : 无
* 输    出    	 : XPT2046返回12位数据
*******************************************************************************/
u16	xpt2046_read_data(void)
{
	u8 i;
	u16 dat=0;

	CLK = 0;
	_nop_();
	for(i=0;i<12;i++)//循环12次,每次读取一位,大于一个字节数,所以返回值类型是u16
	{
		dat <<= 1;
		CLK = 1;
		_nop_();
		CLK = 0; //CLK由高到低产生一个下降沿,从而读取数据
		_nop_();
		dat |= DOUT;//先读取高位,再读取低位。	
	}
	return dat;	
}

3.ET2046返回AD值

/*******************************************************************************
* 函 数 名       : xpt2046_read_adc_value
* 函数功能		 : XPT2046读AD数据
* 输    入       : cmd:指令
* 输    出    	 : XPT2046返回AD值
*******************************************************************************/
u16 xpt2046_read_adc_value(u8 cmd)
{
	u8 i;
	u16 adc_value=0;

	CLK = 0;//先拉低时钟
	CS  = 0;//使能XPT2046
	xpt2046_wirte_data(cmd);//发送命令字
	for(i=6; i>0; i--);//延时等待转换结果,这个时候进行AD转换
	CLK = 1;//发送应该
	_nop_();
	CLK = 0;//发送一个时钟,清除BUSY
	_nop_();
	adc_value=xpt2046_read_data();
	CS = 1;//关闭XPT2046
	return adc_value;
}

4.main函数

/*******************************************************************************
* 函 数 名       : main
* 函数功能		 : 主函数
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void main()
{	
	u16 adc_value=0;
	float adc_vol;//ADC电压值
	u8 adc_buf[3];

	while(1)
	{	
		//0x94:对应AINT0--0b 1001 1100			
		adc_value=xpt2046_read_adc_value(0x94);//测量电位器
		adc_vol=5.0*adc_value/4096;//将读取的AD值转换为电压
		adc_value=adc_vol*10;//放大10倍,即保留小数点后一位
		adc_buf[0]=gsmg_code[adc_value/10]|0x80;
		adc_buf[1]=gsmg_code[adc_value%10];
	   	adc_buf[2]=0x3e;//显示单位V
		smg_display(adc_buf,6);		
	}		
}

4.代码实践【AD转换】

1.将12bit位的数值分2次输出

//AD value是12bit的,分2波出去【因为一次只能输出8位】
void uart_send_advalue(u16 val){
	uart_send_byte((val>>8)&0xff);	//高8位
	uart_send_byte(val&0xff);	//低8位
	uart_send_byte(0);//分割符
}

2. 计算电压值

3.读取AD数值:

ET2046.c

#include"ET2046.h"

/*******************************************************************************
* 函 数 名       : xpt2046_read_adc_value
* 函数功能		 : XPT2046读AD数据
* 输    入       : cmd:指令
* 输    出    	 : XPT2046返回AD值
*******************************************************************************/
u16 xpt2046_read_adc_value(u8 cmd)
{
	u8 i;
	u16 adc_value=0; //局部变量的初始化非常重要

	CLK = 0;//先拉低时钟
	CS  = 0;//使能XPT2046
	
	//写入数据
	for(i=0;i<8;i++)//循环8次,每次传输一位,共一个字节
	{
		//先准备好数据,在置CLK=0
		DIN = cmd >> 7;//先传高位再传低位
		cmd <<= 1;//将低位移到高位
		CLK = 0;//CLK由低到高产生一个上升沿,从而写入数据
		_nop_();	
		CLK = 1;
		_nop_();
	}
	
	
	for(i=6; i>0; i--);//延时等待转换结果,这个时候进行AD转换
	CLK = 1;//发送应该
	_nop_();
	CLK = 0;//发送一个时钟,清除BUSY
	_nop_();
	
	
	for(i=0;i<12;i++)//循环12次,每次读取一位,大于一个字节数,所以返回值类型是u16
	{
		adc_value <<= 1;
		CLK = 1;
		_nop_();
		CLK = 0; //CLK由高到低产生一个下降沿,从而读取数据
		_nop_();
		adc_value |= DOUT;//先读取高位,再读取低位。	
	}
	
	

	CS = 1;//关闭ET2046
	return adc_value;
}

ET2046.h

#ifndef _xpt2046_H
#define _xpt2046_H


#include "reg51.h"
#include  "intrins.h" 

typedef unsigned int u16;	//对系统默认数据类型进行重定义
typedef unsigned char u8;
typedef unsigned long u32;



//管脚定义
sbit DOUT = P1^3;	  //输出
sbit CLK  = P1^0;	  //时钟
sbit DIN  = P1^2;	  //输入
sbit CS   = P1^1;	  //片选


//函数声明
u16 xpt2046_read_adc_value(u8 cmd);

#endif

main.c

#include"ET2046.h"
#include"uart.h"

#define CMD_READ_AIN0 0x94	//滑动变阻器
#define CMD_READ_AIN1 0xD4	//NTC--热敏电阻
#define CMD_READ_AIN2 0xA4	//GR1--光敏电阻
#define CMD_READ_AIN3 0xE4	//外部输入的电压值


void Delay400000us()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	_nop_();
	i = 17;
	j = 208;
	k = 27;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}



//AD value是12bit的,分2波出去【因为一次只能输出8位】
void uart_send_advalue(u16 val){
	uart_send_byte((val>>8)&0xff);	//高8位
	uart_send_byte(val&0xff);	//低8位
	uart_send_byte(0);//分割符
}


void main(){
	
	//注意:定义一个变量要记得初始化,要不然后面可能出现问题
	u16 val=0;//12bit数值
	
	uart_init();
	
	while(1){
		val=xpt2046_read_adc_value(CMD_READ_AIN0);
	
		uart_send_advalue(val);
		
		Delay400000us();
	}
}

4.串口直接显示电压值

1.关键点

(1)直接显示电压值,而不是采样AD值

(2)以文本方式显示,而不是十六进制方式

2.将数值转换为十进制

//以文本方式发送c过去,意思就是要串口助手用文本方式来查看,看到的是
//这个数字本身
void uart_send_text(unsigned char c){
	//思路就是把c以十进制方式显示的几个数字,挨个变成文本发出去
	unsigned char i;//因为c是unsigned char 范围是0-255
	
	//先计算得出c的最高位,然后发出去
	i=c/100;
	uart_send_byte(i+48);//+48是对应ASCII
	
	//计算次高位
	c=c%100;
	i=c/10;
	uart_send_byte(i+48);
	
	//计算个位
	c=c%10;
	i=c;
	uart_send_byte(i+48);
	
	//发送一个换行
	uart_send_byte('\r');
	uart_send_byte('\n');
}

因为我们这里的电压是5V,对应5000mV,明显上面的0-255不在范围内,所以我们要求传入的是unsigned int c,【0-2的16次方】才足够

//因为这个函数的范围是unsigned char---->是2的8次方
//但是我们最大电压值为:5V----5000mV,所以我们这里要使用unsigned int
//以文本方式发送c过去,意思就是要串口助手用文本方式来查看,看到的是
//这个数字本身
void uart_send_text2(unsigned int c){
	//思路就是把c以十进制方式显示的几个数字,挨个变成文本发出去
	unsigned char i;//因为c是unsigned char 范围是0-255
	
	//因为我们知道电压值不会超过5000mV,所以只考虑显示1万以内的数据
	//先计算得出c的最高位,然后发出去
	i=c/1000;
	uart_send_byte(i+48);//+48是对应ASCII
	
	c=c%1000;
	i=c/100;
	uart_send_byte(i+48);//+48是对应ASCII
	
	//计算次高位
	c=c%100;
	i=c/10;
	uart_send_byte(i+48);
	
	//计算个位
	c=c%10;
	i=c;
	uart_send_byte(i+48);
	
	//发送一个换行
	uart_send_byte('\r');
	uart_send_byte('\n');
}

5.DA转换

将数字转换为模拟的

1.DA转换的原理

为了让数字量转换成模拟量,必须将每一位代码按其权重的大小转换为相应的模拟量,然后再把这些模拟量相加。

2.原理图和案例分析

1.运算放大器(LM358)

放大作用:将数字信号-----》模拟信号

隔离作用:防止输出信号影响输入信号

2.PWM数字信号

当输入的PWM数字信号一直为1,则输出的模拟信号一直为高电压

如果输入的PWM数字信号一直为0,则输出的模拟信号一直为低电压

关键点:取决于输入的PWM信号的高低电平所占的时间。【连续变化的模拟量】

3.LM358

其实不接LM358,直接用IO口连接LED实现现象也一样。(说明灯的亮度只与PWM输入的电压值的大小有关)

4.注意点

真正的DA一般是专用芯片或者CPU内置模块,给数字值输出平滑模拟量

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1056390.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小谈设计模式(13)—外观模式

小谈设计模式&#xff08;13&#xff09;—外观模式 专栏介绍专栏地址专栏介绍 外观模式主要目的角色分析外观&#xff08;Facade&#xff09;角色子系统&#xff08;Subsystem&#xff09;角色客户端&#xff08;Client&#xff09;角色 工作原理核心思想总结简化接口解耦客户…

Page Cache的产生和释放

Page Cache是如何产生和释放的&#xff0c;通俗一点&#xff0c;就是它的“生”&#xff08;分配&#xff09;与“死”&#xff08;释放&#xff09;&#xff0c;即 Page Cache 的生命周期。 Page Cache产生 Page Cache有两种产生的方式&#xff1a; Buffered I/O&#xff08…

十四天学会C++之第一天(入门和基本语法)

C的起源和历史 C诞生于20世纪80年代初&#xff0c;它的创造者是计算机科学家Bjarne Stroustrup。当时&#xff0c;Stroustrup在贝尔实验室工作&#xff0c;他希望为C语言添加一些功能&#xff0c;以便更好地支持系统开发。这个愿望促使他创建了C。 C的名字来源于它的基因&…

检索qpython文件夹下的.py,将文件复制单独文件夹并给出进度条

基本任务 检索qpython文件夹下的.py&#xff0c;将文件复制单独文件夹并给出进度条详细说明 首先导入了os和shutil模块&#xff0c;它们分别用于进行文件、文件夹操作和复制文件操作。 然后定义了源文件夹路径和目标文件夹路径。源文件夹路径指定了需要遍历的文件夹&#xff…

C语言刷题(Day1)

前言 本章我们带来几个经典得C语言练习题。 不要认为之前学过C语言&#xff0c;刷过这些题就不愿意再做题了。对待技术&#xff0c;我们永远要怀以一种空杯心态。 温故而知新&#xff0c;可以为师矣。 老师说&#xff0c;每道题都要尝试用不同得解法&#xff0c;去思考更多的…

动态规划算法(2)--最大子段和与最长公共子序列

目录 一、最大子段和 1、什么是最大子段和 2、暴力枚举 3、分治法 4、动态规划 二、最长公共子序列 1、什么是最长公共子序列 2、暴力枚举法 3、动态规划法 4、完整代码 一、最大子段和 1、什么是最大子段和 子段和就是数组中任意连续的一段序列的和&#xff0c;而…

Django之模板

一&#xff09;模板&#xff08;T&#xff09; 什么时候会使用模板呢&#xff1f; 仅对于Django这个框架来说&#xff0c;因为其是默认前后端不分离的框架&#xff08;前后端不分离值开发时前后端的代码在一起&#xff0c;不通过接口的方式连接&#xff0c;通过模板渲染的方式…

七大基于比较的排序算法(JAVA)

目录 冒泡排序 优化&#xff1a; 堆排序 插入排序 希尔排序 归并排序 快速排序 优化 选择排序 排序算法的稳定性&#xff1a; 大小相同的元素在排序前后相对位置相同就称其为稳定的排序。 注&#xff1a;一个本身就是稳定的排序 是可以实现为不稳定的排序的 &#x…

JavaSE学习之--抽象类,接口,内部类

&#x1f495;"没有眼泪我们就会迷路&#xff0c;彻底变成石头&#xff0c;我们的心会变成冰凌&#xff0c;吻会变成冰块。"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;JavaSE学习之--抽象类&#xff0c;接口&#xff0c;内部类 目录 一.抽象…

【牛客网】OR59 字符串中找出连续最长的数字串

题目 思路 创建两个字符串 temp 和 ret 创建指针i用来遍历字符串通过i遍历字符串,如果遇到数字则将这个数组加到字符串temp中 i,如果遇到字母,则判断temp字符串的长度和ret字符串的长度,如果temp<ret则说明这个字符串不是要的字符串,如果temp>ret则说明此时temp字符串是…

线性表的链式存储结构——链表

一、顺序表优缺点 优点&#xff1a;我们知道顺序表结构简单&#xff0c;便于随机访问表中任一元素&#xff1b; 缺点&#xff1a;顺序存储结构不利于插入和删除&#xff0c;不利于扩充&#xff0c;也容易造成空间浪费。 二、链表的定义 ①&#xff1a;概念&#xff1a; 用一组任…

springmvc-页面跳转表单标签其他标签tomcat控制台中文乱码问题

1. WEB-INF下页面跳转 容器启动后&#xff0c;如何默认显示web-inf目录下的系统首页。 2. ModelAttribute来注解非请求处理方法 用途&#xff1a;预加载数据&#xff0c;会在每个RequestMapping方法执行之前调用。 特点&#xff1a;无需返回视图&#xff0c;返回类型void 示例…

【计算机网络黑皮书】应用层

【事先声明】 这是对于中科大的计算机网络的网课的学习笔记&#xff0c;感谢郑烇老师的无偿分享 书籍是《计算机网络&#xff08;自顶向下方法 第6版&#xff09;》 需要的可以私信我&#xff0c;无偿分享&#xff0c;课程简介下也有 课程连接 目录 应用层网络应用的原理应用架…

[BJDCTF2020]The mystery of ip

打开环境 点击flag&#xff0c;提示ip&#xff0c;这里确实就比较容易联想到x-forwarded-for 点击hint 这个好像没啥用 使用bp抓包 添加请求头 X-Forwarded-For:1 试一下 发现ip可控 后来查了发现 PHP可能存在Twig模版注入漏洞 参考https://www.cnblogs.com/zzjdbk/p/13…

Scala第十七章节

Scala第十七章节 scala总目录 文档资料下载 章节目标 了解集合的相关概念掌握Traversable集合的用法掌握随机学生序列案例 1. 集合 1.1 概述 但凡了解过编程的人都知道程序 算法 数据结构这句话, 它是由著名的瑞士计算机科学家尼古拉斯沃斯提出来的, 而他也是1984年图灵…

ADO连接Access的前期绑定方法实例(下)

【分享成果&#xff0c;随喜正能量】眾生多悲苦&#xff0c;發願‬菩提心。願今天所有聽見我、看見我、憶念我的眾生&#xff0c;因我心而‬生喜悅&#xff01;除消身心的痛苦&#xff01;種下脫解‬的種子&#xff01;願我等‬身心念力所及之處一切眾切‬生因佛得度&#xff0…

【AI视野·今日CV 计算机视觉论文速览 第258期】Mon, 2 Oct 2023

AI视野今日CS.CV 计算机视觉论文速览 Mon, 2 Oct 2023 (showing first 100 of 112 entries) Totally 100 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computer Vision Papers Multi-task View Synthesis with Neural Radiance Fields Authors Shuhong Zheng, Zh…

Vue的模板语法

Vue的模板语法 Vue 使用一种基于 HTML 的模板语法&#xff0c;使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。所有的 Vue 模板都是语法层面合法的 HTML&#xff0c;可以被符合规范的浏览器和 HTML 解析器解析。 测试准备 为了方便测试&#xff0c;先将vue-base项…

【AI视野·今日Robot 机器人论文速览 第四十五期】Mon, 2 Oct 2023

AI视野今日CS.Robotics 机器人学论文速览 Mon, 2 Oct 2023 Totally 42 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers Learning Decentralized Flocking Controllers with Spatio-Temporal Graph Neural Network Authors Siji Chen, Yanshen Sun, …