STC12C5A60S2单片机实现AD采样并液晶显示(数字燃油表)
- 格式:doc
- 大小:57.00 KB
- 文档页数:5
S T C12C5A60S2单片机简介-CAL-FENGHAI-(2020YEAR-YICAI)_JINGBIANSTC12C5A60S2单片机简介:STC12C5A60S2/AD/PWM系列单片机是宏晶科技生产的单时钟/机器周期(1T)的单片机,是高速/低功耗/超强抗干扰的新一代8051单片机,指令代码完全兼容传统8051,但速度快8-12倍。
内部集成MAX810专用复位电路,2路PWM,8路高速10位A/D转换(250K/S),针对电机控制,强干扰场合。
工作电压:STC12C5A60S2系列工作电压:5.5V-3.3V(5V单片机)STC12C5A60S2是哪一类单片机?是8051系列单片机,与普通51单片机相比有以下特点:1、同样晶振的情况下,速度是普通51的8~12倍2、有8路10位AD3、多了两个定时器,带PWM功能4、有SPI(串行外设接口)接口5、有EEPROM6、有1K内部扩展RAM7、有WATCH_DOG8、多一个串口9、IO口可以定义,有四种状态10、中断优先级有四种状态可定义单片2.4G无线射频收发芯片nRF24L01:可接受5V电平的输入,工作电压1.9~3.6V,单片无线收发器芯片,GFSK:高斯频移键控,在调制之前通过一个高斯低通滤波器来限制信号的频谱宽度。
数字调制方法,如:ASK——幅移键控调制,把二进制符号0和1分别用不同的幅度来表示。
FSK——频移键控调制,即用不同的频率来表示不同的符号。
如2KHz表示0,3KHz表示1。
GFSK——高斯频移键控,在调制之前通过一个高斯低通滤波器来限制信号的频谱宽度。
单片机的PCA模块?:PCA(可编程计数器阵列Programmable Counter Array)可编程计数器阵列(PCA)提供增强的定时器功能,与标准8051计数器/定时器相比,它需要较少的CPU 干预。
由高字节(PCAH)和低字节(PCAL)组成。
25页报告出现的传感器:RPR220:反射型光电探测器。
A/D转换测试程序(ADC查询方式)#include “”//头文件在STC公司主页上下载#include ””//与STC12C5A60S2单片机ADC相关的寄存器说明//Sfr ADC_CONTR = 0xBC;//AD转换控制寄存器Sfr ADC_RES = 0xBD;//AD转换结果寄存器高Sfr ADC_RESL = 0xBE;//AD转换结果寄存器低Sfr P1ASF = 0x9D;//P1口模拟转换功能控制寄存器Sfr AURX1 = 0xA2;//AD转换结果存储方式控制位#define ADC_POWER 0x80 //ADC电源开#define ADC_FLAG 0x10 //ADC结束标志位#define ADC_START 0x08 //ADC启动控制位设为开#define ADC_SPEEDLL 0x00 //设为540个时钟周期ADC一次#define ADC_SPEEDL 0x20 //设为360个时钟周期ADC一次#define ADC_SPEEDH 0x40 //设为180个时钟周期ADC一次#define ADC_SPEEDHH 0x60 //设为90个时钟周期ADC一次void AD_init(void);void delay(unsigned int a);unsigned int AD_get(unsigned char n);float AD_work(unsigned char n);void main(){unsigned char i;AD_init();while(1){for(i=0;i<8;i++){AD_work(i);delay(20);}}}unsigned int AD_get(unsigned char n) //第n通道ADC采样函数{unsigned int adc_data;ADC_RES = 0; //清零ADC_CONTR=ADC_POWER|ADC_SPEEDLL|n|ADC_START;//打开AD转换电源、设定转换速度、设定通道号、AD转换开始_nop_();_nop_();_nop_();_nop_();//要经过4个CPU时钟的延时,其值才能够保证被设置进ADC_CONTR?寄存器?while(!(ADC_CONTR&ADC_FLAG)); //等待转换完成adc_data=ADC_RES; //转换结果计算,取8位结果ADC_CONTR&=~ADC_FLAG;//关闭AD转换,ADC_FLAG位由软件清0return adc_data;???//返回ADC的值}float AD_work(unsigned char n){float AD_val; //定义处理后的数值AD_val为浮点数unsigned char i;for(i=0;i<100;i++)AD_val+=AD_get(n); //转换100次求平均值(提高精度)AD_val/=100;AD_val=(AD_val*5)/256; //AD的参考电压是单片机上的5v,所以乘5即为实际电压值return AD_val;}void AD_init(void){P1ASF = 0xff; //P1口全部作为模拟功能A/D使用ADC_RES = 0; //清零转换结果寄存器高8位ADC_CONTR = ADC_POWER|ADC_SPEEDLL;delay(2); //等待1ms,让AD电源稳定}void delay(unsigned int a){unsigned int i;while (a--){i=5000;while(i--);}}。
STC12C5A60S2中的AD转换逐次逼近原理AD 里面包含da,当输入电压Vin时,da的最高位是1,即为0.5Vref与输入信号比较,如果输入大于0.5Vref则比较器输出为1,同时da的最高位为1,反之DA最高位则为0,通过8次比较后得到8个01数据即完成ad转换。
现在说下程序中用到stc12单片机两个寄存器ADC_CONTR;主要用来配置ad启动的工作模式;还有个result的寄存器程序中的注意点:配置完ADC_CONTR后要延时4个时钟周期先把程序附上#include "stc12.h"#include "intrins.h"#include "ad.h"uint ad;#define ADC_POWER 0X80 //ADC最高位给adc部分供电,类似于片选#define ADC_START 0X08 //模数转换启动控制位#define ADC_FLAG 0x10 //ad转换需要时间,这个是转换完成标志位#define ADC_SPEEDLL 0X00 //540 clock#define ADC_SPEEDL 0X20 //360 clock#define ADC_SPEEDH 0X40 //180 clock#define ADC_SPEEDHH 0X60 //90 clockuchar ADCresult(uchar aa) //这里的参数是哪个口来ad转换{P1ASF=0X01; //这里的选择和用哪一个P1口作为ad采样ADC_CONTR=ADC_POWER|ADC_SPEEDLL|ADC_START|aa;//ADC_CONTR=0X88|aa;_nop_();_nop_();_nop_();_nop_();//设置ADC_CONTR寄存器后需加4个CPU时钟周期的延时,才能保证值被写入ADC_CONTR寄存器while (!(ADC_CONTR & ADC_FLAG)); //等待ADC_CONTR,这里的ADC_FLAG相当于一个常数,不是寄存器里面的某个位//while(!ADC_FLAG);//ADC_FLAG=0;ADC_CONTR &= ~ADC_FLAG; //Close ADC 将标志位清零等待下次硬件置1ad=(ADC_RES<<2)+ADC_RESL; //打开10位AD采集功能如果用8位AD 屏掉这句把下一句改为Vo=(float)(ADC_RESL)*500/256; 即可//ADC_RES结果寄存器的高2位;ADC_RES结果寄存器的低8位ad=(float)(ad)*5*100/1024; //Return ADCresult(为显示整数,这里将电压值扩大了十倍)//10位AD采集即2的10次方满值为1024 这里用1024表示5伏的电压//那么用采集到的数量值除以1024 在乘以5 得到的值就是采集的电压数值//这里又*100 是为了扩大100倍显示小数位//ADC_RES*(5/256)为采集的电压值然后扩大10倍便于计算return ad;}这里只是个ad.c源文件,这里有几个问题想说一下1.怎么知道是10位还是8位的ad结果;你可以在ADCresult(uchar aa)最前面加一条AUXR1&=0x04;什么意思呢,转换结果的低2位放在ADC_RES,高8位ADC_RESL 中2为什么不用//while(!ADC_FLAG);//ADC_FLAG=0;这两条因为ADC_FLAG相当于常量前面用宏定义而头文件里只有ADC_CONTR的地址映射;但是如果在头文件中用sbit ADC_FLAG=ADC_CONTR^4会出现错误,具体原因还不清楚先说到这吧。
STC12C5A60S2单片机开发学习板产品使用手册【简要说明】一、尺寸:长83mmX宽79mmX高18mm二、主要芯片:STC12C5A60S2单片机三、工作电压:直流6~15伏四、、特点:1、具有电源指示;2、所有I/O口已引出;3、可以实现与电脑串口通信;4、可以实现双串口通讯;5、具有上电复位和手动复位;6、附带SD卡读写接口;7、支持STC串口下载;8、双串口通讯(注:只能使用COM1下载程序);9、八路LED灯(注:可拔出短路帽,断开LED灯);10、可端子接线供电、可排针引电;11、7805供电,输入电压范围宽,且确保AD参考电压准确(注:因无外部参考电压点)五、提供相关软件、资料、原理图适用场合:单片机学习、电子竞赛、产品开发、毕业设计。
注意啦:本产品提供的所有程序都附带原理图以及说明!【图片标注】【原理图】(放大可以看清楚)【PCB尺寸图】【开发板支持同系列单片机的型号】STC单片机最新型号——STC12C5A60S2STC12C5A60S2/AD/PWM系列单片机是宏晶科技生产的单时钟/机器周期(1T)的单片机,是高速/低功耗/超强抗干扰的新一代8051单片机,指令代码完全兼容传统8051,但速度快8-12倍。
内部集成MAX810专用复位电路,2路PWM,8路高速10位A/D转换(250K/S),针对电机控制,强干扰场合。
1.增强型8051 CPU,1T,单时钟/机器周期,指令代码完全兼容传统8051;2.工作电压:STC12C5A60S2系列工作电压:5.5V-3.3V(5V单片机)STC12LE5A60S2系列工作电压:3.6V-2.2V(3V单片机);3.工作频率范围:0 - 35MHz,相当于普通8051的 0~420MHz;4.用户应用程序空间8K /16K / 20K / 32K / 40K / 48K / 52K / 60K / 62K字节;5.片上集成1280字节RAM;6.通用I/O口(36/40/44个),复位后为:准双向口/弱上拉(普通8051传统I/O口),可设置成四种模式:准双向口/弱上拉,推挽/强上拉,仅为输入/高阻,开漏,每个I/O口驱动能力均可达到20mA,但整个芯片最大不要超过55Ma;7. ISP(在系统可编程)/IAP(在应用可编程),无需专用编程器,无需专用仿真器可通过串口(P3.0/P3.1)直接下载用户程序,数秒即可完成一片;8.有EEPROM功能(STC12C5A62S2/AD/PWM无内部EEPROM);9. 看门狗;10.内部集成MAX810专用复位电路(外部晶体12M以下时,复位脚可直接1K电阻到地);11.外部掉电检测电路:在P4.6口有一个低压门槛比较器,5V单片机为1.32V,误差为+/-5%,3.3V单片机为1.30V,误差为+/-3%;12.时钟源:外部高精度晶体/时钟,内部R/C振荡器(温漂为+/-5%到+/-10%以内) 1用户在下载用户程序时,可选择是使用内部R/C振荡器还是外部晶体/时钟,常温下内部R/C振荡器频率为:5.0V单片机为:11MHz~15.5MHz,3.3V单片机为:8MHz~12MHz,精度要求不高时,可选择使用内部时钟,但因为有制造误差和温漂,以实际测试为准;13.共4个16位定时器两个与传统8051兼容的定时器/计数器,16位定时器T0和T1,没有定时器2,但有独立波特率发生器做串行通讯的波特率发生器再加上2路PCA模块可再实现2个16位定时器;14. 2个时钟输出口,可由T0的溢出在P3.4/T0输出时钟,可由T1的溢出在P3.5/T1输出时钟;15.外部中断I/O口7路,传统的下降沿中断或低电平触发中断,并新增支持上升沿中断的PCA模块, Power Down模式可由外部中断唤醒,INT0/P3.2,INT1/P3.3,T0/P3.4, T1/P3.5, RxD/P3.0,CCP0/P1.3(也可通过寄存器设置到P4.2 ), CCP1/P1.4 (也可通过寄存器设置到P4.3);16. PWM(2路)/PCA(可编程计数器阵列,2路):——也可用来当2路D/A使用——也可用来再实现2个定时器——也可用来再实现2个外部中断(上升沿中断/下降沿中断均可分别或同时支持);17.A/D转换, 10位精度ADC,共8路,转换速度可达250K/S(每秒钟25万次)18.通用全双工异步串行口(UART),由于STC12系列是高速的8051,可再用定时器或PCA软件实现多串口;19. STC12C5A60S2系列有双串口,后缀有S2标志的才有双串口,RxD2/P1.2(可通过寄存器设置到P4.2),TxD2/P1.3(可通过寄存器设置到P4.3);20.工作温度范围:-40 - +85℃(工业级) / 0 - 75℃(商业级)21.封装:PDIP-40,LQFP-44,LQFP-48 I/O口不够时,可用2到3根普通I/O口线外接74HC164/165/595(均可级联)来扩展I/O口, 还可用A/D做按键扫描来节省I/O口,或用双CPU,三线通信,还多了串口。
单片机STC12C5A60S2模块简介1、综述系统采用STC12C5A16S2单片机为核心,配合USB转串口芯片CH340T、RS232芯片Max232、四位共阳数码管、LED、按键和蜂鸣器组成最小系统。
单片机内部集成双串口、8路10bitADC和两路八位PWM。
系统由MINI USB供电和提供程序下载接口,使得电路大大简化,通用性增强.2、模块分析2、1电源模块全系统工作在+5V且功耗较低,所以采用USB电源供电即可满足。
电源前级接入500mA自恢复保险丝,提供短路保护,芯片前级对地分别连接100uF 和0。
1uF进行电源滤波。
电源部分引入三脚单联扭子开关将USB转串口芯片CH340T的电源与芯片电源进行隔离,以方便下载程序。
图2.1 电源模块2。
2、USB转串口STC12C5A16S2单片机可以通过串口烧写程序,系统采用CH340T将USB转换为串口信号,以供单片机下载程序,电路如图2.2:图2.2USB转串口模块图中UD+与UD—为计算机USB信号,RxD与TxD信号为CH340T转换后接到单片机的串口信号.2。
3、单片机模块单片机模块由复位电路,晶振电路和蜂鸣器电路组成,然后将多余I/O口外接以供扩展。
图2.3 单片机模块2。
4、RS232模块使用Max232将单片机的第二串口引出,通过DB9接口实现与外界串口通信。
图2。
4 RS232模块2。
5、四位共阳数码管和LED模块单片机P2.0端口用于8个LED选通,P2。
1-P2。
4端口用于数码管位选;P0.0—P0。
7用于数码管段选和8个LED选择。
图2。
5四位共阳数码管图2.6 8位LED模块2.6、按键模块单片机P3。
2-P3.5端口用于扫描按键状态,组成1X4键盘。
图2.7 按键模块3、操作步骤1、安装CH340T芯片驱动;2、将MINI USB电缆连接到开发板,进入计算机设备管理器—端口选项,会出现CH340T,并显示COM X,记住此端口号;3、拨动开关,电源指示灯有高亮和微亮两种状态;4、编写程序文件,编译生成。
A/D转换测试程序(ADC查询方式)#include “stc12c5a.h”//头文件在STC公司主页上下载#include ”intrins.h”//与STC12C5A60S2单片机ADC相关的寄存器说明//Sfr ADC_CONTR = 0xBC;//AD转换控制寄存器Sfr ADC_RES = 0xBD;//AD转换结果寄存器高Sfr ADC_RESL = 0xBE;//AD转换结果寄存器低Sfr P1ASF = 0x9D;//P1口模拟转换功能控制寄存器Sfr AURX1 = 0xA2;//AD转换结果存储方式控制位#define ADC_POWER 0x80 //ADC电源开#define ADC_FLAG 0x10 //ADC结束标志位#define ADC_START 0x08 //ADC启动控制位设为开#define ADC_SPEEDLL 0x00 //设为540个时钟周期ADC一次#define ADC_SPEEDL 0x20 //设为360个时钟周期ADC一次#define ADC_SPEEDH 0x40 //设为180个时钟周期ADC一次#define ADC_SPEEDHH 0x60 //设为90个时钟周期ADC一次void AD_init(void);void delay(unsigned int a);unsigned int AD_get(unsigned char n);float AD_work(unsigned char n);void main(){unsigned char i;AD_init();while(1){for(i=0;i<8;i++){AD_work(i);delay(20);}}}unsigned int AD_get(unsigned char n) //第n通道ADC采样函数{unsigned int adc_data;ADC_RES = 0; //清零ADC_CONTR=ADC_POWER|ADC_SPEEDLL|n|ADC_START;//打开AD转换电源、设定转换速度、设定通道号、AD转换开始_nop_();_nop_();_nop_();_nop_();//要经过4个CPU时钟的延时,其值才能够保证被设置进ADC_CONTR?寄存器?while(!(ADC_CONTR&ADC_FLAG)); //等待转换完成adc_data=ADC_RES; //转换结果计算,取8位结果ADC_CONTR&=~ADC_FLAG;//关闭AD转换,ADC_FLAG位由软件清0return adc_data;???//返回ADC的值}float AD_work(unsigned char n){float AD_val; //定义处理后的数值AD_val为浮点数unsigned char i;for(i=0;i<100;i++)AD_val+=AD_get(n); //转换100次求平均值(提高精度)AD_val/=100;AD_val=(AD_val*5)/256; //AD的参考电压是单片机上的5v,所以乘5即为实际电压值return AD_val;}void AD_init(void){P1ASF = 0xff; //P1口全部作为模拟功能A/D使用ADC_RES = 0; //清零转换结果寄存器高8位ADC_CONTR = ADC_POWER|ADC_SPEEDLL;delay(2); //等待1ms,让AD电源稳定}void delay(unsigned int a){unsigned int i;while (a--){i=5000;while(i--);}}。
#include<reg52.h>#include<intrins.h>#define FOSC 18432000L#define BAUD 9600typedef unsigned char BYTE;typedef unsigned int WORD;sbit led=P1^0;sbit key1=P3^4;sbit key2=P3^5;BYTE code table[]={3,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40}; sfr ADC_CONTR=0xbc; //ADC控制寄存器sfr ADC_RES=0xbd; //ADC转换结果寄存器sfr ADC_LOW2=0xbe; //ADC转换结果寄存器sfr P1ASF=0x9d; //模拟功能控制寄存器#define ADC_POWER 0x80//电源控制位#define ADC_FLAG 0x10; //模数转换标志位#define ADC_START 0x08; //模数转换器转换器启动控制位#define ADC_SPEEDLL 0x00; //AD转换所用的时间90个时钟周期转换#define ADC_SPEEDL 0x20; //180个时钟周期转换#define ADC_SPEEDH 0x40; //360个#define ADC_SPEEDHH 0x60; //540个void InitUart();void InitADC();void SendData(BYTE dat);BYTE GetADCResult(BYTE ch);void Delay(WORD n);void keyscan();void showresult(BYTE ch);BYTE t1=0,t2=0,cl=1,i,c,a;WORD aa,num,t0,flgh;void delay(WORD z){WORD i,j;for(i=z;i>0;i--)for(j=110;j>0;j--);}void main(){InitUart();InitADC();while(1){keyscan();if(flgh==1){flgh=0;ES=0;TI=1;SBUF=GetADCResult();while(!TI);TI=0;ES=1;}}}BYTE GetADCResult(BYTE ch){ADC_CONTR=0xC0;_nop_();_nop_();_nop_();_nop_();P1ASF=0x00;//选择P1.0作为A/D转换通道ADC_CONTR|=0x08;//启动A/D转换while((ADC_CONTR&0x10)==0);//等待A/D转换结束ADC_CONTR&=0xE7;return(ADC_RES);}void InitUart(){SCON=0x5a;TMOD=0x21;TH0=(65536-45872)%256;TL0=(65536-45872)/256;TH1=TL1=-(FOSC/12/32/BAUD);TR1=1;ET0=1;REN=1;TR0=1;SM0=0;SM1=1;EA=1;ES=1;}void InitADC(){P1ASF=0xff;ADC_RES=0;ADC_CONTR=0x80;Delay(2);}void keyscan(){if(key1==0){delay(10);if(key1==0){cl++;num=table[cl];while(!key1);}}if(key2==0){delay(10);if(key2==0){cl--;num=table[cl];while(!key2);}}}void ser()interrupt 4{RI=0;a=SBUF;c=1;}void timer_1() interrupt 3{TL1 = 0x3c; //200usTH1 = 0xff;t1++;t2++;if(t1 <= cl)led = 0; //这三行通过t1与cl比较,控制led亮灭,也就是PWM调光。
STC单片机内部ADC采集电压用数码管显示////////////////////////////////////////////////////////////////////////////////////////////////////// ///特点:/// ///1、数码管显示用中断方式/// ///2、STC12C5A60S2内ADC采样电压值,先采样30次然后去掉上下10个再取平均值/// ///3、采集数据用串口发送到PC /// ///------------------------------------------------------------------------shenzhen---iqss----2011/02/23--------/// //////////////////////////////////////////////////////////////////////////////////////////////////////#include <reg51.h>#define uchar unsigned char#define uint unsigned int#define segp P0#define scanp P2uchar code tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82, 0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e}; //不带点段驱动信号uchar code tab_d[]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10}; //带点段驱动uchar code scan[]={0xfe,0xfd,0xfb,0xf7}; //位扫描驱动信号uint display[4]={0,0,0,0}; //初始显示数字uint con=0, _data=0,data2=0; //con显示循环变量_data为ADC采样值临时变量data显示数据临时变量sfr P1ASF=0x9d; //下面五行为ADC定义sfr ADC_CONTR=0xbc;sfr ADC_RES=0xbd;sfr ADC_RESL=0xbe;sfr AUXR1=0xa2;void t0_t1_init(); //t0显示扫描定时器和t1串口比特率定时器初始化函数void adc_init(); //adc初始化函数void uart_out(uchar byte); //串口发送字节函数uint average(uint buffer[30]); //采样数据处理函数void AD(); //电压采样30次函数void delay1ms(uchar x); //延时函数void main(){t0_t1_init();adc_init();while(1){AD();}}/////////定时器初始化///////void t0_t1_init(){ SCON=0x50;PCON=0;TMOD=0x21;TH1=TL1=0xe6;TH0=0xf0;TL0=0x60;EA=ET0=1;// ES=1;TR1=1;TR0=1;}/////ADC初始化///////void adc_init(){ P1ASF=0x01; //启动P10为ADC模拟输入口把内部上拉电阻断开AUXR1 &= 0xfb; //adrj_0 高8位在ADC_RESADC_RES=0; //初值ADC_CONTR=0x80; //开启ADC电源SPEED_1_1,chs000(选择AD采样通道p10) delay1ms(2);// IE|=0xa0;}//////采集30次电压值//////void AD(){ char i;uint temp_buf[30]={0};for(i=0;i<30;i++){ ADC_CONTR |=0x08; //开启转换while((ADC_CONTR&0x10)==0);ADC_CONTR &=0xe7; //清除标志temp_buf[i]=ADC_RES; //取出数值到temp_buf}_data=average(temp_buf); //采样30次后的数据代入处理函数处理后返回处理后的数值,给下面用串口发送出去uart_out(_data);}/////先对整个数组的三十个值进行从小到大的排列,/////////再去掉最大5个和最少5个再求平均值;函数返回temp值///uint average(uint buffer[30]){uchar i,j;uint temp;for(i=1; i<30; i++)for(j=29; j>=i; --j){if(buffer[j-1] > buffer[j]){temp = buffer[j-1];buffer[j-1] = buffer[j];buffer[j] = temp;}}temp = 0;for(i=5; i<25; i++){temp += buffer[i];}temp = (uint)(((float)temp) / 20 + 0.5);return(temp);}///显示数据处理及扫描显示中断服务函数//// void t0_4ms(void) interrupt 1{data2=_data;data2=_data*19.53;display[3]=tab_d[data2/1000];display[2]=tab[(data2/100)%10];display[1]=tab[(data2/10)%10];display[0]=tab[data2%10];TH0=0xf0;TL0=0x60;if(++con==5) con=1;// segp=0xff;segp=display[con-1];scanp=scan[con-1];}///串口发送节字函数////void uart_out(uchar byte){ SBUF=byte;while(TI==0);TI=0;}///1ms延时////void delay1ms(uchar x){ uchar i,j;for(i=0;i<x;i++)for(j=0;j<250;j++); }。
数字燃油表模拟电路设计可实现以下功能:
1、推动滑动变阻器,能使LCD从0显示到100
2、档显示数字低于10时,灯亮,LCD显示“oil low”
以下为C程序:
#include<reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
#define Delay4us(){_nop_();_nop_();_nop_();_nop_();}
sbit LED=P1^0;
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
sbit SCL=P2^0; //I2C时钟引脚
sbit SDA=P2^1; //I2C数据输入输出引脚uchar Recv_Buffer[4]; //数据接收缓冲
uint Voltage[]={'0','0','0','0'}; //数据分解为电压x.xx
bit bdata IIC_ERROR; //I2C错误标志位
uchar LCD_Line_1[]={" "};
//延时
void delay(int ms)
{
uchar i;
while(ms--) for(i=0;i<250;i++) Delay4us();
}
//LCD忙检测
bit LCD_Busy_Check()
{
bit Result;
LCD_RS=0;LCD_RW=1;
LCD_EN=1;Delay4us();Result=(bit)(P0&0x80);
LCD_EN=0;
return Result;
}
//写指令
void LCD_Write_Command(uchar cmd)
{
while(LCD_Busy_Check());
LCD_RS=0;LCD_RW=0;LCD_EN=0;_nop_();_nop_();
P0=cmd;Delay4us();
LCD_EN=1;Delay4us();LCD_EN=0;
}
// 写数据
void LCD_Write_Data(uchar dat)
{
while(LCD_Busy_Check());
LCD_RS=1;LCD_RW=0;LCD_EN=0;P0=dat;Delay4us();
LCD_EN=1;Delay4us();LCD_EN=0;
}
//初始化
void LCD_Initialise()
{
LCD_Write_Command(0x38);delay(5);
LCD_Write_Command(0x0c);delay(5);
LCD_Write_Command(0x06);delay(5);
LCD_Write_Command(0x01);delay(5);
}
//设置显示位置
void LCD_Set_Position(uchar pos)
{
LCD_Write_Command(pos|0x80);
}
//显示一行
void LCD_Display_A_Line(uchar Line_Addr,uchar s[])
{
uchar i;
LCD_Set_Position(Line_Addr);
for(i=0;i<16;i++)LCD_Write_Data(s[i]);
}
// 将模数转换后得到的值分解存入缓存
void Convert_To_V oltage(uchar val)
{
uchar Tmp; //最大值为255,对应100L,255/100=2.55 val=val*0.393;
V oltage[2]=val/100+'0'; //
Tmp=val%100; //
V oltage[1]=Tmp/10+'0';
Tmp=Tmp%10;
V oltage[0]=Tmp+'0';
}
//启动I2C总线
void IIC_Start()
{
SDA=1;SCL=1;Delay4us();SDA=0;Delay4us();SCL=0;
}
//停止I2C总线
void IIC_Stop()
{
SDA=0;SCL=1;Delay4us();SDA=1; Delay4us();SCL=0;
}
// 从机发送应答位
void Slave_ACK()
{
SDA=0;SCL=1;Delay4us();SCL=0;SDA=1;
}
// 从机发送非应答位
void Slave_NOACK()
{
SDA=1;SCL=1;Delay4us();SCL=0;SDA=0;
}
//发送一字节
void IIC_SendByte(uchar wd)
{
uchar i;
for(i=0;i<8;i++) //循环移入8位
{
SDA=(bit)(wd&0x80);_nop_();_nop_();
SCL=1;Delay4us();SCL=0;wd<<=1;
}
Delay4us();
SDA=1; //释放总线并准备读取应答SCL=1;
Delay4us();
IIC_ERROR=SDA; //IIC_ERROR=1表示无应答SCL=0;
Delay4us();
}
//接收一字节
uchar IIC_ReceiveByte()
{
uchar i,rd=0x00;
for(i=0;i<8;i++)
{
SCL=1;rd<<=1;rd|=SDA;Delay4us();SCL=0;Delay4us();
}
SCL=0;Delay4us();
return rd;
}
//连续读入4路通道的A/D转换结果并保存到Recv_Buffer
void ADC_PCF8591(uchar CtrlByte)
{
uchar i;
IIC_Start();
IIC_SendByte(0x90); // 发送写地址
if(IIC_ERROR==1)return;
// IIC_SendByte(CtrlByte); //发送控制字节
//if(IIC_ERROR==1)return;
IIC_Start(); //重新发送开始命令
IIC_SendByte(0x91); // 发送读地址
if(IIC_ERROR==1)return;
IIC_ReceiveByte(); //空读一次,调整读顺序
Slave_ACK(); //收到一字节后发送一个应答位for(i=0;i<4;i++)
{
Recv_Buffer[i++]=IIC_ReceiveByte();
Slave_ACK(); //收到一个字节后发送一个应答位}
Slave_NOACK();
IIC_Stop(); //收到一个字节后发送一个非应答位}
// 向PCF8591发送1字节进行AD转换
//主程序
void main()
{
LED=0;
LCD_Initialise();
while(1)
{
ADC_PCF8591(0x04);
if(Recv_Buffer[0]<26)
{
LCD_Display_A_Line(0x00, "Oil Low! ");
LED=1;
}
else
{
Convert_To_V oltage(Recv_Buffer[0]);
LCD_Line_1[2]=V oltage[2];
LCD_Line_1[3]=V oltage[1];
LCD_Line_1[4]=V oltage[0];
LCD_Display_A_Line(0x00, LCD_Line_1);
LED=0;
}
delay(50);
}
}。