|
//软件平台: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[2]={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”
}
else CLR_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
这里我只是提出一个高速晶振时对于精确延时的寻求解决办法,这里用到定时器做延时有点大材小用了!
望高手能指点一二或者有兴趣的同僚多多交流 |
阿莫论坛20周年了!感谢大家的支持与爱护!!
知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)
|