帮忙调试一下 M128外挂16MHz晶振的DS18B20程序……郁闷两个晚上没调出
//软件平台:AVR Studio 4//硬件:Atmega128 +DS18B20 +LCD1602
//系统时钟:外部16MHz晶体振荡器
//使用资源:
// PA--------LCD1602 //DB0-DB7
// PG0-------RS
// PG1-------RW
// PG2-------EN
// PF0---------DS18B20
//程序
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
//#include <util/delay.h>
#include "1602.h"
/*------宏定义------*/
#define uchar unsigned char
#define uint unsigned int
#define BIT(x) (1<<(x))
#define NOP() asm("nop")
#define WDR() asm("wdr")
#define dat 1 //PF0
//Atmega128 PF0(PIN61)(ADC0)
#define CLR_18B20 PORTF&=~BIT(dat) //数据线强制拉低
#define SET_18B20 PORTF|=BIT(dat) //数据线强制拉高
#define OUT_18B20 DDRF|=BIT(dat) //主机控制,信号线为输出"1"
#define IN_18B20 DDRF&=~BIT(dat) //释放总线,信号线为输入"0"
#define STU_18B20 PINF&BIT(dat) //查询总线状态
uchar temp_num[]={"Temperature:"};
//uchar temp_data={0x00,0x00}; //读出温度暂放
float temperature; //显示温度值
//定时器T0初始化
//T0 的64分频最大定时周期1024us 单步间隔4us
void timer0_init(void)
{
TCCR0= 0x00;//停止定时器
// TCNT0= 0x00;//初始值
// OCR0 = 0xFF;//匹配值
// TIMSK |= 0x00;//中断允许
// TCCR0= 0x03;//启动定时器
}
//端口初始化
void port_init(void)
{
DDRA=0xff;
PORTA=0x00;
DDRG=0xff;
PORTG=0xff;
DDRF=0xff;
PORTF=0x00;
}
void init_devices(void)
{
cli(); //禁止所有中断
MCUCR= 0x00; //MCU控制寄存器
MCUCSR = 0x80;//禁止JTAG//MCU控制和状态寄存器
// GICR = 0x00;
port_init();
timer0_init();
// sei();//开全局中断
}
//使用定时器精确定时
void delay_time(uchar time)
{
TCNT0= time;//初始值
TCCR0= 0x03;//启动定时器
while((TIFR&(1<<TOV0))==0);//查询标志位
// while ((TIFR&0x01));
TIFR|=(1<<TOV0); //软件清零标志位
// TIFR |= 0x01;
TCCR0= 0x00;//停止定时器
}
//Initialization the DS18B20
void init_18B20(void)
{
OUT_18B20; //设置为输出
SET_18B20; //上拉电阻使能
CLR_18B20; //强制拉低,并等待480~960us;
delay_time(0x6A);
SET_18B20; //释放总线
IN_18B20; //等待18B20响应
delay_time(0xF1);//等待15-60us,60us内18B20会将总线拉低
while(~STU_18B20); //总线被18B20拉低表示存在
OUT_18B20;
SET_18B20;
}
//Write one byte to DS18B20
void write_byte_18B20(uchar val)
{
uchar j;
uchar y;
for(j=0;j<8;j++)
{
y=val>>j; //先写低位
y&=0x01; //查询要写入的位是“0”或者“1”
if(y) //如果要写入的是“1”
{
NOP();
NOP();
NOP();
SET_18B20; //写入“1”
}
elseCLR_18B20; //强制拉低,产生写间隙,写“0”时保持即可
//输出一位之后,维持时间大于60us,小于120us
delay_time(0xF1);
SET_18B20; //DATA=1,上拉
delay_time(0xFD);
}
SET_18B20; //8位写完成后置位总线
}
//Read one byte from DS18B20
uchar read_byte_18B20(void)
{
uchar x;
uchar retval=0;
for(x=0;x<8;x++)
{
retval>>=1;//先读取低位字节,右移保存
OUT_18B20; //主机控制总线
CLR_18B20; //强制拉低
//之后在到读取出数据为止,15us内必须读出数据
SET_18B20; //释放总线,生成读间隙
IN_18B20; //总线设置为输入
if(STU_18B20) retval|=0x80; //读出为“1”信号
delay_time(0xF1);
}
// SET_18B20; //读完8位后置位总线
return(retval); //返回读到的值
}
//Read temperature from DS18B20
void read_temp_18B20(void)
{
uint k=0;
uchar temp_L=0; //温度低八位
uchar temp_H=0; //温度高八位
init_18B20();
write_byte_18B20(0xcc); //跳过ROM
write_byte_18B20(0x44); //启动温度转换
for(;k<750;k++) {delay_time(0x00);} //??????此处延时是否需要?
//数据手册提示说要750ms左右,蒙了……温度转换效率那么低还要来干球!!
init_18B20(); //重新复位
write_byte_18B20(0xcc); //跳过ROM
write_byte_18B20(0xbe); //发读数据指令
temp_L=read_byte_18B20(); //温度数据低8位
temp_H=read_byte_18B20(); //温度数据高8位
if(temp_H<0xf8) temperature=((temp_H*256)+temp_L)*0.0625; //大于0的情况
else
{
temperature = (((~temp_H)*256)+(~temp_L)+1)*0.0625; //小于0的情况
temperature = -temperature;
}
}
void display_temp(void)
{
uint temp_B,temp_S,temp_G,temp_F1;
if(temperature>=0)
{
temp_B= ((int)temperature)/100;
temp_S= ((int)temperature)%100/10;
temp_G= ((int)temperature)%100%10;
temp_F1 = ((int)(temperature*100))%100/10;
// temp_F2 = ((int)(temperature*100))%10;
WriteNum(2,1,temp_B+0x30);
WriteNum(2,2,temp_S+0x30);
WriteNum(2,3,temp_G+0x30);
WriteChar(2,4,1,"."); //标志行
WriteNum(2,5,temp_F1+0x30);
// WriteNum(2,6,temp_F2+0x30);
}
else
{
temp_B= ((int)(-temperature))/100;
temp_S= ((int)(-temperature))%100/10;
temp_G= ((int)(-temperature))%100%10;
temp_F1 = ((int)((-temperature)*100))%100/10;
// temp_F2 = ((int)((-temperature)*100))%10;
WriteChar(2,1,1,"-"); //标志行
WriteNum(2,2,temp_B+0x30);
WriteNum(2,3,temp_S+0x30);
WriteNum(2,4,temp_G+0x30);
WriteChar(2,5,1,"."); //标志行
WriteNum(2,6,temp_F1+0x30);
// WriteNum(2,7,temp_F2+0x30);
}
}
int main(void)
{
init_devices();
LcdInit(); //初始化
WriteChar(1,0,12,temp_num); //标志行
// init_18B20();
while(1)
{
read_temp_18B20();
display_temp();
// for(z=0;z<18;z++) {s_ms(60000);}
}
}
/*-----------------------------------------------------------------------------------------
//包含LCD1602的头文件如下
-----------------------------------------------------------------------------------------*/
#define uchar unsigned char
#define uint unsigned int
#define RS 0
#define RW 1
#define EN 2
void s_ms(uint ms)
{
uint i=0;
for(i=0;i<1000;i++){for(;ms>1;ms--);}
}
//查忙
void busy(void)
{
uchar temp;
s_ms(500);
PORTG&=~(1<<RS); //RS=0
s_ms(500);
PORTG|=(1<<RW); //RW=1
s_ms(500);
while(temp)
{
PORTG|=(1<<EN); //EN=1
s_ms(500);
DDRA=0x00; //A口变输入
PORTA=0xff; //上拉使能
s_ms(500);
temp = PINA&0x80; //读取A口
s_ms(500);
DDRA=0xff;
PORTA=0xff; //A口变输出
s_ms(500);
PORTG&=~(1<<EN); //EN=0
s_ms(500);
}
}
//写指令
void writecom(uchar com)
{
busy();
s_ms(500);
PORTG&=~(1<<RS); //RS=0
s_ms(500);
PORTG&=~(1<<RW); //RW=0
s_ms(500);
PORTG|=(1<<EN); //EN=1
s_ms(500);
PORTA = com; //输出指令
s_ms(500);
PORTG&=~(1<<EN); //EN=0
s_ms(500);
}
//1602初始化
void LcdInit(void)
{
writecom(0x38);
s_ms(1000);
writecom(0x01);
s_ms(10000);
s_ms(1000);
s_ms(1000);
s_ms(1000);
s_ms(1000);
s_ms(1000);
s_ms(1000);
writecom(0x02);
s_ms(1000);
writecom(0x06);
s_ms(1000);
writecom(0x0c);
s_ms(1000);
writecom(0x38);
s_ms(1000);
}
//写数据
void writedata(uchar data)
{
busy();
s_ms(500);
PORTG|=(1<<RS); //RS=1
s_ms(500);
PORTG&=~(1<<RW); //RW=0
s_ms(500);
PORTG|=(1<<EN); //EN=1
s_ms(500);
PORTA = data; //输出数据
s_ms(500);
PORTG&=~(1<<EN); //EN=0
s_ms(500);
}
//读数据
uchar readdata(void)
{
uchar temp;
busy();
s_ms(500);
PORTG|=(1<<RS);//RS=1
s_ms(500);
PORTG|=(1<<RW);//RW=1
s_ms(500);
PORTG|=(1<<EN);//EN=1
s_ms(500);
DDRA=0x00; //A端口变输入
s_ms(500);
temp = PINA; //读A端口
s_ms(500);
DDRA=0xff; //A端口变输出
s_ms(500);
PORTG&=~(1<<EN); //EN=0
s_ms(500);
return temp;
}
//=================================================
// 描述: 写LCD内部CGRAM函数
// 入口: ‘num’要写的数据个数
// ‘pbuffer’要写的数据的首地址
// 出口: 无
//================================================
void WriteCGRAM(uint num, const uint *pBuffer)
{
uint i,t;
writecom(0x40);
PORTG|=(1<<RS);
PORTG&=~(1<<RW);
for(i=num;i!=0;i--)
{
t = *pBuffer;
PORTG|=(1<<EN);
PORTA = t;
PORTG&=~(1<<EN);
pBuffer++;
}
}
//=================================================
//描述:写菜单函数,本程序使用的LCD规格为 16 * 2
//入口:菜单数组首地址
//出口:无
//=================================================
void WriteMenu(const uchar *pBuffer)
{
uchar i,t;
writecom(0x80); //数据地址
PORTG|=(1<<RS);
PORTG&=~(1<<RW);
s_ms(50);
for(i=0;i<16;i++)
{
t = *pBuffer;
PORTA = t;
PORTG|=(1<<EN);
s_ms(50);
PORTG&=~(1<<EN);
pBuffer++;
}
writecom(0xC0);
PORTG|=(1<<RS);
PORTG&=~(1<<RW);
s_ms(50);
for(i=0;i<16;i++)
{
t = *pBuffer;
PORTA = t;
PORTG|=(1<<EN);
s_ms(50);
PORTG&=~(1<<EN);
pBuffer++;
}
}
//====================================================
// 描述:在任意位置写数字函数
// 入口:’row‘表示要写数字所在的行地址,只能为1或2
// ’col‘表示要写数字所在的列地址,只能为0--15
// ‘num’表示要写的数字,只能为0--9
// 出口:无
//===================================================
void WriteNum(uchar row,uchar col,uchar num)
{
if (row == 1) row = 0x80 + col;
else row = 0xC0 + col;
writecom(row);
PORTG|=(1<<RS);
s_ms(500);
PORTG&=~(1<<RW);
s_ms(500);
PORTA = num;
s_ms(500);
PORTG|=(1<<EN);
s_ms(500);
PORTG&=~(1<<EN);
s_ms(500);
}
//================================================================
// 描述:在任意位置写任意多个字符
// 入口:’row‘要写的字符所在的行,只能为1或2;
// ‘col’要写的字符所在的列,只能为0---15
// ‘num’要写字符的个数
// ‘pbuffer’要写字符的首地址
//==================================================================
void WriteChar(uchar row,uchar col,uint num,uchar *pBuffer)
{
uchar i,t;
if (row == 1) row = 0x80 + col;
else row = 0xC0 + col;
writecom(row);
PORTG|=(1<<RS);
s_ms(500);
PORTG&=~(1<<RW);
s_ms(500);
for(i=num;i!=0;i--)
{
t = *pBuffer;
s_ms(500);
PORTA = t;
s_ms(500);
PORTG|=(1<<EN);
s_ms(500);
PORTG&=~(1<<EN);
s_ms(500);
pBuffer++;
}
}
问题点:
1:定时器用做延时时如何初始化以及如何查询标志位?-----T0为例
个人想法是初始化其实可以忽略,只要程序一开始关闭定时器即可,这样在以后需要延时的地方再启动它,不过是否需要设置匹配值OCR0,我还是搞不大明白;查询定时器是否溢出的标示位TOV0是否如我编写的那样查询?总感觉有点悬乎……不知道查询方式对没
2:温度转换子程序中发转换命令后是否需要等待那么长时间?太悬了……我看到很多个版本,有的说是必须要,有的说不要也照样跑得过去
3:直接如程序里的方式来判断温度正/负是否妥当?
4:目前读出一个数值:015.9-------------不知是哪里的数值,数值保持不变
也许用内部晶振的话估计能找到好多参考,而且是可以直接移植的参考……M8低速模式下跟C51单片机我都调出过N次的DS18B20
这里我只是提出一个高速晶振时对于精确延时的寻求解决办法,这里用到定时器做延时有点大材小用了!
望高手能指点一二或者有兴趣的同僚多多交流 没有大虾路过吗?……伤哀 宁万大哥!不用这么拼命吧 ……严重的对楼上无语,我的帖都被你翻到~~~~~~~ 几十微秒别用定时器了.没必要.中断一下,或者查询一下,时间差不多.
楼主,没谁有兴趣看代码.
不过我给你个我试过了,也是本站某兄弟写的,绝对可用,不过是8M的,需要把延时程序改下.改一下端口定义.
里面什么定时器什么的你都不用管了.
http://www.ouravr.com/bbs/bbs_content.jsp?bbs_sn=657257&bbs_page_no=1&search_mode=1&search_text=18B20&bbs_id=1000 我搞的时候情况和楼主相反,是高速时钟下没问题,低速时钟下才碰到困难。
不过某人逛这里的时候,看到个帖子,一下子就解决了。
帖上我当时的BLOG,希望对楼主有用。
http://hi.baidu.com/bg4uvr/blog/item/263145335bdbb045ad4b5f8f.html //Write one byte to DS18B20
void write_byte_18B20(uchar val)
{
uchar j;
uchar y;
for(j=0;j<8;j++)
{
y=val>>j; //先写低位
y&=0x01; //查询要写入的位是“0”或者“1”
if(y) //如果要写入的是“1”
{
NOP();
NOP();
NOP();
SET_18B20; //写入“1”
}
elseCLR_18B20; //强制拉低,产生写间隙,写“0”时保持即可
//输出一位之后,维持时间大于60us,小于120us
delay_time(0xF1);
SET_18B20; //DATA=1,上拉
delay_time(0xFD);
}
SET_18B20; //8位写完成后置位总线
}
写“1”时也要拉低总线20us左右以通知ds18b20,否则它不知道的。 楼上诸位大虾,感激不尽、感激不尽……
四楼的大哥提醒得对,确实很多人不喜欢看别人的代码,包括我自己,也不喜欢看别人的代码,很头大啊!!!
可没办法三……问题就出在延时代码上,只好附上来了
我原先写的程序没用定时器的,可这18B20比较挑,用内嵌的循环写成的延时又不好把握时长,所以…… 请教一下:怎样把主机接收的DS18b20数据(二进制补码)转换为10进制数呢?
楼主用了这段代码:
if(temp_H<0xf8)
temperature=((temp_H*256)+temp_L)*0.0625; //大于0的情况
else
{
temperature = (((~temp_H)*256)+(~temp_L)+1)*0.0625; //小于0的情况
temperature = -temperature;
} lonelynw 发表于 2009-3-28 10:35 static/image/common/back.gif
楼上诸位大虾,感激不尽、感激不尽……
四楼的大哥提醒得对,确实很多人不喜欢看别人的代码,包括我自己, ...
想问问您这个高速晶振下的18B20问题解决了没... 我最近也碰到这个情况 想向您求助一下 lonelynw 发表于 2009-3-28 10:35 static/image/common/back.gif
楼上诸位大虾,感激不尽、感激不尽……
四楼的大哥提醒得对,确实很多人不喜欢看别人的代码,包括我自己, ...
感觉您测出的温度不是15.9而是 I5.9是英文字母 "i" 的大写 我只学过C51……瞎说说啊……
应该是时钟问题,在对DS18B20进行读写的子程序里面用到的delay一般就是几个至十几个NOP,16MHz相当于51单片机一百多MHz,我在C51下晶振从12Mhz(12T单倍速)换成了20Mhz丫都罩不住……别说您这么快了
贴上一小段C51的代码:
/*
DQ为输入数据的接口,接ds18b20的中间
调用方法:
uint ds18b20_get_temp();
得到的温度如果最高位=1则温度为负温度,否则正温度
*/
#include<reg52.h>
#include"ds18b20_driver.h"
#include"delay_api.h"
#define jump_ROM 0xCC
#define start 0x44
#define read_EEROM 0xBE
sbit DQ=1^7;
uchar TMPL,TMPH;
unsigned char reset_ds18b20(void)
{
uchar deceive_ready;
DQ = 0;
macdelay(29);
DQ = 1;
macdelay(3);
deceive_ready = DQ;
macdelay(25);
return(deceive_ready);
}
uchar ds18b20_read_bit(void)
{
uchar i;
DQ = 0;
DQ = 1;
for(i=0; i<3; i++);
return(DQ);
}
void ds18b20_write_bit(uchar bitval)
{
DQ=0;if(bitval==1)
DQ=1;
macdelay(5);
DQ=1;
}
uchar ds18b20_read_byte(void)
{
uchar i,m,receive_data;
m = 1;
receive_data = 0;
for(i=0; i<8; i++)
{
if(ds18b20_read_bit())
{
receive_data = receive_data + (m << i);
}
macdelay(6);
}
return(receive_data);
}
void ds18b20_write_byte(uchar val)
{
uchar i,temp;
for(i=0; i<8; i++)
{
temp = val >> i;
temp = temp & 0x01;
ds18b20_write_bit(temp);
macdelay(5);
}
}
uint ds18b20_get_temp(uchar cf)
{
uint temp;
uchar tflag;
reset_ds18b20();
ds18b20_write_byte(jump_ROM);
ds18b20_write_byte(start);
reset_ds18b20();
ds18b20_write_byte(jump_ROM);
ds18b20_write_byte(read_EEROM);
TMPL = ds18b20_read_byte();
TMPH = ds18b20_read_byte();
temp=TMPH;
temp<<=8;
temp=temp|TMPL;
if(temp<0x0fff){
tflag=0;
}else{
tflag=1;
temp=~temp+1;
}
if(cf){
temp=temp*0.1125+32+tflag*0x1000;
}else{
temp=temp*0.0625+tflag*0x1000;
}
return temp;
}
/*温度模块结束*/
里面使用的macdelay如下:
void macdelay(uint i){
uint j=0;
for(j=0;j<i;j++){
}
}
在20Mhz下,将macdelay的延时扩充为本来的两倍就正常了
所以不妨试试。 以后也要用到 弄时序方面的示波器不离手 在程序里面丢脉冲到其他引脚 用同步来看时序走到了哪一步 那一步不通 就抓住了再分析 lisun0410 发表于 2013-2-1 00:55 static/image/common/back.gif
想问问您这个高速晶振下的18B20问题解决了没... 我最近也碰到这个情况 想向您求助一下 ...
应该是延时大多过长了 xqn2012 发表于 2013-2-3 11:19 static/image/common/back.gif
应该是延时大多过长了
额 晶振从8M变成16M 延时需要适当增加 我先翻倍 然后逐渐减少 现在已经调通了 mark以备查,18b20的时序很麻烦 lisun0410 发表于 2013-2-3 15:38 static/image/common/back.gif
额 晶振从8M变成16M 延时需要适当增加 我先翻倍 然后逐渐减少 现在已经调通了 ...
其实我是想说,延时比较麻烦,如果是avr studio话,还要优化。就是慢慢调的 xqn2012 发表于 2013-2-4 09:34 static/image/common/back.gif
其实我是想说,延时比较麻烦,如果是avr studio话,还要优化。就是慢慢调的 ...
额 好吧 我用的CVAVR 我也调了两天的ATMEGA12816MHZ 下的DS18B20。。 好累啊,不知道哪里错了。求楼主指点哪里要注意的。
页:
[1]