#define F_CPU 10000000UL // 晶振:10MHz(更低频率的晶振也是可以的,无需修改程序代码)
#include <avr/io.h>
#include <util/delay.h>
#define BIT(n) (1 << (n))
#define RS_0 (PORTD &= ~BIT(0))
#define RS_1 (PORTD |= BIT(0))
#define RW_0 (PORTD &= ~BIT(1)) // 1602液晶的RW端
#define RW_1 (PORTD |= BIT(1))
#define E_0 (PORTD &= ~BIT(2))
#define E_1 (PORTD |= BIT(2))
#define AS_0 (PORTD &= ~BIT(3))
#define AS_1 (PORTD |= BIT(3))
#define DS_0 (PORTD &= ~BIT(4))
#define DS_1 (PORTD |= BIT(4))
#define RW2_0 (PORTD &= ~BIT(5)) // DS12887的RW端
#define RW2_1 (PORTD |= BIT(5))
#define CS_0 (PORTD &= ~BIT(6))
#define CS_1 (PORTD |= BIT(6))
void LCD1602_BusyWait(void)
{
DDRA = 0x00; // 一定要提前将I/O口切换为读状态
PORTA = 0xff; // 打开上拉电阻
RS_0;
RW_1;
E_1;
while (PINA & 0x80);
DDRA = 0xff;
E_0;
}
void LCD1602_WriteCmd(uint8_t cmd)
{
LCD1602_BusyWait();
RS_0;
RW_0;
PORTA = cmd;
E_1;
E_0;
}
void LCD1602_WriteData(uint8_t data)
{
LCD1602_BusyWait();
RS_1;
RW_0;
PORTA = data;
E_1;
E_0;
}
void LCD1602_WriteString(const char *s)
{
while (*s)
LCD1602_WriteData(*s++);
}
void LCD1602_WriteNumber(uint8_t num)
{
LCD1602_WriteData('0' + num / 100);
LCD1602_WriteData('0' + num % 100 / 10);
LCD1602_WriteData('0' + num % 10);
}
void LCD1602_WriteHex(uint8_t num)
{
const char *list = "0123456789ABCDEF";
LCD1602_WriteData(list[num >> 4]);
LCD1602_WriteData(list[num & 0x0f]);
}
void LCD1602_Init(void)
{
_delay_ms(15);
LCD1602_WriteCmd(0x38);
_delay_ms(5);
LCD1602_WriteCmd(0x38);
_delay_ms(5);
LCD1602_WriteCmd(0x38);
LCD1602_WriteCmd(0x01);
LCD1602_WriteCmd(0x0c);
}
uint8_t DS12887_Read(uint8_t addr)
{
uint8_t data;
// 初始状态
/*AS_0;
DS_0;
RW2_0;
CS_1;
asm("nop");*/
// tASD>=20ns
DS_1;
RW2_1;
AS_1;
DDRC = 0xff;
PORTC = addr;
asm("nop"); // PW_ASH>=60ns
CS_0;
AS_0;
// AS=0后,经过时间tAHL>=10ns,收回数据, I/O进入读的状态
asm("nop");
DDRC = 0x00;
PORTC = 0x00; // 关闭上拉电阻(因为本人的硬件电路中在单片机I/O口与DS12887数据端口之间接了限流电阻,如果不关闭的话,读出的数据可能是错误的,如2017读出来却是201F)(如果没接限流电阻,就不需要关闭)
DS_0; // AS=0与DS=0的时间为tASED>=40ns
// 经过tDDR=20~120ns的时间后,即可读取数据
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop"); // 这里必须多延时几个时钟周期, 否则读出来的将会是addr|PINC
asm("nop"); // 比如在0x08地址处写入0x02,读出来的却是0x08|0x02=0x0c
data = PINC;
DS_1; // DS低电平时间PW_EH>=125ns
CS_1;
AS_1;
DDRC = 0xff;
_delay_ms(10);
return data;
}
void DS12887_Write(uint8_t addr, uint8_t data)
{
// 初始状态
DDRC = 0xff;
/*AS_0;
DS_0;
RW2_0;
CS_1;
asm("nop");*/
// tASD>=20ns
DS_1;
RW2_1;
AS_1;
PORTC = addr;
asm("nop"); // PW_ASH>=60ns
CS_0;
AS_0;
asm("nop"); // tAHL>=10ns
PORTC = data;
asm("nop"); // tASED>=40ns
RW2_0;
asm("nop"); // PW_EH>=125ns
asm("nop");
RW2_1;
CS_1;
AS_1;
_delay_ms(10);
}
int main(void)
{
uint8_t day;
DDRD = 0x7f; // 配置时钟和液晶的非数据I/O口
PORTD |= 0x80; // PD7设为输入,并打开上拉电阻
LCD1602_Init(); // 初始化液晶
// 写入时间
/*
DS12887_Write(0x0b, 0x82); // 停止走时
DS12887_Write(0x32, 0x20); // 年份前两位
DS12887_Write(0x09, 0x17); // 年份后两位
DS12887_Write(0x08, 0x02); // 月份
DS12887_Write(0x07, 0x08); // 日期
DS12887_Write(0x04, 0x19); // 小时
DS12887_Write(0x02, 0x03); // 分钟
DS12887_Write(0x00, 0x20); // 秒
DS12887_Write(0x06, 0x03); // 星期也必须手动写入,无法自动计算
DS12887_Write(0x0a, 0x20); // 打开晶振以及分频器,采用BCD码格式,关夏令时功能,开24小时制
DS12887_Write(0x0b, 0x02); // 时钟开始走时
*/
// 自由RAM测试
/*
DS12887_Write(0x38, 0xab);
DS12887_Write(0x39, 0x17);
LCD1602_WriteData(' ');
LCD1602_WriteHex(DS12887_Read(0x38));
LCD1602_WriteHex(DS12887_Read(0x39));
*/
// 显示标题
LCD1602_WriteCmd(0x80);
LCD1602_WriteString("Date:");
LCD1602_WriteCmd(0xc0);
LCD1602_WriteString("Time:");
while (1)
{
// 日期
LCD1602_WriteCmd(0x85);
LCD1602_WriteHex(DS12887_Read(0x32));
LCD1602_WriteHex(DS12887_Read(0x09));
LCD1602_WriteData('-');
LCD1602_WriteHex(DS12887_Read(0x08));
LCD1602_WriteData('-');
LCD1602_WriteHex(DS12887_Read(0x07));
// 如果电池已耗尽,则显示感叹号
if ((DS12887_Read(0x0d) & 0x80) == 0)
LCD1602_WriteData('!');
else
LCD1602_WriteData(' ');
// 时间
LCD1602_WriteCmd(0xc5);
LCD1602_WriteHex(DS12887_Read(0x04));
LCD1602_WriteData(':');
LCD1602_WriteHex(DS12887_Read(0x02));
LCD1602_WriteData(':');
LCD1602_WriteHex(DS12887_Read(0x00));
// 星期
day = DS12887_Read(0x06); // 取值范围1~7
LCD1602_WriteData("?MTWTFSS"[day]); // 星期的第一个字母
LCD1602_WriteData("?ouehrau"[day]); // 第二个字母
LCD1602_WriteData("?neduitn"[day]); // 第三个字母
_delay_ms(100);
}
}