51单片机完全学习——AD转换

一、AD转换中的一些概念

  1. 位数,AD转换后转出来的二进制由几位二进制数来表示。位数越多,越细腻。
  2. 量程:AD转换器可以接受的模拟量的范围
  3. 精度:简单理解就是转出来到底有多准
  4. 分辨力:AD转换器转出来的二进制数,每一格表示多少
  5. 转换速率:转换时间,精度越高,转换速率越慢

二、如何实现AD转换

由于51单片机内部并没有AD转换的相关模块,因此使用外接外部芯片的方式,来进行AD转换的操作,我们使用的是XPT2046这个芯片来做的,这个芯片使用的是SPI的接口,主要有4根线来进行通讯:DCLK:外部时钟信号输入 CS:片选信号,低电平时芯片使能。DIN:串行数据输入端,当CS为低电平时,数据在DCLK的上升沿锁存。DOUT:串行数据输出端,数据在DCLK的下降沿移出,当CS为高电平时为高阻态。

ET2046的控制字:bit7:始终为1, bit6-4:决定采用的是那一路(AIN0, AIN1, AIN2, AIN3)

bit6-4
AIN0X+011/001
AIN1Y+101
AIN2VBAT010
AIN3AUX110

bit3:设置采样位数,0表示12bit, 1表示8bit
bit2:1表示用的单端模式,0表示用差分模式
bit1-0:power down模式使能,00表示使能,也就是在转换期间处于低功耗模式

三、AD转换时序分析及程序实现

有了时序图我们只需要按照时序图进行编程就行,需要注意的是这个通信无论是发送还是接收都是高位在前,低位在后。在发送数据的时候,一定要将数据放好,然后在产生上升沿,这样就可以了。在接收数据的时候,先产生下降沿然后再去读取数据,这样就好了。你可以将整个时序写成一个函数,也可以将这个时序拆分成几个不同的函数来实现。这两种方法在本质上没有区别,在这里我演示后面一种方法。需要注意的是,我这里没有去检测BUSY线的状态,而是给他足够的一段延时,保证他一定可以完成AD转换。下面就是具体的函数实现


void delay15us(void)   //误差 0us
{
    unsigned char a;
    for(a=6;a>0;a--);
}

void spi_write(uchar cmd)
{
	uchar i = 0;
	for (i=0; i<8; i++)
	{
		DIN = (cmd >> 7);
		_nop_();
		cmd <<= 1;
		DCLK = 1;
		_nop_();
		DCLK = 0;
		_nop_();
	}
}

uint spi_read(void)
{
	uint date = 0;
	uchar i = 0;
	for (i=0; i<12; i++)
	{
		DCLK = 1;
		_nop_();
		DCLK = 0;
		_nop_();
		 
		date <<= 1;
		date |= DOUT;
		_nop_();
	}
	return date;
}


//0-3分别是AIN0-AIN3,分表表示电位器,热敏电阻,光敏电阻,外部输入
//我这里都采用的是12位的AD转换
uint ad_convert(uchar cmd)
{
	uchar i = 0, addr[4] = {0x94, 0xd4, 0xa4, 0xe4};
	uint date = 0;
	DCLK = 0;
	_nop_();
	CS = 0;
	_nop_();
	spi_write(addr[cmd]);
	delay15us();
	date = spi_read();
	CS = 1;
	return date;
}

针对上面的函数,其实你只需要使用最后一个就可以了,前面的函数都会在这个函数里面调用,需要注意的是,最后这个函数的输出量是采集到的12位的AD输出,还没有进行电压值的转换。如果需要电压值,需要进一步进行计算。下面来写一个函数,进行计算,并在串口打印出来,我这里的串口打印直接打印的是字符,而不是HEX编码。对于程序里面的一些端口的定义,宏定义,函数声明等我就没有写在里面,自己补充就可以了。


//在AD转换中通过串口以字符形式发送转换到的电压值
//temp是12位转换到的AD值
//这里我只保留了小数点后3位
void uart_send_ad(uint temp)
{
	  float date = 0;
	  uchar i = 0, dat[4] = 0;
	  date = ((temp + 0.0) / 4096) * 5;
		temp = date * 1000;
		for (i=0; i<4; i++)
		{
			dat[i] = (temp % 10) + '0';
			
			temp /= 10;
		}
	  for (i=0; i<4; i++)
		{
			uart_send_byte(dat[3-i]);
			if (i == 0)
			{
				uart_send_byte('.');
			}
		}
}

关于 ET2046 错误代码的信息,在现有的引用材料中并未提及相关内容。然而,可以从常见的 IT 技术领域推测可能涉及的内容。通常情况下,“ET”可能是某种技术模块或框架的缩写,比如 Linux 中的 Epoll 或其他网络通信机制;而“2046”则很可能是特定场景下的错误编号。 以下是综合分析后的解答: ### 关于 ET2046 的潜在含义 #### 1. **Epoll 相关的可能性** 如果 ET 是指 Linux 下的 Epoll(一种高效的 I/O 多路复用机制),那么 ET2046 可能是一个自定义的应用层错误代码。例如,在开发基于 Epoll 的服务器时,开发者可能会定义一系列错误码用于调试和日志记录。在这种假设下,ET2046 可能表示以下情况之一: - 套接字读取失败[^1]。 - 文件描述符已关闭但仍尝试访问[^1]。 - 数据未完全从内核缓冲区读取完毕即终止操作[^2]。 对于这种情况,建议检查应用的日志输出以及具体的上下文信息,以便进一步定位问题。 #### 2. **热更新框架中的错误** 另一种可能性是 ET2046 来源于某个热更新框架,如 ILRuntime 提供的功能集。虽然现有引用仅提到 ILRuntime 实现了 C# 脚本的动态加载与运行支持[^3],但如果存在类似的错误编码体系,则 ET2046 可能对应如下情形: - 动态库加载失败。 - DLL 文件校验不通过。 - 执行过程中抛出了不可恢复的异常。 针对此方向,需查阅具体项目的文档或源码以确认其确切意义。 --- ### 示例代码片段 为了帮助排查此类问题,下面提供一段简单的伪代码示例,展示如何捕获并打印类似 ET2046 这样的错误消息: ```cpp #include <iostream> #include <string> void HandleError(int errorCode) { switch(errorCode){ case 2046: std::cout << "Error Code: ET2046 - Description of the error goes here." << std::endl; break; default: std::cout << "Unknown Error Occurred!" << std::endl; } } int main() { int err = 2046; // Simulate an error code being returned from some function. HandleError(err); return 0; } ``` --- ### 总结 由于缺乏更多背景细节,当前尚无法精准界定 ET2046 的实际用途及其引发的原因。不过依据前述推断路径,可以初步将其归类至操作系统级 API 使用失误或者高级编程环境中插件管理不当所致的结果之中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值