Часы на микроконтроллере PIC16F887 с будильником, днем недели и резервным питанием

Крушневич С.П..

ВВЕДЕНИЕ.

Конструкция выходного дня. В один прекрасный выходной день возникло сильное желание собрать часы на 7-ми сегментных индикаторах, с указанием дня недели, будильником и главное... способные сохранять работу без электричества (это после очередного кратковременного отключения света). Тут и на радиорынок ехал, купил индикаторы и пошло-поехало

Паять или программировать?

 Так как это конструкция выходного дня, я решил сделать всю схему на макетной плате. Дабы минимизировать количество внешних компонентов, я установил недорогой МК PIC16F887 (усовершенствованный аналог PIC16F877A) и распаял все вокруг него.

Макетная плата. Часы на микроконтроллере PIC16F887 с семиссегментным светодиодным индикатором, будильником, энергонезависимым питанием и днем недели. Clock PIC PICMicro

Схема электрическая принципиальная

Схема получилась достаточно простая, так как вся логика работы спряталась в прошивке.

Схема электрическая прнинципиальная. Часы с семиссегментным светодиодным индикатором, будильником, энергонезависимым питанием и днем недели. PIC16F887
Нажмите для увеличения

UPD 18.08.2017: Схема отрисованна на чистовике. Предыдущий вариант содержал неточности и находится здесь.

 

Энергонезависимость обеспечивает ионистор 1 Ф 5,5 В. Во время испытаний ионистор успешно питал часы в течении суток. Так как вариант питания от ионистора разрабатывался позже, я не учел возможного упрощения схемы подачи питания и пришлось городить схему контроля напряжения питания на оптопаре для того, чтобы вовремя погасить индикаторы. Анализ наличия напряжения питания я провожу в функции динамической индикации, перед выводом очередного символа. Как только я замечаю, что сигнал на ножке пропал, я сразу гашу все сегменты и вспомогательные светодиоды. Далее в основной функции (когда приходит время) я отправляю МК в режим спячки. Будит МК прерывание от таймера TMR1 с интервалом в 2 сек. После увеличения значения числа секунд, в основной функции снова проверяется наличие напряжения питания и МК снова засыпает на 2 секунды.

Ионистор 1F 5.5V. Ionistor. Часы на микроконтроллере

Корпус

После долгих раздумий я решился на металлический корпус и прозрачную переднюю панель.

Часы в корпусе. Часы на микроконтроллере с семиссегментным светодиодным индикатором, будильником, энергонезависимым питанием и днем недели Часы в корпусе. Часы PIC16F887 с семиссегментным светодиодным индикатором, будильником, энергонезависимым питанием и днем недели
Корпус оклеен самоклейкой
Часы в корпусе. Часы PIC16F887 с семиссегментным светодиодным индикатором, будильником, энергонезависимым питанием и днем недели. PIC16F887 Часы в корпусе. Часы PIC16F887 с семиссегментным светодиодным индикатором, будильником, энергонезависимым питанием и днем недели. PIC16F887

 

Описание

В нормальном режиме на часах отображается текущее время и день недели (светодиодным столбиком).
Нажатие на кнопку "Часы" переводит часы в режим установки часов, минут и дня недели. Выбор осуществляется кнопкой "часы", изменение - кнопками "+" и "-".
Нажатие на кнопку "Будильник" переводит часы в режим установки будильника. Вначале отображается состояние будильника "включенно-On" или "выключенно-OF", далее указывается часы и минуты срабатывания.

Прошивка для микроконтроллера PIC16F887


Прошивка от 17.10.2009, скачать .hex, скачать исходный код

