一、设计目的
1、学习并掌握IIC总线的工作原理
2、学习并掌握AD转换器工作原理;
3、学习并掌握AD转换程序设计方法;
4、学习并掌握串行通信程序设计;
二、实验设备
1、计算机;
2、单片机开发板;
3、Keil C51编程软件;
4、Proteus仿真软件;
三、设计内容
1、功能
(1)检测通道0电位器的模拟电压并显示(X.XX伏),
(2)将电压值通过串口发送到上位机串口调试助手显示,
(3)上位机串口调试助手显示(文本形式显示):“V:X.XX”回车(X.XX是电压值,逐行显示)
(4)上位机可以通过发送不同命令控制采集并显示哪个通道:通道0、通道1、通道2的模拟电压;
2、硬件电路原理图
3、设计程序(5号、宋体、单倍行距、分两栏,要求程序设计规范化,添加必要的说明和注释)
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
单片机源程序如下:
main.c中
/***************************************************************
* 实 验 名 :PCF8591AD转换串口实验
* 实验说明 :采用AD转换器采集模拟电压、转换为数字量、显示数字量。
* 连接方式 :
(1)P0口连接数码管的段控制:
CN3接CN16,A接P00,B接P01,C接P02,D接P03,E接P04,F接P05,G接P06,DP接P07
(2)P2.4-P2.7连接数码管的位控制:
CN1接CN17,PUT1接P24,PUT2接P25,PUT3接P26,PUT4接P27
(3)P1口连接矩阵按键:
CN16接CN14,KEY1到KEY8分别对应P10到P17
(3)PCF8591的SDA、SCL连接P2.1、P2.0:
PCF8591的IIC数据脚SDA连接P2.1;
PCF8591的IIC时钟脚SCL连接P2.0;
PCF8591的VCC连接开发板VCC;
PCF8591的GND连接开发板GND。
****************************************************************/
//包含文件
#include <reg51.h>
#include <intrins.h>
#include <IIC.h>
//宏定义
#define BAUD 9600 //波特率设置,bps
#define FOSC 11059200L //晶振设置,默认使用11.0592M Hz
//#define FOSC 12000000L //晶振设置,使用12M Hz
//#define FOSC 24000000L //晶振设置,使用24M Hz
#define AddWr 0x90 //写数据地址
#define AddRd 0x91 //读数据地址
//IO接口定义
#define KEY_PORT P1
#define LED_PORT P0
sbit wela_1 = P2^4;
sbit wela_2 = P2^5;
sbit wela_3 = P2^6;
sbit wela_4 = P2^7;
//全局变量定义
extern bit ack; //应答标志位
unsigned char key_value;
unsigned char qian,bai,shi,ge;
unsigned char CHX; //作为通道选择
unsigned long readadc; //AD转换结果
unsigned char state;
//LED显示字模 0-F 共阳模式
unsigned code table[]={0Xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
/*******************************************************************************
* 函 数 名 :Delayms
* 函数功能 :实现 ms级的延时
* 输 入 :ms
* 输 出 :无
*******************************************************************************/
void Delayms(unsigned int ms)
{
unsigned int i,j;
for(i=0;i<ms;i++)
#if FOSC == 11059200L
for(j=0;j<114;j++);
#elif FOSC == 12000000L
for(j=0;j<123;j++);
#elif FOSC == 24000000L
for(j=0;j<249;j++);
#else
for(j=0;j<114;j++);
#endif
}
/*******************************************************************************
* 函 数 名 :delay
* 函数功能 :延时子函数,短暂延时
* 输 入 :timer
* 输 出 :无
*******************************************************************************/
void delay(unsigned int timer)
{
while(timer--);
}
/*******************************************************************************
* 函 数 名 :ReadADC
* 函数功能 :读AD转值程序 输入参数 Chl 表示需要转换的通道,范围从0-3 返回值范围0-255
* 输 入 :Chl
* 输 出 :Val
*******************************************************************************/
unsigned char ReadADC(unsigned char Chl)
{
unsignedchar Val;
IIC_start(); //启动总线
IIC_write(AddWr); //发送器件地址,准备写
if(ack==0) return(0);
IIC_write(0x40|Chl); //发送器件子地址,Chl表示需要转换的通道
if(ack==0) return(0);
IIC_start();
IIC_write(AddRd); //发送器件地址,准备读
if(ack==0) return(0);
Val=IIC_read(); //读取AD转换结果
IIC_ACK(1); //发送非应答位
IIC_stop(); //结束总线
return(Val); //返回AD转换结果
}
/*******************************************************************************
* 函 数 名 :WriteDAC
* 函数功能 :往DAC通道写数据,进行DA转换
* 输 入 :dat
* 输 出 :
*******************************************************************************/
bit WriteDAC(unsigned char dat)
{
IIC_start(); //启动总线
IIC_write(AddWr); //发送器件地址,准备写
if(ack==0)
return(0);
IIC_write(0x40); //发送器件子地址
if(ack==0)
return(0);
IIC_write(dat); //发送数据,进行DA转换
if(ack==0)
return(0);
IIC_stop();
}
/*******************************************************************************
* 函 数 名 :LEDdisplay
* 函数功能 :循环显示各个位上的数据
* 输 入 :num要显示的数据
* 输 出 :无
*******************************************************************************/
void LEDdisplay()
{
wela_1 = 1; //关闭所有数码管
wela_2 = 1;
wela_3 = 1;
wela_4 = 1;
wela_4=0; //显示千位
LED_PORT=table[qian];
Delayms(1);
LED_PORT = 0xff;
wela_4=1;
wela_3=0; //显示百位
LED_PORT=table[bai] & 0x7f;
Delayms(1);
LED_PORT = 0xff;
wela_3=1;
wela_2=0; //显示十位
LED_PORT=table[shi];
Delayms(1);
LED_PORT = 0xff;
wela_2=1;
wela_1=0; //显示个位
LED_PORT=table[ge];
Delayms(1);
LED_PORT = 0xff;
}
/*******************************************************************************
* 函 数 名 :KeyScan
* 函数功能 :4*4键盘扫描
* 输 入 :无
* 输 出 :num 获取的键值,若没有键值则返回0xff
*******************************************************************************/
unsigned char KeyScan()
{
unsigned char temp,num;
num = 0xff;
KEY_PORT=0xfe;
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
Delayms(5); //延时消抖
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=KEY_PORT;
switch(temp)
{
case 0xee:num=1;
break;
case 0xde:num=2;
break;
case 0xbe:num=3;
break;
case 0x7e:num=4;
break;
}
while(temp!=0xf0)
{
temp=KEY_PORT;
temp=temp&0xf0;
}
}
}
KEY_PORT=0xfd;
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
Delayms(5); //延时消抖
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=KEY_PORT;
switch(temp)
{
case 0xed:num=5;
break;
case 0xdd:num=6;
break;
case 0xbd:num=7;
break;
case 0x7d:num=8;
break;
}
while(temp!=0xf0)
{
temp=KEY_PORT;
temp=temp&0xf0;
}
}
}
KEY_PORT=0xfb;
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
Delayms(5); //延时消抖
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=KEY_PORT;
switch(temp)
{
case 0xeb:num=9;
break;
case 0xdb:num=10;
break;
case 0xbb:num=11;
break;
case 0x7b:num=12;
break;
}
while(temp!=0xf0)
{
temp=KEY_PORT;
temp=temp&0xf0;
}
}
}
KEY_PORT=0xf7;
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
Delayms(5); //延时消抖
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=KEY_PORT;
switch(temp)
{
case 0xe7:num=13;
break;
case 0xd7:num=14;
break;
case 0xb7:num=15;
break;
case 0x77:num=16;
break;
}
while(temp!=0xf0)
{
temp=KEY_PORT;
temp=temp&0xf0;
}
}
}
return num;
}
/*******************************************************************************
* 函 数 名 :UsartConfiguration
* 函数功能 :串口设置
* 输 入 :无
* 输 出 :无
*******************************************************************************/
void UsartConfiguration()
{
SCON = 0X50; //设置为工作方式1:10位异步收发器
TMOD |= 0x20; //设置计数器工作方式2:8位自动重装计数器
PCON = 0X80; //波特率加倍:SMOD =1
TH1 = 256-(FOSC/12/32/(BAUD/2)); //计算定时器1初值
TL1 = 256-(FOSC/12/32/(BAUD/2));
TR1 = 1;//启动定时器T1
ES=1; //开串口中断
EA = 1; //开总中断
}
/*******************************************************************************
* 函 数 名 :UsartSend
* 函数功能 :串口发送电压
* 输 入 :无
* 输 出 :无
*******************************************************************************/
void UsartSend()
{
//发送 V
TI = 0; //清除发送中断标志位TI
SBUF='V'; //收到的数据放入到发送SBUF,启动串口发送
while(!TI); //等待发送数据完成
//发送 :
TI = 0; //清除发送中断标志位TI
SBUF=':'; //收到的数据放入到发送SBUF,启动串口发送
while(!TI); //等待发送数据完成
//发送电压整数
TI = 0; //清除发送中断标志位TI
SBUF=readadc%1000/100+'0';//收到的数据放入到发送SBUF,启动串口发送
while(!TI); //等待发送数据完成
//发送小数点
TI = 0; //清除发送中断标志位TI
SBUF='.';//收到的数据放入到发送SBUF,启动串口发送
while(!TI);
//发送电压十分位
TI = 0; //清除发送中断标志位TI
SBUF=readadc%100/10+'0';//收到的数据放入到发送SBUF,启动串口发送
while(!TI); //等待发送数据完成
//等待发送数据完成
//发送电压百分位
TI = 0; //清除发送中断标志位TI
SBUF=readadc%10+'0';//收到的数据放入到发送SBUF,启动串口发送
while(!TI); //等待发送数据完成
//发送换行符
TI = 0; //清除发送中断标志位TI
SBUF='\n'; //收到的数据放入到发送SBUF,启动串口发送
while(!TI); //等待发送数据完成
TI=0;
}
/*******************************************************************************
* 函 数 名 :main
* 函数功能 :主函数
* 输 入 :无
* 输 出 :无
*******************************************************************************/
void main()
{
int count = 0;
state =1;
UsartConfiguration();
while(1)
{
key_value=KeyScan();
if(key_value!=0xff)
state=key_value;
if(state==1) //按键1按下,选择通道0,即电位器调节实验
CHX=0;
else if(state==2) //按键2按下,选择通道1,即光敏电阻实验
CHX=1;
else if(state==3) //按键3按下,选择通道2,即热敏电阻实验
CHX=2;
else if(state==4) //按键4按下,选择通道3,接地
CHX=3;
if(count >= 200) //采样周期1s
{
//readadc=ReadADC(CHX)/256*5*100;
readadc=ReadADC(CHX)*5.0*100/255; //调用ReadADC函数读取AD转换结果
UsartSend();//发送电压到串口
count = 0;
}
qian=readadc/1000; //显示AD转换结果
bai=readadc%1000/100;
shi=readadc%100/10;
ge=readadc%10;
LEDdisplay();
count++;
}
}
/*******************************************************************************
* 函 数 名 :UsartInt
* 函数功能 :串口中断服务函数
* 输 入 :无
* 输 出 :无
*******************************************************************************/
void UsartInt() interrupt 4
{
char receiveData; //存放接收的函数
if(RI == 1) //RI=1说明是接收到数据产生的中断
{
receiveData=SBUF; //从接收SBUF中取得接收到的数据
//判断接受值,改变读取通道
switch (receiveData)
{
case '0' :state = 1;break;
case '1' :state = 2;break;
case '2' :state = 3;break;
default :break;
}
RI = 0; //清除接收中断标志位RI
}
}/***************************************************************
* 实 验 名 :PCF8591AD转换串口实验
* 实验说明 :采用AD转换器采集模拟电压、转换为数字量、显示数字量。
* 连接方式 :
(1)P0口连接数码管的段控制:
CN3接CN16,A接P00,B接P01,C接P02,D接P03,E接P04,F接P05,G接P06,DP接P07
(2)P2.4-P2.7连接数码管的位控制:
CN1接CN17,PUT1接P24,PUT2接P25,PUT3接P26,PUT4接P27
(3)P1口连接矩阵按键:
CN16接CN14,KEY1到KEY8分别对应P10到P17
(3)PCF8591的SDA、SCL连接P2.1、P2.0:
PCF8591的IIC数据脚SDA连接P2.1;
PCF8591的IIC时钟脚SCL连接P2.0;
PCF8591的VCC连接开发板VCC;
PCF8591的GND连接开发板GND。
****************************************************************/
//包含文件
#include <reg51.h>
#include <intrins.h>
#include <IIC.h>
//宏定义
#define BAUD 9600 //波特率设置,bps
#define FOSC 11059200L //晶振设置,默认使用11.0592M Hz
//#define FOSC 12000000L //晶振设置,使用12M Hz
//#define FOSC 24000000L //晶振设置,使用24M Hz
#define AddWr 0x90 //写数据地址
#define AddRd 0x91 //读数据地址
//IO接口定义
#define KEY_PORT P1
#define LED_PORT P0
sbit wela_1 = P2^4;
sbit wela_2 = P2^5;
sbit wela_3 = P2^6;
sbit wela_4 = P2^7;
//全局变量定义
extern bit ack; //应答标志位
unsigned char key_value;
unsigned char qian,bai,shi,ge;
unsigned char CHX; //作为通道选择
unsigned long readadc; //AD转换结果
unsigned char state;
//LED显示字模 0-F 共阳模式
unsigned code table[]={0Xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
/*******************************************************************************
* 函 数 名 :Delayms
* 函数功能 :实现 ms级的延时
* 输 入 :ms
* 输 出 :无
*******************************************************************************/
void Delayms(unsigned int ms)
{
unsigned int i,j;
for(i=0;i<ms;i++)
#if FOSC == 11059200L
for(j=0;j<114;j++);
#elif FOSC == 12000000L
for(j=0;j<123;j++);
#elif FOSC == 24000000L
for(j=0;j<249;j++);
#else
for(j=0;j<114;j++);
#endif
}
/*******************************************************************************
* 函 数 名 :delay
* 函数功能 :延时子函数,短暂延时
* 输 入 :timer
* 输 出 :无
*******************************************************************************/
void delay(unsigned int timer)
{
while(timer--);
}
/*******************************************************************************
* 函 数 名 :ReadADC
* 函数功能 :读AD转值程序 输入参数 Chl 表示需要转换的通道,范围从0-3 返回值范围0-255
* 输 入 :Chl
* 输 出 :Val
*******************************************************************************/
unsigned char ReadADC(unsigned char Chl)
{
unsignedchar Val;
IIC_start(); //启动总线
IIC_write(AddWr); //发送器件地址,准备写
if(ack==0) return(0);
IIC_write(0x40|Chl); //发送器件子地址,Chl表示需要转换的通道
if(ack==0) return(0);
IIC_start();
IIC_write(AddRd); //发送器件地址,准备读
if(ack==0) return(0);
Val=IIC_read(); //读取AD转换结果
IIC_ACK(1); //发送非应答位
IIC_stop(); //结束总线
return(Val); //返回AD转换结果
}
/*******************************************************************************
* 函 数 名 :WriteDAC
* 函数功能 :往DAC通道写数据,进行DA转换
* 输 入 :dat
* 输 出 :
*******************************************************************************/
bit WriteDAC(unsigned char dat)
{
IIC_start(); //启动总线
IIC_write(AddWr); //发送器件地址,准备写
if(ack==0)
return(0);
IIC_write(0x40); //发送器件子地址
if(ack==0)
return(0);
IIC_write(dat); //发送数据,进行DA转换
if(ack==0)
return(0);
IIC_stop();
}
/*************************************
* 函 数 名 :LEDdisplay
* 函数功能 :循环显示各个位上的数据
* 输 入 :num要显示的数据
* 输 出 :无
*************************************/
void LEDdisplay()
{
wela_1 = 1; //关闭所有数码管
wela_2 = 1;
wela_3 = 1;
wela_4 = 1;
wela_4=0; //显示千位
LED_PORT=table[qian];
Delayms(1);
LED_PORT = 0xff;
wela_4=1;
wela_3=0; //显示百位
LED_PORT=table[bai] & 0x7f;
Delayms(1);
LED_PORT = 0xff;
wela_3=1;
wela_2=0; //显示十位
LED_PORT=table[shi];
Delayms(1);
LED_PORT = 0xff;
wela_2=1;
wela_1=0; //显示个位
LED_PORT=table[ge];
Delayms(1);
LED_PORT = 0xff;
}
/*************************************
* 函 数 名 :KeyScan
* 函数功能 :4*4键盘扫描
* 输 入 :无
* 输 出 :num 获取的键值,若没有键值则返回0xff
*************************************/
unsigned char KeyScan()
{
unsigned char temp,num;
num = 0xff;
KEY_PORT=0xfe;
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
Delayms(5); //延时消抖
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=KEY_PORT;
switch(temp)
{
case 0xee:num=1;
break;
case 0xde:num=2;
break;
case 0xbe:num=3;
break;
case 0x7e:num=4;
break;
}
while(temp!=0xf0)
{
temp=KEY_PORT;
temp=temp&0xf0;
}
}
}
KEY_PORT=0xfd;
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
Delayms(5); //延时消抖
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=KEY_PORT;
switch(temp)
{
case 0xed:num=5;
break;
case 0xdd:num=6;
break;
case 0xbd:num=7;
break;
case 0x7d:num=8;
break;
}
while(temp!=0xf0)
{
temp=KEY_PORT;
temp=temp&0xf0;
}
}
}
KEY_PORT=0xfb;
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
Delayms(5); //延时消抖
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=KEY_PORT;
switch(temp)
{
case 0xeb:num=9;
break;
case 0xdb:num=10;
break;
case 0xbb:num=11;
break;
case 0x7b:num=12;
break;
}
while(temp!=0xf0)
{
temp=KEY_PORT;
temp=temp&0xf0;
}
}
}
KEY_PORT=0xf7;
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
Delayms(5); //延时消抖
temp=KEY_PORT;
temp=temp&0xf0;
while(temp!=0xf0)
{
temp=KEY_PORT;
switch(temp)
{
case 0xe7:num=13;
break;
case 0xd7:num=14;
break;
case 0xb7:num=15;
break;
case 0x77:num=16;
break;
}
while(temp!=0xf0)
{
temp=KEY_PORT;
temp=temp&0xf0;
}
}
}
return num;
}
/*************************************
* 函 数 名 :UsartConfiguration
* 函数功能 :串口设置
* 输 入 :无
* 输 出 :无
*************************************/
void UsartConfiguration()
{
SCON = 0X50; //设置为工作方式1:10位异步收发器
TMOD |= 0x20; //设置计数器工作方式2:8位自动重装计数器
PCON = 0X80; //波特率加倍:SMOD =1
TH1 = 256-(FOSC/12/32/(BAUD/2)); //计算定时器1初值
TL1 = 256-(FOSC/12/32/(BAUD/2));
TR1 = 1;//启动定时器T1
ES=1; //开串口中断
EA = 1; //开总中断
}
/*************************************
* 函 数 名 :UsartSend
* 函数功能 :串口发送电压
* 输 入 :无
* 输 出 :无
*************************************/
void UsartSend()
{
//发送 V
TI = 0; //清除发送中断标志位TI
SBUF='V'; //收到的数据放入到发送SBUF,启动串口发送
while(!TI); //等待发送数据完成
//发送 :
TI = 0; //清除发送中断标志位TI
SBUF=':'; //收到的数据放入到发送SBUF,启动串口发送
while(!TI); //等待发送数据完成
//发送电压整数
TI = 0; //清除发送中断标志位TI
SBUF=readadc%1000/100+'0';//收到的数据放入到发送SBUF,启动串口发送
while(!TI); //等待发送数据完成
//发送小数点
TI = 0; //清除发送中断标志位TI
SBUF='.';//收到的数据放入到发送SBUF,启动串口发送
while(!TI);
//发送电压十分位
TI = 0; //清除发送中断标志位TI
SBUF=readadc%100/10+'0';//收到的数据放入到发送SBUF,启动串口发送
while(!TI); //等待发送数据完成
//等待发送数据完成
//发送电压百分位
TI = 0; //清除发送中断标志位TI
SBUF=readadc%10+'0';//收到的数据放入到发送SBUF,启动串口发送
while(!TI); //等待发送数据完成
//发送换行符
TI = 0; //清除发送中断标志位TI
SBUF='\n'; //收到的数据放入到发送SBUF,启动串口发送
while(!TI); //等待发送数据完成
TI=0;
}
/*************************************
* 函 数 名 :main
* 函数功能 :主函数
* 输 入 :无
* 输 出 :无
*************************************/
void main()
{
int count = 0;
state =1;
UsartConfiguration();
while(1)
{
key_value=KeyScan();
if(key_value!=0xff)
state=key_value;
if(state==1) //按键1按下,选择通道0,即电位器调节实验
CHX=0;
else if(state==2) //按键2按下,选择通道1,即光敏电阻实验
CHX=1;
else if(state==3) //按键3按下,选择通道2,即热敏电阻实验
CHX=2;
else if(state==4) //按键4按下,选择通道3,接地
CHX=3;
if(count >= 200) //采样周期1s
{
//readadc=ReadADC(CHX)/256*5*100;
readadc=ReadADC(CHX)*5.0*100/255; //调用ReadADC函数读取AD转换结果
UsartSend();//发送电压到串口
count = 0;
}
qian=readadc/1000; //显示AD转换结果
bai=readadc%1000/100;
shi=readadc%100/10;
ge=readadc%10;
LEDdisplay();
count++;
}
}
/*************************************
* 函 数 名 :UsartInt
* 函数功能 :串口中断服务函数
* 输 入 :无
* 输 出 :无
*************************************/
void UsartInt() interrupt 4
{
char receiveData; //存放接收的函数
if(RI == 1) //RI=1说明是接收到数据产生的中断
{
receiveData=SBUF; //从接收SBUF中取得接收到的数据
//判断接受值,改变读取通道
switch (receiveData)
{
case '0' :state = 1;break;
case '1' :state = 2;break;
case '2' :state = 3;break;
default :break;
}
RI = 0; //清除接收中断标志位RI
}
}
四、本任务中的几个功能如何实现?(说明设计思路)
设计思路:
1. 首先借助例程,实验采集电压,但例程所给为八个二进制位,不代表电压值,需要经过公式转化,小数位的显示,可以采用电压值扩大100倍,给第二位数码管后加小数点,实现了显示2个电压。
2. 串口发送电压值,移植上次课例程,实现相应ASCII的转化。
3. 用上位机发送数据来实现控制检测通道,采用中断法接受串口数据,用switch来判断接收值,并改变state状态,实现通道的切换。
全部资料51hei下载地址:
PCF8591AD转换应用设计.rar
(10.27 MB, 下载次数: 27)
|