单片机课设
题目是DS18B20的通信,用CH452搭配数码管显示。DS18B20最主要的是单总线协议,这个我之前没有接触过。上来只好老老实实的看数据手册。
对于DS18B20来说,有下面的这些特点:
( 1 )采用单总线的接口方式 与微处理器连接时仅需要一条口线即可实现微处理器与 DS18B20 的双向通讯。单总线具有经济性好,抗干扰能力强,适合于恶劣环境的现场温度测量,使用方便等优点,使用户可轻松地组建传感器网络,为测量系统的构建引入全新概念。
( 2 )测量温度范围宽,测量精度高 DS18B20 的测量范围为 -55 ℃ ~+ 125 ℃ ; 在 -10~+ 85°C范围内,精度为 ± 0.5°C 。
( 3 )在使用中不需要任何外围元件。
( 4 )持多点组网功能 多个 DS18B20 可以并联在惟一的单线上,实现多点测温。
( 5 )供电方式灵活 DS18B20 可以通过内部寄生电路从数据线上获取电源。因此,当数据线上的时序满足一定的要求时,可以不接外部电源,从而使系统结构更趋简单,可靠性更高。
( 6 )测量参数可配置 DS18B20 的测量分辨率可通过程序设定 9~12 位。
( 7 ) 负压特性电源极性接反时,温度计不会因发热而烧毁,但不能正常工作。
( 8 )掉电保护功能 DS18B20 内部含有 EEPROM ,在系统掉电以后,它仍可保存分辨率及报警温度的设定值。
这里面需要注意的主要是内部的寄生电路,这个电路可以不接电源来实现工作。但是转换温度的时间会增加。在老师给我们的板子上接了电源。同时也给信号端口接了上拉电阻。所以也是在程序上简化了一些。下面来一一介绍DS18B20的工作通信协议、DS18B20的配置以及CH452的显示部分。
DS18B20的工作通信协议:
DS18B20是单总线协议,结构上的简单一定会导致时序上的复杂。在时序上要求严格按照数据手册上面的要求来完成。对于每
一次的写命令来说都要按照下面的步骤来执行:
即 初始化命令——>Rom命令——>函数命令(主要是温度转换)之所以把这个写在前面,是因为这个非常的重要而且很容易被忽视。在自己写初步的程序的时候,一直没有正常的通讯。看了别人的程序才恍然大悟。主要是一开始这个步骤忽略了。介绍完了命令的步骤。下面开始说明初始化,写0 、写1和读0、读1这几步。
(初始化时序)
(读写1 0)
这里的话没有什么好说的,需要注意的主要是三点:(1)注意时序的要求,不同的单片机延时也不一样,我用的是STC15的和89的就不同。(2)注意释放总线 (3)可以利用初始化后面的应答信号确定是否响应,有时候会用到。
DS18B20的配置:
DS18B20内部有48位的唯一ID,这个ID在只有一个芯片的时候没有什么用。但是要是实现多个DS18B20挂载在一个总线的时候这个是不可缺少的。所以我们首先要读出来这个ID。ID可以通过CRC校验确定是否正确。
我读出的芯片ID是{0x28,0x79,0xA9,0xE6,0x05,0x00,0x00,0xA5}和{0x28,0xd8,0x60,0xc0,0x06,0x00,0x00,0x82}这样就通过不同的ID来确定不同新片了。
uchar CRC8()
{
uchar i,x; uchar crcbuff;
crc=0;
for(x = 0; x <8; x++)
{
crcbuff=fCode[x];
for(i = 0; i < 8; i++)
{
if(((crc ^ crcbuff)&0x01)==0)
crc >>= 1;
else {
crc ^= 0x18; //CRC=X8+X5+X4+1
crc >>= 1;
crc |= 0x80;
}
crcbuff >>= 1;
}
}
return crc;
}
(CRC校验程序)
CH452的显示部分:
CH452是键盘扫描和显示芯片。我参照的是官方的例程,(ˇˍˇ) 向里面写数据就可自动的实现显示和键盘读写。下面贴出原理图
最后给出源程序,希望有借鉴的价值。
#include
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int
#define FOSC 11059200L //系统频率
#define BAUD 115200 //串口波特率
//命令定义
#define CH452_NOP 0x0000 // 空操作
#define CH452_RESET 0x0201 // 复位
#define CH452_LEVEL 0x0100 // 加载光柱值,需另加7位数据
#define CH452_CLR_BIT 0x0180 // 段位清0,需另加6位数据
#define CH452_SET_BIT 0x01C0 // 段位置1,需另加6位数据
#define CH452_SLEEP 0x0202 // 进入睡眠状态
#define CH452_LEFTMOV 0x0300 // 设置移动方式-左移
#define CH452_LEFTCYC 0x0301 // 设置移动方式-左循环
#define CH452_RIGHTMOV 0x0302 // 设置移动方式-右移
#define CH452_RIGHTCYC 0x0303 // 设置移动方式-右循环
#define CH452_SELF_BCD 0x0380 // 自定义BCD码,需另加7位数据
#define CH452_SYSOFF 0x0400 // 关闭显示、关闭键盘
#define CH452_SYSON1 0x0401 // 开启显示
#define CH452_SYSON2 0x0403 // 开启显示、键盘
#define CH452_SYSON2W 0x0423 // 开启显示、键盘, 真正2线接口
#define CH452_NO_BCD 0x0500 // 设置默认显示方式,可另加3位扫描极限
#define CH452_BCD 0x0580 // 设置BCD译码方式,可另加3位扫描极限
#define CH452_TWINKLE 0x0600 // 设置闪烁控制,需另加8位数据
#define CH452_GET_KEY 0x0700 // 获取按键,返回按键代码
#define CH452_DIG0 0x0800 // 数码管位0显示,需另加8位数据
#define CH452_DIG1 0x0900 // 数码管位1显示,需另加8位数据
#define CH452_DIG2 0x0a00 // 数码管位2显示,需另加8位数据
#define CH452_DIG3 0x0b00 // 数码管位3显示,需另加8位数据
#define CH452_DIG4 0x0c00 // 数码管位4显示,需另加8位数据
#define CH452_DIG5 0x0d00 // 数码管位5显示,需另加8位数据
#define CH452_DIG6 0x0e00 // 数码管位6显示,需另加8位数据
#define CH452_DIG7 0x0f00 // 数码管位7显示,需另加8位数据
#define RADIX 0x02 //小数点
// 显示部分引脚定义
sbit din = P2^6; // 串行数据输出,接CH451的数据输入
sbit load=P3^3; //串行命令加载,上升延激活
sbit dout=P2^7; //INT1,键盘中断和键值数据输入,接CH451的数据输出
sbit dclk = P2^5;
sbit DS=P1^3; //define interface
sbit beep = P1^7;
sfr AUXR = 0x8e; //辅助寄存器 //串行数据时钟上升延激活
unsigned char code number[]={0xbd,0x18,0xd5,0xd9,0x78,0xe9,0xed,0x98,0xfd,0xf9};
uchar i,j,cmd1,keycode;
uint temp; // variable of temperature
uint temped; // 源码
uchar flag1; // sign of the result positive or negative
uchar flag_key;
uchar ch451_key;
uchar flag_wendu;
uint cmd;
bit busy;
bit presence;
unsigned char crc;
uchar fCode[8]; //序列号数组
uchar DSrom1[8]={0x28,0x79,0xA9,0xE6,0x05,0x00,0x00,0xA5}; //板子上的 //2个器件每个64位序列号
uchar DSrom2[8]={0x28,0xd8,0x60,0xc0,0x06,0x00,0x00,0x82}; //ROM1 //2个器件每个64位序列号
uint f[2]; //温度数组
uint e[2]; //原温度数组
void CH452_Write(unsigned short cmd); // ch452写程序
void CH452_Read(void); // ch452读程序
void Delay500us(); //延时
void Delay30us(); //延时
void Delay240us(); //延时
void Delay5us(); //延时
void Delay60us(); //延时
void Delay2us(); //延时
void Delay1000us(); //延时
void Delay100ms(); //延时
void Delay80us();
void Delay20us();
void Delay1ms();
void delay_b20(uint n); //单片机定时1us 不准!!!!!!
void uartinit(void); //串口初始化
void SendData(uint dat); //发送数据
void SendString(char *s); //发送字符串
void dsreset(void); // Ds1820初始化
bit tmpreadbit(void); //read a bit
uchar tmpread(void); //read a byte date
void tmpwritebyte(uchar dat); //write a byte to ds18b20
void tmpchange(void); //DS18B20 begin change
void display(uint temp); //显示程序
void exti1init ();
void DispCode(); // 读序列号
void read_dealtemp0(); // 读多个温度
void read_dealtemp1(); //读取温度
uchar CRC8() ;
void delay_b20(uint n)//STC12C5A单片机定时1us
{
while(n--)
{
_nop_();
}
}
void exti1init (){
INT0 = 1;
IT0 = 1; //设置INT0的中断类型 (1:仅下降沿 0:上升沿和下降沿)
EX0 = 1; //使能INT0中断
EA = 1;
}
void display(uint temp) //显示程序
{
uchar A1,A2,A3,A4;
A1 = temp/1000;
A2 = temp % 1000 / 100;
A3 = temp % 100 / 10;
A4 = temp % 10;
CH452_Write(CH452_DIG4 | number[ A1 ]);
CH452_Write(CH452_DIG5 | number[ A2]|RADIX);
CH452_Write(CH452_DIG6 | number[A3]);
CH452_Write(CH452_DIG7 | number[A4]);
}
void tmpchange(void) //DS18B20 begin change
{
dsreset();
Delay240us();
tmpwritebyte(0xcc); // 跳过ROM
tmpwritebyte(0x44); // initiates a single temperature conversion
}
/*
响应多个温度 首先是复位 然后ROM 接着RAm最后计算
*/
void read_dealtemp0(){ //读多个温度
uchar i,j;
uchar a,b;
float tt;
j = 0;
dsreset();
Delay240us() ;
tmpwritebyte(0x55); //输入序列号
for(i=0;i<8;i++) {
tmpwritebyte(DSrom1[i]);//发送64位序列号
}
tmpwritebyte(0xbe);
a=tmpread();
b=tmpread();
temp=b;
temp<<=8; //two byte compose a int variable
temp=temp|a;
temped=temp; // 源码
tt=temp*0.0625;
temp=tt*100+0.5;
f[j]=temp;
e[j]=temped;
Delay100ms();
}
void read_dealtemp1(){ //读多个温度
uchar i,j;
uchar a,b;
float tt;
j = 1;
dsreset();
Delay240us() ;
tmpwritebyte(0x55); //输入序列号
for(i=0;i<8;i++) {
tmpwritebyte(DSrom2[i]);//发送64位序列号
}
tmpwritebyte(0xbe);
a=tmpread();
b=tmpread();
temp=b;
temp<<=8; //two byte compose a int variable
temp=temp|a;
temped=temp; // 源码
tt=temp*0.0625;
temp=tt*100+0.5;
f[j]=temp;
e[j]=temped;
Delay100ms();
}
uint tmp() //get the temperature
{
float tt;
uchar a,b;
dsreset();
Delay240us() ;
tmpwritebyte(0xcc);
tmpwritebyte(0xbe);
a=tmpread();
b=tmpread();
temp=b;
temp<<=8; //two byte compose a int variable
temp=temp|a;
temped=temp; // 源码
tt=temp*0.0625;
temp=tt*100+0.5;
return temp; // 转换的温度
}
void tmpwritebyte(uchar dat) //write a byte to ds18b20
{
uchar j;
bit testb;
for(j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if(testb) //write 1
{
DS=0;
Delay5us();
DS=1;
Delay60us();
}
else
{
DS=0; //write 0
Delay60us();
DS=1;
Delay5us();
}
}
}
void DispCode()//读取序列号
{
uchar i;
dsreset();
tmpwritebyte(0x33);
for (i=0;i<8;i++)
{
fCode[i]=tmpread();
}
}
uchar tmpread(void) //read a byte date
{
uchar i,j,dat;
dat=0;
for(i=1;i<=8;i++)
{
j=tmpreadbit();
dat=(j<<7)|(dat>>1); //读出的数据最低位在最前面,这样刚好一个字节在DAT里
}
return(dat);
}
bit tmpreadbit(void) //read a bit
{
bit dat;
DS=0;//i++ for delay
Delay2us();
DS=1;
Delay2us();
dat=DS;
Delay60us();
return (dat);
}
void dsreset(void) //send reset and initialization command
{
DS = 1; //DQ复位
Delay2us();//延时
DS = 0; //DQ拉低
Delay500us(); //精确延时大于480us
DS = 1; //拉高
Delay30us();
presence = DS;
Delay60us();
DS = 1;
}
void SendString(char *s)
{
while (*s) //检测字符串结束标志
{
SendData(*s++); //发送当前字符
}
}
void SendData(uint dat)
{
while (busy); //等待前面的数据发送完成
busy = 1;
SBUF = dat; //写数据到UART数据寄存器
}
void uartinit(void){
SCON = 0x50; //8位可变波特率
AUXR = 0x40; //定时器1为1T模式
TMOD = 0x20; //定时器1为模式2(8位自动重载)
TL1 = (256 - (FOSC/32/BAUD)); //设置波特率重装值
TH1 = (256 - (FOSC/32/BAUD));
TR1 = 1; //定时器1开始工作
ES = 1; //使能串口中断
EA = 1;
}
void Delay1ms() //@11.0592MHz
{
unsigned char i, j;
_nop_();
_nop_();
_nop_();
i = 11;
j = 190;
do
{
while (--j);
} while (--i);
}
void Delay100ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
_nop_();
i = 5;
j = 52;
k = 195;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay20us() //@11.0592MHz
{
unsigned char i;
_nop_();
_nop_();
_nop_();
i = 52;
while (--i);
}
void Delay80us() //@11.0592MHz
{
unsigned char i, j;
_nop_();
i = 1;
j = 217;
do
{
while (--j);
} while (--i);
}
void Delay1000us() //@11.0592MHz
{
unsigned char i, j;
_nop_();
_nop_();
_nop_();
i = 11;
j = 190;
do
{
while (--j);
} while (--i);
}
void Delay2us() //@11.0592MHz
{
unsigned char i;
i = 3;
while (--i);
}
void Delay60us() //@11.0592MHz
{
unsigned char i, j;
i = 1;
j = 162;
do
{
while (--j);
} while (--i);
}
void Delay5us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 11;
while (--i);
}
void Delay240us() //@11.0592MHz
{
unsigned char i, j;
_nop_();
_nop_();
i = 3;
j = 145;
do
{
while (--j);
} while (--i);
}
void Delay500us() //@11.0592MHz
{
unsigned char i, j;
_nop_();
_nop_();
i = 6;
j = 93;
do
{
while (--j);
} while (--i);
}
void Delay30us() //@11.0592MHz
{
unsigned char i;
_nop_();
_nop_();
i = 80;
while (--i);
}
uchar CRC8()
{
uchar i,x; uchar crcbuff;
crc=0;
for(x = 0; x <8; x++)
{
crcbuff=fCode[x];
for(i = 0; i < 8; i++)
{
if(((crc ^ crcbuff)&0x01)==0)
crc >>= 1;
else {
crc ^= 0x18; //CRC=X8+X5+X4+1
crc >>= 1;
crc |= 0x80;
}
crcbuff >>= 1;
}
}
return crc;
}
void CH452_Write(unsigned short cmd){
load=0; //命令开始,LOAD=0
for(i=0;i!=12;i++) //送入12位数据,低位在前
{
dclk=0;
din=cmd&1;
dclk=1; //上升沿有效
cmd=cmd>>1;
}
load=1; //加载数据,LOAD上升沿
}
void CH452_Read(void)
{
cmd1=0x07; //读按键的命令字
load=0;
for(i=0;i!=4;i++) // 只需要发出高4位,多发也可以,但应该确保最后留下的4位是该命令码
{
din=cmd1&1;
dclk=0;
cmd1>>=1; //往右移一位
dclk=1; //产生时钟上升沿锁通知CH451输入位数据
}
load=1; //产生加载上升沿通知CH451处理命令数据
for(j=0;j<100;j++){ }
keycode=0; //清除keycode
for(i=0;i!=7;i++)
{
keycode<<=1; //数据移入keycode,高位在前,低位在后
if (dout)
{ keycode++;} //从高到低读入451的数据
// keycode|=CH452_DOUT;
dclk=0; //产生时钟下升沿通知CH451输出下一位
dclk=1;
}
}
//中断服务程序
void exint0() interrupt 0 //INT0中断入口
{
}
/*----------------------------
UART 中断服务程序
-----------------------------*/
void Uart() interrupt 4 using 1
{
if (RI)
{
RI = 0; //清除RI位
}
if (TI)
{
TI = 0; //清除TI位
busy = 0; //清忙标志
}
}
void main(void){
uchar path1;
path1 = 0;
CH452_Write(CH452_RESET);
CH452_Write(CH452_SYSON2); //CH452初始化
flag_wendu=1; // 显示温度标记
// presence =0; // 响应信号 0是OK
// DispCode(); //读序列号 读的时候只能是单线的操作
// CRC8(); //校验crc, 0 是ok
while (1){
CH452_Read();
if (keycode==0x44){
path1 =0 ;
beep=0;
Delay100ms();
beep=1;
}
if (keycode==0x45){
path1 =1;
beep=0;
Delay100ms();
beep=1;
}
if (keycode==0x46){
path1 =2;
beep=0;
Delay100ms();
beep=1;
}
if (keycode==0x47){
path1 =3;
beep=0;
Delay100ms();
beep=1;
}
keycode=0;
switch(path1)
{
case 0: display(f[0]); break;
case 1: display(e[0]); break;
case 2: display(f[1]); break;
case 3: display(e[1]); break;
}
tmpchange();//初始化
read_dealtemp0();//读取温度
tmpchange();//初始化
read_dealtemp1();//读取温度
}
}