单片机小白
第一行向左滚动
第二行固定不动
右下角笑脸顺时针旋转一周,第一行向左滚动一个字符,且温度刷新一次
单片机存储空间不同,u8 a[16+sizeof(str)+16] 处可能会报溢出,改成
u8 pdata a[16+sizeof(str)+16] 就行
pdata:外部扩展RAM的低256个字节
运行效果:
为动态滚动效果,以下为截图
主函数main.c:
#include <reg52.h>
#include "ds18b20.h"
#include "lcd1602.h"
u8 code cg_addr[]={0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78};
//其中八个CGRom的地址
u8 code read_cg[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07};
//以上八个CGRom的编号
u8 code user[][8]={
{0x1F,0x0A,0x0A,0x1F,0x0A,0x0A,0x12,0x02}, //开
{0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x00}, //心
{0x00,0x00,0x04,0x01,0x10,0x11,0x1F,0x00},
{0x07,0x05,0x07,0x00,0x00,0x00,0x00,0x00} //摄氏度的点
};
u8 code xiao[][8]={
{0x03,0x04,0x0A,0x08,0x0A,0x09,0x04,0x03}, //变幻左部笑脸
{0x03,0x04,0x09,0x0A,0x0A,0x09,0x04,0x03},
{0x03,0x04,0x09,0x0A,0x08,0x0A,0x04,0x03},
{0x03,0x04,0x0A,0x08,0x08,0x0A,0x04,0x03},
{0x18,0x04,0x0A,0x02,0x0A,0x12,0x04,0x18}, //变幻右部笑脸
{0x18,0x04,0x0A,0x02,0x02,0x0A,0x04,0x18},
{0x18,0x04,0x12,0x0A,0x02,0x0A,0x04,0x18},
{0x18,0x04,0x12,0x0A,0x0A,0x12,0x04,0x18}
};
u8 code str[]="tempereture :";
//自定义图形函数
/*思路是1.选中一个CGRAM(地址)
01 ... ... 设置第3.4.5位选中前8个CGROM
设置低三位选CGRAM的8行像素点
(文章用的是设置初地址然后for(...8...)循环选中)
2.向CGRAM填充图形(字符型为5*7,高电平亮)先填充行,自上而下依次重复操作
0x1.. .....(8421 8421码)
例如 dian[] = {0x07,0x05,0x07,0x00,0x00,0x00,0x00,0x00}
只有低5位有效
0x07=000 00111代表第一行的从右自左的第1.2.3个像素点亮 0 0 0
0x05=000 00101代表第一行的从右自左的第1.3个像素点亮 0 0
0x07=000 00111代表第三行的从右自左的第1.2.3个像素点亮 0 0 0
3.设置要显示的位置
4。从相应的CGRAM中取出自定义图形显示
( 0000 0000 设置低三位选中前八个CGRAM编码)
*/
void user_xie(u8 x,u8 y,u8 cg,u8 *User)
{
u8 i;
LcdWriteCmd(cg_addr[cg]); //设定 CGRAM 地址
for(i=0;i<8;i++ )
{
LcdWriteData(User[i]); //写入自定义图形
}
LcdSetCursor(x,y); //显示的位置,x为列,y为行
LcdWriteData(read_cg[cg]); //从 CGRAM 里取出自定义图形显示
}
//延时函数
//主要是引脚P2^5与蜂鸣器共用,不设置一下太吵人了
void delay_ds(unsigned short ms)
{
u8 T;
while(ms--)
{
for(T=0;T<250;T++);
}
}
void xiao_xz() //笑脸旋转+延时(相当于四帧动画)
{
user_xie(14,1,5,xiao[0]);
user_xie(15,1,6,xiao[4]);
delay_ds(125);
user_xie(14,1,5,xiao[1]);
user_xie(15,1,6,xiao[5]);
delay_ds(100);
user_xie(14,1,5,xiao[2]);
user_xie(15,1,6,xiao[6]);
delay_ds(100);
user_xie(14,1,5,xiao[3]);
user_xie(15,1,6,xiao[7]);
delay_ds(100);
}
//主函数
void main()
{
u8 a[16+sizeof(str)+16];
//为数组a定义空间,前16位与后十六位均为空字符,中间为要显示的字符串
//这里不能用strlen,sizeof为运算符,strlen为函数
u8 df[8];
s16 qw;
u8 i,j,index;
i=j=index=0;
InitLcd1602(); //lcd1602初始化
for(i=0;i<16;i++) //前16位与后十六位均为空字符,中间为要显示的字符串
a[i]= ' ';
for(i=0;i<(sizeof(str)-1);i++)
a[16+i]=str[i];
for(i=(16+sizeof(str)-1);i<sizeof(a);i++)
a[i]= ' ';
while(1)
{
qw=datapros(DS18B20temp_data()); //DS18B20温度数据
tostring(df,qw); //数字转字符串函数,添加小数点(保留一位小数)
LcdShowStr(11,1,"C"); //从CGROM中选中相应字符显示
user_xie(0,1,0,user[0]); //自定义图形函数
user_xie(1,1,1,user[1]);
user_xie(2,1,2,user[2]);
user_xie(10,1,3,user[3]);
if(j==0) //开机画面
{
LcdShowStr(5,1,"....");
LcdShowStr(0,0,"Make By LuDeYue!");
user_xie(14,1,5,xiao[0]);
user_xie(15,1,6,xiao[4]);
delay_ds(450);
}
j-=1;
LcdShowStr_Shift(0,0,a+index,16);
/*滚动显示函数,lcd的列数为16,所以每次只能选取16位字符进行显示
a+index类似指针(a为初地址,index为偏移量),指向所偏移的地址后向后取15个(从指向地址处算共16个)字符出来显示*/
index++;//显示一次后指向下一处地址,同理可更改为向右滚动
if(index>=(16+sizeof(str)-1))//如果字符串已全部左移的话(显示后16个空字符)置零,重新开始
{
index=0;
}
LcdShowStr(5,1,df); //温度数据显示
xiao_xz(); //笑脸旋转+延时
}
}
ds18b20.h:
#include <reg52.h>
#include <intrins.h>
#define u8 unsigned char
#define u16 unsigned int
#define s16 signed int
sbit IO_18B20=P3^7;
u8 DQ;
void Delay(u16 i)//延时
{
while(i--);
}
void init_18B20()//DS18B20初始化
{
IO_18B20=0;
Delay(100);
IO_18B20=1;
Delay(5);
DQ=IO_18B20;
Delay(80);
}
void write_cmd(u8 cmd)//DS18B20写时序
{
u8 i;
for(i=0;i<8;i++)
{
IO_18B20=0;
_nop_();
IO_18B20=cmd&0x01;
Delay(6);
IO_18B20=1;
cmd>>=1;
}
}
u8 read_cmd()//DS18B20读时序
{
u8 j,by,byte;
for(j=0;j<8;j++)
{
IO_18B20=0;
_nop_();
IO_18B20=1;
_nop_();
_nop_();
by=IO_18B20;
byte=(byte>>1)|(by<<7);
Delay(4);
}
return byte;
}
void change_temper()//温度转换
{
init_18B20();
if(DQ==0)
{
write_cmd(0xcc);//跳过ROM
write_cmd(0x44);//温度转换后存储在暂存寄存器的2个字节长度的温度寄存器中
}
}
void read_temper()//读温度
{
init_18B20();
if(DQ==0)
{
write_cmd(0xcc);//跳过ROM
write_cmd(0xbe);//读取暂存寄存器
}
}
s16 DS18B20temp_data()//采集DS18B20中cache温度数据
{
s16 temp;
u8 temph,templ;
change_temper();
read_temper();
templ=read_cmd();
temph=read_cmd();
temp=temph;
temp<<=8;
temp|=templ;
return temp;
}
s16 datapros(s16 temp)//将采集的数据按照规定的分辨率转化为数字
{
s16 tp;
if(temp<0)
{
temp=~temp;
temp+=1;
tp=temp;
temp=tp*0.0625*10+0.5;
}
else
{
tp=temp;
temp=tp*0.0625*10+0.5; //0.0625为分辨率 10:保留一位小数 0.5:精度
}
return temp;
}
lcd1602.h:
#include <reg52.h>
#include <intrins.h>
#define LCD1602_DB P0
sbit LCD1602_RW=P2^5;
sbit LCD1602_RS=P2^6;
sbit LCD1602_E=P2^7;
//lcd1602忙闲状态判断
void LcdWaitReady()
{
u8 sta;
LCD1602_DB=0xFF;
LCD1602_RS=0;
LCD1602_RW=1;
do{
LCD1602_E=1;
sta=LCD1602_DB;
LCD1602_E=0;
}while(sta&0x80);
}
//写命令
void LcdWriteCmd(u8 cmd)
{
LcdWaitReady();
LCD1602_RS=0;
LCD1602_RW=0;
LCD1602_DB=cmd;
LCD1602_E=1;
LCD1602_E=0;
}
//写数据(1602字符输入(CGROM固定字库))
void LcdWriteData(u8 dat)
{
LcdWaitReady();
LCD1602_RS=1;
LCD1602_RW=0;
LCD1602_DB=dat;
LCD1602_E=1;
LCD1602_E=0;
}
//lcd1602初始化
void InitLcd1602()
{
LcdWriteCmd(0x38); //功能设置命令
LcdWriteCmd(0x38); //有时候LCD1602不听话,只显示一行,那就在说几次
LcdWriteCmd(0x0C); //显示开/关及光标设置 、
LcdWriteCmd(0x06); //显示模式设置
LcdWriteCmd(0x01); //清屏指令
}
//显示的位置设置
void LcdSetCursor(u8 x,u8 y)//x为列,y为行
{
u8 addr;
if(y==0)
addr=0x80+x;//第一行
else
addr=0xC0+x;//第二行
LcdWriteCmd(addr);
}
//显示
void LcdShowStr(u8 x,u8 y,u8 *str)
{
LcdSetCursor(x,y);
while(*str!='\0')
{
LcdWriteData(*str++);
}
}
//滚动的显示
void LcdShowStr_Shift(u8 x,u8 y,u8 *str,u8 len)
{
LcdSetCursor(x,y);
while(len--)
{
LcdWriteData(*str++);
}
}
void tostring(u8 *str, int dat)//数字转字符串并添加小数点
{
signed char i = 0;
u8 buf[6];
if (dat < 0)
{
dat = -dat;
*str++ = '-';
}
do {
buf[i++] = dat % 10;
dat /= 10;
} while (dat > 0);
while (i-- > 0)
{
if(i==0)
{
*str++='.';
*str++ = buf[i] + '0';
}
else
*str++ = buf[i] + '0';
}
*str = '\0';
}