程序源码
- /*=====================================================================
- * STC15F204EA(28脚) 四位数码管语音时钟
- * 语音报时 / DS1302时钟 / 两键校时 / 闹钟蜂鸣
- *---------------------------------------------------------------------
- * 硬件连接(依原理图):
- * 数码管段选(共阳,低电平点亮): a=P2.0 b=P2.1 c=P2.2 d=P2.3
- * e=P2.4 f=P2.5 g=P2.6 dp=P2.7
- * 位选(8550驱动,低电平选通): DS1=A1/P3.2 DS2=A2/P3.3
- * DS3=A3/P3.4 DS4=A4/P3.5 (时分显示)
- * DS1302: CE=P1.0 I/O=P1.1 SCLK=P1.2 (32768晶振,3V电池后备)
- * 蜂鸣器: P1.5 (低电平响,有源蜂鸣器)
- * 按键: S1=P3.1(另一端GND) S2=P3.0(另一端GND) 与串口复用
- * 语音板: P1四针插座 = P3.1(TXD) P3.0(RXD) GND 5V, 9600bps
- * 协议: SYN6288兼容 (0xFD+长度+GBK文本+异或和)
- * 若你的语音板协议不同,只需改 voice_build()/voice_send()
- * 空闲脚: P1.3/P1.4(J01/J02) P3.6/P3.7(P3插座) P0.0/P0.1
- *---------------------------------------------------------------------
- * 按键操作:
- * 短按 S1: 小时+1(写入DS1302) 短按 S2: 分钟+1(秒清零)
- * 长按 S1(1秒): 进入/确认退出闹钟设置(退出时保存并开启闹钟)
- * 长按 S2(1秒): 正常模式-闹钟开/关; 设置模式-取消退出
- * 闹钟设置模式下: S1=闹钟时+1 S2=闹钟分+1 10秒无操作自动取消退出
- * 同时按住 S1+S2: 语音播报当前时间
- * 闹钟触发: 蜂鸣器响60秒,期间任意短按停止;触发时语音播报时间
- * 显示小数点(dp): 常亮=闹钟已开启 熄灭=闹钟关闭 快闪=闹钟设置中
- *---------------------------------------------------------------------
- * 注意: STC-ISP下载时内部IRC请选择 11.0592MHz (与下面 FOSC 一致),
- * 若用其他频率,需同步修改 FOSC 及两个定时器初值。
- *=====================================================================*/
- #include <reg51.h>
- #include <intrins.h>
- typedef unsigned char u8;
- typedef unsigned int u16;
- #define FOSC 11059200UL /* 内部IRC频率,须与STC-ISP设置一致 */
- #define KEY_LONG 200 /* 长按判定 200*5ms = 1秒 */
- sbit DS_CE = P1^0; /* DS1302 片选 */
- sbit DS_IO = P1^1; /* DS1302 数据 */
- sbit DS_SCLK = P1^2; /* DS1302 时钟 */
- sbit BUZZER = P1^5; /* 蜂鸣器,低电平响 */
- sbit KEY_S1 = P3^1; /* S1键(与TXD复用) */
- sbit KEY_S2 = P3^0; /* S2键(与RXD复用) */
- /*------------------------- 数码管 -------------------------
- * 共阳管,段选低电平点亮: 段码表0-9 (bit0=a ... bit6=g, bit7=dp) */
- u8 code SEG_TAB[10] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
- u8 code DIG_SEL[4] = {0x04,0x08,0x10,0x20}; /* 位选 A1-A4 = P3.2-P3.5 */
- u8 disp_buf[4] = {0xFF,0xFF,0xFF,0xFF}; /* 显示缓冲(DS1-DS4) */
- u8 dp_mask = 0xFF; /* 0xFF=dp灭 0x7F=dp亮 */
- /*------------------------- 全局变量 -------------------------*/
- volatile u8 tick2ms = 0; /* 2ms节拍(按键用) */
- volatile u8 rd_tick = 0; /* 250ms节拍(读时钟用) */
- u8 hh = 12, mm = 0, ss = 0, last_ss = 0xFF; /* 当前时间 */
- u8 alarm_h = 7, alarm_m = 0; /* 闹钟时间(存DS1302 RAM) */
- bit alarm_on = 0; /* 闹钟使能 */
- bit ring_on = 0, ring_done = 0; /* 响铃状态/本次已触发 */
- u8 ring_sec = 0;
- u8 mode = 0; /* 0正常 1闹钟设置 */
- u16 mode_idle = 0; /* 设置模式无操作计时 */
- bit dp_blink = 0;
- u8 blink_t = 0;
- u8 k1_db = 0, k2_db = 0; /* 按键按下计时(5ms单位) */
- bit k1_long = 0, k2_long = 0, combo = 0;
- bit voice_req = 0, voice_alarm = 0; /* 语音请求/是否闹钟播报 */
- u8 vbuf[36];
- u8 vlen;
- /*========================= DS1302 驱动 =========================*/
- void ds_write_byte(u8 dat)
- {
- u8 i;
- for (i = 0; i < 8; i++) {
- DS_SCLK = 0;
- DS_IO = dat & 0x01;
- dat >>= 1;
- _nop_(); _nop_(); _nop_(); _nop_();
- DS_SCLK = 1;
- _nop_(); _nop_(); _nop_(); _nop_();
- }
- }
- u8 ds_read_byte(void)
- {
- u8 i, dat = 0;
- DS_IO = 1; /* 准双向口,先写1再读 */
- for (i = 0; i < 8; i++) {
- DS_SCLK = 1;
- DS_SCLK = 0; /* 下降沿后DS1302输出数据 */
- _nop_(); _nop_(); _nop_(); _nop_();
- dat >>= 1;
- if (DS_IO) dat |= 0x80;
- }
- return dat;
- }
- void ds_write(u8 reg, u8 dat)
- {
- DS_CE = 0; DS_SCLK = 0; DS_CE = 1;
- ds_write_byte(reg);
- ds_write_byte(dat);
- DS_CE = 0;
- }
- u8 ds_read(u8 reg)
- {
- u8 dat;
- DS_CE = 0; DS_SCLK = 0; DS_CE = 1;
- ds_write_byte(reg | 0x01);
- dat = ds_read_byte();
- DS_CE = 0;
- return dat;
- }
- u8 dec_to_bcd(u8 v) { return (v / 10) << 4 | v % 10; }
- u8 bcd_to_dec(u8 v) { return (v >> 4) * 10 + (v & 0x0F); }
- /* 初始化:关写保护;时钟停振或数据无效则写默认12:00:00(24小时制) */
- void ds1302_init(void)
- {
- u8 s, h, m;
- DS_CE = 0; DS_SCLK = 0;
- ds_write(0x8E, 0x00);
- s = ds_read(0x81);
- h = ds_read(0x85);
- m = ds_read(0x83);
- if ((s & 0x80) || (h & 0x80) ||
- (h & 0x3F) > 0x23 || (m & 0x7F) > 0x59) {
- ds_write(0x84, 0x12);
- ds_write(0x82, 0x00);
- ds_write(0x80, 0x00); /* CH=0 启动时钟 */
- }
- }
- void read_time(void)
- {
- ss = bcd_to_dec(ds_read(0x81) & 0x7F);
- mm = bcd_to_dec(ds_read(0x83) & 0x7F);
- hh = bcd_to_dec(ds_read(0x85) & 0x3F);
- }
- /* 闹钟数据存于DS1302内部RAM,掉电不丢失 */
- void alarm_save(void)
- {
- ds_write(0xC0, alarm_h);
- ds_write(0xC2, alarm_m);
- ds_write(0xC4, (u8)alarm_on);
- }
- void alarm_load(void)
- {
- alarm_h = ds_read(0xC1);
- alarm_m = ds_read(0xC3);
- alarm_on = ds_read(0xC5) ? 1 : 0;
- if (alarm_h > 23 || alarm_m > 59) { /* RAM无效则给默认值 */
- alarm_h = 7; alarm_m = 0; alarm_on = 0;
- alarm_save();
- }
- }
- /*========================= 语音播报 ==========================
- * SYN6288兼容: 0xFD + 长度高 + 长度低 + GBK文本 + 异或校验, 9600bps
- * 其他语音板只需改此文件中 T_xxx 文本与 voice_send() 发送格式 */
- u8 code T_NOW[] = {0xCF,0xD6,0xD4,0xDA,0xCA,0xB1,0xBC,0xE4}; /* 现在时间 */
- u8 code T_ALARM[] = {0xC4,0xD6,0xD6,0xD3,0xCA,0xB1,0xBC,0xE4,0xB5,0xBD}; /* 闹钟时间到 */
- u8 code T_COMMA[] = {0xA3,0xAC}; /* , */
- u8 code T_DIAN[] = {0xB5,0xE3}; /* 点 */
- u8 code T_FEN[] = {0xB7,0xD6}; /* 分 */
- u8 code T_ZHENG[] = {0xD5,0xFB}; /* 整 */
- void vpush(u8 code *p, u8 n)
- {
- while (n--) vbuf[vlen++] = *p++;
- }
- void vnum(u8 v) /* 数字转ASCII,不补零 */
- {
- if (v >= 10) vbuf[vlen++] = '0' + v / 10;
- vbuf[vlen++] = '0' + v % 10;
- }
- void voice_build(u8 is_alarm)
- {
- vlen = 0;
- if (is_alarm) {
- vpush(T_ALARM, sizeof(T_ALARM));
- vpush(T_COMMA, sizeof(T_COMMA));
- }
- vpush(T_NOW, sizeof(T_NOW));
- vnum(hh);
- vpush(T_DIAN, sizeof(T_DIAN));
- vnum(mm);
- vpush(mm ? T_FEN : T_ZHENG, sizeof(T_FEN));
- }
- void voice_send(void)
- {
- u8 i, chk;
- chk = 0 ^ vlen; /* 长度高字节为0 */
- TI = 0; SBUF = 0xFD; while (!TI);
- TI = 0; SBUF = 0; while (!TI);
- TI = 0; SBUF = vlen; while (!TI);
- for (i = 0; i < vlen; i++) {
- chk ^= vbuf[i];
- TI = 0; SBUF = vbuf[i]; while (!TI);
- }
- TI = 0; SBUF = chk; while (!TI);
- }
- /*========================= 显示与节拍 =========================*/
- void Timer0_ISR(void) interrupt 1
- {
- static u8 pos = 0;
- TH0 = 0xF8; TL0 = 0xCC; /* 2ms @11.0592M 12T */
- P3 |= 0x3C; /* 关全部位选(P3.2-P3.5) */
- P2 = 0xFF; /* 消隐 */
- P2 = disp_buf[pos] & dp_mask;
- P3 &= ~DIG_SEL[pos]; /* 开当前位 */
- pos = (pos + 1) & 0x03;
- tick2ms++;
- rd_tick++;
- }
- void update_display(void)
- {
- u8 h, m;
- if (mode == 0) { h = hh; m = mm; }
- else { h = alarm_h; m = alarm_m; }
- disp_buf[0] = SEG_TAB[h / 10];
- disp_buf[1] = SEG_TAB[h % 10];
- disp_buf[2] = SEG_TAB[m / 10];
- disp_buf[3] = SEG_TAB[m % 10];
- }
- void ring_off(void)
- {
- ring_on = 0;
- BUZZER = 1;
- }
- /*========================= 按键处理 ==========================*/
- void key_scan(void) /* 每5ms调用一次, 0=按下 */
- {
- if (mode && (!KEY_S1 || !KEY_S2)) mode_idle = 0;
- /* 同时按住两键>100ms: 语音报当前时间 */
- if (!KEY_S1 && !KEY_S2) {
- if (k1_db >= 20 && k2_db >= 20 && !combo && !k1_long && !k2_long) {
- combo = 1;
- if (ring_on) ring_off();
- voice_req = 1;
- voice_alarm = 0;
- }
- } else if (KEY_S1 && KEY_S2) {
- combo = 0;
- }
- /* ---- S1 ---- */
- if (!KEY_S1) {
- if (k1_db < 250) k1_db++;
- if (k1_db == KEY_LONG && !combo && !k1_long) {
- k1_long = 1;
- if (ring_on) ring_off();
- else if (mode == 0) { mode = 1; mode_idle = 0; } /* 进设置 */
- else { alarm_save(); alarm_on = 1; mode = 0; } /* 确认退出 */
- }
- } else {
- if (k1_db >= 4 && k1_db < KEY_LONG && !combo && !k1_long) { /* 短按 */
- if (ring_on) ring_off();
- else if (mode == 0) {
- hh++; if (hh > 23) hh = 0;
- ds_write(0x84, dec_to_bcd(hh));
- } else {
- alarm_h++; if (alarm_h > 23) alarm_h = 0;
- }
- }
- k1_db = 0; k1_long = 0;
- }
- /* ---- S2 ---- */
- if (!KEY_S2) {
- if (k2_db < 250) k2_db++;
- if (k2_db == KEY_LONG && !combo && !k2_long) {
- k2_long = 1;
- if (mode == 0) {
- alarm_on = !alarm_on; /* 长按S2: 闹钟开/关 */
- alarm_save();
- } else {
- mode = 0; /* 设置模式长按S2: 取消退出 */
- }
- }
- } else {
- if (k2_db >= 4 && k2_db < KEY_LONG && !combo && !k2_long) { /* 短按 */
- if (ring_on) ring_off();
- else if (mode == 0) {
- mm++; if (mm > 59) mm = 0;
- ds_write(0x82, dec_to_bcd(mm));
- ds_write(0x80, 0x00); /* 秒清零,便于对准 */
- } else {
- alarm_m++; if (alarm_m > 59) alarm_m = 0;
- }
- }
- k2_db = 0; k2_long = 0;
- }
- }
- /*============================= 主程序 =============================*/
- void main(void)
- {
- P1 = 0xFF; P2 = 0xFF; P3 = 0xFF; /* 全部置高(蜂鸣器不响,显示消隐) */
- /* T0: 方式1,12T,2ms中断,用于扫描与节拍 */
- TMOD &= 0xF0;
- TH0 = 0xF8; TL0 = 0xCC;
- ET0 = 1; TR0 = 1;
- /* T1: 方式2,12T,作UART波特率发生器 9600bps @11.0592M */
- TMOD &= 0x0F;
- TMOD |= 0x20;
- TH1 = 0xFD; TL1 = 0xFD;
- SCON = 0x40; /* 方式1,不开接收 */
- TR1 = 1;
- ds1302_init();
- alarm_load();
- read_time();
- update_display();
- EA = 1;
- while (1) {
- /*---- 250ms: 读时钟/闹钟判断 ----*/
- if (rd_tick >= 125) {
- rd_tick = 0;
- read_time();
- if (ss != last_ss) { /* 每秒事件 */
- last_ss = ss;
- if (ring_on && ++ring_sec >= 60) ring_off();
- }
- if (alarm_on && !ring_on) {
- if (hh == alarm_h && mm == alarm_m && ss < 2 && !ring_done) {
- ring_done = 1; /* 触发闹钟 */
- ring_on = 1;
- BUZZER = 0;
- ring_sec = 0;
- voice_req = 1;
- voice_alarm = 1;
- }
- } else {
- if (hh != alarm_h || mm != alarm_m) ring_done = 0;
- }
- }
- /*---- 5ms: 按键/设置超时/小数点闪烁 ----*/
- if (tick2ms >= 5) {
- tick2ms -= 5;
- key_scan();
- if (mode) { /* 设置模式10秒无操作退出 */
- if (++mode_idle >= 2000) { mode = 0; mode_idle = 0; }
- } else {
- mode_idle = 0;
- }
- if (++blink_t >= 50) { /* 250ms翻转 */
- blink_t = 0;
- dp_blink = !dp_blink;
- }
- if (mode) dp_mask = dp_blink ? 0x7F : 0xFF;
- else dp_mask = alarm_on ? 0x7F : 0xFF;
- }
- /*---- 语音发送(须等两键都释放,因为按键与串口复用) ----*/
- if (voice_req && KEY_S1 && KEY_S2) {
- voice_build(voice_alarm);
- voice_send();
- voice_req = 0;
- }
- update_display();
- }
- }
复制代码 |