基于MSP430G2553开发板万年历加温湿度
材料:MSP430G2553开发板一块、DS1302时钟芯片、CR1220纽扣电池、CR1220纽扣电池、32.768Khz晶振、小纽扣按键三个、DHT11温湿度传感器、跳帽三个、LCD12864(自带电位器,3.3v供电需要调节对比度)
一、DS1302
DS1302 是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周、时、分、秒进行计时。DS1302采用三线SPI接口与单片机通讯,在进行一次读写操作时,我们要先发送控制字(要进行得操作和相应寄存器地址),然后对该寄存器写入数据或者读出数据。
DS1302读时隙:
控制字在串行时钟SCLK每个上升沿将一位数据读入,将一个字节(8位)控制字写入后,读出数据,读出数据在串行时钟SCLK每个下降沿读出一位数据,写入数据和读出数据得时钟沿刚好相反。无论是读出数据还是写入数据,都是从低位到高位的读(写)。
DS1302写时隙:
写时隙也是和读差不多,首先写入一个字节的控制字,也就是我们要进行的操作和相关寄存器地址,不同在于在每个串行时钟SCLK每个上升沿写入一位数据,传输8位控制字后写入数据,无论是控制字还是数据,都是从低位到高位写入(读出)。
我们一般只对DS1302时间相关的寄存及写入和读出数据,和时间相关的寄存器如下:
秒寄存器(0x80写,0x81读):BIT7为’1‘时,时钟停止。其他位位秒的数据位。
小时寄存器(0x82写,0x83读):BIT7为’1‘为12小时模式,为’0‘为24小时模式
在十二小时模式,BIT5表示上下午标志位,BIT5 为'1'表示下午。在24小时模式,BIT5是第二个10小 时位。
控制寄存器(0x8f写,0x8e读):BIT0~BIT6全为0,BIT7位’1‘为写保护,写保护状态下禁止向任何一个寄存器写入数据。
二、DHT11
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。DHT11 DATA接口是用于与单片机通讯和同步,DHT11一次传输的数据为40位(5个字节),DHT11传输数据时是从高到低传输,这个与DS1302不同,DS1302传输数据是从低位到高位传输。数据格式:8BIT湿度整数数据+8BIT湿度小数数据 +8BIT温度整数数据+8BIT温度小数数据 +8BIT校验和。
其中,8位校验和=8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据。
单片机与DHT11通讯过程:
1.单片机发送开始信号。DHT11总线空闲时位高点平,单片机把总线电平拉低,在拉低电平总线要大于18ms。
2.DHT11收到开始信号后发送80us的低电平响应信号。单片机检测总线位低电平,说明DHT11收到开始信号,把总线拉高80us准备准备发送数据。
3.单片机接收数据,每一bit数据都以50us低电平时隙开始,高电平的长短定了数据位是0还是1。数据为“0”:50us的低电平 + 26-28us的高电平
数据为“1”:50us的低电平 + 70us的高电平
4.数据传输完毕,单片机拉低总线50us,总线进入空闲状态。
DHT11不会主动去采集数据,只有当收到单片机发来的开始信号后DHT11开始采集数据,并发送40BIT数据。
三、原理图
四、PCB:
五、实物图:
六、代码:
#include "msp430g2553.h"
#include "in430.h"
#include "stdio.h"
#include "string.h"
#define uint unsigned int
#define uchar unsigned char
#define ulong unsigned long
//标题定义
unsigned char aa[14]={"万年历加温湿度"};
unsigned char bb[4]={"日期"};
unsigned char cc[4]={"时间"};
unsigned char flag,flag1,s1num,value;//flag不掉电用标志,flag1时钟暂停标志,s1num功能键按下去的次数,value存放数据的值
signed char year,month,date,hour,minute,second,week;//
char dis_buf[16]; //lcd上排显示缓冲区
char day[7][10]={"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};
char ch;
char ch1;
int *R;
int s=0,g=0;
int f = 1;
char str[];
int f1 = 0;
int f2 = 0;
int lacktime = 0;
unsigned char digit[]={"0123456789"};//定义字符数组显示数字
#define SCLK BIT0 //E
#define SID BIT1 //R/W
#define CS BIT2 //RS
#define LCDPORT P2OUT
#define SID_1 LCDPORT |= SID
#define SID_0 LCDPORT &= ~SID
#define SCLK_1 LCDPORT |= SCLK
#define SCLK_0 LCDPORT &= ~SCLK
#define CS_1 LCDPORT |= CS
#define CS_0 LCDPORT &= ~CS
#define CLK_1 P2OUT|=BIT5//1302芯片时钟端口
#define CLK_0 P2OUT&=~BIT5
#define CLK_OUT P2DIR|=BIT5
#define CLK_IN P2DIR&=~BIT5
#define DATA_1 P2OUT|=BIT4//1302芯片数据端口
#define DATA_0 P2OUT&=~BIT4
#define DATA_OUT P2DIR|=BIT4
#define DATA_IN P2DIR&=~BIT4
#define DATA (P2IN&BIT4)
#define RST_1 P2OUT|=BIT3//复位端口
#define RST_0 P2OUT&=~BIT3
#define RST_OUT P2DIR|=BIT3
#define RST_IN P2DIR&=~BIT3
//宏定义DS1302数据地址
#define write_second 0x80
#define read_second 0x81
#define write_minute 0x82
#define read_minute 0x83
#define write_hour 0x84
#define read_hour 0x85
#define write_date 0x86
#define read_date 0x87
#define write_month 0x88
#define read_month 0x89
#define write_year 0x8c
#define read_year 0x8d
#define write_day 0x8a
#define read_day 0x8b
#define write_protect 0x8e
#define write_power 0x90
uint i=0;
//温湿度数据变量
uint com_data,respond,temp,untemp;
uint TH_data,TL_data,RH_data,RL_data,CK_data;
uint TH_temp,TL_temp,RH_temp,RL_temp,CK_temp;
uchar s1num,num,shis,shig,wens,weng;
//延时程序
#define CPU_F ((double)1000000)
#define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0))
#define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x/1000.0))
void Uart0Sends(uchar *s);
void sendbyte(unsigned char data);
void write_cmd(uchar cmd);
void lcd_pos(uchar x,uchar y);
void Checkin();
void Init_RxTx();
void delay(unsigned int t)
{
unsigned int i,j;
for(i=0; i
for(j=0; j<3; j++);
}
void sendbyte(unsigned char data)
{
unsigned int i;
for(i=0;i<8;i++)
{
if((data<<i)&0x80)
{
SID_1;
}
else
{
SID_0;
}
SCLK_0;
SCLK_1;
}
}
void write_cmd(uchar cmd)
{
CS_1;
sendbyte(0xf8);
sendbyte(cmd&0xf0);
sendbyte((cmd<<4)&0xf0);
delay(1);
}
void write_dat(uchar dat)
{
CS_1;
sendbyte(0xfa);
sendbyte(dat&0xf0);
sendbyte((dat<<4)&0xf0);
delay(1);
}
void lcd_pos(uchar x,uchar y)
{
uchar pos;
switch(x)
{
case 1:pos=0x80;break;
case 2:pos=0x90;break;
case 3:pos=0x88;break;
case 4:pos=0x98;break;
default:pos=0x80;
}
pos += y;
write_cmd(pos);
}
void lcd_string(char *s)
{
unsigned char lcd_temp;
lcd_temp=*s;
while(lcd_temp!=0x00)
{
write_dat(lcd_temp);
lcd_temp=*(++s);
}
}
//LCD12864初始化
void LCD_init(void)
{
write_cmd(0x01);//清除显示
delay_ms(5);
write_cmd(0x30);//功能设定
delay_ms(5);
write_cmd(0x02);//地址归为
delay_ms(5);
write_cmd(0x0C);//整体显示ON
delay_ms(5);
write_cmd(0x06);//指定在资料的读取及写入时,设定游标的移动方向及指定显示的移位
delay_ms(5);
}
//
void Write1302(unsigned char dat)
{
uchar i;
DATA_OUT;
CLK_0;
delay_us(2);
for(i=0;i<8;i++)
{
if(dat&0x01)
DATA_1;
else DATA_0;
delay_us(2);
CLK_1;
delay_us(2);
CLK_0;
dat>>=1;
}
}
void WriteSet1302(uchar Cmd,uchar dat)
{
RST_0;
CLK_0;
RST_1;
delay_us(2);
Write1302(Cmd);
Write1302(dat);
CLK_1;
RST_0;
}
uchar Read1302(void)
{
uchar i,dat;
DATA_IN;//
DATA_0;//
delay_us(2);
for(i=0;i<8;i++)
{
dat>>=1;
if(DATA!=0)
dat|=0x80;
else dat&=0x7f;
CLK_1;
delay_us(2);
CLK_0;
delay_us(2);
}
DATA_OUT;//
return dat;
}
uchar ReadSet1302(uchar Cmd)
{
uchar dat;
RST_0;
CLK_0;
RST_1;
Write1302(Cmd);
dat=Read1302();
CLK_1;
RST_0;
return dat;
}
//初始化DS1302的程序
void ds1302_init(void)
{
flag= ReadSet1302(0x81);
if(flag&0x80) //判断时钟芯片是否关闭,即可不掉电
{
WriteSet1302(write_protect,0x00);//关闭写保护
WriteSet1302(write_second,0x45); //根据写秒寄存器命令字,写入秒的初始值
WriteSet1302(write_minute,0x24); //根据写分寄存器命令字,写入分的初始值
WriteSet1302(write_hour,0x09); //根据写小时寄存器命令字,写入小时的初始值
WriteSet1302(write_date,0x13); //根据写日寄存器命令字,写入日的初始值
WriteSet1302(write_month,0x04); //根据写月寄存器命令字,写入月的初始值
WriteSet1302(write_year,0x16); //根据写年寄存器命令字,写入年的初始值
WriteSet1302(write_day,0x07); //周日
WriteSet1302(write_power,0xa5); //一个二极管压降,电阻4K
WriteSet1302(write_protect,0x80);//打开写保护
}
}
//DS1302数据处理函数,此函数将个位、十位分别用char表示,方便用数码管显示
void DisplaySecond(unsigned char x)
{
unsigned char i,j;
i=x/10;//取十位
j=x;//取个位
lcd_pos(3,6);
//delay_ms(500);
write_dat(digit[i]);
write_dat(digit[j]);
delay_ms(50);
}
void DisplayMinute(unsigned char x)
{
unsigned char i,j;
i=x/10;//取十位
j=x;//取个位
lcd_pos(3,4);
write_dat(digit[i]);
write_dat(digit[j]);
delay_ms(50);
}
void DisplayHour(unsigned char x)
{
unsigned char i,j;
i=x/10;//取十位
j=x;//取个位
lcd_pos(3,2);
write_dat(digit[i]);
write_dat(digit[j]);
delay_ms(50);
}
void DisplayDate(unsigned char x)
{
unsigned char i,j;
i=x/10;//取十位
j=x;//取个位
lcd_pos(2,6);
write_dat(digit[i]);
write_dat(digit[j]);
delay_ms(50);
}
void DisplayMonth(unsigned char x)
{
unsigned char i,j;
i=x/10;//取十位
j=x;//取个位
lcd_pos(2,4);
write_dat(digit[i]);
write_dat(digit[j]);
delay_ms(50);
}
void DisplayWeek(char we)
{
lcd_pos(4,3);
dis_buf[0]=day[we-1][0];
dis_buf[1]=day[we-1][1];
dis_buf[2]=day[we-1][2];
dis_buf[3]=day[we-1][3];
dis_buf[4]=day[we-1][4];
dis_buf[5]=day[we-1][5];
dis_buf[6]=day[we-1][6];
dis_buf[7]=day[we-1][7];
dis_buf[8]=day[we-1][8];
dis_buf[9]=day[we-1][9];
lcd_string(dis_buf);
}
void DisplayYear(unsigned char x)
{
unsigned char i,j;
i=x/10;//取十位
j=x;//取个位
lcd_pos(2,2);
write_dat(digit[i]);
write_dat(digit[j]);
delay_ms(50);
}
void IO_init(void)
{
///12864和1302端口定义///
P2SEL&=~(BIT0 + BIT1 + BIT2);//P2.0,P2.1,P2.2 为I/O端口
P2DIR |= BIT0 + BIT1 + BIT2 + BIT3 + BIT4 + BIT5;
CLK_OUT;
DATA_OUT;
RST_OUT;
///按键端口定义///
P1DIR&=~(BIT7+BIT5+BIT4+BIT3); //输入
P1DIR |= BIT6;
P1OUT|=(BIT7+BIT5+BIT4);//P1输出为1
P1OUT &=~ (BIT6+BIT3);
P1REN|=BIT7+BIT5+BIT4; //用于按键接地型
P1IE|=BIT7+BIT5+BIT4+BIT3; //允许中断
P1IES|=BIT7+BIT5+BIT4; //下升沿触发
P1IFG&=~(BIT7+BIT5+BIT4+BIT3); //消除中断标志
}
void DCO_init(void)
{
BCSCTL1 = CALBC1_1MHZ; //设定cpu时钟DCO频率为1MHz
DCOCTL = CALDCO_1MHZ;
}
void display()
{
lcd_pos(1,0);//标题
for(i=0;i<14;i++)
{
write_dat(aa[i]);
}
lcd_pos(2,0);//日期
for(i=0;i<4;i++)
{
write_dat(bb[i]);
}
lcd_pos(3,0);//时间
for(i=0;i<4;i++)
{
write_dat(cc[i]);
}
}
///标志位初始化///
void init_flag(void)
{
flag1=0;
}
//收发信号检测,数据读取
char receive()
{
uint i;
com_data=0;
P1DIR &=~ BIT0;
for(i=0;i<=7;i++)
{
respond=2;
while((!(P1IN&BIT0))&&respond++);
delay_us(20);
if(P1IN&BIT0)
{
temp=1;
respond=2;
while((P1IN&BIT0)&&respond++);
}
else
temp=0;
com_data<<=1;
com_data|=temp;
}
return(com_data);
}
//湿度读取子程序
//温度高8位== TL_data
//温度低8位== TH_data
//湿度高8位== RH_data
//湿度低8位== RH_data
//检验位 == CK_data
void GET_TRH()
{
//主机拉低18MS
P1DIR |=BIT0;
P1OUT &=~BIT0;
delay_ms(18);
P1OUT |=BIT0;
//DATA总线由上啦电阻拉高 主机延时20us
delay_us(20);
//主机设为输入 判断从机的响应信号
P1DIR &=~ BIT0;
//判断DHT11 是否有低电平响应信号
if(!(P1IN&BIT0))
{
respond=2;
//判断DHT12发出80us的低电平响应信号是否结束
while((!(P1IN&BIT0))&& respond++);
respond=2;
//判断从机是否发出80us的高电平
while((P1IN&BIT0) && respond++);
//数据接收状态
RH_temp = receive();
RL_temp = receive();
TH_temp = receive();
TL_temp = receive();
CK_temp = receive();
P1DIR |=BIT0;
P1OUT |=BIT0;
//数据检验
untemp=(RH_temp+RL_temp+TH_temp+TL_temp);
if(untemp==CK_temp)
{
RH_data = RH_temp;
RL_data = RL_temp;
TH_data = TH_temp;
TL_data = TL_temp;
CK_data = CK_temp;
}
}
//湿度整数部分
s=(int)(RH_data/10);
g=(int)(RH_data);
shis= (char)(0X30+RH_data/10); //湿度的十位
shig= (char)(0X30+RH_data); //湿度的个位
//温度整数部分
wens= (char)(0X30+TH_data/10); //温度的十位
weng= (char)(0X30+TH_data); //温度的个位
//RH_data=27;
//Uart0Sends_data("{\"M\":\"update\",\"ID\":\"994\",\"V\":{\"1156\":\"d\"}}\n",RH_data);
lcd_pos(4,0);
lcd_string("湿度");
write_dat(shis);
write_dat(shig);
lcd_pos(4,3);
lcd_string("%");
lcd_pos(4,4);
lcd_string("温度");
write_dat(wens);
write_dat(weng);
lcd_pos(4,7);
lcd_string("℃");
//delay_ms(500);
//DispStr(2,0," ");
//DispStr(3,0," ");
}
void main(void)
{
// Stop watchdog timer to prevent time out reset
delay_ms(20);
WDTCTL = WDTPW + WDTHOLD;
unsigned char ReadValue;
DCO_init();//时钟设置
IO_init();//端口初始化
LCD_init();//12864初始化
delay(5);//延迟
ds1302_init();//1302初始化
delay_ms(5);
//init_flag();
display();//显示
_EINT();//开全局中断
while(1)
{
if(flag1==0)
{
ReadValue = ReadSet1302(0x81);//从秒寄存器读数据
second=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);//将读出数据转化
DisplaySecond(second);//显示秒
lcd_pos(3,7);
lcd_string("秒");
ReadValue = ReadSet1302(0x83);//从分寄存器读数据
minute=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);//将读出数据转化
DisplayMinute(minute);//显示分
lcd_pos(3,5);
lcd_string("分");
ReadValue = ReadSet1302(0x85);//从时寄存器读数据
hour=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);//将读出数据转化
DisplayHour(hour);//显示时
lcd_pos(3,3);
lcd_string("时");
lcd_pos(2,7);
lcd_string("日");
ReadValue = ReadSet1302(0x87);//从日寄存器读数据
date=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);//将读出数据转化
DisplayDate(date);//显示日
lcd_pos(2,5);
lcd_string("月");
ReadValue = ReadSet1302(0x89);//从月寄存器读数据
month=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);//将读出数据转化
DisplayMonth(month);//显示月
ReadValue = ReadSet1302(0x8d);//从年寄存器读数据
year=((ReadValue&0xf0)>>4)*10 + (ReadValue&0x0F);//将读出数据转化
DisplayYear(year);//显示年
lcd_pos(2,3);
lcd_string("年");
delay_ms(50);
GET_TRH();
}
}
}
#pragma vector=PORT1_VECTOR
__interrupt void Port1(void)
{
_DINT();//关中断
delay_ms(10);
if((P1IN&BIT4)==0) //如果为低,即按键真的按下了 因为是下降沿触发中断,所以要检测是否为低
{
++s1num;
flag1=1;//时钟暂停
switch(s1num)//按下几次,光标就移动到哪个位置
{
case 1:write_cmd(0x90+2);
//write_cmd(0x0F);//显示光标
break;
case 2:write_cmd(0x90+2);//光标到年的位置
write_cmd(0x0F);//显示光标
break;
case 3:write_cmd(0x90+4);//光标到月的位置
write_cmd(0x0F);//显示光标
break;
case 4:write_cmd(0x90+6);//光标到日的位置
write_cmd(0x0F);//显示光标
break;
case 5:write_cmd(0x88+2);//光标到时的位置
write_cmd(0x0F);//显示光标
break;
case 6:write_cmd(0x88+4);//光标到分的位置
write_cmd(0x0F);//显示光标
break;
case 7:write_cmd(0x88+6);//光标到秒的位置
write_cmd(0x0F);//显示光标
break;
case 8:s1num=0;
flag1=0;
write_cmd(0x0C);
default:s1num=0;
flag1=0;
write_cmd(0x0C);
}
}
if(s1num!=0)//如果功能键按下
{
if((P1IN&BIT5)==0)//+ 键按下
{
switch(s1num)//光标到了哪个位置
{
case 2:year++;
if(year>=100) year=0;
write_cmd(0x90+2);
write_dat((year)/10+'0');
write_dat((year)+'0');
value=(year)/10*16+(year);
WriteSet1302(0x8e,0x00);
WriteSet1302(0x8c,value);
WriteSet1302(0x8e,0x80);
write_cmd(0x90+2);
break;
case 3:month++;
if(month>=13) month=1;
write_cmd(0x90+4);
write_dat((month)/10+'0');
write_dat((month)+'0');
value=(month)/10*16+(month);
WriteSet1302(0x8e,0x00);
WriteSet1302(0x88,value);
WriteSet1302(0x8e,0x80);
write_cmd(0x90+4);
break;
case 4:date++;
if(date>=32) date=1;
write_cmd(0x90+6);
write_dat((date)/10+'0');
write_dat((date)+'0');
value=(date)/10*16+(date);
WriteSet1302(0x8e,0x00);
WriteSet1302(0x86,value);
WriteSet1302(0x8e,0x80);
write_cmd(0x90+6);
break;
case 5:hour++;
if(hour>=24) hour=0;
write_cmd(0x88+2);
write_dat((hour)/10+'0');
write_dat((hour)+'0');
value=(hour)/10*16+(hour);
WriteSet1302(0x8e,0x00);
WriteSet1302(0x84,value);
WriteSet1302(0x8e,0x80);
write_cmd(0x88+2);
break;
case 6:minute++;
if(minute==60) minute=0;
write_cmd(0x88+4);
write_dat((minute)/10+'0');
write_dat((minute)+'0');
value=(minute)/10*16+(minute);
WriteSet1302(0x8e,0x00);
WriteSet1302(0x82,value);
WriteSet1302(0x8e,0x80);
write_cmd(0x88+4);
break;
case 7:second++;
if(second>=60) second=0;
write_cmd(0x88+6);
write_dat((second)/10+'0');
write_dat((second)+'0');
value=(second)/10*16+(second);
WriteSet1302(0x8e,0x00);
WriteSet1302(0x80,value);
WriteSet1302(0x8e,0x80);
write_cmd(0x88+6);
break;
default:s1num=0;
flag1=0;
write_cmd(0x0C);
}
}
}
if(s1num!=0)//如果功能键按下
{
if((P1IN&BIT7)==0)//- 键按下
{
switch(s1num)//光标到了哪个位置
{
case 2:year--;
if(year==-1) year=99;
write_cmd(0x90+2);
write_dat((year)/10+'0');
write_dat((year)+'0');
value=(year)/10*16+(year);
WriteSet1302(0x8e,0x00);
WriteSet1302(0x8c,value);
WriteSet1302(0x8e,0x80);
write_cmd(0x90+2);
break;
case 3:month--;
if(month==0) month=12;
write_cmd(0x90+4);
write_dat((month)/10+'0');
write_dat((month)+'0');
value=(month)/10*16+(month);
WriteSet1302(0x8e,0x00);
WriteSet1302(0x88,value);
WriteSet1302(0x8e,0x80);
write_cmd(0x90+4);
break;
case 4:date--;
if(date==0) date=31;
write_cmd(0x90+6);
write_dat((date)/10+'0');
write_dat((date)+'0');
value=(date)/10*16+(date);
WriteSet1302(0x8e,0x00);
WriteSet1302(0x86,value);
WriteSet1302(0x8e,0x80);
write_cmd(0x90+6);
break;
case 5:hour--;
if(hour==-1) hour=23;
write_cmd(0x88+2);
write_dat((hour)/10+'0');
write_dat((hour)+'0');
value=(hour)/10*16+(hour);
WriteSet1302(0x8e,0x00);
WriteSet1302(0x84,value);
WriteSet1302(0x8e,0x80);
write_cmd(0x88+2);
break;
case 6:minute--;
if(minute==-1) minute=59;
write_cmd(0x88+4);
write_dat((minute)/10+'0');
write_dat((minute)+'0');
value=(minute)/10*16+(minute);
WriteSet1302(0x8e,0x00);
WriteSet1302(0x82,value);
WriteSet1302(0x8e,0x80);
write_cmd(0x88+4);
break;
case 7:second--;
if(second==-1) second=59;
write_cmd(0x88+6);
write_dat((second)/10+'0');
write_dat((second)+'0');
value=(second)/10*16+(second);
WriteSet1302(0x8e,0x00);
WriteSet1302(0x80,value);
WriteSet1302(0x8e,0x80);
write_cmd(0x88+6);
break;
default:s1num=0;
flag1=0;
write_cmd(0x0C);
}
}
}
P1IFG &=~(BIT7+BIT5+BIT4);
//_EINT();//开中断
}
本文地址:https://blog.csdn.net/yuwusheng18/article/details/108553989