#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);
}
}