ADC0832模数转换和LCD显示

柔情痞子 提交于 2019-12-22 01:18:51

环境

软件

  • uVision V4.02
  • ISIS Professional 7.8

芯片

  • AT89C51
  • LM016L(LCD)
  • ADC0832

仿真图

在这里插入图片描述

实现效果

显示电压值并用图标显示百分比
具体效果如下所示:
在这里插入图片描述

相关代码及资源

https://github.com/duganlx/DSP

操作小记

芯片介绍

接口说明

  • CS:片选使能,低电平芯片使能
  • CH0:模拟输入通道0,或作为IN+/-使用
  • CH1:模拟输入通道0,或作为IN+/-使用
  • GND:芯片参考0电位(地)
  • DI:数据信号输入,选择通道控制
  • DO:数据信号输出,转换数据输出
  • CLK:芯片时钟输入
  • VCC/REF:电源输入及参考电压输入(复用)

ADC0832 为 8 位分辨率 A/D 转换芯片,其最高分辨可达** 256 级**,可以适应一般的模拟量转换要求。其内部电源输入与参考电压的复用,使得芯片的模拟电压输入0~5V 之间。芯片转换时间仅为 32μS,具有双数据输出可作为数据校验,以减少数据误差,转换速度快且稳定性能强。独立的芯片使能输入,使多器件挂接和处理器控制变的更加方便。通过** DI 数据输入端**,可以轻易的实现通道功能的选择

单片机控制原理

正常情况下,ADC0832与单片机的接口应为4条数据线,分别是CSCLKDODI。但由于DO端与DI端在通信时并未同时有效并与单片机的接口是双向的,所以电路设计时可以将DODI并联在一个数据线上使用。在这里插入图片描述
当ADC0832 未工作时,其CS输入端应为高电平,此时芯片禁用CLKDO/DI的电平任意
进行A/D转换时,须先将CS使能端置于低电平并且保持低电平直到转换完全结束。此时芯片开始转换工作,同时由处理器向芯片时钟输入端CLK输入时钟脉冲,DO/DI端则使用DI端输入通道功能选择的数据信号。

  • 第1个时钟脉冲的下沉之前DI端必须是高电平,表示启动信号
  • 第2、3个脉冲下沉之前DI端应输入2位数据用于选择通道功能,具体如下图所示:

在这里插入图片描述
如上图所示:

  • 当两位数据为10时,只对CH0进行单通道转换
  • 当两位数据为11时,只对CH1进行单通道转换
  • 当两位数据为00时,将CH0作为正输入端IN+,将CH1作为负输入端IN-
  • 当两位数据为01时,将CH0作为负输入端IN-,将CH1作为正输入端IN+
  • 第3个脉冲的下沉之后DI端的输入电平失去输入作用,此时DO/DI端则开始利用数据输出DO进行转换数据的读取
  • 第4个脉冲下沉开始DO端输出转换数据最高位DATA7,随后每一个脉冲下沉DO端输出下一位数据
  • 直到第11个脉冲时发出最低位数据DATA0一个字节的数据输出完成
  • 也从第11个脉冲开始输出下一个相反字节的数据,即从第11个脉冲的下沉输出DATA0
  • 随后输出8位数据,直到第19个脉冲时数据输出完成,也标志一次A/D转换的结束
  • CS高电平禁用芯片,直接将转换后的数据进行处理就可以了
时序图

在这里插入图片描述

代码编写

引用库文件

#include<reg51.h>
#include<string.h>
#include<intrins.h>

宏定义

#define Uint unsigned int
#define Uchar unsigned char

全局变量定义

//对应仿真图中引脚的对接
sbit CS=P1^0; // chip select
sbit CLK=P1^1; // clock
sbit DIO=P1^2; // data input and output
sbit RS=P2^0; // data register status register
sbit RW=P2^1; // read/write
sbit EN=P2^2; // enable

Uchar disp_buff1[]="VOLTAGE:  0.00V";
Uchar disp_buff2[16];

延时函数

/*
* 延时2微秒
* 
* @return
*/
void delay2us()
{
	_nop_();
	_nop_();
}

AD转换