Листинг прошивки (версия 17.10.2009)    1:  #include "16F887.h"
   2:  
   3:  #device *=16
   4:  #device adc=10
   5:  
   6:  #FUSES NOWDT                  //No Watch Dog Timer
   7:  #FUSES INTRC_IO               //Internal RC Osc, no CLKOUT
   8:  #FUSES PUT                    //Power Up Timer
   9:  #FUSES MCLR                   //Master Clear pin enabled
  10:  #FUSES NOPROTECT              //Code not protected from reading
  11:  #FUSES NOCPD                  //No EE protection
  12:  #FUSES NOBROWNOUT             //No brownout reset
  13:  #FUSES NOIESO                 //Internal External Switch Over mode disabled
  14:  #FUSES NOFCMEN                //Fail-safe clock monitor disabled
  15:  #FUSES NOLVP                  //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
  16:  #FUSES NODEBUG                //No Debug mode for ICD
  17:  #FUSES NOWRT                  //Program memory not write protected
  18:  #FUSES BORV21                 //Brownout reset at 2.1V
  19:  
  20:  #use delay(clock=4000000,RESTART_WDT)
  21:  #use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,restart_wdt,errors)
  22:  //#use i2c(Master,Slow,sda=PIN_C4,scl=PIN_C3,restart_wdt,force_hw)
  23:  #use fast_io(A)                 // Работа с портами без переключения
  24:  #use fast_io(B)                 // каждый раз регистров TRIS
  25:  //#use fast_io(C) //*
  26:  //#use fast_io(D) //*
  27:  //#use fast_io(E) //*
  28:  
  29:  unsigned char Segment1,Segment2,Segment3,Segment4;  //Отображаемая цифра
  30:  unsigned char ShowSegment=0,Blick=0;
  31:  unsigned char Segment1blick, Segment2blick, Segment3blick, Segment4blick; // Мигание сегмента
  32:  #define BlickRate 256
  33:  
  34:  unsigned char rtc_day,rtc_mon,rtc_year,rtc_hour,rtc_min,rtc_sec; //Время в формате часов DS1307
  35:  unsigned char RefreshClock=1;
  36:  unsigned char year;
  37:  unsigned char mon,day,hour,min,sec,msec,week;
  38:  
  39:  #byte PORTA = 0x05
  40:  #byte PORTB = 0x06
  41:  #byte PORTC = 0x07
  42:  #byte PORTD = 0x08
  43:  #byte PORTE = 0x09
  44:  #byte TRISA = 0x85
  45:  #byte TRISB = 0x86
  46:  #byte TRISC = 0x87
  47:  #byte TRISD = 0x88
  48:  
  49:  unsigned char KeyStatus=0;
  50:  #bit KeyTime  = KeyStatus.1
  51:  #bit KeyAlarm = KeyStatus.0
  52:  #bit KeyPlus  = KeyStatus.2
  53:  #bit KeyMinus = KeyStatus.3
  54:  
  55:  unsigned char ClockMode=0;
  56:  //Нормальный режим. Отображение текущего времени
  57:  #define ClockModeNormal 0
  58:  //Режим настройки
  59:  #define ClockModeTune   1
  60:  
  61:  //Светодиоды секунд (нижнее двоеточье)
  62:  #bit  SecLED = PORTE.0
  63:  //Светодиоды секунд/Часы идут (верхнее двоеточье)
  64:  #bit  TimeLED = PORTE.1
  65:  //Будильник включен
  66:  #bit  AlarmLED = PORTC.1
  67:  unsigned char BlickTimeSecLed=0;
  68:  
  69:  //Светодиоды дней недели
  70:  #bit PNLED = PORTC.5
  71:  #bit VTLED = PORTC.4
  72:  #bit SRLED = PORTC.3
  73:  #bit CHLED = PORTA.7
  74:  #bit PTLED = PORTA.6
  75:  #bit SBLED = PORTA.5
  76:  #bit VSLED = PORTA.4
  77:  unsigned char WeekBlick=0;
  78:  
  79:  #bit SavePower = PORTE.2
  80:  
  81:  
  82:  #define nota_Start 255
  83:  #define nota_steep 10
  84:  
  85:  #define nota_DO nota_Start
  86:  #define nota_RE nota_DO-nota_steep
  87:  #define nota_MI nota_RE-nota_steep
  88:  #define nota_FA nota_MI-nota_steep
  89:  #define nota_SO nota_FA-nota_steep
  90:  #define nota_LA nota_SO-nota_steep
  91:  #define nota_SI nota_LA-nota_steep
  92:  #define nota_DL nota_SI-nota_steep
  93:  
  94:  //Нота для сигнала ОК
  95:  #define OK_SND nota_DL
  96:  //Длительность сигнала ОК
  97:  #define OK_delay 250
  98:  
  99:  unsigned char AlarmOn,AlarmMin,AlarmHour,DisableCurAlarm;
 100:  
 101:  void StartInit(void)
 102:  {
 103:     set_tris_a(0b00001111);
 104:     set_tris_b(0b00000000);
 105:     set_tris_c(0b11000011);
 106:     set_tris_d(0b00001111);
 107:     set_tris_e(0b11111100);
 108:     //port_d_pullups(true);
 109:     setup_timer_0(RTCC_INTERNAL); //|RTCC_DIV_256
 110:     setup_wdt(WDT_18MS|WDT_TIMES_128);
 111:     setup_timer_1(T1_EXTERNAL|T1_DIV_BY_1|T1_CLK_OUT);
 112:     setup_timer_2(T2_DIV_BY_1,127,1);
 113:  //   setup_oscillator(OSC_4MHZ);
 114:     setup_ccp1(CCP_OFF);
 115:  //   set_pwm1_duty(512); // 1/2 Периода - меандр
 116:     setup_comparator(NC_NC_NC_NC);// This device COMP currently not supported by the PICWizard
 117:     setup_adc_ports(NO_ANALOGS|VSS_VDD);
 118:     setup_adc(ADC_OFF);
 119:     enable_interrupts(INT_TIMER0);
 120:     enable_interrupts(INT_TIMER1);
 121:     enable_interrupts(GLOBAL);
 122:     sec=min=hour=0;
 123:  }
 124:  
 125:  unsigned char ValueToSegment(unsigned char val)
 126:  {
 127:  char res=242;
 128:  
 129:  switch(val)
 130:   {  
 131:        case 0: res=0b00111111; break; 
 132:        case 1: res=0b00000110; break; 
 133:        case 2: res=0b01011011; break; 
 134:        case 3: res=0b01001111; break; 
 135:        case 4: res=0b01100110; break; 
 136:        case 5: res=0b01101101; break; 
 137:        case 6: res=0b01111100; break; 
 138:        case 7: res=0b00000111; break; 
 139:        case 8: res=0b01111111; break; 
 140:        case 9: res=0b01100111; break; 
 141:        case 45: res=0b01000000; break; 
 142:        case 65: res=0b01110111; break; // A      
 143:        case 67: res=0b00111001; break; // C
 144:        case 70: res=0b01110001; break; // F
 145:        case 72: res=0b01110110; break; // H
 146:        case 79: res=0b00111111; break; // O
 147:        case 80: res=0b01110011; break; // P      
 148:        case 110: res=0b01010100; break; // n
 149:        case 193: res=0b01111101; break; // Б      
 150:        case 195: res=0b00110001; break; // Г
 151:        case 207: res=0b00110111; break; // П
 152:        case 215: res=0b01100110; break; // Ч
 153:        case 228: res=0b01011111; break; // д
 154:        case 116: res=0b01111000; break; // t
 155:  
 156:        default: res=0; // Символ, выводимый по умолчанию
 157:   } 
 158:  return res;
 159:  }
 160:  
 161:  
 162:  #int_TIMER1
 163:  void  TIMER1_isr(void) 
 164:  {
 165:  sec+=2;
 166:  if (sec>=60)
 167:     { sec=0;
 168:       min++;
 169:     if (min>59)
 170:       { min=0;
 171:         hour++;
 172:         if (hour>23)
 173:          { hour=0;
 174:            week++;
 175:            if (week>7) week=1;
 176:          }
 177:         DisableCurAlarm=0; //Защита от повторного срабатывания будильника
 178:       }                    //+ заранее можно выключить будильник
 179:     }
 180:  
 181:  if (BlickTimeSecLed==1)
 182:   { if ((secLed==0)&&(SavePower==1))
 183:      { secLed=TimeLED=1; }
 184:     else
 185:      { secLed=TimeLED=0; }
 186:   }
 187:  }
 188:  
 189:  #define Seg1sel 0b00011111
 190:  #define Seg2sel 0b00101111
 191:  #define Seg3sel 0b01001111
 192:  #define Seg4sel 0b10001111
 193:  #define SegMask 0b00001111
 194:  
 195:  
 196:  #int_TIMER0
 197:  void TIMER0_isr(void) 
 198:  {
 199:  KeyStatus=PORTD|0b11110000; //Маскируем ненужные 
 200:  #asm
 201:         comf KeyStatus,1
 202:  #endasm
 203:  
 204:  if (SavePower==0) 
 205:   { PORTB=0;
 206:        PORTD = (PORTD&SegMask);
 207:        secLed=TimeLED=0;
 208:        PNLED=VTLED=SRLED=CHLED=PTLED=SBLED=VSLED=0;   
 209:        ShowSegment=10;
 210:      }
 211:  
 212:  //PORTD=(PORTD&SegMask); Без транзисторов
 213:  PORTD=0b00001111;
 214:  if (ShowSegment==0)
 215:   { 
 216:        if ( (Segment1blick==1) && (Blick<(BlickRate/2)) )
 217:         PORTB = 0;
 218:           else
 219:            PORTB = Segment1;
 220:     PORTD = (PORTD&SegMask)|Seg1sel;
 221:   } 
 222:  
 223:  if (ShowSegment==1)
 224:   {    
 225:        if((Segment2blick==1)&&(Blick<(BlickRate/2)))
 226:         PORTB = 0;
 227:           else
 228:            PORTB = Segment2;
 229:        PORTD = (PORTD&SegMask)|Seg2sel; 
 230:   }
 231:  
 232:  if (ShowSegment==2)
 233:   { 
 234:        if((Segment3blick==1)&&(Blick<(BlickRate/2)))
 235:         PORTB = 0;
 236:           else
 237:            PORTB = Segment3;
 238:  
 239:     PORTD = (PORTD&SegMask)|Seg3sel;
 240:   }
 241:  if (ShowSegment==3)
 242:   { 
 243:        if((Segment4blick==1)&&(Blick<(BlickRate/2)))
 244:         PORTB = 0;
 245:           else
 246:            PORTB = Segment4;
 247:  
 248:     PORTD = (PORTD&SegMask)|Seg4sel;
 249:   }
 250:  
 251:  
 252:  ShowSegment++;
 253:  if (ShowSegment>=4) ShowSegment=0;
 254:  
 255:  Blick++;
 256:  if (Blick>BlickRate) Blick=0;
 257:  }
 258:  
 259:  
 260:  //Преобразования из двоичной системы в двоично-десятичную
 261:  unsigned char rtc_format(unsigned char rtc_data)
 262:  {
 263:  unsigned char buf[2],rtc_res_data;
 264:  
 265:  sprintf(buf,"%2d",rtc_data);
 266:  if (buf[0]==0x20) buf[0]=48;
 267:  rtc_res_data=(buf[0]-48)<<4;
 268:  rtc_res_data+=(buf[1]-48);
 269:  
 270:  return rtc_res_data;
 271:  }
 272:  
 273:  
 274:  #define ttTime 0
 275:  #define ttAlarm 1
 276:  
 277:  void ShowCyrTime(unsigned char tt)
 278:  {
 279:  unsigned char HourH,HourL,MinH,MinL;
 280:  
 281:  if (tt==ttTime) 
 282:    { rtc_min=rtc_format(min);
 283:      rtc_hour=rtc_format(hour);
 284:    }
 285:  if (tt==ttAlarm) 
 286:    { rtc_min=rtc_format(Alarmmin);
 287:      rtc_hour=rtc_format(Alarmhour);
 288:    }
 289:  
 290:  
 291:  HourH=(rtc_hour&0b11110000)>>4;
 292:  HourL=rtc_hour&0b00001111;
 293:  
 294:  MinH =(rtc_min&0b11110000)>>4;
 295:  MinL =rtc_min&0b00001111;
 296:  
 297:  
 298:  
 299:  //if ((Year>=9)||(ClockMode!=ClockModeNormal))
 300:   {
 301:  //Гашение незначущего нуля
 302:  if (HourH==0) Segment1=ValueToSegment(' ');
 303:        else
 304:                Segment1=ValueToSegment(HourH);
 305:  Segment2=ValueToSegment(HourL);
 306:  Segment3=ValueToSegment(MinH);
 307:  Segment4=ValueToSegment(MinL);
 308:   }
 309:  /*  else
 310:   {
 311:  Segment1=ValueToSegment("-");
 312:  Segment2=ValueToSegment("-");
 313:  Segment3=ValueToSegment("-");
 314:  Segment4=ValueToSegment("-");
 315:   }
 316:  */
 317:  }
 318:  
 319:  void WaitKeyUp(void)
 320:  {
 321:  while (KeyStatus!=0)
 322:    { KeyStatus=0;
 323:      delay_ms(255); 
 324:      delay_ms(255);
 325:    }
 326:  }
 327:  
 328:  void WaitKeyDown(void)
 329:  {
 330:  while (KeyStatus==0)
 331:    { KeyStatus=0;
 332:      delay_ms(255); 
 333:      delay_ms(255);
 334:    }
 335:  }
 336:  
 337:  void sound(unsigned int8 nota, unsigned int8 delayF)
 338:  {
 339:  //F - частота
 340:  //delayF - длительность, мс
 341:  
 342:  setup_ccp1(CCP_PWM);   // Configure CCP1 as a PWM
 343:  setup_timer_2(T2_DIV_BY_1, nota, 1);
 344:  set_pwm1_duty(nota/2);
 345:  //enable_interrupts(INT_TIMER2);
 346:  
 347:  delay_ms(delayF);
 348:  
 349:  //disable_interrupts(INT_TIMER2);
 350:  setup_ccp1(CCP_OFF);
 351:  }
 352:  
 353:  void ShowCyrWeek(void)
 354:  {
 355:  PNLED=VTLED=SRLED=CHLED=PTLED=SBLED=VSLED=0;
 356:  
 357:  if (week==1) { PNLED=1; 
 358:                 Segment3=ValueToSegment('П');
 359:                 Segment4=ValueToSegment('H'); }
 360:  if (week==2) { VTLED=1; 
 361:                 Segment3=ValueToSegment(8);
 362:                 Segment4=ValueToSegment('H'); }
 363:  if (week==3) { SRLED=1; 
 364:                 Segment3=ValueToSegment('C');
 365:                 Segment4=ValueToSegment('P'); }
 366:  if (week==4) { CHLED=1; 
 367:                 Segment3=ValueToSegment('Ч');
 368:                 Segment4=ValueToSegment('Г'); }
 369:  if (week==5) { PTLED=1; 
 370:                 Segment3=ValueToSegment('П');
 371:                 Segment4=ValueToSegment('A'); }
 372:  if (week==6) { SBLED=1; 
 373:                 Segment3=ValueToSegment('C');
 374:                 Segment4=ValueToSegment('Б'); }
 375:  if (week==7) { VSLED=1; 
 376:                 Segment3=ValueToSegment('H');
 377:                 Segment4=ValueToSegment('д'); }
 378:  
 379:  if (week>7)  { VSLED=1; 
 380:                 Segment3=ValueToSegment('-');
 381:                 Segment4=ValueToSegment('-'); }
 382:  
 383:  }
 384:  
 385:  
 386:  void SetTime(void)
 387:  {
 388:  unsigned char NeedWriteTime=0;
 389:  
 390:  BlickTimeSecLed=0;
 391:  SecLED=1;
 392:  TimeLED=1;
 393:  
 394:  ClockMode=ClockModeTune;
 395:  
 396:  sound(OK_SND,OK_delay);
 397:  WaitKeyUp();
 398:  
 399:  //Наводим часы
 400:  
 401:  Segment1blick=1;
 402:  Segment2blick=1;
 403:  Segment3blick=0;
 404:  Segment4blick=0;
 405:  
 406:  //DS1307ReadTime();
 407:  
 408:  while (KeyTime!=1)
 409:    {
 410:     KeyStatus=0;
 411:  
 412:     ShowCyrTime(ttTime); 
 413:     delay_ms(255); delay_ms(255); 
 414:     if (KeyPlus==1) 
 415:        { Hour++; 
 416:          if (Hour>23) hour=0;
 417:          NeedWriteTime=1; }
 418:     if (KeyMinus==1) 
 419:        { Hour--; 
 420:          if(Hour>23) Hour=23; 
 421:          NeedWriteTime=1; }
 422:     rtc_Hour=rtc_format(Hour); // Переводим в формат часов
 423:    }
 424:  
 425:  sound(OK_SND,OK_delay);
 426:  WaitKeyUp();
 427:  
 428:  //Наводим минуты
 429:  
 430:  Segment1blick=0;
 431:  Segment2blick=0;
 432:  Segment3blick=1;
 433:  Segment4blick=1;
 434:  
 435:  while (KeyTime!=1)
 436:    {
 437:     KeyStatus=0;
 438:     ShowCyrTime(ttTime); 
 439:     delay_ms(255); delay_ms(255); 
 440:     if (KeyPlus==1) 
 441:        { Min++; 
 442:          if (Min>59) Min=0;
 443:          NeedWriteTime=1; }
 444:     if (KeyMinus==1) 
 445:        { Min--; 
 446:          if(Min>59) Min=59; 
 447:          NeedWriteTime=1; }
 448:  
 449:     rtc_Min=rtc_format(Min); // Переводим в формат часов
 450:    }
 451:  
 452:  Segment1blick=0;
 453:  Segment2blick=0;
 454:  Segment3blick=1;
 455:  Segment4blick=1;
 456:  
 457:  sound(OK_SND,OK_delay);
 458:  WaitKeyUp();
 459:  
 460:  //Наводим день недели
 461:  Segment1=ValueToSegment('H');
 462:  Segment2=ValueToSegment('-');
 463:  //Segment3=ValueToSegment('');
 464:  //Segment4=ValueToSegment(MinL);
 465:  
 466:  while (KeyTime!=1)
 467:    {
 468:     KeyStatus=0;
 469:     ShowCyrWeek(); 
 470:     delay_ms(255); delay_ms(255); 
 471:     if (KeyPlus==1) 
 472:        { week++; 
 473:          if (week>7) week=1;
 474:          NeedWriteTime=1; }
 475:     if (KeyMinus==1) 
 476:        { week--; 
 477:          if(week==0) week=7; 
 478:          NeedWriteTime=1; }
 479:    }
 480:  
 481:  Year=9;
 482:  
 483:  //if (NeedWriteTime==1) DS1307SetTime();
 484:  
 485:  Segment1blick=0;
 486:  Segment2blick=0;
 487:  Segment3blick=0;
 488:  Segment4blick=0;
 489:  
 490:  sound(OK_SND,OK_delay);
 491:  WaitKeyUp();
 492:  
 493:  BlickTimeSecLed=1;
 494:  }
 495:  
 496:  void ShowWeek(void)
 497:  {
 498:  unsigned char tweek;
 499:  tweek=week;
 500:  
 501:  if ((WeekBlick==1) && (Blick<(BlickRate/2))) tweek=10;
 502:  
 503:  if (tweek==1) PNLED=1; else PNLED=0;
 504:  if (tweek==2) VTLED=1; else VTLED=0;
 505:  if (tweek==3) SRLED=1; else SRLED=0;
 506:  if (tweek==4) CHLED=1; else CHLED=0;
 507:  if (tweek==5) PTLED=1; else PTLED=0;
 508:  if (tweek==6) SBLED=1; else SBLED=0;
 509:  if (tweek==7) VSLED=1; else VSLED=0;
 510:  }
 511:  
 512:  void ReadAlarm(void)
 513:  { 
 514:    AlarmOn   = read_eeprom(0x01);
 515:    AlarmMin  = read_eeprom(0x02);
 516:    AlarmHour = read_eeprom(0x03);
 517:  }
 518:  
 519:  void WriteAlarm(void)
 520:  { 
 521:    write_eeprom(0x01,AlarmOn);
 522:    write_eeprom(0x02,AlarmMin);
 523:    write_eeprom(0x03,AlarmHour);
 524:  }
 525:  /*
 526:  #int_TIMER2
 527:  void  TIMER2_isr(void) 
 528:  {
 529:  //output_toggle(PIN_E0);
 530:  }
 531:  */
 532:  
 533:  
 534:  
 535:  void SetAlarm(void)
 536:  { 
 537:  unsigned char NeedWriteTime=0;
 538:  
 539:  BlickTimeSecLed=0;
 540:  SecLED=1;
 541:  TimeLED=1;
 542:  
 543:  ClockMode=ClockModeTune;
 544:  
 545:  sound(OK_SND,OK_delay);
 546:  
 547:  WaitKeyUp();
 548:  
 549:  //Вывести диалог: "Будильник вкл/выкл"
 550:  Segment1blick=0;
 551:  Segment2blick=0;
 552:  Segment3blick=1;
 553:  Segment4blick=1;
 554:  
 555:  Segment1=ValueToSegment('Б');
 556:  Segment2=ValueToSegment('-');
 557:  
 558:  while (KeyAlarm!=1)
 559:   { KeyStatus=0;
 560:        if (AlarmOn==1)
 561:           { Segment3=ValueToSegment('O');
 562:             Segment4=ValueToSegment('n');
 563:           }
 564:          else
 565:           { Segment3=ValueToSegment('O');
 566:             Segment4=ValueToSegment('F');
 567:           }
 568:  
 569:     delay_ms(255); delay_ms(255); 
 570:     if (KeyPlus==1) 
 571:        { AlarmOn=1;         
 572:          NeedWriteTime=1; }
 573:     if (KeyMinus==1) 
 574:        { AlarmOn=0;
 575:          NeedWriteTime=1; }
 576:      }
 577:  sound(OK_SND,OK_delay);
 578:  WaitKeyUp();
 579:  
 580:  if (AlarmOn==0) goto NoAlarm;
 581:  
 582:  Segment1blick=1;
 583:  Segment2blick=1;
 584:  Segment3blick=0;
 585:  Segment4blick=0;
 586:  
 587:  //DS1307ReadTime();
 588:  
 589:  while (KeyAlarm!=1)
 590:    {
 591:     KeyStatus=0;
 592:  
 593:     ShowCyrTime(ttAlarm); 
 594:     delay_ms(255); delay_ms(255); 
 595:     if (KeyPlus==1) 
 596:        { AlarmHour++; 
 597:          if (AlarmHour>23) AlarmHour=0;
 598:          NeedWriteTime=1; }
 599:     if (KeyMinus==1) 
 600:        { AlarmHour--; 
 601:          if(AlarmHour>23) AlarmHour=23; 
 602:          NeedWriteTime=1; }
 603:    }
 604:  sound(OK_SND,OK_delay);
 605:  WaitKeyUp();
 606:  
 607:  Segment1blick=0;
 608:  Segment2blick=0;
 609:  Segment3blick=1;
 610:  Segment4blick=1;
 611:  
 612:  while (KeyAlarm!=1)
 613:    {
 614:     KeyStatus=0;
 615:     ShowCyrTime(ttAlarm); 
 616:     delay_ms(255); delay_ms(255); 
 617:     if (KeyPlus==1) 
 618:        { AlarmMin++; 
 619:          if (AlarmMin>59) AlarmMin=0;
 620:          NeedWriteTime=1; }
 621:     if (KeyMinus==1) 
 622:        { AlarmMin--; 
 623:          if(AlarmMin>59) AlarmMin=59; 
 624:          NeedWriteTime=1; }
 625:    }
 626:  
 627:  Segment1blick=1;
 628:  Segment2blick=1;
 629:  Segment3blick=1;
 630:  Segment4blick=1;
 631:  
 632:  NoAlarm:
 633:  
 634:  if (NeedWriteTime==1)  
 635:     {
 636:       WriteAlarm();
 637:     }
 638:  
 639:  sound(OK_SND,OK_delay);
 640:  WaitKeyUp();
 641:  
 642:  
 643:    Segment1blick=0;
 644:    Segment2blick=0;
 645:    Segment3blick=0;
 646:    Segment4blick=0;
 647:  
 648:    BlickTimeSecLed=1;
 649:  
 650:  }
 651:  
 652:  void Alarm(void)
 653:  {
 654:  unsigned char i;
 655:  Segment1blick=1;
 656:  Segment2blick=1;
 657:  Segment3blick=1;
 658:  Segment4blick=1;
 659:  
 660:   //Подаю короткий пик
 661:  sound(128,100);
 662:  
 663:  //*********
 664:  i=1;
 665:  while (i<7)
 666:   { sound(nota_SI,150);
 667:     i++;
 668:     delay_ms(255);delay_ms(255);delay_ms(255);delay_ms(255);
 669:     delay_ms(255);delay_ms(255);delay_ms(255);delay_ms(255);
 670:     delay_ms(255);delay_ms(255);delay_ms(255);delay_ms(255);
 671:     if (KeyStatus!=0) i=7;
 672:   }
 673:  //*********
 674:  
 675:  
 676:  //KeyStatus=0;
 677:  //Пауза
 678:  delay_ms(255);
 679:  delay_ms(255);
 680:  //Даю 3 коротких ПИКа
 681:  if (KeyStatus==0)
 682:    { sound(1000,100);
 683:      delay_ms(100);
 684:      sound(1000,100);
 685:      delay_ms(255);
 686:      sound(1000,100);
 687:      delay_ms(255);
 688:      //Пауза
 689:      delay_ms(255);
 690:      delay_ms(255);    
 691:    }
 692:  //Пауза
 693:     delay_ms(255);delay_ms(255);delay_ms(255);delay_ms(255);
 694:     delay_ms(255);delay_ms(255);delay_ms(255);delay_ms(255);
 695:     delay_ms(255);delay_ms(255);delay_ms(255);delay_ms(255);
 696:     delay_ms(255);delay_ms(255);delay_ms(255);delay_ms(255);
 697:     delay_ms(255);delay_ms(255);delay_ms(255);delay_ms(255);
 698:     delay_ms(255);delay_ms(255);delay_ms(255);delay_ms(255);
 699:  
 700:  
 701:  while(KeyStatus==0)
 702:    { 
 703:  //Музон
 704:      //ми до ми до , фа ми ре 
 705:  sound(nota_MI,200); delay_ms(5);
 706:  sound(nota_DO,200); delay_ms(5);
 707:  sound(nota_MI,200); delay_ms(5);
 708:  sound(nota_DO,200); delay_ms(5);
 709:  sound(nota_FA,200); delay_ms(5);
 710:  sound(nota_MI,200); delay_ms(5);
 711:  sound(nota_RE,200); delay_ms(5);
 712:      if (KeyStatus!=0) goto keyPressed;
 713:  //соль соль ля си до до до 
 714:  sound(nota_SO,200); delay_ms(5);
 715:  sound(nota_SO,200); delay_ms(5);
 716:  sound(nota_LA,200); delay_ms(5);
 717:  sound(nota_SI,200); delay_ms(5);
 718:  sound(nota_DO,200); delay_ms(5);
 719:  sound(nota_DO,200); delay_ms(5);
 720:  sound(nota_DO,200); delay_ms(5);
 721:    keyPressed:;
 722:  
 723:    ShowCyrTime(ttTime);
 724:    delay_ms(255);
 725:    delay_ms(255);
 726:    }
 727:  
 728:  Segment1blick=0;
 729:  Segment2blick=0;
 730:  Segment3blick=0;
 731:  Segment4blick=0;
 732:  
 733:  DisableCurAlarm=1;
 734:  WaitKeyUp();
 735:  }
 736:  
 737:  void ShowTemperature()
 738:  {
 739:  signed int16 Temperature,t; 
 740:  
 741:  BlickTimeSecLed=SecLED=0;
 742:  Segment1=ValueToSegment('t');
 743:  Segment2=ValueToSegment(' ');
 744:  Segment3=ValueToSegment(' ');
 745:  Segment4=ValueToSegment(' ');
 746:  
 747:  // Настройка АЦП
 748:  setup_adc_ports(sAN0|sAN1|VSS_VDD);
 749:  setup_adc(ADC_CLOCK_INTERNAL);
 750:  set_adc_channel( 0 );
 751:  delay_ms(100);
 752:  
 753:  //Измерение температуры
 754:  Temperature = read_adc(ADC_START_AND_READ);
 755:  
 756:  //Отключение АЦП
 757:  setup_adc_ports(NO_ANALOGS|VSS_VDD);
 758:  setup_adc(ADC_OFF);
 759:  
 760:  //Отображение температуры 
 761:  { unsigned char buf[4];
 762:  
 763:   if (Temperature>=0) t=Temperature;
 764:   if (Temperature<0)  t=Temperature*-1;
 765:    sprintf(buf,"%3ld",t);
 766:  
 767:  Segment2=ValueToSegment(buf[0]-48);
 768:  Segment3=ValueToSegment(buf[1]-48);
 769:  Segment4=ValueToSegment(buf[2]-48);
 770:  Segment3|=0b10000000; // Десятичная точка
 771:  if (t<10) Segment3=ValueToSegment('0'); // "ноль" целых.
 772:  //Выводим знак минус
 773:  if ((Temperature<0)&&(Temperature>-200)) Segment2|=0b01000000;
 774:  if ((Temperature<=-200)) Segment1=ValueToSegment('-');
 775:  }
 776:  
 777:  //Выходим
 778:  delay_ms(250);delay_ms(250);
 779:  delay_ms(250);delay_ms(250);
 780:  delay_ms(250);delay_ms(250);
 781:  delay_ms(250);delay_ms(250);
 782:  WaitKeyUp();
 783:  BlickTimeSecLed=1;
 784:  }
 785:  
 786:  void main(void)
 787:  {
 788:  StartInit();
 789:  Segment1=ValueToSegment(45);
 790:  Segment2=ValueToSegment(45);
 791:  Segment3=ValueToSegment(45);
 792:  Segment4=ValueToSegment(45);
 793:  delay_ms(255);delay_ms(255);delay_ms(255);delay_ms(255);delay_ms(255);
 794:  ReadAlarm(); //Чтение установок будильника
 795:  DisableCurAlarm=0;
 796:  /*
 797:  sound(nota_DO,700);
 798:  sound(nota_RE,700);
 799:  sound(nota_MI,700);
 800:  sound(nota_FA,700);
 801:  sound(nota_SO,700);
 802:  sound(nota_LA,700);
 803:  sound(nota_SI,700);
 804:  sound(nota_DL,700);
 805:  */
 806:  
 807:  //ми до ми до , фа ми ре 
 808:  sound(nota_MI,200); delay_ms(50);
 809:  sound(nota_DO,200); delay_ms(50);
 810:  sound(nota_MI,200); delay_ms(50);
 811:  sound(nota_DO,200); delay_ms(50);
 812:  sound(nota_FA,200); delay_ms(50);
 813:  sound(nota_MI,200); delay_ms(50);
 814:  sound(nota_RE,200); delay_ms(50);
 815:  //соль соль ля си до до до 
 816:  sound(nota_SO,200); delay_ms(50);
 817:  sound(nota_SO,200); delay_ms(50);
 818:  sound(nota_LA,200); delay_ms(50);
 819:  sound(nota_SI,200); delay_ms(50);
 820:  sound(nota_DO,200); delay_ms(50);
 821:  sound(nota_DO,200); delay_ms(50);
 822:  sound(nota_DO,200); delay_ms(50);
 823:  
 824:  WaitKeyDown();
 825:  delay_ms(255);
 826:  WaitKeyUp();
 827:  
 828:  while(1)
 829:   { 
 830:    ClockMode=ClockModeNormal;
 831:    BlickTimeSecLed=1;
 832:  //  DS1307ReadTime();
 833:    ShowCyrTime(ttTime); 
 834:    ShowWeek();
 835:  //  AlarmLED=AlarmOn;
 836:    if (AlarmOn==1) Segment4|=0b10000000;
 837:    keyStatus=0;
 838:    delay_ms(255);
 839:    if (KeyTime==1) SetTime();
 840:    if (KeyAlarm==1) SetAlarm();
 841:    if (KeyPlus==1)  ShowTemperature();
 842:    if (((Min==AlarmMin)&&(Hour==AlarmHour)&&(AlarmOn==1)&&(DisableCurAlarm==0))) Alarm();
 843:    
 844:    if (SavePower==0)  //Переходим в режим экономии энергии
 845:   { while(SavePower==0)
 846:   { restart_wdt();
 847:            sleep();
 848:     delay_us(3);
 849:            //спим, пока свет не включат
 850:   }
 851:   }
 852:   }
 853:  }


Прошивка от 15.01.2011, скачать .hex
1. Добавлена поддержка датчика температуры DS18B20. Сигнальный выход датчика следует подключить к ножке №2 микроконтроллера и соеденить через резистор на 4,7 кОм с питанием МК. Температура отображается по нажатию кнопки "+".

Часы успешно эксплуатируются с 05.05.2009.

upd: 19.07.2017. Часы продолжают работать в режиме 24/7

upd: 18.08.2017. Ruslan Aktaliev, радиолюбитель из Молдавии, повторил конструкцию часов и предоставил свои материалы:

Часы PIC16F887 с семиссегментным светодиодным индикатором, будильником, энергонезависимым питанием и днем недели. PIC16F887

Чертеж платы в sLayout6

18.08.2017