/*
* AD转换
*
* @return 输出数据
*/
Uchar get_AD_Res()
{
	Uchar i, data1=0, data2=0;
	CS=0;
	//第一个周期:转换开始
	CLK=0;DIO=1;delay2us();
	CLK=1;delay2us();
	
	//第二个周期:选择单通道还是双通道 DIO=0双通道差分 或 DIO=1单通道
	CLK=0;DIO=1;delay2us(); 
	CLK=1;delay2us();
	
	//第三个周期:DIO选择CH1-->如果DIO=0 选择CH0
	CLK=0;DIO=0;delay2us();
	CLK=1;delay2us();
	
	//等待
	CLK=0;DIO=1;delay2us(); 
	
	//先进来的为最高位,后进来为最低位
	for(i=0; i<8; i++)
	{
		CLK=1;delay2us();
		CLK=0;delay2us();
		/*
		* 0000_0000|0000_000想=0000_000想
		* 0000_00想0|0000_000翔=0000_00想翔
		* 0000_0想翔0|0000_000子=0000_0想翔子
		*/
		data1=(data1<<1)|(Uchar)DIO; 
	}
	
	//先进来的为最低位,后进来为最高位
	for(i=0; i<8; i++)
	{
		/*
		* 0000_0000|0000_000子=0000_000子
		* 0000_000子|0000_00翔0=0000_00翔子
		* 0000_00翔子|0000_0想00=0000_0想翔子
		*/
		data2= data2|(Uchar)DIO<<i;
		CLK=1;delay2us();
		CLK=0;delay2us();
		 
	}
	
	//禁止片选
	CS=1;
	
	return (data1==data2)?data1:0;
}

lcd相关函数

lcd具体用法及函数说明可以参看:1602字符液晶显示

/*
* 延时
*  
* @param x 时间(不精确)
* @return
*/
void delay_ms(Uint x)
{
	Uchar t;
	while(x--)
		for(t=0; t<120; t++);
}

/*
* 检测BF(busy flag)位状态
* 
* @return
*/
bit test_BF()
{
	Uchar LCD_status;
	P0=0xFF;
	EN=0;RS=0;RW=1;
	EN=1;delay2us();LCD_status=P0;
	EN=0;
	return (LCD_status&0x80)?1:0;
}

/*
* 写命令
*
* @param cmd 八位命令
* @return
*/
void write_CMD(Uchar cmd)
{
	while(test_BF());
	EN=0; RS=0; RW=0;
	P0=cmd;
	EN=1; _nop_(); EN=0;
}

/*
* 写数据(一位一位的写)
*
* @param data8 八位数据
* @return
*/
void write_Data(Uchar data8)
{
	while(test_BF());
	EN=0; RS=1; RW=0;
	P0=data8;
	EN=1; _nop_(); EN=0;
}

/*
* 初始化
*
* @return
*/
void init()
{
	write_CMD(0x38);delay_ms(1);
	write_CMD(0x01);delay_ms(1);
	write_CMD(0x06);delay_ms(1);
	write_CMD(0x0C);delay_ms(1);
}

/*
* 写字符串
*
* @param r row
* @param c column
* @param str 字符串
* @return
*/
void write_Str(int r, int c, char *str)
{
	int i=0;	
	unsigned char Addressx[] = {0x80, 0xC0};
	unsigned char StartAdd = (Addressx[r] | c);//按位或

	write_CMD(StartAdd);
	
	for(i = 0; i < 16; i++){
		if(str[i]==0) break;
		write_Data(str[i]);
	}
	// 如果不够16位,用空格填充
	for(;i < 16; i++){
		write_Data(' '); 	
	}
}

主函数

void main()
{
	Uchar i=0,AD=0;
	Uint d=0;
	init();
	
	while(1)
	{
		AD=get_AD_Res(); 
		d=(AD*500.0/256.0); //d需Uint 
		
		disp_buff1[10]=d/100+'0';
		disp_buff1[12]=(d/10)%10+'0';
		disp_buff1[13]=d%10+'0';
		
		write_Str(0,0,disp_buff1);
		
		//填充实心和空心的框框
		i=(Uint)AD*16/256;
		memset(disp_buff2,'\xFF',i);
		memset(disp_buff2+i,'\xDB',16-i);
		write_Str(1,0,disp_buff2);
	}
	
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!