Радио электроника

набор конструктор недорого . активные проекты – часы на ГРИ – усилитель на радиолампах с зеленым глазком и 2 индикаторами ИН13 – фонарь 900 люмен на светодиодной лампе, 3 часа на 2 батарейках 18650 – радар на ардуино используя ультразвук – светодиодная линейка для цифровой техники как в старых ЭВМ – в основном с применением dc-dc на mc34063 в нескольких вариантах, 12 на 5 вольт и 3 на 207 вольт , 12 на 200 и 600 преобразователь напряжения.

для часов применяется Arduino nixieclock.biz Arduinix / варианты программы под включение индикаторов постоянным током – в статике, также используются все эффекты быстрого переключения. * arduino = Atmega328p и вариант на 16 мегагерц кварцем – преимущественно за 5 (3 )доллара не за 55.

Я разработал Arduino-совместимый модуль драйвера трубок Nixie, чтобы сделать такие проекты намного проще!

маленький совет посмотреть с компа – 150 картинок и 600 страниц то ли наоборот. Много видео клипов а то и программ – телефону будет трудновато.

+483 Запостил dekuNukem 01.03.2018 17:40

что то стал програмки Ардуино скетчи редактировать в Akelpad с несколькими дополнительными програмками – показывающими коды -они же делают простую проверку правильности синтаксис. файл записываю с расширением c bas asm . плагины позволяют выбрать подходящий язык программирования.

часы на индикаторных лампах. Комплектуха 6 штук для статики К155ИД1 (можно упростить поставив одну или две – только будет динамическая индикация), кристалл Atmega328p на 16 мегагерц – Arduino CH340g (Chinese). Лампы ИН12 ИН1 ИН16 – самые маленькие а то и ИН18. в случае статики 3 регистра 74HC595 а если плата с динамической индикацией – pc817 6 штук или tlp627 и -или – 6 пар транзисторов mpsa92 mpsa42 (кт503е кт3102г подобрать по параметрам надо 180 вольт) . В преобразователе напряжения mc34063 а второй вариант использовать от ардуино pwm. Катушка – смотрим запись Фонарик на светодиоде или Часы nixie – от блока питания компьютера желтое кольцо (заказать в Китае Индуктор 15А и домотать вторичную, еще лучше если есть альсифер еще из СССР колечки).

  • +37 dekuNukem 01.03.2018 17:41 Я полагаю, что многие люди были соблазнены ярким оранжевым свечением трубок Никси, однако эти трубки требуют высокого напряжения питания, старых микросхем драйверов и схем мультиплексирования. И я полагаю, что эта сложность отпугнула многих энтузиастов с проектами. Я тоже думал о создании собственных часов Nixie, и вместо традиционного подхода я решил разработать специальную плату драйверов, называемую exixe. Модули exixe работают с трубками Nixie IN-12 и IN-14 и устраняют необходимость в старинных чипах и мультиплексировании. Вы просто используете 3-х проводный SPI для управления яркостью каждой цифры плюс подсветка RGB. Эти модули просты в использовании, интегрируемы и могут использоваться повторно. Вы можете использовать Arduino, Raspberry Pi, STM32 или что-нибудь еще с GPIO. И, конечно, этот проект с открытым исходным кодом, поэтому, пожалуйста, прочитайте подробности здесь: https://github.com/dekuNukem/exixe Я также провел небольшой производственный цикл, поэтому не стесняйтесь получить несколько, если вам интересно: https://www.tindie.com/products/dekuNukem/exixe-miniture-nixie-tube-driver-modules/ Откладывая в сторону, я надеюсь, что этот проект снизит входной барьер в проектах труб Nixie. Никси труба Ретро метроном? Youtube суб счетчик? Непрочитанные сообщения уведомителя? Таймер ускоренного запуска / секундомер? Хипстерский мультиметр? Так много интересных проектных идей! В любом случае, спасибо за чтение и не стесняйтесь задавать вопросы.
    • +8 -Argih 02.03.2018 13:09 Отправить один на EEVLOG почтовый пакет, я думаю, это было бы здорово для вашего бизнеса
    • +3 Lewtfish 01.03.2018 20:41 Есть ли шанс, что вы можете отправить в Нидерланды? Хотел бы иметь немного!
      • +3 dekuNukem 01.03.2018 21:41 Конечно! Стоимость доставки во всем мире одинакова. Подробности см. В разделе «Информация о доставке» на этой странице .
    • +1 ShitInMyCunt-2dollar 02.03.2018 08:11 Есть ли шанс, что IN-16 сработает? Или они принципиально разные?
      • +3 dekuNukem 02.03.2018 09:13 Я только что взглянул на таблицу данных IN-16 , похоже, она будет работать. Расположение анодного штифта такое же, и трубка может поместиться на модуле. Порядок цифр отличается, поэтому вам нужно будет настроить программное обеспечение, но с аппаратной точки зрения я думаю, что это совместимо.
        • +1 ShitInMyCunt-2dollar 02.03.2018 09:20 Благодарю. Я купил их давным-давно, и у меня были бесконечные проблемы с тем, чтобы заставить их работать так, как я этого хочу – главным образом потому, что я недостаточно знаю, как правильно их водить. У меня есть старые советские чипы, которые специально созданы для них, но, по сути, мое программирование недостаточно хорошее. Я безнадежный. Я ждал что-то подобное, чтобы прийти вместе. Надеюсь, я смогу воспользоваться этими вещами.
    • +1 albionhere 02.03.2018 07:33 Я абсолютный n00b, так что это может быть глупым вопросом. Недавно мне дали комплект для изготовления трубок VFD. Может ли это сработать и для тех?
      • +1 dekuNukem 02.03.2018 18:29 Трубка VFD требует совершенно другого способа вождения, поэтому она не совместима с модулями exixe.
    • +1 DracarysMeansFire 01.03.2018 17:48 Выглядит очень круто, я ищу NOS NIXI, и когда я их нахожу. Вот как я буду их использовать.
  • +6 Zouden 01.03.2018 18:36 Вау, действительно хорошая работа! PCB профессионального уровня, даже имеет закругленные углы! Что такое QFP, чип Atmega?
    • +7 dekuNukem 01.03.2018 18:43 Это STM32F042K6T6, универсальный маленький чип.
  • +4 vivalarevoluciones 01.03.2018 23:13 Боже мой, я просто потратил много времени на изучение этого, купил несколько тюбиков амазонки, которые все испортились, поэтому я закончил получать этот комплект. http://www.arduinix.com/
    не могу дождаться, чтобы спаять мой комплект вместе. редактировать Черт, брат, после прочтения комментариев, я должен был дождаться, дам, это намного лучше. я ненавижу торопиться с покупкой запчастей.
  • +2 bestofpawnee 01.03.2018 18:24 Это действительно круто!
  • +1 bpoag 02.03.2018 16:11 Хочу хочу хочу хочу хочу хочу .. Вы должны продать схему Sparkfun ..
    • +1 dekuNukem 02.03.2018 16:57 Я думаю, это было бы слишком нишевым для них, чтобы продать, в любом случае у меня есть несколько на Tindie, так что посмотрите, если вы заинтересованы.
      • +1 zappadoing 02.03.2018 20:08 так круто! Благодарю. приказал.
        • +1 dekuNukem 02.03.2018 22:15 Спасибо за поддержку! Здесь вечер пятницы, и я отправлю их в следующий понедельник. В то же время, пожалуйста, не стесняйтесь взглянуть на руководство по началу работы, чтобы увидеть, что ждет впереди.
  • +1 would_you_date_me 02.03.2018 07:45 Трубы Никси шумят, когда они переключаются?
    • +3 dekuNukem 02.03.2018 09:15 Нет, они полностью молчат. Они также потребляют небольшое количество энергии (2,5 мА) и совсем не нагреваются.
  • +1 maedhros338 02.03.2018 00:38 Хорошая работа! Отличная идея и выглядит как отличное исполнение. Не могу дождаться, чтобы попробовать их сам!
  • +1 Terradice 02.03.2018 08:06 Черт, дай мне, мне нужно
  • +1 steeeeeeved 01.03.2018 23:47 Эпическая работа, сэр!
  • +1 DrTBag 02.03.2018 11:12 Выглядит как хороший проект, и вы выбрали отличную цену. Я надеюсь, что это хорошо для вас.
  • +1 MrScafir 02.03.2018 07:46 Это так круто! Спасибо за это и еще больше спасибо за то, что сделали его открытым исходным кодом!
  • +1 ancientsceptre 02.03.2018 04:45 Я люблю тебя
  • +1 Rxke2 02.03.2018 12:58 Можно ли использовать другие ники, паяя их проводом к платам PCB, или PCB оптимизированы для эксклюзивного использования с 12-14?
    • +1 dekuNukem 02.03.2018 16:51 Вы можете использовать другие трубки для этого, но расположение выводов трубки будет другим, поэтому вы должны убедиться, что они подключены правильно. Например, труба IN-16 будет работать с модулями exixie-14.
  • +1 lancelon 02.03.2018 15:45 / и / sanels
    • +1 sanels 02.03.2018 21:49 да, я видел это. но это просто делает вещи слишком лол (и много дороже)
  • +1 FRESH_OUTTA_800AD 04.03.2018 19:58 Какой хороший источник труб? Это все старые акции СССР? Изменить, только что нашел вашу ссылку на этом
  • +1 FRESH_OUTTA_800AD 06.03.2018 03:23 Очень круто! У вас есть еще фотографии, которые демонстрируют возможность подсветки? Или это больше о продавце трубки?
  • +1 fefemess 06.08.2018 20:32 Я давно искал что-то подобное. Спасибо. Эль Пси Конгруо
  • +1 [deleted] 02.03.2018 20:58 Впечатляет. Выглядит профессионально и гладко, как ад
  • +1 fischoderaal 02.03.2018 17:38 Приветствия для этого с открытым исходным кодом!
  • +1 joe0400 01.03.2018 19:58 Ваша доставка в Великобританию? Можно ли отправить в США? Мне нужен лучший комплект Nixie от Arduino.
    • +2 dekuNukem 01.03.2018 21:38 Да, я отправляю в обе эти страны. Смотрите эту страницу для деталей.
  • +1 drimago 27.04.2018 15:05 Этот продукт идеально подходит для моего проекта. Один вопрос, однако: как вы приводите их в действие? Мне все еще нужен преобразователь 5В в 180В?

#include <mega8515.h>
#include <delay.h>
#include <bcd.h>
#include <avr_eeprom.c>
#include <mega8515.h>
#include <delay.h>
#include <bcd.h>
#include <avr_eeprom.c>
#include <I2C.c>
#include <disp_out.c>

#define snd     PORTB.0                 //нога пищалки
#define pen     PORTA.4                 //нога power enable высоковольтного источника

#define RTC     0b11010000              //I2C-адрес часов

#define sec_addr        0x01            //адреса регистров микросхемы RTC
#define min_addr        0x02
#define hrs_addr        0x03
#define day_addr        0x05
#define mnth_addr       0x06
#define year_addr       0x07

#define a_sec_addr      0x0E
#define a_min_addr      0x0D
#define a_hrs_addr      0x0C
#define a_day_addr      0x0B
#define a_mnth_addr     0x0A
#define a_flag          0x0F

//команды через RS-232
#define start_cond      0x80
#define set_time        0x81
#define set_date        0x82
#define set_alarm       0x83
#define set_clock_mode  0x84
#define set_date_sht    0x85
#define set_data_sht    0x86
#define alarm_on_off    0x87
#define show_sec        0x88
#define show_data       0x89
#define get_time        0x8A
#define get_date        0x8B
#define show_date       0x8C
#define power_en        0x8D
#define reserved        0x8E
#define stop_cond       0x8F

#define but1            !PINA.3         //кнопки
#define but2            !PINA.2
#define but3            !PINA.1
#define but4            !PINA.0

#define start_autoshow  TCCR0=0x05      //запустить таймер для обновления показаний по прерыванию       
#define stop_autoshow   TCCR0=0x00      //остановить таймер

unsigned char timer_int = 0;            //счетчик срабатываний таймера для кнопок 

unsigned char flag_1 = 0;               //флаги для длительного нажатия кнопок
unsigned char flag_2 = 0;
unsigned char flag_3 = 0;
unsigned char flag_4 = 0;

//режимы работы дисплея
unsigned char clock_mode = 1;           //режим часов: 1-ЧЧ:ММ:СС, 2-ЧЧ:ММ
unsigned char date_mode = 0;            //режим даты: ДД:ММ:ГГ
unsigned char sec_mode = 0;             //режим секунд: :СС
unsigned char data_mode = 0;            //режим показа данных с RS-232

signed char sec = 0;                    //время
signed char min = 0;
signed char hrs = 0;
signed char day = 1;
signed char mnth = 1;
signed char year = 7;

signed char a_sec = 0;                  //время будильника
signed char a_min = 0;
signed char a_hrs = 0;
signed char a_day = 1;
signed char a_mnth = 1;
signed char a_year = 0;

unsigned char b_1 = 0;                  //байты, получаемые через RS-232
unsigned char b_2 = 0;
unsigned char b_3 = 0;
unsigned char b_4 = 0;
unsigned char b_5 = 0;
unsigned char b_6 = 0;
unsigned char b_7 = 0;
unsigned char b_cnt = 0;                //счетчик принятых байт
bit recieve_cmp = 0;                    //флаг окончания приема последовательности байт

unsigned char first_b = 0;              //байты для отображения, полученные через RS-232 
unsigned char second_b = 0;
unsigned char third_b = 0;
unsigned char point_b = 0;

bit alarm_flag = 0;                     //флаг активности будильника
unsigned char flag = 0;                 //флаг будильника в памяти RTC
unsigned char date_show_ms = 0;         //время показа даты в мс/100
unsigned char data_show_ms = 0;         //время показа данных из RS-232 в мс/100
unsigned char date_cnt = 0;             //счетчики для ограничения времени показа
unsigned char data_cnt = 0;
unsigned char show_cnt = 0;             //счетчик циклов таймера показа

//прототипы некоторых функций
void sound (int time);
void WriteRTC (unsigned char data, unsigned char addr);
unsigned char ReadRTC (unsigned char addr);

#define RXB8 1
#define TXB8 0
#define UPE 2
#define OVR 3
#define FE 4
#define UDRE 5
#define RXC 7

#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<OVR)
#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)

// USART Receiver buffer
#define RX_BUFFER_SIZE 20
char rx_buffer[RX_BUFFER_SIZE];

#if RX_BUFFER_SIZE<256
unsigned char rx_wr_index,rx_rd_index,rx_counter;
#else
unsigned int rx_wr_index,rx_rd_index,rx_counter;
#endif

#ifndef _DEBUG_TERMINAL_IO_
// Get a character from the USART Receiver buffer
#define _ALTERNATE_GETCHAR_
#pragma used+
char getchar(void)
{
char data;
while (rx_counter==0);
data=rx_buffer[rx_rd_index];
if (++rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0;
#asm("cli")
--rx_counter;
#asm("sei")
return data;
}
#pragma used-
#endif


// USART Receiver interrupt service routine
interrupt [USART_RXC] void usart_rx_isr(void)
        {
        char status,data;
        status=UCSRA;
        data=UDR;
        if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
                {
                rx_buffer[rx_wr_index]=data;
                if (++rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0;
                if (++rx_counter == RX_BUFFER_SIZE) rx_counter=0;
                
                if (b_cnt) ++b_cnt;                                  //если счетчик не нулевой - увеличиваем счетчик
                if (!b_cnt && data == start_cond) ++b_cnt;           //если первый полученный байт правильный, увеличиваем счетчик в первый раз
                };
                
        if (b_cnt == 7)                                              //если получили уже все 7 байт
                {
                b_1 = getchar();                                     //сохранили их содержимое
                b_2 = getchar();
                b_3 = getchar();
                b_4 = getchar();
                b_5 = getchar();
                b_6 = getchar();
                b_7 = getchar();                
                recieve_cmp = 1;                                     //выставили флаг окончания приема                                                       
                b_cnt = 0;                                           //обнулили счетчик  
                rx_counter=0;
                rx_wr_index=0;
                rx_rd_index=0;
                }
}

// Standard Input/Output functions
#include <stdio.h>

// External Interrupt 0 service routine - обработка будильника
interrupt [EXT_INT0] void ext_int0_isr(void)
        {
        flag = ReadRTC (0x0F);                                          //сброс флага будильника в RTC
        
        while (!but1 && !but2 && !but3 && !but4 && alarm_flag)          //пока не нажали на кнопку и есть флаг активности тревоги
                {       
                hrs = ReadRTC (hrs_addr);       
                min = ReadRTC (min_addr);
                disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0);
                sound (100);
                delay_ms (150);
                sound (100);
                delay_ms (500);
                putsf ("Alarm! ");
                }
        while (but1 || but2 || but3 || but4)  #asm("wdr");     
        }

// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
        {
        TCNT0=0x27;                                                     //таймер перепоняется каждые 10мс     
        
        if (++show_cnt == 5)                                            //каждые 50мс
                {
                if (clock_mode == 1 && !date_mode && !sec_mode && !data_mode)
                        {
                        hrs = ReadRTC (hrs_addr);                       //читаем и показываем время ЧЧ:ММ:СС
                        min = ReadRTC (min_addr);
                        sec = ReadRTC (sec_addr);
                        disp_out (hrs/10, hrs%10, min/10, min%10, sec/10, sec%10, alarm_flag, 0, 1, 0, 1, 0);
                        }
                if (clock_mode == 2 && !date_mode && !sec_mode && !data_mode)
                        {
                        hrs = ReadRTC (hrs_addr);                       //читаем и показываем время ЧЧ:ММ
                        min = ReadRTC (min_addr);
                        disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, alarm_flag, 0, 0, 1, 0, 0);
                        }
                if (date_mode && (4*date_cnt <= date_show_ms) && !sec_mode && !data_mode)          //читаем и показываем дату ДД:ММ:ГГ
                        {
                        ++date_cnt;                                     //счетчик показов
                        day = ReadRTC (day_addr);                       
                        mnth = ReadRTC (mnth_addr);
                        year = ReadRTC (year_addr);
                        disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, alarm_flag, 0, 1, 0, 1, 0);
                        }
                        else 
                                {
                                date_mode = 0;
                                date_cnt = 0;
                                }
                if (sec_mode && !data_mode)                             //читаем и показываем секунды :СС
                        {
                        sec = ReadRTC (sec_addr);                       
                        disp_out (10, 10, 10, 10, sec/10, sec%10, alarm_flag, 0, 0, 0, 1, 0);
                        }
                if (data_mode && (4*data_cnt <= data_show_ms))          //показываем данные из RS-232
                        {
                        ++data_cnt;
                        disp_out (first_b/10, first_b%10, second_b/10, second_b%10, third_b/10, third_b%10, alarm_flag, ((point_b & 0x02)>>1), ((point_b & 0x08)>>4), ((point_b & 0x04)>>2), ((point_b & 0x02)>>1), (point_b & 0x01));
                        }
                        else 
                                {
                                data_mode = 0;
                                data_cnt = 0;
                                }
                show_cnt = 0;
                }
        }

// Timer 1 overflow interrupt service routine
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
        {
        TCNT1H=0xD5;                 //таймер переполняется раз в секунду
        TCNT1L=0xCF;

        timer_int++;
        
        if (but1) flag_1 = timer_int;
                else flag_1 = 0;
        if (but2) flag_2 = timer_int;
                else flag_2 = 0;
        if (but3) flag_3 = timer_int;
                else flag_3 = 0;
        if (but4) flag_4 = timer_int;
                else flag_4 = 0;        
        
        if (timer_int == 1) sound (20);
        if (timer_int == 2)
                {
                sound (20);
                delay_ms(40);
                sound (20);
                }
        if (timer_int == 3)
                {
                sound (20);
                delay_ms(40);
                sound (20);
                delay_ms(40);
                sound (20);
                timer_int = 0;
                }
        }

unsigned char ReadRTC (unsigned char addr)      //процедура чтения байта из RTC - см. даташит
        {                                       //возвращает байт из адреса addr
        unsigned char data = 0;
        unsigned char data2 = 0;
        i2c_start_soft ();
        i2c_tx_soft (RTC);
        i2c_tx_soft (addr);                
        i2c_stop_soft ();
        
        i2c_start_soft ();
        i2c_tx_soft (RTC+1);
        data = i2c_rx_soft (1);
        data2 = i2c_rx_soft (0);                //сделано чтение байта вникуда, иначе подглючивает
        i2c_stop_soft ();
        data = bcd2bin (data);
        return data;
        }
        
void WriteRTC (unsigned char data, unsigned char addr)  //процедура записи байта в RTC - см. даташит
        {                                               //пишет data по адресу addr
        data = bin2bcd (data);
        i2c_start_soft ();
        i2c_tx_soft (RTC);
        i2c_tx_soft (addr);
        i2c_tx_soft (data);                
        i2c_stop_soft ();
        }
        
void sound (int time)           //Процедура для пищания, частота сильно около 3 кГц ;)) 
        {
        int i = 0;
        while (i < 4*time)
                {  
                snd = 1;
                delay_us(150);
                snd = 0;
                delay_us(150);                
                i++;
                }                                                                                                                                                                                                                                                                                     
        }

void reset_flag (void)
        {
        TCCR1B=0x00;                            //остановили таймер 1
        TCNT1H=0xD5;
        TCNT1L=0xCF;
        timer_int = 0;                          //сбросили счетчик прерывания таймера 1
        flag_1 = 0; 
        flag_2 = 0; 
        flag_3 = 0; 
        flag_4 = 0;  //сбросили все флаги входа
        }

void Power_en (void)
        {
        reset_flag ();
        pen = !pen;
        }
        
void Error (void)
        {
        b_cnt = 0;
        recieve_cmp = 0;
        b_1 = 0;
        b_2 = 0;
        b_3 = 0;
        b_4 = 0;
        b_5 = 0;
        b_6 = 0;
        b_7 = 0;
        rx_rd_index = 0;
        rx_counter=0;
        rx_wr_index=0;
        putsf ("Error!");
        }
        
void Time_setup (void)          //процедура установки времени
        {
        unsigned char i = 0;    //счетчик
        bit hrs_s = 0;          //флаги осуществленной установки часов, минут и т.д.
        bit min_s = 0;
        bit day_s = 0;
        bit mnth_s = 0;
        bit year_s = 0;
                
        stop_autoshow;
        reset_flag ();
        
        hrs = ReadRTC (hrs_addr);                                                               //прочитали текущее время из RTC
        min = ReadRTC (min_addr);                                               
        disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0);                    //мигнули часами один раз
        delay_ms (400);
        disp_out (10, 10, 10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0);
        delay_ms (400);
        while ((++i < 10) && !but1)                                                             //пока не мигнули 10 раз или не нажали кнопку 1
                {
                hrs_s = 1;                                                                      //поставили флаг, что пытались устанавливать часы
                disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0);            //мигаем временем
                delay_ms (400);
                disp_out (10, 10, 10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0);
                delay_ms (400);
                while (but4)                                                                    //если удерживаем кнопку 4
                        {
                        if (++hrs > 23) hrs = 0;                                                //увеличиваем счетчик часов
                        disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0);    //раз в 200 мс
                        delay_ms (400);
                        i = 0;                                                                  //и обнуляем счетчик миганий
                        }
                while (but3)                                                                    //если удерживаем кнопку 3
                        {                                                                       //уменьшаем счетчик часов
                        if (--hrs < 0) hrs = 23;                                                //раз в 200 мс
                        disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0);    //и обнуляем счетчик миганий
                        delay_ms (400);
                        i = 0;
                        }
                
                }
        
        if (but1)                               //если нажали на кнопку 1
                {
                delay_ms(10);
                WriteRTC (hrs, hrs_addr);       //записали установленное значение часов в RTC
                i = 0;                   
                sound(50);                      //пискнули
                while (but1);                   //ждем, пока отпустим кнопку 
                }
                
        hrs = ReadRTC (hrs_addr);                                               //аналогично для установки минут
        min = ReadRTC (min_addr);
        if (i == 0) 
                {
                disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0);
                delay_ms (400);
                }
        if (i == 0) 
                {
                disp_out (10, hrs/10, hrs%10, 10, 10, 10, 0, 0, 0, 1, 0, 0);
                delay_ms (400);
                }        
        while ((++i < 10) && !but1 && hrs_s)
                {
                min_s = 1;
                disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0);
                delay_ms (400);
                disp_out (10, hrs/10, hrs%10, 10, 10, 10, 0, 0, 0, 1, 0, 0);
                delay_ms (400);
                while (but4) 
                        {
                        if (++min > 59) min = 0;
                        disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0);
                        delay_ms (400);
                        i = 0;
                        }
                while (but3) 
                        {
                        if (--min < 0) min = 59;
                        disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0);
                        delay_ms (400);
                        i = 0;
                        }
                }
                
        if (but1)
                {
                delay_ms(10);
                WriteRTC (min, min_addr);
                i = 0;
                sound(50);
                while (but1);
                }                
        
        year = ReadRTC (year_addr);                                             //потом устанавливаем год
        if (i == 0)
                {
                disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                delay_ms (400);
                }
        if (i == 0)
                {
                disp_out (day/10, day%10, mnth/10, mnth%10, 10, 10, 0, 0, 1, 0, 1, 0);
                delay_ms (400);
                }
        while ((++i < 10) && !but1 && min_s)
                {
                year_s = 1;
                disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                delay_ms (400);
                disp_out (day/10, day%10, mnth/10, mnth%10, 10, 10, 0, 0, 1, 0, 1, 0);
                delay_ms (400);
                while (but4) 
                        {
                        if (++year > 99) year = 0;
                        disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                        delay_ms (400);
                        i = 0;
                        }
                while (but3) 
                        {
                        if (--year < 0) year = 99;
                        disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                        delay_ms (400);
                        i = 0;
                        }
                }        
        
        if (but1)
                {
                delay_ms(10);
                WriteRTC (year, year_addr);
                i = 0;
                sound(50);
                while (but1);
                }
                
        day = ReadRTC (day_addr);                                               //потом месяц
        mnth = ReadRTC (mnth_addr);
        if (i == 0)
                {
                disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                delay_ms (400);
                }
        if (i == 0)
                {
                disp_out (day/10, day%10, 10, 10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                delay_ms (400);
                }        
        while ((++i < 10) && !but1 && year_s)
                {
                mnth_s = 1;
                disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                delay_ms (400);
                disp_out (day/10, day%10, 10, 10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                delay_ms (400);
                while (but4) 
                        {
                        if (++mnth > 12) mnth = 1;
                        disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                        delay_ms (400);
                        i = 0;
                        }
                while (but3) 
                        {
                        if (--mnth < 1) mnth = 12;
                        disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                        delay_ms (400);
                        i = 0;
                        }
                }
        
        if (but1)
                {
                delay_ms(10);
                WriteRTC (mnth, mnth_addr);
                i = 0;
                sound(50);
                while (but1);
                }
        
        day = ReadRTC (day_addr);                                               //исходя из установленных месяца и года
        mnth = ReadRTC (mnth_addr);                                             //устанавливаем число
        year = ReadRTC (year_addr);                                             //учитываю возможное количество дней в месяце
        if (i == 0)
                {
                disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                delay_ms (400);
                }
        if (i == 0)
                {
                disp_out (10, 10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                delay_ms (400);
                }        
        while ((++i < 10) && !but1 && mnth_s)
                {
                day_s = 1;
                disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                delay_ms (400);
                disp_out (10, 10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                delay_ms (400);
                while (but4) 
                        {
                        ++day;
                        if ((day > 28) && (mnth == 2) &&  (year % 4)) day = 1;                                          //для февраля
                        if ((day > 29) && (mnth == 2) &&  !(year % 4)) day = 1;                                         //для февраля високосного года
                        if ((day > 30) && ((mnth == 4) || (mnth == 6) || (mnth == 9) || (mnth == 11))) day = 1;         //для месяцев с 30 днями
                        if (day > 31) day = 1;                                                                          //для месяцев с 31 днем
                        disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                        delay_ms (400);
                        }
                while (but3) 
                        {
                        if (--day < 1)
                                {
                                day = 31;
                                if ((mnth == 4) || (mnth == 6) || (mnth == 9) || (mnth == 11)) day = 30;
                                if ((mnth == 2) &&  !(year % 4)) day = 29;
                                if ((mnth == 2) &&  (year % 4)) day = 28;
                                }
                        disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0);
                        delay_ms (400);
                        }
                }
        
        if (but1)
                {
                delay_ms(10);
                WriteRTC (day, day_addr);
                i = 0;
                sound(50);
                while (but1);
                }
        start_autoshow;                
        }
        
void Alarm_setup (void)         //процедура установки времени будильника - вцелом, аналогично установке времени
        {
        unsigned char i = 0;
        bit a_hrs_s = 0;
        bit a_min_s = 0;
        
        stop_autoshow;
        reset_flag ();
        
        a_hrs = ReadRTC (a_hrs_addr);                                                                   //прочитали текущее время из RTC
        a_min = ReadRTC (a_min_addr);                                               
        disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0);                    //мигнули временем один раз
        delay_ms (400);
        disp_out (10, 10, 10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0);
        delay_ms (400);
        while ((++i < 10) && !but1)                                                                     //пока не мигнули 10 раз или не нажали кнопку 1
                {
                a_hrs_s = 1;                                                                            //поставили флаг, что пытались устанавливать часы
                disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0);            //мигаем временем
                delay_ms (400);
                disp_out (10, 10, 10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0);
                delay_ms (400);
                while (but4)                                                                            //если удерживаем кнопку 4
                        {
                        if (++a_hrs > 23) a_hrs = 0;                                                    //увеличиваем счетчик часов
                        disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0);    //раз в 200 мс
                        delay_ms (400);
                        i = 0;                                                                          //и обнуляем счетчик миганий
                        }
                while (but3)                                                                            //если удерживаем кнопку 3
                        {                                                                               //уменьшаем счетчик часов
                        if (--a_hrs < 0) a_hrs = 23;                                                    //раз в 200 мс
                        disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0);    //и обнуляем счетчик миганий
                        delay_ms (400);
                        i = 0;
                        }
                
                }
        
        if (but1)                                       //если нажали на кнопку 1
                {
                delay_ms(10);
                WriteRTC (a_hrs, a_hrs_addr);           //записали установленное значение часов в RTC
                i = 0;                   
                sound(50);                              //пискнули
                while (but1);                           //ждем, пока отпустим кнопку 
                }
                
        a_hrs = ReadRTC (a_hrs_addr);                   //аналогично для установки минут
        a_min = ReadRTC (a_min_addr);
        if (i == 0) 
                {
                disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0);
                delay_ms (400);
                }
        if (i == 0) 
                {
                disp_out (10, a_hrs/10, a_hrs%10, 10, 10, 10, 0, 0, 0, 1, 0, 0);
                delay_ms (400);
                }        
        while ((++i < 10) && !but1 && a_hrs_s)
                {
                a_min_s = 1;
                disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0);
                delay_ms (400);
                disp_out (10, a_hrs/10, a_hrs%10, 10, 10, 10, 0, 0, 0, 1, 0, 0);
                delay_ms (400);
                while (but4) 
                        {
                        if (++a_min > 59) a_min = 0;
                        disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0);
                        delay_ms (400);
                        i = 0;
                        }
                while (but3) 
                        {
                        if (--a_min < 0) a_min = 59;
                        disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0);
                        delay_ms (400);
                        i = 0;
                        }
                }
                
        if (but1)
                {
                delay_ms(10);
                WriteRTC (a_min, a_min_addr);
                i = 0;
                sound(50);
                while (but1);
                }                
        start_autoshow;
        }
        
void Alarm_OnOff (void)                 //процедура включения/выключения будильника
        {
        reset_flag ();
        alarm_flag = !alarm_flag;       //проинвертированли флаг включения будильника
        
        writeEEPROM (2, alarm_flag);    //записали в ЕЕПРОМ состояние флага
        
        if (alarm_flag)                 //если будильник включен - включили его в RTC
                {
                flag = ReadRTC (0x0F);
                WriteRTC (bcd2bin (0b10000000), a_mnth_addr);
                flag = ReadRTC (0x0F);
                }
                else                    //если выключен - выключили
                        {
                        flag = ReadRTC (0x0F);
                        WriteRTC (0, a_mnth_addr);
                        flag = ReadRTC (0x0F);
                        }
        }
        
void Sys_setup (void)
        {
        unsigned char i = 0;
        bit mode_s = 0;
        bit date_s = 0;
        bit data_s = 0;
        
        stop_autoshow;
        reset_flag ();
        
        disp_out (10, 10, 10, 10, clock_mode, 10, 0, 0, 0, 0, 0, 0);                            //устанавливаем формат показа времени                   
        delay_ms (400);
        disp_out (10, 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0);
        delay_ms (400);
        while ((++i < 10) && !but1)                                                                     
                {
                mode_s = 1;                                                                            
                disp_out (10, 10, 10, 10, clock_mode, 10, 0, 0, 0, 0, 0, 0);                    
                delay_ms (400);
                disp_out (10, 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0);
                delay_ms (400);
                while (but4)                                                                            
                        {
                        if (++clock_mode > 2) clock_mode = 1;                                                    
                        disp_out (10, 10, 10, 10, clock_mode, 10, 0, 0, 0, 0, 0, 0);                    
                        delay_ms (400);
                        i = 0;                                                                          
                        }
                while (but3)                                                                            
                        {                                                                               
                        if (--clock_mode < 1) clock_mode = 2;                                                    
                        disp_out (10, 10, 10, 10, clock_mode, 10, 0, 0, 0, 0, 0, 0);                    
                        delay_ms (400);
                        i = 0;
                        }
                }
                
        if (but1)
                {
                delay_ms(10);
                writeEEPROM (3, clock_mode); 
                i = 0;
                sound(50);
                while (but1);
                }
                
        if (i == 0)                                                                        //длительность показа даты
                {
                disp_out (10, 10, 10, 10, date_show_ms/10, date_show_ms%10, 0, 0, 0, 0, 0, 1);
                delay_ms (400);
                }
        if (i == 0) 
                {
                disp_out (10, 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0);
                delay_ms (400);
                }
        while ((++i < 10) && !but1 && mode_s)                                                                     
                {
                date_s = 1;                                                                            
                disp_out (10, 10, 10, 10, date_show_ms/10, date_show_ms%10, 0, 0, 0, 0, 0, 1);
                delay_ms (400);
                disp_out (10, 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0);
                delay_ms (400);
                while (but4)                                                                            
                        {
                        date_show_ms = date_show_ms + 2;
                        if (date_show_ms > 98) date_show_ms = 2;                                                    
                        disp_out (10, 10, 10, 10, date_show_ms/10, date_show_ms%10, 0, 0, 0, 0, 0, 1);
                        delay_ms (400);
                        i = 0;                                                                          
                        }
                while (but3)                                                                            
                        {                                                                               
                        date_show_ms = date_show_ms - 2;
                        if (date_show_ms < 2) date_show_ms = 98;                                                    
                        disp_out (10, 10, 10, 10, date_show_ms/10, date_show_ms%10, 0, 0, 0, 0, 0, 1);
                        delay_ms (400);
                        i = 0;
                        }
                }
        if (but1)
                {
                delay_ms(10);
                writeEEPROM (4, date_show_ms); 
                i = 0;
                sound(50);
                while (but1);
                }
                
        if (i == 0)                                                             //длительность показа данных
                {
                disp_out (10, 10, 10, 10, data_show_ms/10, data_show_ms%10, 0, 0, 0, 0, 0, 1);
                delay_ms (400);
                }
        if (i == 0) 
                {
                disp_out (10, 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0);
                delay_ms (400);
                }
        while ((++i < 10) && !but1 && date_s)                                                                     
                {
                data_s = 1;                                                                            
                disp_out (10, 10, 10, 10, data_show_ms/10, data_show_ms%10, 0, 0, 0, 0, 0, 1);
                delay_ms (400);
                disp_out (10, 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0);
                delay_ms (400);
                while (but4)                                                                            
                        {
                        data_show_ms = data_show_ms + 2;
                        if (data_show_ms > 98) data_show_ms = 2;                                                    
                        disp_out (10, 10, 10, 10, data_show_ms/10, data_show_ms%10, 0, 0, 0, 0, 0, 1);
                        delay_ms (400);
                        i = 0;                                                                          
                        }
                while (but3)                                                                            
                        {                                                                               
                        data_show_ms = data_show_ms - 2;
                        if (data_show_ms < 2) data_show_ms = 98;                                                    
                        disp_out (10, 10, 10, 10, data_show_ms/10, data_show_ms%10, 0, 0, 0, 0, 0, 1);
                        delay_ms (400);
                        i = 0;
                        }
                }
        if (but1)
                {
                delay_ms(10);
                writeEEPROM (5, data_show_ms); 
                i = 0;
                sound(50);
                while (but1);
                }        
                        
        start_autoshow;
        }

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port A initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=In Func2=In Func1=In Func0=In 
// State7=0 State6=0 State5=0 State4=1 State3=P State2=P State1=P State0=P 
PORTA=0x1F;
DDRA=0xF0;

// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=Out 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=0 
PORTB=0x00;
DDRB=0x01;

// Port C initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In 
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T 
PORTC=0x00;
DDRC=0x00;

// Port D initialization
// Func7=Out Func6=Out Func5=In Func4=In Func3=In Func2=In Func1=Out Func0=In 
// State7=0 State6=0 State5=T State4=T State3=T State2=T State1=0 State0=T 
PORTD=0x00;
DDRD=0xC2;

// Port E initialization
// Func2=In Func1=In Func0=In 
// State2=T State1=T State0=T 
PORTE=0x00;
DDRE=0x00;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 10,800 kHz
// Mode: Normal top=FFh
// OC0 output: Disconnected
TCCR0=0x05;
TCNT0=0x27;
OCR0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 10,800 kHz
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: On
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
//TCCR1B=0x05;
TCNT1H=0xD5;
TCNT1L=0xCF;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// External Interrupt(s) initialization
// INT0: On
// INT0 Mode: Falling Edge
// INT1: Off
// INT2: Off
GICR|=0x40;
MCUCR=0x02;
EMCUCR=0x00;
GIFR=0x40;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x82;

// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud rate: 9600
UCSRA=0x00;
UCSRB=0x98;
UCSRC=0x86;
UBRRH=0x00;
//UBRRL=0x47;
UBRRL=0x05;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;

// Watchdog Timer initialization
// Watchdog Timer Prescaler: OSC/2048k
#pragma optsize-
WDTCR=0x1F;
WDTCR=0x0F;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

if (readEEPROM (2) != 0) alarm_flag = 1;        //прочитали из ЕЕПРОМ флаг активности будильника
clock_mode = readEEPROM (3);                    //режим показа
date_show_ms = readEEPROM (4);                  //время показа даты
data_show_ms = readEEPROM (5);                  //время показа данных

if (clock_mode > 2) clock_mode = 1;
if (date_show_ms > 98) date_show_ms = 10;
if (data_show_ms > 98) data_show_ms = 10;

i2c_init_soft();                                //проинициировали i2c

WriteRTC (0, a_sec_addr);                       //проинициировали RTC
WriteRTC (bcd2bin (0b11000000), a_day_addr);
WriteRTC (0, 0x13);
WriteRTC (0, 0x09);
WriteRTC (bcd2bin (0b10000000), 0x08);
flag = ReadRTC (0x0F);

// Global enable interrupts
#asm("sei")

pen = 0;

while (1)
        {
        #asm("wdr")
        
        if (recieve_cmp)
                {
                #asm("cli")
                if (b_1 == start_cond && b_7 == stop_cond)
                        {
                        switch (b_2)
                                {
                                case set_time:    
                                                {
                                                if (b_3 < 24 && b_4 < 60)
                                                        {
                                                        WriteRTC (b_3, hrs_addr);
                                                        WriteRTC (b_4, min_addr);
                                                        putsf ("OK ");
                                                        putsf ("current time: "); putchar (ReadRTC (hrs_addr)/10+48); putchar (ReadRTC (hrs_addr)%10+48); putchar (':'); putchar (ReadRTC (min_addr)/10+48); putchar (ReadRTC (min_addr)%10+48); putchar (':'); putchar (ReadRTC (sec_addr)/10+48); putchar (ReadRTC (sec_addr)%10+48);
                                                        sound (30);
                                                        }
                                                        else Error ();
                                                }; break;
                                                
                                case set_date:    
                                                {
                                                if (b_5 > 99 || b_5 < 7 || b_4 < 1 || b_4 > 12 ||
                                                        ((b_3 > 28) && (b_4 == 2) &&  (b_5 % 4)) ||
                                                        ((b_3 > 29) && (b_4 == 2) &&  !(b_5 % 4)) ||
                                                        ((b_3 > 30) && ((b_4 == 4) || (b_4 == 6) || (b_4 == 9) || (b_4 == 11))) || b_3 > 31) Error ();
                                                        else
                                                                {
                                                                WriteRTC (b_3, day_addr);
                                                                WriteRTC (b_4, mnth_addr);
                                                                WriteRTC (b_5, year_addr);
                                                                putsf ("OK ");
                                                                putsf ("current date: "); putchar (ReadRTC (day_addr)/10+48); putchar (ReadRTC (day_addr)%10+48); putchar (':'); putchar (ReadRTC (mnth_addr)/10+48); putchar (ReadRTC (mnth_addr)%10+48); putchar (':'); putchar (ReadRTC (year_addr)/10+48); putchar (ReadRTC (year_addr)%10+48);
                                                                sound (30);
                                                                }
                                                }; break;
                                
                                case set_alarm:   
                                                {
                                                if (b_3 < 24 && b_4 < 60)
                                                        {
                                                        WriteRTC (b_3, a_hrs_addr);
                                                        WriteRTC (b_4, a_min_addr);
                                                        putsf ("OK ");
                                                        putsf ("alarm time: "); putchar (ReadRTC (a_hrs_addr)/10+48); putchar (ReadRTC (a_hrs_addr)%10+48); putchar (':'); putchar (ReadRTC (a_min_addr)/10+48); putchar (ReadRTC (a_min_addr)%10+48);
                                                        putsf (" current alarm status: ");
                                                               if (alarm_flag) putsf ("on");                                 
                                                                       else putsf ("off");
                                                        sound (30);
                                                        }
                                                        else Error ();;
                                                }; break;
                                                  
                                case set_clock_mode:
                                                {
                                                if (b_3 < 1 || b_3 > 2) Error ();
                                                        else
                                                                {
                                                                clock_mode = b_3;
                                                                writeEEPROM (3, clock_mode);
                                                                if (clock_mode == 1) putsf ("OK HH.MM.SS");
                                                                        else putsf ("OK HH.MM");
                                                                sound (30);
                                                                }
                                                }; break;
                                                
                                case set_date_sht:    
                                                {
                                                if (b_3 < 2 || b_3 > 98) Error ();
                                                        else
                                                                {
                                                                date_show_ms = b_3;
                                                                writeEEPROM (3, date_show_ms);
                                                                putsf ("OK "); putchar (date_show_ms/10+48); putchar ('.');  putchar (date_show_ms%10+48); putsf ("sec");
                                                                sound (30);
                                                                }
                                                }; break;   
                                
                                case set_data_sht:    
                                                {
                                                if (b_3 < 2 || b_3 > 98) Error ();
                                                        else
                                                                {
                                                                data_show_ms = b_3;
                                                                writeEEPROM (4, data_show_ms);
                                                                putsf ("OK "); putchar (data_show_ms/10+48); putchar ('.');  putchar (data_show_ms%10+48); putsf ("sec");
                                                                sound (30);
                                                                }
                                                }; break;                
                                
                                case alarm_on_off:    
                                                {
                                                if (b_3 > 1) Error ();
                                                        else
                                                                {
                                                                alarm_flag = b_3;
                                                                writeEEPROM (1, alarm_flag);    //записали в ЕЕПРОМ состояние флага
        
                                                                if (alarm_flag)                 //если будильник включен - включили его в RTC
                                                                        {
                                                                        flag = ReadRTC (0x0F);
                                                                        WriteRTC (bcd2bin (0b10000000), a_mnth_addr);
                                                                        flag = ReadRTC (0x0F);
                                                                        }
                                                                        else                    //если выключен - выключили
                                                                                {
                                                                                flag = ReadRTC (0x0F);
                                                                                WriteRTC (0, a_mnth_addr);
                                                                                flag = ReadRTC (0x0F);
                                                                                }
                                                               putsf ("current alarm status: ");
                                                               if (alarm_flag) {putsf ("on at "); putchar (ReadRTC (a_hrs_addr)/10+48); putchar (ReadRTC (a_hrs_addr)%10+48); putchar (':'); putchar (ReadRTC (a_min_addr)/10+48); putchar (ReadRTC (a_min_addr)%10+48);}                                 
                                                                       else putsf ("off");
                                                               sound (30);
                                                               } 
                                                }; break;                                
                                
                                case show_sec:    
                                                {
                                                if (b_3 > 1) Error ();
                                                        else
                                                               {
                                                               sec_mode = b_3;
                                                               putsf ("currently ");
                                                               if (sec_mode) putsf ("show sec on");                                 
                                                                       else putsf ("show sec off");
                                                               }
                                                }; break; 
                                
                                case show_data:    
                                                {
                                                if (b_6 > 63 || b_5 > 99 || b_4 > 99 || b_3 > 99) Error ();
                                                        else
                                                               {
                                                               first_b = b_3;
                                                               second_b = b_4;
                                                               third_b = b_5;
                                                               point_b = b_6;
                                                               data_mode = 1;
                                                               data_cnt = 0;
                                                               putsf ("OK");
                                                               }
                                                }; break;
                                
                                case get_time:    
                                                {
                                                if (b_6 != 0 || b_5 != 0 || b_4 != 0 || b_3 != 0) Error ();
                                                        else {putsf ("current time: "); putchar (ReadRTC (hrs_addr)/10+48); putchar (ReadRTC (hrs_addr)%10+48); putchar (':'); putchar (ReadRTC (min_addr)/10+48); putchar (ReadRTC (min_addr)%10+48); putchar (':'); putchar (ReadRTC (sec_addr)/10+48); putchar (ReadRTC (sec_addr)%10+48);}
                                                }; break;

                                case get_date:    
                                                {
                                                if (b_6 != 0 || b_5 != 0 || b_4 != 0 || b_3 != 0) Error ();
                                                        else {putsf ("current date: "); putchar (ReadRTC (day_addr)/10+48); putchar (ReadRTC (day_addr)%10+48); putchar (':'); putchar (ReadRTC (mnth_addr)/10+48); putchar (ReadRTC (mnth_addr)%10+48); putchar (':'); putchar (ReadRTC (year_addr)/10+48); putchar (ReadRTC (year_addr)%10+48);}
                                                }; break;
                                                
                                case show_date:    
                                                {
                                                if (b_6 != 0 || b_5 != 0 || b_4 != 0 || b_3 != 0) Error ();
                                                        else
                                                               {
                                                               date_mode = 1;
                                                               date_cnt = 0;
                                                               putsf ("OK");
                                                               }
                                                }; break;
                                                
                                case power_en:    
                                                {
                                                if (b_6 != 0 || b_5 != 0 || b_4 != 0 || b_3 != 0) Error ();
                                                        else
                                                               {
                                                               Power_en ();
                                                               putsf ("power ");
                                                               if (!pen) putsf ("on");
                                                                       else putsf ("off");         
                                                               }
                                                }; break;
                                
                                default: Error ();
                                } 
                        }
                        else 
                                {
                                Error ();
                                sound (30);
                                delay_ms (30);
                                sound (30);
                                delay_ms (30);
                                sound (30);
                                delay_ms (30);
                                }
                rx_counter=0;
                rx_wr_index=0;
                rx_rd_index=0;
                recieve_cmp = 0;
                #asm("sei")
                }
        
        if (but1)                                               //если нажали кнопку 1
                {
                delay_ms(10);
                if (but1) 
                        {
                        TCCR1B=0x05;                            //запустили таймер 1
                        while (but1) #asm("wdr");                           //ждем, пока отпустим кнопку
                        switch (flag_1)
                                {
                                case 0: break;
                                case 1: Alarm_setup (); break;
                                case 2: Time_setup ();  break;
                                case 3: Sys_setup ();
                                }
                        reset_flag ();
                        }
                }
                
        if (but2)                                               //если нажали кнопку 2
                {
                delay_ms(10);
                if (but2) 
                        {
                        TCCR1B=0x05;                            //запустили таймер 1
                        while (but2) #asm("wdr");                           //ждем, пока отпустим кнопку
                        switch (flag_2)
                                {
                                case 0: 
                                        {
                                        date_mode = 1;                          //включили показ даты
                                        date_cnt = 0;                           //обнулили счетчик показов
                                        } break;
                                case 1: break;
                                case 2: break;
                                case 3: 
                                }
                        reset_flag ();
                        }
                }
                
        if (but3)                                               //если нажали кнопку 3
                {
                delay_ms(10);
                if (but3) 
                        {
                        TCCR1B=0x05;                            //запустили таймер 1
                        while (but3) #asm("wdr");                           //ждем, пока отпустим кнопку
                        switch (flag_3)
                                {
                                case 0: sec_mode = ~sec_mode; break;
                                case 1: Power_en (); break;
                                case 2: break;
                                case 3: 
                                }
                        reset_flag ();
                        }
                }                        
        
        if (but4)                                               //если нажали кнопку 4
                {
                delay_ms(10);
                if (but4) 
                        {
                        TCCR1B=0x05;                            //запустили таймер 1
                        while (but4) #asm("wdr");                           //ждем, пока отпустим кнопку
                        switch (flag_4)
                                {
                                case 0: break;
                                case 1: Alarm_OnOff (); break;
                                case 2: break;
                                case 3:
                                }
                        reset_flag ();
                        }
                }
        };
}



// EEPROM Data Read Function - чтение байта из EEPROM по заданному адресу
unsigned int readEEPROM (unsigned int addr)
        {
        unsigned int data = 0;
        while (EECR.1 != 0);        
        EEAR = addr;      
        EECR.0 = 1;
        EECR.0 = 0;
        data = EEDR;
        return data;
        }

// EEPROM Data Write Function - запись байта data в EEPROM по заданному адресу
void writeEEPROM (unsigned int addr, unsigned char data)
        {
        while (EECR.1 != 0);
        EEAR = addr;
        EEDR = data;
        EECR.2 = 1;
        EECR.1 = 1;
        }

// EEPROM Data Copy Function - копирование данных из ячейки в ячейку
void copyEEPROM (unsigned int saddr, unsigned char taddr)
        {
        writeEEPROM (taddr, readEEPROM (saddr));
        }
        
// EEPROM 2-byte Data Write Function - запись двухбайтового числа в 2 последовательные ячейки с адресом первой addr 
void _2bwriteEEPROM (unsigned int addr, unsigned int data)
        {
        writeEEPROM (addr, (unsigned char)(data / 256));
        writeEEPROM (addr + 1, (unsigned char)(data % 256));
        }
        
// EEPROM 2-byte Data Read Function - чтение двухбайтового числа из 2 последовательных ячеек с адресом первой addr
unsigned int _2breadEEPROM (unsigned int addr)
        {
        unsigned int data = 0;
        data = 256 * readEEPROM(addr) + readEEPROM(addr + 1);
        return data;
        }


#define sdata           PORTA.5                               
#define strobe          PORTA.7                               
#define shift           PORTA.6                               

unsigned char sign (char digit)        //индикатор
        {
        unsigned char temp = 0;
        
        switch (digit)
                {
                case 1 :  temp = 0b00000001; break;
                case 2 :  temp = 0b00000100; break;
                case 3 :  temp = 0b00000101; break;
                case 4 :  temp = 0b00001000; break;
                case 5 :  temp = 0b00001001; break;
                case 6 :  temp = 0b00001100; break;
                case 7 :  temp = 0b00001101; break;
                case 8 :  temp = 0b00000010; break;
                case 9 :  temp = 0b00000011; break;
                case 0 :  temp = 0b00000000; break;
                default:  temp = 0b00001111; 
                }
       return temp;
       } 

void disp_out (char dig1, char dig2, char dig3, char dig4, char dig5, char dig6, char p1, char p2, char p3, char p4, char p5, char p6)
        {
        unsigned long int show = 0;
        unsigned char i = 0;
        
        show = show + sign (dig6);
        show = (show << 4) + sign (dig5);
        show = (show << 4) + sign (dig4);
        show = (show << 4) + sign (dig3);
        show = (show << 4) + sign (dig2);
        show = (show << 4) + sign (dig1);
        show = (show << 8);
        if (p1) show = show | 0x02;
        if (p2) show = show | 0x04;
        if (p3) show = show | 0x08;
        if (p4) show = show | 0x10;
        if (p5) show = show | 0x20;
        if (p6) show = show | 0x40;
        
        for (i = 0; i < 32; i++)
                {
                if ((show & 1)) sdata = 1;
                        else sdata = 0;
                shift = 1;
                shift = 0;
                show = show >> 1;
                };
        strobe = 1;
        strobe = 0;
        }
        
#define SDA_LN          7                               //линия SDA
#define SCL_LN          6                               //линия SCL
#define I2C_PIN         PIND                            //порт входа
#define I2C_DDR         DDRD                            //порт направления
#define I2C_PORT        PORTD                           //порт выхода

#define IN_LN           0                               //линия на вход
#define OUT_LN          1                               //линия на выход

#define I2C_T           0.00001                         //период импульса синхронизации (1/I2C_T = частота шины)
                                                        //при программной реализации шины

unsigned char i2c_error_soft;                           //если >0, то произошла ошибка при работе с I2C

//Прототипы функций

void i2c_init_soft(void);
void i2c_start_soft(void);
void i2c_stop_soft(void);
void i2c_tx_soft(unsigned char byte);
unsigned char i2c_rx_soft(unsigned char last_byte);
unsigned char in_sda(void);
void sda_io(unsigned char io_c);
void scl_set(unsigned char set_c);
void sda_set(unsigned char set_c);

//Инициализация программной шины I2C
void i2c_init_soft(void)
{
  I2C_DDR&=~(1<<SDA_LN);                              //изначально линии SDA
  I2C_DDR&=~(1<<SCL_LN);                              //и SCL в высокоимпедансном состоянии
  I2C_PORT&=~(1<<SDA_LN);                             //и на них поддерживается за счет внешних резисторов
  I2C_PORT&=~(1<<SCL_LN);                             //высокий уровень
  i2c_error_soft=0;                                   //изначально ошибок нет :-)
}

//Возвращает уровень линии SDA
unsigned char in_sda(void)
{
  if(I2C_PIN&(1<<SDA_LN))
    return 1;
  else
    return 0;
}

//Устанавливает линию SDA на вход или выход
void sda_io(unsigned char io_c)
{
  if(io_c==IN_LN)
    I2C_DDR&=~(1<<SDA_LN);
  else
    I2C_DDR|=(1<<SDA_LN);
  delay_us(10);
}

//Устанавливает уровень на линии SCL
void scl_set(unsigned char set_c)
{
  if(set_c)
    {
      I2C_DDR&=~(1<<SCL_LN);
      I2C_PORT&=~(1<<SCL_LN);
    }
  else
    {
      I2C_DDR|=(1<<SCL_LN);
      I2C_PORT&=~(1<<SCL_LN);
    }
  delay_us(10);
}

//Устанавливает уровень на линии SDA
void sda_set(unsigned char set_c)
{
  if(set_c)
    {
      I2C_DDR&=~(1<<SDA_LN);
      I2C_PORT&=~(1<<SDA_LN);
    }
  else
    {
      I2C_DDR|=(1<<SDA_LN);
      I2C_PORT&=~(1<<SDA_LN);
    }
  delay_us(10);
}

//Формирует условие "СТАРТ"
void i2c_start_soft(void)
{
  if(i2c_error_soft)
    return;
  scl_set(1);
  sda_set(0);
  scl_set(0);
}

//Формирует условие "СТОП"
void i2c_stop_soft(void)
{
  sda_set(0);
  scl_set(1);
  sda_set(1);
  if(i2c_error_soft)
    i2c_init_soft();
}

//Посылка байта
void i2c_tx_soft(unsigned char byte)
{
  unsigned char i=0;
  
  if(i2c_error_soft)
    return;
  for(i=0;i<8;i++)
    {
      if(byte&0x80)
        sda_set(1);
      else
        sda_set(0);
      scl_set(1);
      scl_set(0);
      byte<<=1;
    }
  sda_io(IN_LN);
  scl_set(1);
  i2c_error_soft=in_sda();
  scl_set(0);
  sda_io(OUT_LN);
}


//Прием байта, если last_byte=0, то принимаем последний байт и подтверждение от мастера не нужно
unsigned char i2c_rx_soft(unsigned char last_byte)
{
  unsigned char data=0;
  unsigned char mask=0x80;
  unsigned char i=0;
  if(i2c_error_soft)
    return 0;
  sda_io(IN_LN);
  for(i=0;i<8;i++)
    {
      scl_set(1);
      if(in_sda())
        data=data+mask;
      mask>>=1;
      scl_set(0);
    }
  sda_io(OUT_LN);
  if(last_byte)
    sda_set(0);
  else
    sda_set(1);
  scl_set(1);
  scl_set(0);
  return data;
}



 

 #include <I2C.c> #include <disp_out.c> #define snd PORTB.0 //нога пищалки #define pen PORTA.4 //нога power enable высоковольтного источника #define RTC 0b11010000 //I2C-адрес часов #define sec_addr 0x01 //адреса регистров микросхемы RTC #define min_addr 0x02 #define hrs_addr 0x03 #define day_addr 0x05 #define mnth_addr 0x06 #define year_addr 0x07 #define a_sec_addr 0x0E #define a_min_addr 0x0D #define a_hrs_addr 0x0C #define a_day_addr 0x0B #de
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include "ds18x20.h"
#include "onewire.h"
#include "DS1307.h"

#include "i2c.h"

#define DELAY_TIME   120 // интервал индикации времени в миллисекундах
#define DELAY_OTHER  10  // интервал индикации даты и температуры в миллисекундах

#define LED1_ON PORTD |= _BV(PD4) // подсветка ИН-8
//#define LED2_ON PORTD |= _BV(PD3) // подсветка ИН-17 //сделать кнопкой Алярм

//#define LED1_OFF PORTD &= ~_BV(PD4)
//#define LED2_OFF PORTD &= ~_BV(PD3)     

#define BUT_M PIND & (1 << PD0)  // кнопка MENU
#define BUT_U PIND & (1 << PD1)  // кнопка UP
#define BUT_D PIND & (1 << PD2)  // кнопка DOWN
#define BUT_A PIND & (1 << PD3)  // кнопка Alarm

#define SQW_IN PINB & (1 << PB0) // тактовый сигнал от DS1307

#define MAXSENSORS 2

const char digit[11] = {  // масив цифр для отправки на К155ИД1
		0b00000000,
		0b00100000,
		0b00010000,
		0b00110000,
		0b00001000,
		0b00101000,
		0b00011000,
		0b00111000,
		0b00000100,
		0b00100100,
		0b00111100
};

unsigned char eep1 EEMEM;
unsigned char eep2 EEMEM;
unsigned char eep3 EEMEM;

volatile char lev[7] = {255, 1, 1, 255, 255, 255, 255}, a = 0;
volatile uint8_t out[9] = {10, 10, 10, 10, 10, 10};

unsigned char H, M, S;
unsigned char d, m, y;

volatile char delay = 0, vid = 2, press = 0, pr = 0, led = 1, set = 0;

volatile uint8_t fr[6]; // какой разряд изменился
uint8_t tscr[6]; // временный экран
uint8_t iocr = 0, dig = 0;
char ms = 0, brig[2] = {1, 250};
const uint8_t psc[25]={1,21,42,63,84,105,126,147,168,189,210,231,245,231,210,189,168,147,126,105,84,63,42,21,1}; // ступени яркости день
const uint8_t psc2[25]={105,116,127,138,149,162,175,186,198,210,222,234,245,234,222,210,198,186,175,162,149,138,127,116,105}; // ступени яркости ночь

uint8_t Temperature = 255, szero = 1; //-255

uint8_t nSensors, j;
uint8_t cel_frac_bits;

uint8_t gSensorIDs[MAXSENSORS][OW_ROMCODE_SIZE];

uint8_t search_sensors(void) // поиск DS18B20
{
	uint8_t i;
	uint8_t id[OW_ROMCODE_SIZE];
	uint8_t diff, nSensors;

	nSensors = 0;

	for( diff = OW_SEARCH_FIRST;
		diff != OW_LAST_DEVICE && nSensors < MAXSENSORS ; )
	{
		DS18X20_find_sensor( &diff, &id[0] );

		if( diff == OW_PRESENCE_ERR ) {
			Temperature = 255;
			szero = 1;
			break;
		}
		if( diff == OW_DATA_ERR ) {
			Temperature = 255;
			szero = 1;
			break;
		}
		for (i=0; i<OW_ROMCODE_SIZE; i++)
			gSensorIDs[nSensors][i]=id[i];

		nSensors++;
	}

	return nSensors;
}

void soft(char D1, char D2, char D3) { // эффект плавной смены цифр, здесь идет поиск тех цифр, которые надо менять

 uint8_t i;

 if(set == 0){
	tscr[0] = D1 / 10; // запомнили новое время
	tscr[1] = D1 % 10;
	tscr[2] = D2 / 10;
	tscr[3] = D2 % 10;
	tscr[4] = D3 / 10;
	tscr[5] = D3 % 10;

		for (i = 0; (i < 6); i++) // сравнили с тем, что было
			if (tscr[i] != out[i]) // узнаём в каком разряде поменялась цифра
				fr[i] = 1; // цифра изменилась
			else
				fr[i] = 0; // цифра осталась
	iocr = 0;
 }else{
	 for (i = 0; (i < 6); i++) fr[i] = 0; // во время настройки часов эффект отключен
 }
}

void sprint(char D1, char D2, char D3) { // эффект "пробежки" цифр
	uint8_t i, j;

	tscr[0] = D1 / 10; // запомнили новую дату
	tscr[1] = D1 % 10;
	tscr[2] = D2 / 10;
	tscr[3] = D2 % 10;
	tscr[4] = D3 / 10;
	tscr[5] = D3 % 10;

		for (i = 0; i < 6; i++){ // сравниваем текущую цифру с новой
			if (tscr[i] > out[i]) fr[i] = 2; // новая больше
			if (tscr[i] < out[i]) fr[i] = 3; // новая меньше

			for (j = 0; j < 9; j++){ // делаем "пробежку"
				if(fr[i] == 2){if(out[i] != tscr[i])out[i]++;} // новая цифра больше, плюсуем
				if(fr[i] == 3){if(out[i] != tscr[i])out[i]--;} // новая цифра меньше, вычитаем
				if(out[i] == tscr[i])break; //  // новая цифра равна, возвращаемся в начало

				_delay_ms(70);
			}
		}
}

void shift_l(uint16_t T){ // эффект "сдвиг влево"
	uint8_t i;

	for(i = 5; i > 0; i--){ // сдвигаем влево текущие цифры
		out[0] = out[1];
		out[1] = out[2];
		out[2] = out[3];
		out[3] = out[4];
		out[4] = out[5];

		out[i] = 10;

		_delay_ms(100);
	}

	out[0] = 10;

	for(i = 0; i <= 3; i++){ // запоминаем температуру и двигаем ее на нужное место
		out[5-i] = T/100;
		out[6-i] = T/10%10;
		out[7-i] = T%10;
		out[8-i] = 10;

		_delay_ms(100);
	}

	if(szero == 1){
		lev[0] = brig[1];
		out[0] = 0;
	}
}

void shift_r(){ // эффект "сдвиг вправо"
	uint8_t i;

	if(out[0] == 0){ // сдвигаем вправо текущие цифры
	for(i = 0; i <= 5; i++){ // для отрицательной температуры

		out[5] = out[4];
		out[4] = out[3];
		out[3] = out[2];
		out[2] = out[1];
		out[1] = out[0];

		out[i] = 10;

		_delay_ms(100);
	}
	}else{
		for(i = 0; i <= 3; i++){// для положительной температуры

			out[5] = out[4];
			out[4] = out[3];
			out[3] = out[2];
			out[2] = out[1];

			out[i] = 10;

			_delay_ms(100);
		}
	}

	rtc_get_time(&H, &M, &S); // получаем время

	tscr[0] = H / 10; // запоминаем время
	tscr[1] = H % 10;
	tscr[2] = M / 10;
	tscr[3] = M % 10;
	tscr[4] = S / 10;
	tscr[5] = S % 10;

	for(i = 0; i <= 5; i++){ // двигаем время вправо

		out[5] = out[4];
		out[4] = out[3];
		out[3] = out[2];
		out[2] = out[1];
		out[1] = out[0];

		out[0] = tscr[5-i];

		_delay_ms(100);
	}
}

ISR (TIMER2_OVF_vect){ // реализация ШИМ управления яркостью ламп и подсветки
	uint8_t i;

	PORTC = 0x00;

	//LED1_OFF;
	//LED2_OFF;

	OCR2 = lev[dig]; // подготавливаемся к выводу следуйщего разряда

	if (iocr < 25){ // плавная смена яркости цифр
		if (++ms == 18) { // ~38ms
			ms = 0;
			for (i = 0; i < 6; i++) {
				if ((fr[i] == 1) || (iocr == 0)){
					if(brig[0] == 1)lev[i] = psc[iocr]; else lev[i] = psc2[iocr];
				}
				if (iocr == 12 && set == 0)
					out[i] = tscr[i];
			}
			iocr++;
		}
	}
}

ISR (TIMER2_COMP_vect){ // реализация ШИМ управления яркостью ламп и подсветки

	 if(dig < 6){
		 PORTB = digit[out[dig]]; // выводим цифру на дешифратор
		 PORTC = (1<<dig); // переключаем лампы для динамической индикации
	 }else{
		 if(dig == 6 && led == 1){LED1_ON; /*LED2_ON;*/} // яркость подсветки
	 }

	 if (++dig > 6) dig = 0;
}

void get_temp(int sensor){ // получаем температуру с датчиков

	lev[0] = brig[1]; // гастройка яркости вывода температуры, иначе будет выводить с той яркостью, до которой дошло при плавной смене цифр
	lev[1] = brig[1];
	lev[2] = brig[1];
	lev[3] = brig[1];
	lev[4] = brig[1];
	lev[5] = brig[1];

		DS18X20_start_meas(DS18X20_POWER_EXTERN, NULL);

		j = gSensorIDs[0][sensor]; // family-code for conversion-routine

		if (DS18X20_read_meas_single(j, &szero, &Temperature, &cel_frac_bits) != DS18X20_OK) {
			Temperature = 255;
			szero = 1;
		}

	shift_l(DS18X20_temp_to_decicel(szero, Temperature, cel_frac_bits));
}

void get_time(){ // получаем время с DS1307
	rtc_get_time(&H, &M, &S);

	soft(H, M, S); // вызываем плавную смену цифр
}

void get_date(){ // получаем дату с DS1307
	rtc_get_date(&d, &m, &y);

	lev[0] = brig[1];
	lev[1] = brig[1];
	lev[2] = brig[1];
	lev[3] = brig[1];
	lev[4] = brig[1];
	lev[5] = brig[1];

	sprint(d, m, y); // вызываем "пробежку" цифр
}

void display(){ // вывод данных на индикаторы
	switch(vid)
	{
	case 0: get_time(); delay = 0; break; // режим 1 - только время

	case 1:								  // режим 2 - время -> дата
		if(delay <= DELAY_TIME){get_time();}
		if(delay == (DELAY_TIME+1)){get_date();}
		if(delay >= (DELAY_TIME + DELAY_OTHER + 1)){rtc_get_time(&H, &M, &S); sprint(H, M, S); delay = 0;}
		break;

	case 2:								  // режим 3 - время -> температура
		if(delay <= DELAY_TIME){get_time();}

			if(nSensors == 0){delay = 0;}

				if(nSensors == 1){
					if(delay == (DELAY_TIME+1)){get_temp(0);}
					if(delay >= (DELAY_TIME + DELAY_OTHER + 1)){shift_r(); delay = 0; }
				}

					if(nSensors == 2){
						if(delay == (DELAY_TIME+1)){get_temp(0);}
						if(delay == (DELAY_TIME + DELAY_OTHER + 1)){get_temp(1);}
						if(delay >= (DELAY_TIME + (DELAY_OTHER*2) + 1)){shift_r(); delay = 0;}
					}
		break;

	case 3:								  // режим 3 - время -> дата -> температура
		if(delay <= DELAY_TIME){get_time();}
		if(delay == (DELAY_TIME+1)){get_date();}

			if(nSensors == 0){if(delay >= 131){rtc_get_time(&H, &M, &S); sprint(H, M, S); delay = 0;}}

				if(nSensors == 1){
					if(delay == (DELAY_TIME + DELAY_OTHER + 1)){get_temp(0);}
					if(delay >= (DELAY_TIME + (DELAY_OTHER*2) + 1)){shift_r(); delay = 0;}
				}

					if(nSensors == 2){
						if(delay == (DELAY_TIME + DELAY_OTHER + 1)){get_temp(0);}
						if(delay == (DELAY_TIME + (DELAY_OTHER*2) + 1)){get_temp(1);}
						if(delay >= (DELAY_TIME + (DELAY_OTHER*3) + 1)){shift_r(); delay = 0;}
					}
		break;
	}
}

void settings(){ // режим настройки часов

	if(press >= 1 && set <= 2){ // нужно для вывода данных во время настройки
		out[0] = H/10; out[1] = H%10;
		out[2] = M/10; out[3] = M%10;
		out[4] = S/10; out[5] = S%10;
	}
	if(press >= 1 && set > 2){
		out[0] = d/10; out[1] = d%10;
		out[2] = m/10; out[3] = m%10;
		out[4] = y/10; out[5] = y%10;
	}

	switch(set) // включена настройка
	{
	case 1: // настройка часов
		if(~BUT_U){H++; if(H > 23) H = 0; _delay_ms(500);}
		if(~BUT_D){H--; if((~BUT_D) && H == 0) H = 23; _delay_ms(500);}

		if((SQW_IN) || (~BUT_D) || (~BUT_U)){out[0] = H/10; out[1] = H%10; // мигаем часами
		}else{out[0] = 10; out[1] = 10;}
	break;

	case 2: // настройка минут
		if(~BUT_U){M++; if(M > 59) M = 0; _delay_ms(500);}
		if(~BUT_D){M--; if((~BUT_D) && M == 0) M = 59; _delay_ms(500);}

		if((SQW_IN) || (~BUT_D) || (~BUT_U)){out[2] = M/10; out[3] = M%10; // мигаем минутами
		}else{out[2] = 10; out[3] = 10;}
	break;

	case 3: // настройка дня
		if(~BUT_U){d++; if(d > 31) d = 1; _delay_ms(500);}
		if(~BUT_D){d--; if(d < 1) d = 31; _delay_ms(500);}

		if((SQW_IN) || (~BUT_D) || (~BUT_U)){out[0] = d/10; out[1] = d%10; // мигаем днем
		}else{out[0] = 10; out[1] = 10;}
	break;

	case 4: // настройка месяца
		if(~BUT_U){m++; if(m > 12) m = 1; _delay_ms(500);}
		if(~BUT_D){m--; if(m < 1) m = 12; _delay_ms(500);}

		if((SQW_IN) || (~BUT_D) || (~BUT_U)){out[2] = m/10; out[3] = m%10; // мигаем месяцем
		}else{out[2] = 10; out[3] = 10;}
	break;

	case 5: // настройка года
		if(~BUT_U){y++; _delay_ms(500);}
		if(~BUT_D){y--; _delay_ms(500);}

		if((SQW_IN) || (~BUT_D) || (~BUT_U)){out[4] = y/10; out[5] = y%10; // мигаем годом
		}else{out[4] = 10; out[5] = 10;}
	break;
	}
}

void buttons(){ // обработка нажатия кнопок

	if(~BUT_U){if(set == 0)pr++; // кнопка UP

		if(pr == 10){ // управление подсветкой
			if(led == 1) led = 0; else led = 1;

			eeprom_write_byte(&eep1, led); // сохранение настройки в eeprom
		}

	_delay_ms(500);

	}else{
		if(pr >= 1 && pr < 10){ // вызов даты на 2 сек.
			set = 10;
			get_date();
			_delay_ms(2000);
			set = 0;

			pr = 0;
		}

		pr = 0;
	}

	if((~BUT_D) && set == 0){ // кнопка DOWN
		set = 10;

		if(nSensors)get_temp(0); //вызов температуры на 2 сек.
		_delay_ms(2000);

		if(nSensors == 2){ // если есть 2 датчик
			get_temp(1);
			_delay_ms(2000);
		}

		set = 0;
	}

	if(~BUT_M){press++;  // кнопка MENU

		if(set == 2) rtc_set_time(H, M, 0); // если включена настройка времени, отправляем значение в DS1307

		if(press == 1 && set != 0){set++;} // переход по настройкам
		if(press == 10 && set == 0){set = 1; led = 0; _delay_ms(500); led = 1;} // ход в настройки

		if(set > 5){led = 0; _delay_ms(500); led = 1; rtc_set_date(d, m, y); set = 0; press = 0;} // если включена настройка даты,
		//отправляем значение в DS1307 и выходим из настроек

		_delay_ms(500);
	}else{
		if(set == 0 && press >= 1){ // если не вошли в настройки, переключаем режим отображения

			vid++; delay = 0;

			if(vid > 3)vid = 0;

			set = 10;

			out[0] = 10; out[1] = 0; out[2] = vid+1; // индикация текущего режима отображения
			out[3] = 10; out[4] = 10; out[5] = 10;

			_delay_ms(500);

			set = 0;

			eeprom_write_byte(&eep2, vid); // записываем режим отображения в eeprom
		}

		press = 0;
	}
}

int main(){
	/**********************************настройка портов******************************/
	DDRC |= _BV(DDC0) | _BV(DDC1) | _BV(DDC2) | _BV(DDC3) | _BV(DDC4) | _BV(DDC5);

	DDRB |= _BV(DDB2) | _BV(DDB3) | _BV(DDB4) | _BV(DDB5);

	DDRD &= ~_BV(DDD3) | _BV(DDD4);
	//PORTD &= ~_BV(PD3);
	//PORTD |= _BV(PD4);
	/********************************************************************************/

	/**********************************настройка таймера*****************************/
	TCCR2 |= (1 << CS20) | (1 << CS21);
	TIMSK |= _BV(OCIE2) | _BV(TOIE2);
	/********************************************************************************/

	/*******************************настройка переферии******************************/
	i2c_init(); // иництализация протокола i2c

	rtc_init(0, 1, 1); // иництализация DS1307

	ow_set_bus(&PIND, &PORTD, &DDRD, PD5); // иництализация протокола 1-wire

	nSensors = search_sensors(); // поиск датчиков DS18B20

	if(nSensors){DS18X20_start_meas(DS18X20_POWER_EXTERN, NULL);} // если есть датчики, включаем преобразование температуры

	_delay_ms(100);
	/********************************************************************************/

	if(eeprom_read_byte(&eep3) != 1){ // читаем eeprom, если там мусор (первый запуск), пишем свои данные
		eeprom_write_byte(&eep1, 1);
		eeprom_write_byte(&eep2, 3);
		eeprom_write_byte(&eep3, 1);
	}

	led = eeprom_read_byte(&eep1); // читаем значение подсветки из eeprom
	vid = eeprom_read_byte(&eep2); // читаем режим отображения из eeprom

	sei();

	set = 10;

	out[0] = 10; out[1] = 0; out[2] = vid+1; // ндикация текущего режима отображения при включении
	out[3] = 10; out[4] = 10; out[5] = 10;

	_delay_ms(2000);

	set = 0;

	rtc_get_date(&d, &m, &y); //чтение даты для нормально работы режимов отображения

	while(1){

		if(set != 0) settings();

		buttons();

		if((SQW_IN) && set == 0){ // когда приходит сигнал с частотой 1ГЦ. с DS1307 запукаем функцию вывода данных
			if(a < 1){a++; delay++;

				if(H < 7){ // установка уровня яркости
					brig[0] = 0; brig[1] = 105; lev[6] = 200; // ночь
				}else{
					brig[0] = 1; brig[1] = 10; lev[6] = 10; // день
				}

			display();

			}
		}else{
			a = 0;
		}
	}

	return 0;
}

 

fine a_mnth_addr 0x0A #define a_flag 0x0F //команды через RS-232 #define start_cond 0x80 #define set_time 0x81 #define set_date 0x82 #define set_alarm 0x83 #define set_clock_mode 0x84 #define set_date_sht 0x85 #define set_data_sht 0x86 #define alarm_on_off 0x87 #define show_sec 0x88 #define show_data 0x89 #define get_time 0x8A #define get_date 0x8B #define show_date 0x8C #define power_en 0x8D #define reserved 0x8E #define stop_cond 0x8F #define but1 !PINA.3 //кнопки #define but2 !PINA.2 #define but3 !PINA.1 #define but4 !PINA.0 #define start_autoshow TCCR0=0x05 //запустить таймер для обновления показаний по прерыванию  #de
// fading transitions sketch for 6-tube IN-17 board with default connections.
// based on 6-tube sketch by Emblazed
// 
// 02/27/2013 - modded for six bulb board, updated flicker fix by Brad L

// SN74141 : Truth Table
//D C B A #
//L,L,L,L 0
//L,L,L,H 1
//L,L,H,L 2
//L,L,H,H 3
//L,H,L,L 4
//L,H,L,H 5
//L,H,H,L 6#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include "ds18x20.h"
#include "onewire.h"
#include "DS1307.h"

#include "i2c.h"

#define DELAY_TIME   120 // интервал индикации времени в миллисекундах
#define DELAY_OTHER  10  // интервал индикации даты и температуры в миллисекундах

#define LED1_ON PORTD |= _BV(PD4) // подсветка ИН-8
//#define LED2_ON PORTD |= _BV(PD3) // подсветка ИН-17 //сделать кнопкой Алярм

//#define LED1_OFF PORTD &= ~_BV(PD4)
//#define LED2_OFF PORTD &= ~_BV(PD3)     

#define BUT_M PIND & (1 << PD0)  // кнопка MENU
#define BUT_U PIND & (1 << PD1)  // кнопка UP
#define BUT_D PIND & (1 << PD2)  // кнопка DOWN
#define BUT_A PIND & (1 << PD3)  // кнопка Alarm

#define SQW_IN PINB & (1 << PB0) // тактовый сигнал от DS1307

#define MAXSENSORS 2

const char digit[11] = {  // масив цифр для отправки на К155ИД1
   0b00000000,
    0b00100000,
    0b00010000,
    0b00110000,
    0b00001000,
    0b00101000,
    0b00011000,
    0b00111000,
    0b00000100,
    0b00100100,
    0b00111100
};

unsigned char eep1 EEMEM;
unsigned char eep2 EEMEM;
unsigned char eep3 EEMEM;

volatile char lev[7] = {255, 1, 1, 255, 255, 255, 255}, a = 0;
volatile uint8_t out[9] = {10, 10, 10, 10, 10, 10};

unsigned char H, M, S;
unsigned char d, m, y;

volatile char delay = 0, vid = 2, press = 0, pr = 0, led = 1, set = 0;

volatile uint8_t fr[6]; // какой разряд изменился
uint8_t tscr[6]; // временный экран
uint8_t iocr = 0, dig = 0;
char ms = 0, brig[2] = {1, 250};
const uint8_t psc[25]={1,21,42,63,84,105,126,147,168,189,210,231,245,231,210,189,168,147,126,105,84,63,42,21,1}; // ступени яркости день
const uint8_t psc2[25]={105,116,127,138,149,162,175,186,198,210,222,234,245,234,222,210,198,186,175,162,149,138,127,116,105}; // ступени яркости ночь

uint8_t Temperature = 255, szero = 1; //-255

uint8_t nSensors, j;
uint8_t cel_frac_bits;

uint8_t gSensorIDs[MAXSENSORS][OW_ROMCODE_SIZE];

uint8_t search_sensors(void) // поиск DS18B20
{
  uint8_t i;
  uint8_t id[OW_ROMCODE_SIZE];
  uint8_t diff, nSensors;

  nSensors = 0;

  for( diff = OW_SEARCH_FIRST;
    diff != OW_LAST_DEVICE && nSensors < MAXSENSORS ; )
  {
    DS18X20_find_sensor( &diff, &id[0] );

    if( diff == OW_PRESENCE_ERR ) {
      Temperature = 255;
      szero = 1;
      break;
    }
    if( diff == OW_DATA_ERR ) {
      Temperature = 255;
      szero = 1;
      break;
    }
    for (i=0; i<OW_ROMCODE_SIZE; i++)
      gSensorIDs[nSensors][i]=id[i];

    nSensors++;
  }

  return nSensors;
}

void soft(char D1, char D2, char D3) { // эффект плавной смены цифр, здесь идет поиск тех цифр, которые надо менять

 uint8_t i;

 if(set == 0){
  tscr[0] = D1 / 10; // запомнили новое время
  tscr[1] = D1 % 10;
  tscr[2] = D2 / 10;
  tscr[3] = D2 % 10;
  tscr[4] = D3 / 10;
  tscr[5] = D3 % 10;

    for (i = 0; (i < 6); i++) // сравнили с тем, что было
      if (tscr[i] != out[i]) // узнаём в каком разряде поменялась цифра
        fr[i] = 1; // цифра изменилась
      else
        fr[i] = 0; // цифра осталась
  iocr = 0;
 }else{
   for (i = 0; (i < 6); i++) fr[i] = 0; // во время настройки часов эффект отключен
 }
}

void sprint(char D1, char D2, char D3) { // эффект "пробежки" цифр
  uint8_t i, j;

  tscr[0] = D1 / 10; // запомнили новую дату
  tscr[1] = D1 % 10;
  tscr[2] = D2 / 10;
  tscr[3] = D2 % 10;
  tscr[4] = D3 / 10;
  tscr[5] = D3 % 10;

    for (i = 0; i < 6; i++){ // сравниваем текущую цифру с новой
      if (tscr[i] > out[i]) fr[i] = 2; // новая больше
      if (tscr[i] < out[i]) fr[i] = 3; // новая меньше

      for (j = 0; j < 9; j++){ // делаем "пробежку"
        if(fr[i] == 2){if(out[i] != tscr[i])out[i]++;} // новая цифра больше, плюсуем
        if(fr[i] == 3){if(out[i] != tscr[i])out[i]--;} // новая цифра меньше, вычитаем
        if(out[i] == tscr[i])break; //  // новая цифра равна, возвращаемся в начало

        _delay_ms(70);
      }
    }
}

void shift_l(uint16_t T){ // эффект "сдвиг влево"
  uint8_t i;

  for(i = 5; i > 0; i--){ // сдвигаем влево текущие цифры
    out[0] = out[1];
    out[1] = out[2];
    out[2] = out[3];
    out[3] = out[4];
    out[4] = out[5];

    out[i] = 10;

    _delay_ms(100);
  }

  out[0] = 10;

  for(i = 0; i <= 3; i++){ // запоминаем температуру и двигаем ее на нужное место
    out[5-i] = T/100;
    out[6-i] = T/10%10;
    out[7-i] = T%10;
    out[8-i] = 10;

    _delay_ms(100);
  }

  if(szero == 1){
    lev[0] = brig[1];
    out[0] = 0;
  }
}

void shift_r(){ // эффект "сдвиг вправо"
  uint8_t i;

  if(out[0] == 0){ // сдвигаем вправо текущие цифры
  for(i = 0; i <= 5; i++){ // для отрицательной температуры

    out[5] = out[4];
    out[4] = out[3];
    out[3] = out[2];
    out[2] = out[1];
    out[1] = out[0];

    out[i] = 10;

    _delay_ms(100);
  }
  }else{
    for(i = 0; i <= 3; i++){// для положительной температуры

      out[5] = out[4];
      out[4] = out[3];
      out[3] = out[2];
      out[2] = out[1];

      out[i] = 10;

      _delay_ms(100);
    }
  }

  rtc_get_time(&H, &M, &S); // получаем время

  tscr[0] = H / 10; // запоминаем время
  tscr[1] = H % 10;
  tscr[2] = M / 10;
  tscr[3] = M % 10;
  tscr[4] = S / 10;
  tscr[5] = S % 10;

  for(i = 0; i <= 5; i++){ // двигаем время вправо

    out[5] = out[4];
    out[4] = out[3];
    out[3] = out[2];
    out[2] = out[1];
    out[1] = out[0];

    out[0] = tscr[5-i];

    _delay_ms(100);
  }
}

ISR (TIMER2_OVF_vect){ // реализация ШИМ управления яркостью ламп и подсветки
  uint8_t i;

  PORTC = 0x00;

  //LED1_OFF;
  //LED2_OFF;

  OCR2 = lev[dig]; // подготавливаемся к выводу следуйщего разряда

  if (iocr < 25){ // плавная смена яркости цифр
    if (++ms == 18) { // ~38ms
      ms = 0;
      for (i = 0; i < 6; i++) {
        if ((fr[i] == 1) || (iocr == 0)){
          if(brig[0] == 1)lev[i] = psc[iocr]; else lev[i] = psc2[iocr];
        }
        if (iocr == 12 && set == 0)
          out[i] = tscr[i];
      }
      iocr++;
    }
  }
}

ISR (TIMER2_COMP_vect){ // реализация ШИМ управления яркостью ламп и подсветки

   if(dig < 6){
     PORTB = digit[out[dig]]; // выводим цифру на дешифратор
     PORTC = (1<<dig); // переключаем лампы для динамической индикации
   }else{
     if(dig == 6 && led == 1){LED1_ON; /*LED2_ON;*/} // яркость подсветки
   }

   if (++dig > 6) dig = 0;
}

void get_temp(int sensor){ // получаем температуру с датчиков

  lev[0] = brig[1]; // гастройка яркости вывода температуры, иначе будет выводить с той яркостью, до которой дошло при плавной смене цифр
  lev[1] = brig[1];
  lev[2] = brig[1];
  lev[3] = brig[1];
  lev[4] = brig[1];
  lev[5] = brig[1];

    DS18X20_start_meas(DS18X20_POWER_EXTERN, NULL);

    j = gSensorIDs[0][sensor]; // family-code for conversion-routine

    if (DS18X20_read_meas_single(j, &szero, &Temperature, &cel_frac_bits) != DS18X20_OK) {
      Temperature = 255;
      szero = 1;
    }

  shift_l(DS18X20_temp_to_decicel(szero, Temperature, cel_frac_bits));
}

void get_time(){ // получаем время с DS1307
  rtc_get_time(&H, &M, &S);

  soft(H, M, S); // вызываем плавную смену цифр
}

void get_date(){ // получаем дату с DS1307
  rtc_get_date(&d, &m, &y);

  lev[0] = brig[1];
  lev[1] = brig[1];
  lev[2] = brig[1];
  lev[3] = brig[1];
  lev[4] = brig[1];
  lev[5] = brig[1];

  sprint(d, m, y); // вызываем "пробежку" цифр
}

void display(){ // вывод данных на индикаторы
  switch(vid)
  {
  case 0: get_time(); delay = 0; break; // режим 1 - только время

  case 1:                 // режим 2 - время -> дата
    if(delay <= DELAY_TIME){get_time();}
    if(delay == (DELAY_TIME+1)){get_date();}
    if(delay >= (DELAY_TIME + DELAY_OTHER + 1)){rtc_get_time(&H, &M, &S); sprint(H, M, S); delay = 0;}
    break;

  case 2:                 // режим 3 - время -> температура
    if(delay <= DELAY_TIME){get_time();}

      if(nSensors == 0){delay = 0;}

        if(nSensors == 1){
          if(delay == (DELAY_TIME+1)){get_temp(0);}
          if(delay >= (DELAY_TIME + DELAY_OTHER + 1)){shift_r(); delay = 0; }
        }

          if(nSensors == 2){
            if(delay == (DELAY_TIME+1)){get_temp(0);}
            if(delay == (DELAY_TIME + DELAY_OTHER + 1)){get_temp(1);}
            if(delay >= (DELAY_TIME + (DELAY_OTHER*2) + 1)){shift_r(); delay = 0;}
          }
    break;

  case 3:                 // режим 3 - время -> дата -> температура
    if(delay <= DELAY_TIME){get_time();}
    if(delay == (DELAY_TIME+1)){get_date();}

      if(nSensors == 0){if(delay >= 131){rtc_get_time(&H, &M, &S); sprint(H, M, S); delay = 0;}}

        if(nSensors == 1){
          if(delay == (DELAY_TIME + DELAY_OTHER + 1)){get_temp(0);}
          if(delay >= (DELAY_TIME + (DELAY_OTHER*2) + 1)){shift_r(); delay = 0;}
        }

          if(nSensors == 2){
            if(delay == (DELAY_TIME + DELAY_OTHER + 1)){get_temp(0);}
            if(delay == (DELAY_TIME + (DELAY_OTHER*2) + 1)){get_temp(1);}
            if(delay >= (DELAY_TIME + (DELAY_OTHER*3) + 1)){shift_r(); delay = 0;}
          }
    break;
  }
}

void settings(){ // режим настройки часов

  if(press >= 1 && set <= 2){ // нужно для вывода данных во время настройки
    out[0] = H/10; out[1] = H%10;
    out[2] = M/10; out[3] = M%10;
    out[4] = S/10; out[5] = S%10;
  }
  if(press >= 1 && set > 2){
    out[0] = d/10; out[1] = d%10;
    out[2] = m/10; out[3] = m%10;
    out[4] = y/10; out[5] = y%10;
  }

  switch(set) // включена настройка
  {
  case 1: // настройка часов
    if(~BUT_U){H++; if(H > 23) H = 0; _delay_ms(500);}
    if(~BUT_D){H--; if((~BUT_D) && H == 0) H = 23; _delay_ms(500);}

    if((SQW_IN) || (~BUT_D) || (~BUT_U)){out[0] = H/10; out[1] = H%10; // мигаем часами
    }else{out[0] = 10; out[1] = 10;}
  break;

  case 2: // настройка минут
    if(~BUT_U){M++; if(M > 59) M = 0; _delay_ms(500);}
    if(~BUT_D){M--; if((~BUT_D) && M == 0) M = 59; _delay_ms(500);}

    if((SQW_IN) || (~BUT_D) || (~BUT_U)){out[2] = M/10; out[3] = M%10; // мигаем минутами
    }else{out[2] = 10; out[3] = 10;}
  break;

  case 3: // настройка дня
    if(~BUT_U){d++; if(d > 31) d = 1; _delay_ms(500);}
    if(~BUT_D){d--; if(d < 1) d = 31; _delay_ms(500);}

    if((SQW_IN) || (~BUT_D) || (~BUT_U)){out[0] = d/10; out[1] = d%10; // мигаем днем
    }else{out[0] = 10; out[1] = 10;}
  break;

  case 4: // настройка месяца
    if(~BUT_U){m++; if(m > 12) m = 1; _delay_ms(500);}
    if(~BUT_D){m--; if(m < 1) m = 12; _delay_ms(500);}

    if((SQW_IN) || (~BUT_D) || (~BUT_U)){out[2] = m/10; out[3] = m%10; // мигаем месяцем
    }else{out[2] = 10; out[3] = 10;}
  break;

  case 5: // настройка года
    if(~BUT_U){y++; _delay_ms(500);}
    if(~BUT_D){y--; _delay_ms(500);}

    if((SQW_IN) || (~BUT_D) || (~BUT_U)){out[4] = y/10; out[5] = y%10; // мигаем годом
    }else{out[4] = 10; out[5] = 10;}
  break;
  }
}

void buttons(){ // обработка нажатия кнопок

  if(~BUT_U){if(set == 0)pr++; // кнопка UP

    if(pr == 10){ // управление подсветкой
      if(led == 1) led = 0; else led = 1;

      eeprom_write_byte(&eep1, led); // сохранение настройки в eeprom
    }

  _delay_ms(500);

  }else{
    if(pr >= 1 && pr < 10){ // вызов даты на 2 сек.
      set = 10;
      get_date();
      _delay_ms(2000);
      set = 0;

      pr = 0;
    }

    pr = 0;
  }

  if((~BUT_D) && set == 0){ // кнопка DOWN
    set = 10;

    if(nSensors)get_temp(0); //вызов температуры на 2 сек.
    _delay_ms(2000);

    if(nSensors == 2){ // если есть 2 датчик
      get_temp(1);
      _delay_ms(2000);
    }

    set = 0;
  }

  if(~BUT_M){press++;  // кнопка MENU

    if(set == 2) rtc_set_time(H, M, 0); // если включена настройка времени, отправляем значение в DS1307

    if(press == 1 && set != 0){set++;} // переход по настройкам
    if(press == 10 && set == 0){set = 1; led = 0; _delay_ms(500); led = 1;} // ход в настройки

    if(set > 5){led = 0; _delay_ms(500); led = 1; rtc_set_date(d, m, y); set = 0; press = 0;} // если включена настройка даты,
    //отправляем значение в DS1307 и выходим из настроек

    _delay_ms(500);
  }else{
    if(set == 0 && press >= 1){ // если не вошли в настройки, переключаем режим отображения

      vid++; delay = 0;

      if(vid > 3)vid = 0;

      set = 10;

      out[0] = 10; out[1] = 0; out[2] = vid+1; // индикация текущего режима отображения
      out[3] = 10; out[4] = 10; out[5] = 10;

      _delay_ms(500);

      set = 0;

      eeprom_write_byte(&eep2, vid); // записываем режим отображения в eeprom
    }

    press = 0;
  }
}

int main(){
  /**********************************настройка портов******************************/
  DDRC |= _BV(DDC0) | _BV(DDC1) | _BV(DDC2) | _BV(DDC3) | _BV(DDC4) | _BV(DDC5);

  DDRB |= _BV(DDB2) | _BV(DDB3) | _BV(DDB4) | _BV(DDB5);

  DDRD &= ~_BV(DDD3) | _BV(DDD4);
  //PORTD &= ~_BV(PD3);
  //PORTD |= _BV(PD4);
  /********************************************************************************/

  /**********************************настройка таймера*****************************/
  TCCR2 |= (1 << CS20) | (1 << CS21);
  TIMSK |= _BV(OCIE2) | _BV(TOIE2);
  /********************************************************************************/

  /*******************************настройка переферии******************************/
  i2c_init(); // иництализация протокола i2c

  rtc_init(0, 1, 1); // иництализация DS1307

  ow_set_bus(&PIND, &PORTD, &DDRD, PD5); // иництализация протокола 1-wire

  nSensors = search_sensors(); // поиск датчиков DS18B20

  if(nSensors){DS18X20_start_meas(DS18X20_POWER_EXTERN, NULL);} // если есть датчики, включаем преобразование температуры

  _delay_ms(100);
  /********************************************************************************/

  if(eeprom_read_byte(&eep3) != 1){ // читаем eeprom, если там мусор (первый запуск), пишем свои данные
    eeprom_write_byte(&eep1, 1);
    eeprom_write_byte(&eep2, 3);
    eeprom_write_byte(&eep3, 1);
  }

  led = eeprom_read_byte(&eep1); // читаем значение подсветки из eeprom
  vid = eeprom_read_byte(&eep2); // читаем режим отображения из eeprom

  sei();

  set = 10;

  out[0] = 10; out[1] = 0; out[2] = vid+1; // ндикация текущего режима отображения при включении
  out[3] = 10; out[4] = 10; out[5] = 10;

  _delay_ms(2000);

  set = 0;

  rtc_get_date(&d, &m, &y); //чтение даты для нормально работы режимов отображения

  while(1){

    if(set != 0) settings();

    buttons();

    if((SQW_IN) && set == 0){ // когда приходит сигнал с частотой 1ГЦ. с DS1307 запукаем функцию вывода данных
      if(a < 1){a++; delay++;

        if(H < 7){ // установка уровня яркости
          brig[0] = 0; brig[1] = 105; lev[6] = 200; // ночь
        }else{
          brig[0] = 1; brig[1] = 10; lev[6] = 10; // день
        }

      display();

      }
    }else{
      a = 0;
    }
  }

  return 0;
}

//L,H,H,H 7
//H,L,L,L 8
//H,L,L,H 9

// SN74141 (1)
int ledPin_0_a = 2;                
int ledPin_0_b = 3;
int ledPin_0_c = 4;
int ledPin_0_d = 5;

// SN74141 (2)
int ledPin_1_a = 6;                
int ledPin_1_b = 7;
int ledPin_1_c = 8;
int ledPin_1_d = 9;

// anode pins
int ledPin_a_1 = 10;
int ledPin_a_2 = 11;
int ledPin_a_3 = 12;
int ledPin_a_4 = 13;

void setup() 
{
  pinMode(ledPin_0_a, OUTPUT);      
  pinMode(ledPin_0_b, OUTPUT);      
  pinMode(ledPin_0_c, OUTPUT);      
  pinMode(ledPin_0_d, OUTPUT);    
  
  pinMode(ledPin_1_a, OUTPUT);      
  pinMode(ledPin_1_b, OUTPUT);      
  pinMode(ledPin_1_c, OUTPUT);      
  pinMode(ledPin_1_d, OUTPUT);      
  
  pinMode(ledPin_a_1, OUTPUT);      
  pinMode(ledPin_a_2, OUTPUT);      
  pinMode(ledPin_a_3, OUTPUT);     
 
  // NOTE: Grounding on virtual pins 14 and 15 (analog pins 0 and 1) will set the Hour and Mins.
  
  pinMode( 14, INPUT ); // set the virtual pin 14 (pin 0 on the analog inputs ) 
  digitalWrite(14, HIGH); // set pin 14 as a pull up resistor.

  pinMode( 15, INPUT ); // set the virtual pin 15 (pin 1 on the analog inputs ) 
  digitalWrite(15, HIGH); // set pin 15 as a pull up resistor.
 
}

void SetSN74141Chips( int num2, int num1 )
{
  int a,b,c,d;
  
  // set defaults.
  a=0;b=0;c=0;d=0; // will display a zero.
  
  // Load the a,b,c,d.. to send to the SN74141 IC (1)
  switch( num1 )
  {
    case 0: a=0;b=0;c=0;d=0;break;
    case 1: a=1;b=0;c=0;d=0;break;
    case 2: a=0;b=1;c=0;d=0;break;
    case 3: a=1;b=1;c=0;d=0;break;
    case 4: a=0;b=0;c=1;d=0;break;
    case 5: a=1;b=0;c=1;d=0;break;
    case 6: a=0;b=1;c=1;d=0;break;
    case 7: a=1;b=1;c=1;d=0;break;
    case 8: a=0;b=0;c=0;d=1;break;
    case 9: a=1;b=0;c=0;d=1;break;
    default: a=1;b=1;c=1;d=1;
    break;
  }  
  
  // Write to output pins.
  digitalWrite(ledPin_0_d, d);
  digitalWrite(ledPin_0_c, c);
  digitalWrite(ledPin_0_b, b);
  digitalWrite(ledPin_0_a, a);

  // Load the a,b,c,d.. to send to the SN74141 IC (2)
  switch( num2 )
  {
    case 0: a=0;b=0;c=0;d=0;break;
    case 1: a=1;b=0;c=0;d=0;break;
    case 2: a=0;b=1;c=0;d=0;break;
    case 3: a=1;b=1;c=0;d=0;break;
    case 4: a=0;b=0;c=1;d=0;break;
    case 5: a=1;b=0;c=1;d=0;break;
    case 6: a=0;b=1;c=1;d=0;break;
    case 7: a=1;b=1;c=1;d=0;break;
    case 8: a=0;b=0;c=0;d=1;break;
    case 9: a=1;b=0;c=0;d=1;break;
    default: a=1;b=1;c=1;d=1;
    break;
  }
  
  // Write to output pins
  digitalWrite(ledPin_1_d, d);
  digitalWrite(ledPin_1_c, c);
  digitalWrite(ledPin_1_b, b);
  digitalWrite(ledPin_1_a, a);
}


float fadeMax = 5.0f;
float fadeStep = 1.0f;
int NumberArray[6]={0,0,0,0,0,0};
int currNumberArray[6]={0,0,0,0,0,0};
float NumberArrayFadeInValue[6]={0.0f,0.0f,0.0f,0.0f,0.0f,0.0f};
float NumberArrayFadeOutValue[6]={8.0f,8.0f,8.0f,8.0f,8.0f,8.0f};

void DisplayFadeNumberString()
{
 
  // Anode channel 1 - numerals 0,3
  SetSN74141Chips(currNumberArray[0],currNumberArray[3]);   
  digitalWrite(ledPin_a_2, HIGH);   
  delay(NumberArrayFadeOutValue[0]);
  SetSN74141Chips(NumberArray[0],NumberArray[3]);   
  delay(NumberArrayFadeInValue[0]);
  digitalWrite(ledPin_a_2, LOW);
  
  // Anode channel 2 - numerals 1,4
  SetSN74141Chips(currNumberArray[1],currNumberArray[4]);   
  digitalWrite(ledPin_a_3, HIGH);   
  delay(NumberArrayFadeOutValue[1]);
  SetSN74141Chips(NumberArray[1],NumberArray[4]);   
  delay(NumberArrayFadeInValue[1]);
  digitalWrite(ledPin_a_3, LOW);
  
   // Anode channel 3 - numerals 2,5
  SetSN74141Chips(currNumberArray[2],currNumberArray[5]);   
  digitalWrite(ledPin_a_4, HIGH);   
  delay(NumberArrayFadeOutValue[2]);
  SetSN74141Chips(NumberArray[2],NumberArray[5]);   
  delay(NumberArrayFadeInValue[2]);
  digitalWrite(ledPin_a_4, LOW);
  
  // Loop thru and update all the arrays, and fades.
  for( int i = 0 ; i < 6 ; i ++ )
  {
    if( NumberArray[i] != currNumberArray[i] )
    {
      NumberArrayFadeInValue[i] += fadeStep;
      NumberArrayFadeOutValue[i] -= fadeStep;
  
      if( NumberArrayFadeInValue[i] >= fadeMax )
      {
        NumberArrayFadeInValue[i] = 0.0f;
        NumberArrayFadeOutValue[i] = fadeMax;
        currNumberArray[i] = NumberArray[i];
      }
    }
  }  
}

// Defines
long MINS = 60;         // 60 Seconds in a Min.
long HOURS = 60 * MINS; // 60 Mins in an hour.
long DAYS = 12 * HOURS; // 24 Hours in a day. > Note: change the 24 to a 12 for non military time.

long runTime = 0;       // Time from when we started.

// default time sets. clock will start at 12:34:00.  This is so we can count the correct order of tubes.
long clockHourSet = 12;
long clockMinSet  = 34;

int HourButtonPressed = false;
int MinButtonPressed = false;

////////////////////////////////////////////////////////////////////////
//
//
////////////////////////////////////////////////////////////////////////
void loop()     
{
  // Get milliseconds.
  runTime = millis();
  
  int hourInput = digitalRead(14);  
  int minInput  = digitalRead(15);

  if( hourInput == 0 )
    HourButtonPressed = true;
  if( minInput == 0 )
    MinButtonPressed = true;
  
  if( HourButtonPressed == true && hourInput == 1 )
  {
    clockHourSet++;
    HourButtonPressed = false;
  }
  
  if( MinButtonPressed == true && minInput == 1 )
  {
    clockMinSet++;
    MinButtonPressed = false;
  }

  // Get time in seconds.
  long time = (runTime) / 1000; //////////change this value to speed up or slow down the clock, set to smaller number such as 10, 1, or 100 for debugging
  
  // Set time based on offset..
  // long hbump = 60*60*clockHourSet;
  long hbump = 60*60*clockHourSet;
  long mbump = 60*clockMinSet;
  time += mbump + hbump;

  // Convert time to days,hours,mins,seconds
  long days  = time / DAYS;    time -= days  * DAYS; 
  long hours = time / HOURS;   time -= hours * HOURS; 
  long minutes  = time / MINS;    time -= minutes  * MINS; 
  long seconds  = time; 

  // Get the high and low order values for hours,min,seconds. 
  int lowerHours = hours % 10;
  int upperHours = hours - lowerHours;
  int lowerMins = minutes % 10;
  int upperMins = minutes - lowerMins;
  int lowerSeconds = seconds % 10;
  int upperSeconds = seconds - lowerSeconds;
  if( upperSeconds >= 10 )   upperSeconds = upperSeconds / 10;
  if( upperMins >= 10 )      upperMins = upperMins / 10;
  if( upperHours >= 10 )     upperHours = upperHours / 10;
  
  if( upperHours == 0 && lowerHours == 0 )
  {
    upperHours = 1;
    lowerHours = 2;
  }

  // Fill in the Number array used to display on the tubes.
  NumberArray[1] = lowerHours; 
  NumberArray[2] = lowerMins;
  NumberArray[3] = upperMins;
  NumberArray[4] = upperSeconds;  
  NumberArray[5] = upperHours;
  NumberArray[0] = lowerSeconds;//



  // Display.
  DisplayFadeNumberString();
}

 

fine stop_autoshow TCCR0=0x00 //остановить таймер unsigned char timer_int = 0; //счетчик срабатываний таймера для кнопок  unsigned char flag_1 = 0; //флаги для длительного нажатия кнопок unsigned char flag_2 = 0; unsigned char flag_3 = 0; unsigned char flag_4 = 0; //режимы работы дисплея unsigned char clock_mode = 1; //режим часов: 1-ЧЧ:ММ:СС, 2-ЧЧ:ММ unsigned char date_mode = 0; //режим даты: ДД:ММ:ГГ unsigned char sec_mode = 0; //режим секунд: :СС unsigned char data_mode = 0; //режим показа данных с RS-232 signed char sec = 0; //время signed char min = 0; signed char hrs = 0; signed char day = 1; signed char mnth = 1; signed char year = 7; signed char a_sec = 0; //время будильника signed char a_min = 0; signed char a_hrs = 0; signed char a_day = 1; signed char a_mnth = 1; signed char a_year = 0; unsigned char b_1 = 0; //байты, получаемые через RS-232 unsigned char b_2 = 0; unsigned char b_3 = 0; unsigned char b_4 = 0; unsigned char b_5 = 0; unsigned char b_6 = 0; unsigned char b_7 = 0; unsigned char b_cnt = 0; //счетчик принятых байт bit recieve_cmp = 0; //флаг окончания приема последовательности байт unsigned char first_b = 0; //байты для отображения, полученные через RS-232  unsigned char second_b = 0; unsigned char third_b = 0; unsigned char point_b = 0; bit alarm_flag = 0; //флаг активности будильника unsigned char flag = 0; //флаг будильника в памяти RTC unsigned char date_show_ms = 0; //время показа даты в мс/100 unsigned char data_show_ms = 0; //время показа данных из RS-232 в мс/100 unsigned char date_cnt = 0; //счетчики для ограничения времени показа unsigned char data_cnt = 0; unsigned char show_cnt = 0; //счетчик циклов таймера показа //прототипы некоторых функций void sound (int time); void WriteRTC (unsigned char data, unsigned char addr); unsigned char ReadRTC (unsigned char addr); #define RXB8 1 #define TXB8 0 #define UPE 2 #define OVR 3 #define FE 4 #define UDRE 5 #define RXC 7 #define FRAMING_ERROR (1<<FE) #define PARITY_ERROR (1<<UPE) #define DATA_OVERRUN (1<<OVR) #define DATA_REGISTER_EMPTY (1<<UDRE) #define RX_COMPLETE (1<<RXC) // USART Receiver buffer #define RX_BUFFER_SIZE 20 char rx_buffer[RX_BUFFER_SIZE]; #if RX_BUFFER_SIZE<256 unsigned char rx_wr_index,rx_rd_index,rx_counter; #else unsigned int rx_wr_index,rx_rd_index,rx_counter; #endif #ifndef _DEBUG_TERMINAL_IO_ // Get a character from the USART Receiver buffer #define _ALTERNATE_GETCHAR_ #pragma used+ char getchar(void) { char data; while (rx_counter==0); data=rx_buffer[rx_rd_index]; if (++rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0; #asm("cli") --rx_counter; #asm("sei") return data; } #pragma used- #endif // USART Receiver interrupt service routine interrupt [USART_RXC] void usart_rx_isr(void) { char status,data; status=UCSRA; data=UDR; if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0) { rx_buffer[rx_wr_index]=data; if (++rx_wr_index == RX_BUFFER_SIZE) rx_wr_index=0; if (++rx_counter == RX_BUFFER_SIZE) rx_counter=0; if (b_cnt) ++b_cnt; //если счетчик не нулевой - увеличиваем счетчик if (!b_cnt && data == start_cond) ++b_cnt; //если первый полученный байт правильный, увеличиваем счетчик в первый раз }; if (b_cnt == 7) //если получили уже все 7 байт { b_1 = getchar(); //сохранили их содержимое b_2 = getchar(); b_3 = getchar(); b_4 = getchar(); b_5 = getchar(); b_6 = getchar(); b_7 = getchar(); recieve_cmp = 1; //выставили флаг окончания приема  b_cnt = 0; //обнулили счетчик  rx_counter=0; rx_wr_index=0; rx_rd_index=0; } } // Standard Input/Output functions #include <stdio.h> // External Interrupt 0 service routine - обработка будильника interrupt [EXT_INT0] void ext_int0_isr(void) { flag = ReadRTC (0x0F); //сброс флага будильника в RTC while (!but1 && !but2 && !but3 && !but4 && alarm_flag) //пока не нажали на кнопку и есть флаг активности тревоги { hrs = ReadRTC (hrs_addr); min = ReadRTC (min_addr); disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0); sound (100); delay_ms (150); sound (100); delay_ms (500); putsf ("Alarm! "); } while (but1 || but2 || but3 || but4) #asm("wdr");  } // Timer 0 overflow interrupt service routine interrupt [TIM0_OVF] void timer0_ovf_isr(void) { TCNT0=0x27; //таймер перепоняется каждые 10мс  if (++show_cnt == 5) //каждые 50мс { if (clock_mode == 1 && !date_mode && !sec_mode && !data_mode) { hrs = ReadRTC (hrs_addr); //читаем и показываем время ЧЧ:ММ:СС min = ReadRTC (min_addr); sec = ReadRTC (sec_addr); disp_out (hrs/10, hrs%10, min/10, min%10, sec/10, sec%10, alarm_flag, 0, 1, 0, 1, 0); } if (clock_mode == 2 && !date_mode && !sec_mode && !data_mode) { hrs = ReadRTC (hrs_addr); //читаем и показываем время ЧЧ:ММ min = ReadRTC (min_addr); disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, alarm_flag, 0, 0, 1, 0, 0); } if (date_mode && (4*date_cnt <= date_show_ms) && !sec_mode && !data_mode) //читаем и показываем дату ДД:ММ:ГГ { ++date_cnt; //счетчик показов day = ReadRTC (day_addr); mnth = ReadRTC (mnth_addr); year = ReadRTC (year_addr); disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, alarm_flag, 0, 1, 0, 1, 0); } else { date_mode = 0; date_cnt = 0; } if (sec_mode && !data_mode) //читаем и показываем секунды :СС { sec = ReadRTC (sec_addr); disp_out (10, 10, 10, 10, sec/10, sec%10, alarm_flag, 0, 0, 0, 1, 0); } if (data_mode && (4*data_cnt <= data_show_ms)) //показываем данные из RS-232 { ++data_cnt; disp_out (first_b/10, first_b%10, second_b/10, second_b%10, third_b/10, third_b%10, alarm_flag, ((point_b & 0x02)>>1), ((point_b & 0x08)>>4), ((point_b & 0x04)>>2), ((point_b & 0x02)>>1), (point_b & 0x01)); } else { data_mode = 0; data_cnt = 0; } show_cnt = 0; } } // Timer 1 overflow interrupt service routine interrupt [TIM1_OVF] void timer1_ovf_isr(void) { TCNT1H=0xD5; //таймер переполняется раз в секунду TCNT1L=0xCF; timer_int++; if (but1) flag_1 = timer_int; else flag_1 = 0; if (but2) flag_2 = timer_int; else flag_2 = 0; if (but3) flag_3 = timer_int; else flag_3 = 0; if (but4) flag_4 = timer_int; else flag_4 = 0; if (timer_int == 1) sound (20); if (timer_int == 2) { sound (20); delay_ms(40); sound (20); } if (timer_int == 3) { sound (20); delay_ms(40); sound (20); delay_ms(40); sound (20); timer_int = 0; } } unsigned char ReadRTC (unsigned char addr) //процедура чтения байта из RTC - см. даташит { //возвращает байт из адреса addr unsigned char data = 0; unsigned char data2 = 0; i2c_start_soft (); i2c_tx_soft (RTC); i2c_tx_soft (addr); i2c_stop_soft (); i2c_start_soft (); i2c_tx_soft (RTC+1); data = i2c_rx_soft (1); data2 = i2c_rx_soft (0); //сделано чтение байта вникуда, иначе подглючивает i2c_stop_soft (); data = bcd2bin (data); return data; } void WriteRTC (unsigned char data, unsigned char addr) //процедура записи байта в RTC - см. даташит { //пишет data по адресу addr data = bin2bcd (data); i2c_start_soft (); i2c_tx_soft (RTC); i2c_tx_soft (addr); i2c_tx_soft (data); i2c_stop_soft (); } void sound (int time) //Процедура для пищания, частота сильно около 3 кГц ;))  { int i = 0; while (i < 4*time) { snd = 1; delay_us(150); snd = 0; delay_us(150); i++; } } void reset_flag (void) { TCCR1B=0x00; //остановили таймер 1 TCNT1H=0xD5; TCNT1L=0xCF; timer_int = 0; //сбросили счетчик прерывания таймера 1 flag_1 = 0; flag_2 = 0; flag_3 = 0; flag_4 = 0; //сбросили все флаги входа } void Power_en (void) { reset_flag (); pen = !pen; } void Error (void) { b_cnt = 0; recieve_cmp = 0; b_1 = 0; b_2 = 0; b_3 = 0; b_4 = 0; b_5 = 0; b_6 = 0; b_7 = 0; rx_rd_index = 0; rx_counter=0; rx_wr_index=0; putsf ("Error!"); } void Time_setup (void) //процедура установки времени { unsigned char i = 0; //счетчик bit hrs_s = 0; //флаги осуществленной установки часов, минут и т.д. bit min_s = 0; bit day_s = 0; bit mnth_s = 0; bit year_s = 0; stop_autoshow; reset_flag (); hrs = ReadRTC (hrs_addr); //прочитали текущее время из RTC min = ReadRTC (min_addr); disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0); //мигнули часами один раз delay_ms (400); disp_out (10, 10, 10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); while ((++i < 10) && !but1) //пока не мигнули 10 раз или не нажали кнопку 1 { hrs_s = 1; //поставили флаг, что пытались устанавливать часы disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0); //мигаем временем delay_ms (400); disp_out (10, 10, 10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); while (but4) //если удерживаем кнопку 4 { if (++hrs > 23) hrs = 0; //увеличиваем счетчик часов disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0); //раз в 200 мс delay_ms (400); i = 0; //и обнуляем счетчик миганий } while (but3) //если удерживаем кнопку 3 { //уменьшаем счетчик часов if (--hrs < 0) hrs = 23; //раз в 200 мс disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0); //и обнуляем счетчик миганий delay_ms (400); i = 0; } } if (but1) //если нажали на кнопку 1 { delay_ms(10); WriteRTC (hrs, hrs_addr); //записали установленное значение часов в RTC i = 0; sound(50); //пискнули while (but1); //ждем, пока отпустим кнопку  } hrs = ReadRTC (hrs_addr); //аналогично для установки минут min = ReadRTC (min_addr); if (i == 0) { disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); } if (i == 0) { disp_out (10, hrs/10, hrs%10, 10, 10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); } while ((++i < 10) && !but1 && hrs_s) { min_s = 1; disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); disp_out (10, hrs/10, hrs%10, 10, 10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); while (but4) { if (++min > 59) min = 0; disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); i = 0; } while (but3) { if (--min < 0) min = 59; disp_out (10, hrs/10, hrs%10, min/10, min%10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); i = 0; } } if (but1) { delay_ms(10); WriteRTC (min, min_addr); i = 0; sound(50); while (but1); } year = ReadRTC (year_addr); //потом устанавливаем год if (i == 0) { disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); } if (i == 0) { disp_out (day/10, day%10, mnth/10, mnth%10, 10, 10, 0, 0, 1, 0, 1, 0); delay_ms (400); } while ((++i < 10) && !but1 && min_s) { year_s = 1; disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); disp_out (day/10, day%10, mnth/10, mnth%10, 10, 10, 0, 0, 1, 0, 1, 0); delay_ms (400); while (but4) { if (++year > 99) year = 0; disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); i = 0; } while (but3) { if (--year < 0) year = 99; disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); i = 0; } } if (but1) { delay_ms(10); WriteRTC (year, year_addr); i = 0; sound(50); while (but1); } day = ReadRTC (day_addr); //потом месяц mnth = ReadRTC (mnth_addr); if (i == 0) { disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); } if (i == 0) { disp_out (day/10, day%10, 10, 10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); } while ((++i < 10) && !but1 && year_s) { mnth_s = 1; disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); disp_out (day/10, day%10, 10, 10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); while (but4) { if (++mnth > 12) mnth = 1; disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); i = 0; } while (but3) { if (--mnth < 1) mnth = 12; disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); i = 0; } } if (but1) { delay_ms(10); WriteRTC (mnth, mnth_addr); i = 0; sound(50); while (but1); } day = ReadRTC (day_addr); //исходя из установленных месяца и года mnth = ReadRTC (mnth_addr); //устанавливаем число year = ReadRTC (year_addr); //учитываю возможное количество дней в месяце if (i == 0) { disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); } if (i == 0) { disp_out (10, 10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); } while ((++i < 10) && !but1 && mnth_s) { day_s = 1; disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); disp_out (10, 10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); while (but4) { ++day; if ((day > 28) && (mnth == 2) && (year % 4)) day = 1; //для февраля if ((day > 29) && (mnth == 2) && !(year % 4)) day = 1; //для февраля високосного года if ((day > 30) && ((mnth == 4) || (mnth == 6) || (mnth == 9) || (mnth == 11))) day = 1; //для месяцев с 30 днями if (day > 31) day = 1; //для месяцев с 31 днем disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); } while (but3) { if (--day < 1) { day = 31; if ((mnth == 4) || (mnth == 6) || (mnth == 9) || (mnth == 11)) day = 30; if ((mnth == 2) && !(year % 4)) day = 29; if ((mnth == 2) && (year % 4)) day = 28; } disp_out (day/10, day%10, mnth/10, mnth%10, year/10, year%10, 0, 0, 1, 0, 1, 0); delay_ms (400); } } if (but1) { delay_ms(10); WriteRTC (day, day_addr); i = 0; sound(50); while (but1); } start_autoshow; } void Alarm_setup (void) //процедура установки времени будильника - вцелом, аналогично установке времени { unsigned char i = 0; bit a_hrs_s = 0; bit a_min_s = 0; stop_autoshow; reset_flag (); a_hrs = ReadRTC (a_hrs_addr); //прочитали текущее время из RTC a_min = ReadRTC (a_min_addr); disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0); //мигнули временем один раз delay_ms (400); disp_out (10, 10, 10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); while ((++i < 10) && !but1) //пока не мигнули 10 раз или не нажали кнопку 1 { a_hrs_s = 1; //поставили флаг, что пытались устанавливать часы disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0); //мигаем временем delay_ms (400); disp_out (10, 10, 10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); while (but4) //если удерживаем кнопку 4 { if (++a_hrs > 23) a_hrs = 0; //увеличиваем счетчик часов disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0); //раз в 200 мс delay_ms (400); i = 0; //и обнуляем счетчик миганий } while (but3) //если удерживаем кнопку 3 { //уменьшаем счетчик часов if (--a_hrs < 0) a_hrs = 23; //раз в 200 мс disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0); //и обнуляем счетчик миганий delay_ms (400); i = 0; } } if (but1) //если нажали на кнопку 1 { delay_ms(10); WriteRTC (a_hrs, a_hrs_addr); //записали установленное значение часов в RTC i = 0; sound(50); //пискнули while (but1); //ждем, пока отпустим кнопку  } a_hrs = ReadRTC (a_hrs_addr); //аналогично для установки минут a_min = ReadRTC (a_min_addr); if (i == 0) { disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); } if (i == 0) { disp_out (10, a_hrs/10, a_hrs%10, 10, 10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); } while ((++i < 10) && !but1 && a_hrs_s) { a_min_s = 1; disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); disp_out (10, a_hrs/10, a_hrs%10, 10, 10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); while (but4) { if (++a_min > 59) a_min = 0; disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); i = 0; } while (but3) { if (--a_min < 0) a_min = 59; disp_out (10, a_hrs/10, a_hrs%10, a_min/10, a_min%10, 10, 0, 0, 0, 1, 0, 0); delay_ms (400); i = 0; } } if (but1) { delay_ms(10); WriteRTC (a_min, a_min_addr); i = 0; sound(50); while (but1); } start_autoshow; } void Alarm_OnOff (void) //процедура включения/выключения будильника { reset_flag (); alarm_flag = !alarm_flag; //проинвертированли флаг включения будильника writeEEPROM (2, alarm_flag); //записали в ЕЕПРОМ состояние флага if (alarm_flag) //если будильник включен - включили его в RTC { flag = ReadRTC (0x0F); WriteRTC (bcd2bin (0b10000000), a_mnth_addr); flag = ReadRTC (0x0F); } else //если выключен - выключили { flag = ReadRTC (0x0F); WriteRTC (0, a_mnth_addr); flag = ReadRTC (0x0F); } } void Sys_setup (void) { unsigned char i = 0; bit mode_s = 0; bit date_s = 0; bit data_s = 0; stop_autoshow; reset_flag (); disp_out (10, 10, 10, 10, clock_mode, 10, 0, 0, 0, 0, 0, 0); //устанавливаем формат показа времени  delay_ms (400); disp_out (10, 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0); delay_ms (400); while ((++i < 10) && !but1) { mode_s = 1; disp_out (10, 10, 10, 10, clock_mode, 10, 0, 0, 0, 0, 0, 0); delay_ms (400); disp_out (10, 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0); delay_ms (400); while (but4) { if (++clock_mode > 2) clock_mode = 1; disp_out (10, 10, 10, 10, clock_mode, 10, 0, 0, 0, 0, 0, 0); delay_ms (400); i = 0; } while (but3) { if (--clock_mode < 1) clock_mode = 2; disp_out (10, 10, 10, 10, clock_mode, 10, 0, 0, 0, 0, 0, 0); delay_ms (400); i = 0; } } if (but1) { delay_ms(10); writeEEPROM (3, clock_mode); i = 0; sound(50); while (but1); } if (i == 0) //длительность показа даты { disp_out (10, 10, 10, 10, date_show_ms/10, date_show_ms%10, 0, 0, 0, 0, 0, 1); delay_ms (400); } if (i == 0) { disp_out (10, 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0); delay_ms (400); } while ((++i < 10) && !but1 && mode_s) { date_s = 1; disp_out (10, 10, 10, 10, date_show_ms/10, date_show_ms%10, 0, 0, 0, 0, 0, 1); delay_ms (400); disp_out (10, 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0); delay_ms (400); while (but4) { date_show_ms = date_show_ms + 2; if (date_show_ms > 98) date_show_ms = 2; disp_out (10, 10, 10, 10, date_show_ms/10, date_show_ms%10, 0, 0, 0, 0, 0, 1); delay_ms (400); i = 0; } while (but3) { date_show_ms = date_show_ms - 2; if (date_show_ms < 2) date_show_ms = 98; disp_out (10, 10, 10, 10, date_show_ms/10, date_show_ms%10, 0, 0, 0, 0, 0, 1); delay_ms (400); i = 0; } } if (but1) { delay_ms(10); writeEEPROM (4, date_show_ms); i = 0; sound(50); while (but1); } if (i == 0) //длительность показа данных { disp_out (10, 10, 10, 10, data_show_ms/10, data_show_ms%10, 0, 0, 0, 0, 0, 1); delay_ms (400); } if (i == 0) { disp_out (10, 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0); delay_ms (400); } while ((++i < 10) && !but1 && date_s) { data_s = 1; disp_out (10, 10, 10, 10, data_show_ms/10, data_show_ms%10, 0, 0, 0, 0, 0, 1); delay_ms (400); disp_out (10, 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0); delay_ms (400); while (but4) { data_show_ms = data_show_ms + 2; if (data_show_ms > 98) data_show_ms = 2; disp_out (10, 10, 10, 10, data_show_ms/10, data_show_ms%10, 0, 0, 0, 0, 0, 1); delay_ms (400); i = 0; } while (but3) { data_show_ms = data_show_ms - 2; if (data_show_ms < 2) data_show_ms = 98; disp_out (10, 10, 10, 10, data_show_ms/10, data_show_ms%10, 0, 0, 0, 0, 0, 1); delay_ms (400); i = 0; } } if (but1) { delay_ms(10); writeEEPROM (5, data_show_ms); i = 0; sound(50); while (but1); } start_autoshow; } void main(void) { // Declare your local variables here // Input/Output Ports initialization // Port A initialization // Func7=Out Func6=Out Func5=Out Func4=Out Func3=In Func2=In Func1=In Func0=In  // State7=0 State6=0 State5=0 State4=1 State3=P State2=P State1=P State0=P  PORTA=0x1F; DDRA=0xF0; // Port B initialization // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=Out  // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=0  PORTB=0x00; DDRB=0x01; // Port C initialization // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In  // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T  PORTC=0x00; DDRC=0x00; // Port D initialization // Func7=Out Func6=Out Func5=In Func4=In Func3=In Func2=In Func1=Out Func0=In  // State7=0 State6=0 State5=T State4=T State3=T State2=T State1=0 State0=T  PORTD=0x00; DDRD=0xC2; // Port E initialization // Func2=In Func1=In Func0=In  // State2=T State1=T State0=T  PORTE=0x00; DDRE=0x00; // Timer/Counter 0 initialization // Clock source: System Clock // Clock value: 10,800 kHz // Mode: Normal top=FFh // OC0 output: Disconnected TCCR0=0x05; TCNT0=0x27; OCR0=0x00; // Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 10,800 kHz // Mode: Normal top=FFFFh // OC1A output: Discon. // OC1B output: Discon. // Noise Canceler: Off // Input Capture on Falling Edge // Timer 1 Overflow Interrupt: On // Input Capture Interrupt: Off // Compare A Match Interrupt: Off // Compare B Match Interrupt: Off TCCR1A=0x00; TCCR1B=0x00; //TCCR1B=0x05; TCNT1H=0xD5; TCNT1L=0xCF; ICR1H=0x00; ICR1L=0x00; OCR1AH=0x00; OCR1AL=0x00; OCR1BH=0x00; OCR1BL=0x00; // External Interrupt(s) initialization // INT0: On // INT0 Mode: Falling Edge // INT1: Off // INT2: Off GICR|=0x40; MCUCR=0x02; EMCUCR=0x00; GIFR=0x40; // Timer(s)/Counter(s) Interrupt(s) initialization TIMSK=0x82; // USART initialization // Communication Parameters: 8 Data, 1 Stop, No Parity // USART Receiver: On // USART Transmitter: On // USART Mode: Asynchronous // USART Baud rate: 9600 UCSRA=0x00; UCSRB=0x98; UCSRC=0x86; UBRRH=0x00; //UBRRL=0x47; UBRRL=0x05; // Analog Comparator initialization // Analog Comparator: Off // Analog Comparator Input Capture by Timer/Counter 1: Off ACSR=0x80; // Watchdog Timer initialization // Watchdog Timer Prescaler: OSC/2048k #pragma optsize- WDTCR=0x1F; WDTCR=0x0F; #ifdef _OPTIMIZE_SIZE_ #pragma optsize+ #endif if (readEEPROM (2) != 0) alarm_flag = 1; //прочитали из ЕЕПРОМ флаг активности будильника clock_mode = readEEPROM (3); //режим показа date_show_ms = readEEPROM (4); //время показа даты data_show_ms = readEEPROM (5); //время показа данных if (clock_mode > 2) clock_mode = 1; if (date_show_ms > 98) date_show_ms = 10; if (data_show_ms > 98) data_show_ms = 10; i2c_init_soft(); //проинициировали i2c WriteRTC (0, a_sec_addr); //проинициировали RTC WriteRTC (bcd2bin (0b11000000), a_day_addr); WriteRTC (0, 0x13); WriteRTC (0, 0x09); WriteRTC (bcd2bin (0b10000000), 0x08); flag = ReadRTC (0x0F); // Global enable interrupts #asm("sei") pen = 0; while (1) { #asm("wdr") if (recieve_cmp) { #asm("cli") if (b_1 == start_cond && b_7 == stop_cond) { switch (b_2) { case set_time: { if (b_3 < 24 && b_4 < 60) { WriteRTC (b_3, hrs_addr); WriteRTC (b_4, min_addr); putsf ("OK "); putsf ("current time: "); putchar (ReadRTC (hrs_addr)/10+48); putchar (ReadRTC (hrs_addr)%10+48); putchar (':'); putchar (ReadRTC (min_addr)/10+48); putchar (ReadRTC (min_addr)%10+48); putchar (':'); putchar (ReadRTC (sec_addr)/10+48); putchar (ReadRTC (sec_addr)%10+48); sound (30); } else Error (); }; break; case set_date: { if (b_5 > 99 || b_5 < 7 || b_4 < 1 || b_4 > 12 || ((b_3 > 28) && (b_4 == 2) && (b_5 % 4)) || ((b_3 > 29) && (b_4 == 2) && !(b_5 % 4)) || ((b_3 > 30) && ((b_4 == 4) || (b_4 == 6) || (b_4 == 9) || (b_4 == 11))) || b_3 > 31) Error (); else { WriteRTC (b_3, day_addr); WriteRTC (b_4, mnth_addr); WriteRTC (b_5, year_addr); putsf ("OK "); putsf ("current date: "); putchar (ReadRTC (day_addr)/10+48); putchar (ReadRTC (day_addr)%10+48); putchar (':'); putchar (ReadRTC (mnth_addr)/10+48); putchar (ReadRTC (mnth_addr)%10+48); putchar (':'); putchar (ReadRTC (year_addr)/10+48); putchar (ReadRTC (year_addr)%10+48); sound (30); } }; break; case set_alarm: { if (b_3 < 24 && b_4 < 60) { WriteRTC (b_3, a_hrs_addr); WriteRTC (b_4, a_min_addr); putsf ("OK "); putsf ("alarm time: "); putchar (ReadRTC (a_hrs_addr)/10+48); putchar (ReadRTC (a_hrs_addr)%10+48); putchar (':'); putchar (ReadRTC (a_min_addr)/10+48); putchar (ReadRTC (a_min_addr)%10+48); putsf (" current alarm status: "); if (alarm_flag) putsf ("on"); else putsf ("off"); sound (30); } else Error ();; }; break; case set_clock_mode: { if (b_3 < 1 || b_3 > 2) Error (); else { clock_mode = b_3; writeEEPROM (3, clock_mode); if (clock_mode == 1) putsf ("OK HH.MM.SS"); else putsf ("OK HH.MM"); sound (30); } }; break; case set_date_sht: { if (b_3 < 2 || b_3 > 98) Error (); else { date_show_ms = b_3; writeEEPROM (3, date_show_ms); putsf ("OK "); putchar (date_show_ms/10+48); putchar ('.'); putchar (date_show_ms%10+48); putsf ("sec"); sound (30); } }; break; case set_data_sht: { if (b_3 < 2 || b_3 > 98) Error (); else { data_show_ms = b_3; writeEEPROM (4, data_show_ms); putsf ("OK "); putchar (data_show_ms/10+48); putchar ('.'); putchar (data_show_ms%10+48); putsf ("sec"); sound (30); } }; break; case alarm_on_off: { if (b_3 > 1) Error (); else { alarm_flag = b_3; writeEEPROM (1, alarm_flag); //записали в ЕЕПРОМ состояние флага if (alarm_flag) //если будильник включен - включили его в RTC { flag = ReadRTC (0x0F); WriteRTC (bcd2bin (0b10000000), a_mnth_addr); flag = ReadRTC (0x0F); } else //если выключен - выключили { flag = ReadRTC (0x0F); WriteRTC (0, a_mnth_addr); flag = ReadRTC (0x0F); } putsf ("current alarm status: "); if (alarm_flag) {putsf ("on at "); putchar (ReadRTC (a_hrs_addr)/10+48); putchar (ReadRTC (a_hrs_addr)%10+48); putchar (':'); putchar (ReadRTC (a_min_addr)/10+48); putchar (ReadRTC (a_min_addr)%10+48);} else putsf ("off"); sound (30); } }; break; case show_sec: { if (b_3 > 1) Error (); else { sec_mode = b_3; putsf ("currently "); if (sec_mode) putsf ("show sec on"); else putsf ("show sec off"); } }; break; case show_data: { if (b_6 > 63 || b_5 > 99 || b_4 > 99 || b_3 > 99) Error (); else { first_b = b_3; second_b = b_4; third_b = b_5; point_b = b_6; data_mode = 1; data_cnt = 0; putsf ("OK"); } }; break; case get_time: { if (b_6 != 0 || b_5 != 0 || b_4 != 0 || b_3 != 0) Error (); else {putsf ("current time: "); putchar (ReadRTC (hrs_addr)/10+48); putchar (ReadRTC (hrs_addr)%10+48); putchar (':'); putchar (ReadRTC (min_addr)/10+48); putchar (ReadRTC (min_addr)%10+48); putchar (':'); putchar (ReadRTC (sec_addr)/10+48); putchar (ReadRTC (sec_addr)%10+48);} }; break; case get_date: { if (b_6 != 0 || b_5 != 0 || b_4 != 0 || b_3 != 0) Error (); else {putsf ("current date: "); putchar (ReadRTC (day_addr)/10+48); putchar (ReadRTC (day_addr)%10+48); putchar (':'); putchar (ReadRTC (mnth_addr)/10+48); putchar (ReadRTC (mnth_addr)%10+48); putchar (':'); putchar (ReadRTC (year_addr)/10+48); putchar (ReadRTC (year_addr)%10+48);} }; break; case show_date: { if (b_6 != 0 || b_5 != 0 || b_4 != 0 || b_3 != 0) Error (); else { date_mode = 1; date_cnt = 0; putsf ("OK"); } }; break; case power_en: { if (b_6 != 0 || b_5 != 0 || b_4 != 0 || b_3 != 0) Error (); else { Power_en (); putsf ("power "); if (!pen) putsf ("on"); else putsf ("off"); } }; break; default: Error (); } } else { Error (); sound (30); delay_ms (30); sound (30); delay_ms (30); sound (30); delay_ms (30); } rx_counter=0; rx_wr_index=0; rx_rd_index=0; recieve_cmp = 0; #asm("sei") } if (but1) //если нажали кнопку 1 { delay_ms(10); if (but1) { TCCR1B=0x05; //запустили таймер 1 while (but1) #asm("wdr"); //ждем, пока отпустим кнопку switch (flag_1) { case 0: break; case 1: Alarm_setup (); break; case 2: Time_setup (); break; case 3: Sys_setup (); } reset_flag (); } } if (but2) //если нажали кнопку 2 { delay_ms(10); if (but2) { TCCR1B=0x05; //запустили таймер 1 while (but2) #asm("wdr"); //ждем, пока отпустим кнопку switch (flag_2) { case 0: { date_mode = 1; //включили показ даты date_cnt = 0; //обнулили счетчик показов } break; case 1: break; case 2: break; case 3: } reset_flag (); } } if (but3) //если нажали кнопку 3 { delay_ms(10); if (but3) { TCCR1B=0x05; //запустили таймер 1 while (but3) #asm("wdr"); //ждем, пока отпустим кнопку switch (flag_3) { case 0: sec_mode = ~sec_mode; break; case 1: Power_en (); break; case 2: break; case 3: } reset_flag (); } } if (but4) //если нажали кнопку 4 { delay_ms(10); if (but4) { TCCR1B=0x05; //запустили таймер 1 while (but4) #asm("wdr"); //ждем, пока отпустим кнопку switch (flag_4) { case 0: break; case 1: Alarm_OnOff (); break; case 2: break; case 3: } reset_flag (); } } }; } // EEPROM Data Read Function - чтение байта из EEPROM по заданному адресу unsigned int readEEPROM (unsigned int addr) { unsigned int data = 0; while (EECR.1 != 0); EEAR = addr; EECR.0 = 1; EECR.0 = 0; data = EEDR; return data; } // EEPROM Data Write Function - запись байта data в EEPROM по заданному адресу void writeEEPROM (unsigned int addr, unsigned char data) { while (EECR.1 != 0); EEAR = addr; EEDR = data; EECR.2 = 1; EECR.1 = 1; } // EEPROM Data Copy Function - копирование данных из ячейки в ячейку void copyEEPROM (unsigned int saddr, unsigned char taddr) { writeEEPROM (taddr, readEEPROM (saddr)); } // EEPROM 2-byte Data Write Function - запись двухбайтового числа в 2 последовательные ячейки с адресом первой addr void _2bwriteEEPROM (unsigned int addr, unsigned int data) { writeEEPROM (addr, (unsigned char)(data / 256)); writeEEPROM (addr + 1, (unsigned char)(data % 256)); } // EEPROM 2-byte Data Read Function - чтение двухбайтового числа из 2 последовательных ячеек с адресом первой addr unsigned int _2breadEEPROM (unsigned int addr) { unsigned int data = 0; data = 256 * readEEPROM(addr) + readEEPROM(addr + 1); return data; } // fireball generator + power supply burn very nice fireforks #define sdata PORTA.5  #define strobe PORTA.7  #define shift PORTA.6  unsigned char sign (char digit) //индикатор { unsigned char temp = 0; switch (digit) { case 1 : temp = 0b00000001; break; case 2 : temp = 0b00000100; break; case 3 : temp = 0b00000101; break; case 4 : temp = 0b00001000; break; case 5 : temp = 0b00001001; break; case 6 : temp = 0b00001100; break; case 7 : temp = 0b00001101; break; case 8 : temp = 0b00000010; break; case 9 : temp = 0b00000011; break; case 0 : temp = 0b00000000; break; default: temp = 0b00001111; } return temp; } void disp_out (char dig1, char dig2, char dig3, char dig4, char dig5, char dig6, char p1, char p2, char p3, char p4, char p5, char p6) { unsigned long int show = 0; unsigned char i = 0; show = show + sign (dig6); show = (show << 4) + sign (dig5); show = (show << 4) + sign (dig4); show = (show << 4) + sign (dig3); show = (show << 4) + sign (dig2); show = (show << 4) + sign (dig1); show = (show << 8); if (p1) show = show | 0x02; if (p2) show = show | 0x04; if (p3) show = show | 0x08; if (p4) show = show | 0x10; if (p5) show = show | 0x20; if (p6) show = show | 0x40; for (i = 0; i < 32; i++) { if ((show & 1)) sdata = 1; else sdata = 0; shift = 1; shift = 0; show = show >> 1; }; strobe = 1; strobe = 0; } // #define SDA_LN 7 //линия SDA #define SCL_LN 6 //линия SCL #define I2C_PIN PIND //порт входа #define I2C_DDR DDRD //порт направления #define I2C_PORT PORTD //порт выхода #define IN_LN 0 //линия на вход #define OUT_LN 1 //линия на выход #define I2C_T 0.00001 //период импульса синхронизации (1/I2C_T = частота шины) //при программной реализации шины unsigned char i2c_error_soft; //если >0, то произошла ошибка при работе с I2C //Прототипы функций void i2c_init_soft(void); void i2c_start_soft(void); void i2c_stop_soft(void); void i2c_tx_soft(unsigned char byte); unsigned char i2c_rx_soft(unsigned char last_byte); unsigned char in_sda(void); void sda_io(unsigned char io_c); void scl_set(unsigned char set_c); void sda_set(unsigned char set_c); //Инициализация программной шины I2C void i2c_init_soft(void) { I2C_DDR&=~(1<<SDA_LN); //изначально линии SDA I2C_DDR&=~(1<<SCL_LN); //и SCL в высокоимпедансном состоянии I2C_PORT&=~(1<<SDA_LN); //и на них поддерживается за счет внешних резисторов I2C_PORT&=~(1<<SCL_LN); //высокий уровень i2c_error_soft=0; //изначально ошибок нет :-) } //Возвращает уровень линии SDA unsigned char in_sda(void) { if(I2C_PIN&(1<<SDA_LN)) return 1; else return 0; } //Устанавливает линию SDA на вход или выход void sda_io(unsigned char io_c) { if(io_c==IN_LN) I2C_DDR&=~(1<<SDA_LN); else I2C_DDR|=(1<<SDA_LN); delay_us(10); } //Устанавливает уровень на линии SCL void scl_set(unsigned char set_c) { if(set_c) { I2C_DDR&=~(1<<SCL_LN); I2C_PORT&=~(1<<SCL_LN); } else { I2C_DDR|=(1<<SCL_LN); I2C_PORT&=~(1<<SCL_LN); } delay_us(10); } //Устанавливает уровень на линии SDA void sda_set(unsigned char set_c) { if(set_c) { I2C_DDR&=~(1<<SDA_LN); I2C_PORT&=~(1<<SDA_LN); } else { I2C_DDR|=(1<<SDA_LN); I2C_PORT&=~(1<<SDA_LN); } delay_us(10); } //Формирует условие "СТАРТ" void i2c_start_soft(void) { if(i2c_error_soft) return; scl_set(1); sda_set(0); scl_set(0); } //Формирует условие "СТОП" void i2c_stop_soft(void) { sda_set(0); scl_set(1); sda_set(1); if(i2c_error_soft) i2c_init_soft(); } //Посылка байта void i2c_tx_soft(unsigned char byte) { unsigned char i=0; if(i2c_error_soft) return; for(i=0;i<8;i++) { if(byte&0x80) sda_set(1); else sda_set(0); scl_set(1); scl_set(0); byte<<=1; } sda_io(IN_LN); scl_set(1); i2c_error_soft=in_sda(); scl_set(0); sda_io(OUT_LN); } //Прием байта, если last_byte=0, то принимаем последний байт и подтверждение от мастера не нужно unsigned char i2c_rx_soft(unsigned char last_byte) { unsigned char data=0; unsigned char mask=0x80; unsigned char i=0; if(i2c_error_soft) return 0; sda_io(IN_LN); for(i=0;i<8;i++) { scl_set(1); if(in_sda()) data=data+mask; mask>>=1; scl_set(0); } sda_io(OUT_LN); if(last_byte) sda_set(0); else sda_set(1); scl_set(1); scl_set(0); return data; } 

это тест не надо никуда прошивать

 

//**********************************************************************************
//* Main code for an Arduino based Nixie clock. Features:                          *
//*  - Real Time Clock interface for DS3231                                        *
//*  - WiFi Clock interface for the WiFiModule                                     *
//*  - Digit fading with configurable fade length                                  *
//*  - Digit scrollback with configurable scroll speed                             *
//*  - Configuration stored in EEPROM                                              *
//*  - Low hardware component count (as much as possible done in code)             *
//*  - Single button operation with software debounce                              *
//*  - Single K155ID1 for digit display (other versions use 2 or even 6!)          *
//*  - Automatic dimming, using a Light Dependent Resistor                         *
//*  - RGB back light management                                                   *
//*                                                                                *
//*  nixie@protonmail.ch                                                           *
//*                                                                                *
//**********************************************************************************
//**********************************************************************************
// Standard Libraries
#include <avr/io.h>
#include <EEPROM.h>
#include <Wire.h>
#include <avr/wdt.h>

// Clock specific libraries, install these with "Sketch -> Include Library -> Add .ZIP library
// using the ZIP files in the "libraries" directory
#include <DS3231.h>             // https://github.com/NorthernWidget/DS3231 (Wickert 1.0.2)
#include <TimeLib.h>            // http://playground.arduino.cc/code/time (Margolis 1.5.0)

// Other parts of the code, broken out for clarity
#include "ClockButton.h"
#include "Transition.h"
#include "DisplayDefs.h"
#include "I2CDefs.h"

//**********************************************************************************
//**********************************************************************************
//*                               Constants                                        *
//**********************************************************************************
//**********************************************************************************
#define EE_12_24              1      // 12 or 24 hour mode
#define EE_FADE_STEPS         2      // How quickly we fade, higher = slower
#define EE_DATE_FORMAT        3      // Date format to display
#define EE_DAY_BLANKING       4      // Blanking setting
#define EE_DIM_DARK_LO        5      // Dimming dark value
#define EE_DIM_DARK_HI        6      // Dimming dark value
#define EE_BLANK_LEAD_ZERO    7      // If we blank leading zero on hours
#define EE_DIGIT_COUNT_HI     8      // The number of times we go round the main loop
#define EE_DIGIT_COUNT_LO     9      // The number of times we go round the main loop
#define EE_SCROLLBACK         10     // if we use scollback or not
#define EE_FADE               11     // if we use fade or not
#define EE_PULSE_LO           12     // The pulse on width for the PWM mode
#define EE_PULSE_HI           13     // The pulse on width for the PWM mode
#define EE_SCROLL_STEPS       14     // The steps in a scrollback
#define EE_BACKLIGHT_MODE     15     // The back light node
#define EE_DIM_BRIGHT_LO      16     // Dimming bright value
#define EE_DIM_BRIGHT_HI      17     // Dimming bright value
#define EE_DIM_SMOOTH_SPEED   18     // Dimming adaptation speed
#define EE_RED_INTENSITY      19     // Red channel backlight max intensity
#define EE_GRN_INTENSITY      20     // Green channel backlight max intensity
#define EE_BLU_INTENSITY      21     // Blue channel backlight max intensity
#define EE_HV_VOLTAGE         22     // The HV voltage we want to use
#define EE_SUPPRESS_ACP       23     // Do we want to suppress ACP during dimmed time
#define EE_HOUR_BLANK_START   24     // Start of daily blanking period
#define EE_HOUR_BLANK_END     25     // End of daily blanking period
#define EE_CYCLE_SPEED        26     // How fast the color cycling does it's stuff
#define EE_PWM_TOP_LO         27     // The PWM top value if we know it, 0xFF if we need to calculate
#define EE_PWM_TOP_HI         28     // The PWM top value if we know it, 0xFF if we need to calculate
#define EE_HVG_NEED_CALIB     29     // 1 if we need to calibrate the HVGenerator, otherwise 0
#define EE_MIN_DIM_LO         30     // The min dim value
#define EE_MIN_DIM_HI         31     // The min dim value
#define EE_ANTI_GHOST         32     // The value we use for anti-ghosting
#define EE_NEED_SETUP         33     // used for detecting auto config for startup. By default the flashed, empty EEPROM shows us we need to do a setup 
#define EE_USE_LDR            34     // if we use the LDR or not (if we don't use the LDR, it has 100% brightness
#define EE_BLANK_MODE         35     // blank tubes, or LEDs or both
#define EE_SLOTS_MODE         36     // Show date every now and again

// Software version shown in config menu
#define SOFTWARE_VERSION      56

// Display handling
#define DIGIT_DISPLAY_COUNT   1000 // The number of times to traverse inner fade loop per digit
#define DIGIT_DISPLAY_ON      0    // Switch on the digit at the beginning by default
#define DIGIT_DISPLAY_OFF     999  // Switch off the digit at the end by default
#define DIGIT_DISPLAY_NEVER   -1   // When we don't want to switch on or off (i.e. blanking)
#define DISPLAY_COUNT_MAX     2000 // Maximum value we can set to
#define DISPLAY_COUNT_MIN     500  // Minimum value we can set to

#define MIN_DIM_DEFAULT       100  // The default minimum dim count
#define MIN_DIM_MIN           100  // The minimum dim count
#define MIN_DIM_MAX           500  // The maximum dim count

#define SENSOR_LOW_MIN        0
#define SENSOR_LOW_MAX        900
#define SENSOR_LOW_DEFAULT    100  // Dark
#define SENSOR_HIGH_MIN       0
#define SENSOR_HIGH_MAX       900
#define SENSOR_HIGH_DEFAULT   700 // Bright

#define SENSOR_SMOOTH_READINGS_MIN     1
#define SENSOR_SMOOTH_READINGS_MAX     255
#define SENSOR_SMOOTH_READINGS_DEFAULT 100  // Speed at which the brighness adapts to changes

#define BLINK_COUNT_MAX                25   // The number of impressions between blink state toggle

// The target voltage we want to achieve
#define HVGEN_TARGET_VOLTAGE_DEFAULT 180
#define HVGEN_TARGET_VOLTAGE_MIN     150
#define HVGEN_TARGET_VOLTAGE_MAX     200

// The PWM parameters
#define PWM_TOP_DEFAULT   10000
#define PWM_TOP_MIN       300
#define PWM_TOP_MAX       10000
#define PWM_PULSE_DEFAULT 200
#define PWM_PULSE_MIN     100
#define PWM_PULSE_MAX     500
#define PWM_OFF_MIN       50

// How quickly the scroll works
#define SCROLL_STEPS_DEFAULT 4
#define SCROLL_STEPS_MIN     1
#define SCROLL_STEPS_MAX     40

// The number of dispay impessions we need to fade by default
// 100 is about 1 second
#define FADE_STEPS_DEFAULT 50
#define FADE_STEPS_MAX     200
#define FADE_STEPS_MIN     20

#define SECS_MAX  60
#define MINS_MAX  60
#define HOURS_MAX 24

#define COLOUR_CNL_MAX                  15
#define COLOUR_RED_CNL_DEFAULT          15
#define COLOUR_GRN_CNL_DEFAULT          0
#define COLOUR_BLU_CNL_DEFAULT          0
#define COLOUR_CNL_MIN                  0

// Clock modes - normal running is MODE_TIME, other modes accessed by a middle length ( 1S < press < 2S ) button press
#define MODE_MIN                        0
#define MODE_TIME                       0

// Time setting, need all six digits, so no flashing mode indicator
#define MODE_HOURS_SET                  1
#define MODE_MINS_SET                   2
#define MODE_SECS_SET                   3
#define MODE_DAYS_SET                   4
#define MODE_MONTHS_SET                 5
#define MODE_YEARS_SET                  6

// Basic settings
#define MODE_12_24                      7  // 0 = 24, 1 = 12
#define HOUR_MODE_DEFAULT               false
#define MODE_LEAD_BLANK                 8  // 1 = blanked
#define LEAD_BLANK_DEFAULT              false
#define MODE_SCROLLBACK                 9  // 1 = use scrollback
#define SCROLLBACK_DEFAULT              false
#define MODE_FADE                       10 // 1 = use fade
#define FADE_DEFAULT                    false
#define MODE_DATE_FORMAT                11 // 
#define MODE_DAY_BLANKING               12 // 
#define MODE_HR_BLNK_START              13 // 
#define MODE_HR_BLNK_END                14 // 
#define MODE_SUPPRESS_ACP               15 // 1 = suppress ACP when fully dimmed
#define SUPPRESS_ACP_DEFAULT            true
#define MODE_USE_LDR                    16 // 1 = use LDR, 0 = don't (and have 100% brightness)
#define MODE_USE_LDR_DEFAULT            true
#define MODE_BLANK_MODE                 17 // 
#define MODE_BLANK_MODE_DEFAULT         BLANK_MODE_BOTH

// Display tricks
#define MODE_FADE_STEPS_UP              18 // 
#define MODE_FADE_STEPS_DOWN            19 // 
#define MODE_DISPLAY_SCROLL_STEPS_UP    20 // 
#define MODE_DISPLAY_SCROLL_STEPS_DOWN  21 // 
#define MODE_SLOTS_MODE                 22 // 

// Back light
#define MODE_BACKLIGHT_MODE             23 // 
#define MODE_RED_CNL                    24 // 
#define MODE_GRN_CNL                    25 // 
#define MODE_BLU_CNL                    26 // 
#define MODE_CYCLE_SPEED                27 // speed the colour cycle cyles at

// HV generation
#define MODE_TARGET_HV_UP               28 // 
#define MODE_TARGET_HV_DOWN             29 // 
#define MODE_PULSE_UP                   30 // 
#define MODE_PULSE_DOWN                 31 // 

#define MODE_MIN_DIM_UP                 32 // 
#define MODE_MIN_DIM_DOWN               33 // 

#define MODE_ANTI_GHOST_UP              34 // 
#define MODE_ANTI_GHOST_DOWN            35 // 

// Temperature
#define MODE_TEMP                       36 // 

// Software Version
#define MODE_VERSION                    37 // 

// Tube test - all six digits, so no flashing mode indicator
#define MODE_TUBE_TEST                  38

#define MODE_MAX                        38

// Pseudo mode - burn the tubes and nothing else
#define MODE_DIGIT_BURN                 99 // Digit burn mode - accesible by super long press

// Temporary display modes - accessed by a short press ( < 1S ) on the button when in MODE_TIME
#define TEMP_MODE_MIN                   0
#define TEMP_MODE_DATE                  0 // Display the date for 5 S
#define TEMP_MODE_TEMP                  1 // Display the temperature for 5 S
#define TEMP_MODE_LDR                   2 // Display the normalised LDR reading for 5S, returns a value from 100 (dark) to 999 (bright)
#define TEMP_MODE_VERSION               3 // Display the version for 5S
#define TEMP_IP_ADDR12                  4 // IP xxx.yyy.zzz.aaa: xxx.yyy
#define TEMP_IP_ADDR34                  5 // IP xxx.yyy.zzz.aaa: zzz.aaa
#define TEMP_IMPR                       6 // number of impressions per second
#define TEMP_MODE_MAX                   6

#define DATE_FORMAT_MIN                 0
#define DATE_FORMAT_YYMMDD              0
#define DATE_FORMAT_MMDDYY              1
#define DATE_FORMAT_DDMMYY              2
#define DATE_FORMAT_MAX                 2
#define DATE_FORMAT_DEFAULT             2

#define DAY_BLANKING_MIN                0
#define DAY_BLANKING_NEVER              0  // Don't blank ever (default)
#define DAY_BLANKING_WEEKEND            1  // Blank during the weekend
#define DAY_BLANKING_WEEKDAY            2  // Blank during weekdays
#define DAY_BLANKING_ALWAYS             3  // Always blank
#define DAY_BLANKING_HOURS              4  // Blank between start and end hour every day
#define DAY_BLANKING_WEEKEND_OR_HOURS   5  // Blank between start and end hour during the week AND all day on the weekend
#define DAY_BLANKING_WEEKDAY_OR_HOURS   6  // Blank between start and end hour during the weekends AND all day on week days
#define DAY_BLANKING_WEEKEND_AND_HOURS  7  // Blank between start and end hour during the weekend
#define DAY_BLANKING_WEEKDAY_AND_HOURS  8  // Blank between start and end hour during week days
#define DAY_BLANKING_MAX                8
#define DAY_BLANKING_DEFAULT            0

#define BLANK_MODE_MIN                  0
#define BLANK_MODE_TUBES                0  // Use blanking for tubes only 
#define BLANK_MODE_LEDS                 1  // Use blanking for LEDs only
#define BLANK_MODE_BOTH                 2  // Use blanking for tubes and LEDs
#define BLANK_MODE_MAX                  2
#define BLANK_MODE_DEFAULT              2

#define BACKLIGHT_MIN                   0
#define BACKLIGHT_FIXED                 0   // Just define a colour and stick to it
#define BACKLIGHT_PULSE                 1   // pulse the defined colour
#define BACKLIGHT_CYCLE                 2   // cycle through random colours
#define BACKLIGHT_FIXED_DIM             3   // A defined colour, but dims with bulb dimming
#define BACKLIGHT_PULSE_DIM             4   // pulse the defined colour, dims with bulb dimming
#define BACKLIGHT_CYCLE_DIM             5   // cycle through random colours, dims with bulb dimming
#define BACKLIGHT_MAX                   5
#define BACKLIGHT_DEFAULT               0

#define CYCLE_SPEED_MIN                 4
#define CYCLE_SPEED_MAX                 64
#define CYCLE_SPEED_DEFAULT             10

#define ANTI_GHOST_MIN                  0
#define ANTI_GHOST_MAX                  50
#define ANTI_GHOST_DEFAULT              0

#define TEMP_DISPLAY_MODE_DUR_MS        5000

#define USE_LDR_DEFAULT                 true

// Limit on the length of time we stay in test mode
#define TEST_MODE_MAX_MS                60000

#define SLOTS_MODE_MIN                  0
#define SLOTS_MODE_NONE                 0   // Don't use slots effect
#define SLOTS_MODE_1M_SCR_SCR           1   // use slots effect every minute, scroll in, scramble out
#define SLOTS_MODE_MAX                  1
#define SLOTS_MODE_DEFAULT              1

// RTC address
#define RTC_I2C_ADDRESS                 0x68

#define MAX_WIFI_TIME                   5

#define DO_NOT_APPLY_LEAD_0_BLANK     false
#define APPLY_LEAD_0_BLANK            true

//**********************************************************************************
//**********************************************************************************
//*                               Variables                                        *
//**********************************************************************************
//**********************************************************************************

// ***** Pin Defintions ****** Pin Defintions ****** Pin Defintions ******

// SN74141/K155ID1
// These are now managed directly on PORT B, we don't use digitalWrite() for these
#define ledPin_0_a  13    // package pin 19 // PB5
#define ledPin_0_b  10    // package pin 16 // PB2
#define ledPin_0_c  8     // package pin 14 // PB0
#define ledPin_0_d  12    // package pin 18 // PB4

// anode pins
#define ledPin_a_6  0     // low  - Secs  units // package pin 2  // PD0
#define ledPin_a_5  1     //      - Secs  tens  // package pin 3  // PD1
#define ledPin_a_4  2     //      - Mins  units // package pin 4  // PD2
#define ledPin_a_3  4     //      - Mins  tens  // package pin 6  // PD4
#define ledPin_a_2  A2    //      - Hours units // package pin 25 // PC2
#define ledPin_a_1  A3    // high - Hours tens  // package pin 26 // PC3

// button input
#define inputPin1   7     // package pin 13 // PD7

// PWM pin used to drive the DC-DC converter
#define hvDriverPin 9     // package pin 15 // PB1

// Tick led - PWM capable
#define tickLed     11    // package pin 17 // PB3

// PWM capable output for backlight
#define RLed        6     // package pin 12 // PD6
#define GLed        5     // package pin 11 // PD5
#define BLed        3     // package pin 5  // PD3

#define sensorPin   A0    // Package pin 23 // PC0 // Analog input pin for HV sense: HV divided through 390k and 4k7 divider, using 5V reference
#define LDRPin      A1    // Package pin 24 // PC1 // Analog input for Light dependent resistor.

//**********************************************************************************

Transition transition(500, 1000, 3000);

// ********************** HV generator variables *********************
int hvTargetVoltage = HVGEN_TARGET_VOLTAGE_DEFAULT;
int pwmTop = PWM_TOP_DEFAULT;
int pwmOn = PWM_PULSE_DEFAULT;

// All-In-One Rev1 has a mix up in the tube wiring. All other clocks are 
// correct.
#define NOT_AIO_REV1 // [AIO_REV1,NOT_AIO_REV1]

// Used for special mappings of the K155ID1 -> digit (wiring aid)
// allows the board wiring to be much simpler
#ifdef AIO_REV1 
  // This is a mapping for All-In-One Revision 1 ONLY! Not generally used.
  byte decodeDigit[16] = {3,2,8,9,0,1,5,4,6,7,10,10,10,10,10,10};
#else
  byte decodeDigit[16] = {2,3,7,6,4,5,1,0,9,8,10,10,10,10,10,10};
#endif

// Driver pins for the anodes
byte anodePins[6] = {ledPin_a_1, ledPin_a_2, ledPin_a_3, ledPin_a_4, ledPin_a_5, ledPin_a_6};

// precalculated values for turning on and off the HV generator
// Put these in TCCR1B to turn off and on
byte tccrOff;
byte tccrOn;
int rawHVADCThreshold;
double sensorHVSmoothed = 0;

// ************************ Display management ************************
byte NumberArray[6]    = {0, 0, 0, 0, 0, 0};
byte currNumberArray[6] = {0, 0, 0, 0, 0, 0};
byte displayType[6]    = {FADE, FADE, FADE, FADE, FADE, FADE};
byte fadeState[6]      = {0, 0, 0, 0, 0, 0};
byte ourIP[4]          = {0, 0, 0, 0}; // set by the WiFi module, if attached

// how many fade steps to increment (out of DIGIT_DISPLAY_COUNT) each impression
// 100 is about 1 second
int fadeSteps = FADE_STEPS_DEFAULT;
int digitOffCount = DIGIT_DISPLAY_OFF;
int scrollSteps = SCROLL_STEPS_DEFAULT;
boolean scrollback = true;
boolean fade = true;
byte antiGhost = ANTI_GHOST_DEFAULT;
int dispCount = DIGIT_DISPLAY_COUNT + antiGhost;
float fadeStep = DIGIT_DISPLAY_COUNT / fadeSteps;

// For software blinking
int blinkCounter = 0;
boolean blinkState = true;

// leading digit blanking
boolean blankLeading = false;

// Dimming value
const int DIM_VALUE = DIGIT_DISPLAY_COUNT / 5;
int minDim = MIN_DIM_DEFAULT;

unsigned int tempDisplayModeDuration;      // time for the end of the temporary display
int  tempDisplayMode;

int acpOffset = 0;        // Used to provide one arm bandit scolling
int acpTick = 0;          // The number of counts before we scroll
boolean suppressACP = false;
byte slotsMode = SLOTS_MODE_DEFAULT;

byte currentMode = MODE_TIME;   // Initial cold start mode
byte nextMode = currentMode;
byte blankHourStart = 0;
byte blankHourEnd = 0;
byte blankMode = 0;
boolean blankTubes = false;
boolean blankLEDs = false;

// ************************ Ambient light dimming ************************
int dimDark = SENSOR_LOW_DEFAULT;
int dimBright = SENSOR_HIGH_DEFAULT;
double sensorLDRSmoothed = 0;
double sensorFactor = (double)(DIGIT_DISPLAY_OFF) / (double)(dimBright - dimDark);
int sensorSmoothCountLDR = SENSOR_SMOOTH_READINGS_DEFAULT;
int sensorSmoothCountHV = SENSOR_SMOOTH_READINGS_DEFAULT / 8;
boolean useLDR = true;

// ************************ Clock variables ************************
// RTC, uses Analogue pins A4 (SDA) and A5 (SCL)
DS3231 Clock;

// State variables for detecting changes
byte lastSec;
unsigned long nowMillis = 0;
unsigned long lastCheckMillis = 0;

byte dateFormat = DATE_FORMAT_DEFAULT;
byte dayBlanking = DAY_BLANKING_DEFAULT;
boolean blanked = false;
byte blankSuppressStep = 0;    // The press we are on: 1 press = suppress for 1 min, 2 press = 1 hour, 3 = 1 day
unsigned long blankSuppressedMillis = 0;   // The end time of the blanking, 0 if we are not suppressed
unsigned long blankSuppressedSelectionTimoutMillis = 0;   // Used for determining the end of the blanking period selection timeout
boolean hourMode = false;
boolean triggeredThisSec = false;

byte useRTC = false;  // true if we detect an RTC
byte useWiFi = 0; // the number of minutes ago we recevied information from the WiFi module, 0 = don't use WiFi

// **************************** LED management ***************************
boolean upOrDown;

// Blinking colons led in settings modes
int ledBlinkCtr = 0;
int ledBlinkNumber = 0;

byte backlightMode = BACKLIGHT_DEFAULT;

// Back light intensities
byte redCnl = COLOUR_RED_CNL_DEFAULT;
byte grnCnl = COLOUR_GRN_CNL_DEFAULT;
byte bluCnl = COLOUR_BLU_CNL_DEFAULT;
byte cycleCount = 0;
byte cycleSpeed = CYCLE_SPEED_DEFAULT;

// Back light cycling
int colors[3];
int changeSteps = 0;
byte currentColour = 0;

int impressionsPerSec = 0;
int lastImpressionsPerSec = 0;

// ********************** Input switch management **********************
ClockButton button1(inputPin1, false);

// **************************** digit healing ****************************
// This is a special mode which repairs cathode poisoning by driving a
// single element at full power. To be used with care!
// In theory, this should not be necessary because we have ACP every 10mins,
// but some tubes just want to be poisoned
byte digitBurnDigit = 0;
byte digitBurnValue = 0;

//**********************************************************************************
//**********************************************************************************
//*                                    Setup                                       *
//**********************************************************************************
//**********************************************************************************
void setup()
{
  pinMode(ledPin_0_a, OUTPUT);
  pinMode(ledPin_0_b, OUTPUT);
  pinMode(ledPin_0_c, OUTPUT);
  pinMode(ledPin_0_d, OUTPUT);

  pinMode(ledPin_a_1, OUTPUT);
  pinMode(ledPin_a_2, OUTPUT);
  pinMode(ledPin_a_3, OUTPUT);
  pinMode(ledPin_a_4, OUTPUT);
  pinMode(ledPin_a_5, OUTPUT);
  pinMode(ledPin_a_6, OUTPUT);

  pinMode(tickLed, OUTPUT);
  pinMode(RLed, OUTPUT);
  pinMode(GLed, OUTPUT);
  pinMode(BLed, OUTPUT);

  // The LEDS sometimes glow at startup, it annoys me, so turn them completely off
  analogWrite(tickLed, 0);
  analogWrite(RLed, 0);
  analogWrite(GLed, 0);
  analogWrite(BLed, 0);

  // NOTE:
  // Grounding the input pin causes it to actuate
  pinMode(inputPin1, INPUT ); // set the input pin 1
  digitalWrite(inputPin1, HIGH); // set pin 1 as a pull up resistor.

  // Set the driver pin to putput
  pinMode(hvDriverPin, OUTPUT);

  /* disable global interrupts while we set up them up */
  cli();

  // **************************** HV generator ****************************

  TCCR1A = 0;    // disable all PWM on Timer1 whilst we set it up
  TCCR1B = 0;    // disable all PWM on Timer1 whilst we set it up

  // Configure timer 1 for Fast PWM mode via ICR1, with prescaling=1
  TCCR1A = (1 << WGM11);
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);

  tccrOff = TCCR1A;

  TCCR1A |= (1 <<  COM1A1);  // enable PWM on port PD4 in non-inverted compare mode 2

  tccrOn = TCCR1A;

  // Set up timer 2 like timer 0 (for RGB leds)
  TCCR2A = (1 << COM2B1) | (1 << WGM21) | (1 << WGM20);
  TCCR2B = (1 << CS22);

  // we don't need the HV yet, so turn it off
  TCCR1A = tccrOff;

  /* enable global interrupts */
  sei();

  // **********************************************************************

  // Set up the PRNG with something so that it looks random
  randomSeed(analogRead(LDRPin));

  // Test if the button is pressed for factory reset
  for (int i = 0 ; i < 20 ; i++ ) {
    button1.checkButton(nowMillis);
  }

  // User requested factory reset
  if (button1.isButtonPressedNow()) {
    // do this before the flashing, because this way we can set up the EEPROM to
    // autocalibrate on first start (press factory reset and then power off before
    // flashing ends)
    EEPROM.write(EE_HVG_NEED_CALIB, true);

    // Flash some ramdom c0lours to signal that we have accepted the factory reset
    for (int i = 0 ; i < 60 ; i++ ) {
      randomRGBFlash(20);
    }

    // mark that we have done the EEPROM setup
    EEPROM.write(EE_NEED_SETUP, true);
  }

  // If the button is held down while we are flashing, then do the test pattern
  boolean doTestPattern = false;

  // Detect factory reset: button pressed on start or uninitialised EEPROM
  if (EEPROM.read(EE_NEED_SETUP)) {
    doTestPattern = true;
  }

  // Clear down any spurious button action
  button1.reset();

  // Test if the button is pressed for factory reset
  for (int i = 0 ; i < 20 ; i++ ) {
    button1.checkButton(nowMillis);
  }

  if (button1.isButtonPressedNow()) {
    doTestPattern = true;
  }

  // Read EEPROM values
  readEEPROMValues();

  // set our PWM profile
  setPWMOnTime(pwmOn);
  setPWMTopTime(pwmTop);

  // Set the target voltage
  rawHVADCThreshold = getRawHVADCThreshold(hvTargetVoltage);

  // HV GOOOO!!!!
  TCCR1A = tccrOn;

  if (doTestPattern) {
    boolean oldUseLDR = useLDR;
    byte oldBacklightMode = backlightMode;

    // reset the EEPROM values
    factoryReset();

    // turn off LDR
    useLDR = false;

    // turn off Scrollback
    scrollback = false;

    // All the digits on full
    allBright();

    int secCount = 0;
    lastCheckMillis = millis();

    boolean inLoop = true;

    // We don't want to stay in test mode forever
    long startTestMode = lastCheckMillis;

    while (inLoop) {
      nowMillis = millis();
      if (nowMillis - lastCheckMillis > 1000) {
        lastCheckMillis = nowMillis;
        secCount++;
        secCount = secCount % 10;
      }

      // turn off test mode
      blankTubes = (nowMillis - startTestMode > TEST_MODE_MAX_MS);

      loadNumberArraySameValue(secCount);
      outputDisplay();
      checkHVVoltage();

      setLedsTestPattern(nowMillis);
      button1.checkButton(nowMillis);

      if (button1.isButtonPressedNow() && (secCount == 8)) {
        inLoop = false;
        blankTubes = false;
      }
    }

    useLDR = oldUseLDR;
    backlightMode = oldBacklightMode;
  }

  // reset the LEDs
  setLedsTestPattern(0);

  // Set up the HVG if we need to
  if (EEPROM.read(EE_HVG_NEED_CALIB)) {
    calibrateHVG();

    // Save the PWM values
    EEPROM.write(EE_PULSE_LO, pwmOn % 256);
    EEPROM.write(EE_PULSE_HI, pwmOn / 256);
    EEPROM.write(EE_PWM_TOP_LO, pwmTop % 256);
    EEPROM.write(EE_PWM_TOP_HI, pwmTop / 256);

    // Mark that we don't need to do this next time
    EEPROM.write(EE_HVG_NEED_CALIB, false);
  }

  // and return it to target voltage so we can regulate the PWM on time
  rawHVADCThreshold = getRawHVADCThreshold(hvTargetVoltage);

  // Clear down any spurious button action
  button1.reset();

  // initialise the internal time (in case we don't find the time provider)
  nowMillis = millis();
  setTime(12, 34, 56, 1, 3, 2017);
  getRTCTime();

  // Show the version for 1 s
  tempDisplayMode = TEMP_MODE_VERSION;
  tempDisplayModeDuration = TEMP_DISPLAY_MODE_DUR_MS;

  // don't blank anything right now
  blanked = false;
  setTubesAndLEDSBlankMode();

  // mark that we have done the EEPROM setup
  EEPROM.write(EE_NEED_SETUP, false);

  // enable watchdog
  wdt_enable(WDTO_8S);
}

//**********************************************************************************
//**********************************************************************************
//*                              Main loop                                         *
//**********************************************************************************
//**********************************************************************************
void loop()
{
  nowMillis = millis();

  // shows us how fast the inner loop is running
  impressionsPerSec++;

  // -------------------------------------------------------------------------------
  
  if (abs(nowMillis - lastCheckMillis) >= 1000) {
    if ((second() == 0) && (!triggeredThisSec)) {
      if ((minute() == 0)) {
        if (hour() == 0) {
          performOncePerDayProcessing();
        }
        
        performOncePerHourProcessing();
      }
      
      performOncePerMinuteProcessing();
    }

    performOncePerSecondProcessing();

    // Make sure we don't call multiple times
    triggeredThisSec = true;
    
    if ((second() > 0) && triggeredThisSec) {
      triggeredThisSec = false;
    }

    lastCheckMillis = nowMillis;
  }
  
  // -------------------------------------------------------------------------------

  // Check button, we evaluate below
  button1.checkButton(nowMillis);

  // ******* Preview the next display mode *******
  // What is previewed here will get actioned when
  // the button is released
  if (button1.isButtonPressed2S()) {
    // Just jump back to the start
    nextMode = MODE_MIN;
  } else if (button1.isButtonPressed1S()) {
    nextMode = currentMode + 1;

    if (nextMode > MODE_MAX) {
      nextMode = MODE_MIN;
    }
  }

  // ******* Set the display mode *******
  if (button1.isButtonPressedReleased8S()) {
    // 8 Sec press toggles burn mode
    if (currentMode == MODE_DIGIT_BURN) {
      currentMode = MODE_MIN;
    } else {
      currentMode = MODE_DIGIT_BURN;
    }

    nextMode = currentMode;
  } else if (button1.isButtonPressedReleased2S()) {
    currentMode = MODE_MIN;

    // Store the EEPROM if we exit the config mode
    saveEEPROMValues();

    // Preset the display
    allFadeOrNormal(DO_NOT_APPLY_LEAD_0_BLANK);

    nextMode = currentMode;
  } else if (button1.isButtonPressedReleased1S()) {
    currentMode = nextMode;

    if (currentMode > MODE_MAX) {
      currentMode = MODE_MIN;

      // Store the EEPROM if we exit the config mode
      saveEEPROMValues();

      // Preset the display
      allFadeOrNormal(DO_NOT_APPLY_LEAD_0_BLANK);
    }

    nextMode = currentMode;
  }

  // -------------------------------------------------------------------------------

  // ************* Process the modes *************
  if (nextMode != currentMode) {
    setNextMode(nextMode);
  } else {
    processCurrentMode(currentMode);
  }

  // get the LDR ambient light reading
  digitOffCount = getDimmingFromLDR();
  fadeStep = digitOffCount / fadeSteps;

  // One armed bandit trigger every 10th minute
  if ((currentMode != MODE_DIGIT_BURN) && (nextMode != MODE_DIGIT_BURN)) {
    if (acpOffset == 0) {
      if (((minute() % 10) == 9) && (second() == 15)) {
        // suppress ACP when fully dimmed
        if (suppressACP) {
          if (digitOffCount > minDim) {
            acpOffset = 1;
          }
        } else {
          acpOffset = 1;
        }
      }
    }

    // One armed bandit handling
    if (acpOffset > 0) {
      if (acpTick >= acpOffset) {
        acpTick = 0;
        acpOffset++;
        if (acpOffset == 50) {
          acpOffset = 0;
        }
      } else {
        acpTick++;
      }
    }

    // Set normal output display
    outputDisplay();
  } else {
    // Digit burn mode
    digitOn(digitBurnDigit, digitBurnValue);
  }

  // -------------------------------------------------------------------------------

  // Slow regulation of the voltage
  checkHVVoltage();

  // Prepare the tick and backlight LEDs
  setLeds();
}

// ************************************************************
// Called once per second
// ************************************************************
void performOncePerSecondProcessing() {

  // Store the current value and reset
  lastImpressionsPerSec = impressionsPerSec;
  impressionsPerSec = 0;

  // Change the direction of the pulse
  upOrDown = !upOrDown;

  // If we are in temp display mode, decrement the count
  if (tempDisplayModeDuration > 0) {
    if (tempDisplayModeDuration > 1000) {
      tempDisplayModeDuration -= 1000;
    } else {
      tempDisplayModeDuration = 0;
    }
  }

  // decrement the blanking supression counter
  if (blankSuppressedMillis > 0) {
    if (blankSuppressedMillis > 1000) {
      blankSuppressedMillis -= 1000;
    } else {
      blankSuppressedMillis = 0;
    }
  }

  // Get the blanking status, this may be overridden by blanking suppression
  // Only blank if we are in TIME mode
  if (currentMode == MODE_TIME) {
    boolean nativeBlanked = checkBlanking();

    // Check if we are in blanking suppression mode
    blanked = nativeBlanked && (blankSuppressedMillis == 0);

    // reset the blanking period selection timer
    if (nowMillis > blankSuppressedSelectionTimoutMillis) {
      blankSuppressedSelectionTimoutMillis = 0;
      blankSuppressStep = 0;
    }
  } else {
    blanked = false;
  }
  setTubesAndLEDSBlankMode();

  // feed the watchdog
  wdt_reset();
}

// ************************************************************
// Called once per minute
// ************************************************************
void performOncePerMinuteProcessing() {
  if (useWiFi > 0) {
    if (useWiFi == MAX_WIFI_TIME) {
      // We recently got an update, send to the RTC (if installed)
      setRTC();      
    }
    useWiFi--;
  } else {
    // get the time from the external RTC provider - (if installed)
    getRTCTime();
  }
}

// ************************************************************
// Called once per hour
// ************************************************************
void performOncePerHourProcessing() {
}

// ************************************************************
// Called once per day
// ************************************************************
void performOncePerDayProcessing() {
}

// ************************************************************
// Set the seconds tick led(s) and the back lights
// ************************************************************
void setLeds()
{
  int secsDelta;
  if (upOrDown) {
    secsDelta = (nowMillis - lastCheckMillis);
  } else {
    secsDelta = 1000 - (nowMillis - lastCheckMillis);
  }

  // calculate the PWM factor, goes between minDim% and 100%
  float dimFactor = (float) digitOffCount / (float) DIGIT_DISPLAY_OFF;
  float pwmFactor = (float) secsDelta / (float) 1000.0;

  // Tick led output
  analogWrite(tickLed, getLEDAdjusted(255, pwmFactor, dimFactor));

  if (blankLEDs) {
    analogWrite(RLed, 0);
    analogWrite(GLed, 0);
    analogWrite(BLed, 0);
  } else {
    // RGB Backlight PWM led output
    if (currentMode == MODE_TIME) {
      switch (backlightMode) {
        case BACKLIGHT_FIXED:
          analogWrite(RLed, getLEDAdjusted(rgb_backlight_curve[redCnl], 1, 1));
          analogWrite(GLed, getLEDAdjusted(rgb_backlight_curve[grnCnl], 1, 1));
          analogWrite(BLed, getLEDAdjusted(rgb_backlight_curve[bluCnl], 1, 1));
          break;
        case BACKLIGHT_PULSE:
          analogWrite(RLed, getLEDAdjusted(rgb_backlight_curve[redCnl], pwmFactor, 1));
          analogWrite(GLed, getLEDAdjusted(rgb_backlight_curve[grnCnl], pwmFactor, 1));
          analogWrite(BLed, getLEDAdjusted(rgb_backlight_curve[bluCnl], pwmFactor, 1));
          break;
        case BACKLIGHT_CYCLE:
          cycleColours3(colors);
          analogWrite(RLed, getLEDAdjusted(colors[0], 1, 1));
          analogWrite(GLed, getLEDAdjusted(colors[1], 1, 1));
          analogWrite(BLed, getLEDAdjusted(colors[2], 1, 1));
          break;
        case BACKLIGHT_FIXED_DIM:
          analogWrite(RLed, getLEDAdjusted(rgb_backlight_curve[redCnl], 1, dimFactor));
          analogWrite(GLed, getLEDAdjusted(rgb_backlight_curve[grnCnl], 1, dimFactor));
          analogWrite(BLed, getLEDAdjusted(rgb_backlight_curve[bluCnl], 1, dimFactor));
          break;
        case BACKLIGHT_PULSE_DIM:
          analogWrite(RLed, getLEDAdjusted(rgb_backlight_curve[redCnl], pwmFactor, dimFactor));
          analogWrite(GLed, getLEDAdjusted(rgb_backlight_curve[grnCnl], pwmFactor, dimFactor));
          analogWrite(BLed, getLEDAdjusted(rgb_backlight_curve[bluCnl], pwmFactor, dimFactor));
          break;
        case BACKLIGHT_CYCLE_DIM:
          cycleColours3(colors);
          analogWrite(RLed, getLEDAdjusted(colors[0], 1, dimFactor));
          analogWrite(GLed, getLEDAdjusted(colors[1], 1, dimFactor));
          analogWrite(BLed, getLEDAdjusted(colors[2], 1, dimFactor));
          break;
      }
    } else {
      // Settings modes
      ledBlinkCtr++;
      if (ledBlinkCtr > 40) {
        ledBlinkCtr = 0;

        ledBlinkNumber++;
        if (ledBlinkNumber > nextMode) {
          // Make a pause
          ledBlinkNumber = -2;
        }
      }

      if ((ledBlinkNumber <= nextMode) && (ledBlinkNumber > 0)) {
        if (ledBlinkCtr < 3) {
          analogWrite(RLed, 255);
          analogWrite(GLed, 255);
          analogWrite(BLed, 255);
        } else {
          analogWrite(RLed, 0);
          analogWrite(GLed, 0);
          analogWrite(BLed, 0);
        }
      }
    }
  }
}

// ************************************************************
// Show the preview of the next mode - we stay in this mode until the 
// button is released
// ************************************************************
void setNextMode(int displayMode) {
  // turn off blanking
  blanked = false;
  setTubesAndLEDSBlankMode();

  switch (displayMode) {
    case MODE_TIME: {
        loadNumberArrayTime();
        allFadeOrNormal(APPLY_LEAD_0_BLANK);
        break;
      }
    case MODE_HOURS_SET: {
        if (useWiFi > 0) {
          setNewNextMode(MODE_12_24);
        }
        loadNumberArrayTime();
        highlight0and1();
        break;
      }
    case MODE_MINS_SET: {
        loadNumberArrayTime();
        highlight2and3();
        break;
      }
    case MODE_SECS_SET: {
        loadNumberArrayTime();
        highlight4and5();
        break;
      }
    case MODE_DAYS_SET: {
        loadNumberArrayDate();
        highlightDaysDateFormat();
        break;
      }
    case MODE_MONTHS_SET: {
        loadNumberArrayDate();
        highlightMonthsDateFormat();
        break;
      }
    case MODE_YEARS_SET: {
        loadNumberArrayDate();
        highlightYearsDateFormat();
        break;
      }
    case MODE_12_24: {
        loadNumberArrayConfBool(hourMode, displayMode);
        displayConfig();
        break;
      }
    case MODE_LEAD_BLANK: {
        loadNumberArrayConfBool(blankLeading, displayMode);
        displayConfig();
        break;
      }
    case MODE_SCROLLBACK: {
        loadNumberArrayConfBool(scrollback, displayMode);
        displayConfig();
        break;
      }
    case MODE_FADE: {
        loadNumberArrayConfBool(fade, displayMode);
        displayConfig();
        break;
      }
    case MODE_DATE_FORMAT: {
        loadNumberArrayConfInt(dateFormat, displayMode);
        displayConfig();
        break;
      }
    case MODE_DAY_BLANKING: {
        loadNumberArrayConfInt(dayBlanking, displayMode);
        displayConfig();
        break;
      }
    case MODE_HR_BLNK_START: {
        if ((dayBlanking == DAY_BLANKING_NEVER) ||
            (dayBlanking == DAY_BLANKING_WEEKEND) ||
            (dayBlanking == DAY_BLANKING_WEEKDAY) ||
            (dayBlanking == DAY_BLANKING_ALWAYS)) {
          // Skip past the start and end hour if the blanking mode says it is not relevant
          setNewNextMode(MODE_SUPPRESS_ACP);
        }
        loadNumberArrayConfInt(blankHourStart, displayMode);
        displayConfig();
        break;
      }
    case MODE_HR_BLNK_END: {
        loadNumberArrayConfInt(blankHourEnd, displayMode);
        displayConfig();
        break;
      }
    case MODE_SUPPRESS_ACP: {
        loadNumberArrayConfBool(suppressACP, displayMode);
        displayConfig();
        break;
      }
    case MODE_USE_LDR: {
        loadNumberArrayConfBool(useLDR, displayMode);
        displayConfig();
        break;
      }
    case MODE_BLANK_MODE: {
        loadNumberArrayConfInt(blankMode, displayMode);
        displayConfig();
        break;
      }
    case MODE_FADE_STEPS_UP:
    case MODE_FADE_STEPS_DOWN: {
        loadNumberArrayConfInt(fadeSteps, displayMode);
        displayConfig();
        break;
      }
    case MODE_DISPLAY_SCROLL_STEPS_UP:
    case MODE_DISPLAY_SCROLL_STEPS_DOWN: {
        loadNumberArrayConfInt(scrollSteps, displayMode);
        displayConfig();
        break;
      }
    case MODE_SLOTS_MODE: {
        loadNumberArrayConfInt(slotsMode, displayMode);
        displayConfig();
        break;
      }
    case MODE_BACKLIGHT_MODE: {
        loadNumberArrayConfInt(backlightMode, displayMode);
        displayConfig();
        break;
      }
    case MODE_RED_CNL: {
        if ((backlightMode == BACKLIGHT_CYCLE) || (backlightMode == BACKLIGHT_CYCLE_DIM))  {
          // Skip if we are in cycle mode
          setNewNextMode(MODE_CYCLE_SPEED);
        }
        loadNumberArrayConfInt(redCnl, displayMode);
        displayConfig();
        break;
      }
    case MODE_GRN_CNL: {
        loadNumberArrayConfInt(grnCnl, displayMode);
        displayConfig();
        break;
      }
    case MODE_BLU_CNL: {
        loadNumberArrayConfInt(bluCnl, displayMode);
        displayConfig();
        break;
      }
    case MODE_CYCLE_SPEED: {
        if ((backlightMode != BACKLIGHT_CYCLE) && (backlightMode != BACKLIGHT_CYCLE_DIM))  {
          // Show only if we are in cycle mode
          setNewNextMode(MODE_TARGET_HV_UP);
        }
        loadNumberArrayConfInt(cycleSpeed, displayMode);
        displayConfig();
        break;
      }
    case MODE_TARGET_HV_UP:
    case MODE_TARGET_HV_DOWN: {
        loadNumberArrayConfInt(hvTargetVoltage, displayMode);
        displayConfig();
        break;
      }
    case MODE_PULSE_UP:
    case MODE_PULSE_DOWN: {
        loadNumberArrayConfInt(pwmOn, displayMode);
        displayConfig();
        break;
      }
    case MODE_MIN_DIM_UP:
    case MODE_MIN_DIM_DOWN: {
        loadNumberArrayConfInt(minDim, displayMode);
        displayConfig();
        break;
      }
    case MODE_ANTI_GHOST_UP:
    case MODE_ANTI_GHOST_DOWN: {
        loadNumberArrayConfInt(antiGhost, displayMode);
        displayConfig();
        break;
      }
    case MODE_TEMP: {
        loadNumberArrayTemp(displayMode);
        displayConfig();
        break;
      }
    case MODE_VERSION: {
        loadNumberArrayConfInt(SOFTWARE_VERSION, displayMode);
        displayConfig();
        break;
      }
    case MODE_TUBE_TEST: {
        loadNumberArrayTestDigits();
        allNormal();
        break;
      }
    case MODE_DIGIT_BURN: {
        // Nothing: handled separately to suppress multiplexing
      }
  }
}

// ************************************************************
// Show the next mode - once the button is released
// ************************************************************
void processCurrentMode(int displayMode) {
  switch (displayMode) {
    case MODE_TIME: {
        if (button1.isButtonPressedAndReleased()) {
          // Deal with blanking first
          if ((nowMillis < blankSuppressedSelectionTimoutMillis) || blanked) {
            if (blankSuppressedSelectionTimoutMillis == 0) {
              // Apply 5 sec tineout for setting the suppression time
              blankSuppressedSelectionTimoutMillis = nowMillis + TEMP_DISPLAY_MODE_DUR_MS;
            }

            blankSuppressStep++;
            if (blankSuppressStep > 3) {
              blankSuppressStep = 3;
            }

            if (blankSuppressStep == 1) {
              blankSuppressedMillis = 10000;
            } else if (blankSuppressStep == 2) {
              blankSuppressedMillis = 3600000;
            } else if (blankSuppressStep == 3) {
              blankSuppressedMillis = 3600000 * 4;
            }
            blanked = false;
            setTubesAndLEDSBlankMode();
          } else {
            // Always start from the first mode, or increment the temp mode if we are already in a display
            if (tempDisplayModeDuration > 0) {
              tempDisplayModeDuration = TEMP_DISPLAY_MODE_DUR_MS;
              tempDisplayMode++;
            } else {
              tempDisplayMode = TEMP_MODE_MIN;
            }

            if (tempDisplayMode > TEMP_MODE_MAX) {
              tempDisplayMode = TEMP_MODE_MIN;
            }

            tempDisplayModeDuration = TEMP_DISPLAY_MODE_DUR_MS;
          }
        }

        if (tempDisplayModeDuration > 0) {
          blanked = false;
          setTubesAndLEDSBlankMode();
          if (tempDisplayMode == TEMP_MODE_DATE) {
            loadNumberArrayDate();
          }

          if (tempDisplayMode == TEMP_MODE_TEMP) {
            byte timeProv = 10;
            if (useRTC) timeProv += 1;
            if (useWiFi > 0) timeProv += 2;
            loadNumberArrayTemp(timeProv);
          }

          if (tempDisplayMode == TEMP_MODE_LDR) {
            loadNumberArrayLDR();
          }

          if (tempDisplayMode == TEMP_MODE_VERSION) {
            loadNumberArrayConfInt(SOFTWARE_VERSION, 0);
          }

          if (tempDisplayMode == TEMP_IP_ADDR12) {
            if (useWiFi > 0) {
              loadNumberArrayIP(ourIP[0], ourIP[1]);
            } else {
              // we can't show the IP address if we have the RTC, just skip
              tempDisplayMode++;
            }
          }

          if (tempDisplayMode == TEMP_IP_ADDR34) {
            if (useWiFi > 0) {
              loadNumberArrayIP(ourIP[2], ourIP[3]);
            } else {
              // we can't show the IP address if we have the RTC, just skip
              tempDisplayMode++;
            }
          }

          if (tempDisplayMode == TEMP_IMPR) {
            loadNumberArrayConfInt(lastImpressionsPerSec, 0);
          }

          allFadeOrNormal(DO_NOT_APPLY_LEAD_0_BLANK);

        } else {
          if (acpOffset > 0) {
            loadNumberArrayACP();
            allBright();
          } else {
            if (slotsMode > SLOTS_MODE_MIN) {
              if (second() == 50) {

                // initialise the slots values
                loadNumberArrayDate();
                transition.setAlternateValues();
                loadNumberArrayTime();
                transition.setRegularValues();
                allFadeOrNormal(DO_NOT_APPLY_LEAD_0_BLANK);

                transition.start(nowMillis);
              }

              // initialise the slots mode
              boolean msgDisplaying;
              switch (slotsMode) {
                case SLOTS_MODE_1M_SCR_SCR:
                {
                  msgDisplaying = transition.scrollInScrambleOut(nowMillis);
                  break;
                }
              }

              // Continue slots
              if (msgDisplaying) {
                transition.updateRegularDisplaySeconds(second());
              } else {
                // do normal time thing when we are not in slots
                loadNumberArrayTime();

                allFadeOrNormal(APPLY_LEAD_0_BLANK);
              }
            } else {
              // no slots mode, just do normal time thing
              loadNumberArrayTime();

              allFadeOrNormal(APPLY_LEAD_0_BLANK);
            }
          }
        }
        break;
      }
    case MODE_MINS_SET: {
        if (button1.isButtonPressedAndReleased()) {
          incMins();
        }
        loadNumberArrayTime();
        highlight2and3();
        break;
      }
    case MODE_HOURS_SET: {
        if (button1.isButtonPressedAndReleased()) {
          incHours();
        }
        loadNumberArrayTime();
        highlight0and1();
        break;
      }
    case MODE_SECS_SET: {
        if (button1.isButtonPressedAndReleased()) {
          resetSecond();
        }
        loadNumberArrayTime();
        highlight4and5();
        break;
      }
    case MODE_DAYS_SET: {
        if (button1.isButtonPressedAndReleased()) {
          incDays();
        }
        loadNumberArrayDate();
        highlightDaysDateFormat();
        break;
      }
    case MODE_MONTHS_SET: {
        if (button1.isButtonPressedAndReleased()) {
          incMonths();
        }
        loadNumberArrayDate();
        highlightMonthsDateFormat();
        break;
      }
    case MODE_YEARS_SET: {
        if (button1.isButtonPressedAndReleased()) {
          incYears();
        }
        loadNumberArrayDate();
        highlightYearsDateFormat();
        break;
      }
    case MODE_12_24: {
        if (button1.isButtonPressedAndReleased()) {
          hourMode = ! hourMode;
        }
        loadNumberArrayConfBool(hourMode, displayMode);
        displayConfig();
        break;
      }
    case MODE_LEAD_BLANK: {
        if (button1.isButtonPressedAndReleased()) {
          blankLeading = !blankLeading;
        }
        loadNumberArrayConfBool(blankLeading, displayMode);
        displayConfig();
        break;
      }
    case MODE_SCROLLBACK: {
        if (button1.isButtonPressedAndReleased()) {
          scrollback = !scrollback;
        }
        loadNumberArrayConfBool(scrollback, displayMode);
        displayConfig();
        break;
      }
    case MODE_FADE: {
        if (button1.isButtonPressedAndReleased()) {
          fade = !fade;
        }
        loadNumberArrayConfBool(fade, displayMode);
        displayConfig();
        break;
      }
    case MODE_DATE_FORMAT: {
        if (button1.isButtonPressedAndReleased()) {
          dateFormat++;
          if (dateFormat > DATE_FORMAT_MAX) {
            dateFormat = DATE_FORMAT_MIN;
          }
        }
        loadNumberArrayConfInt(dateFormat, displayMode);
        displayConfig();
        break;
      }
    case MODE_DAY_BLANKING: {
        if (button1.isButtonPressedAndReleased()) {
          dayBlanking++;
          if (dayBlanking > DAY_BLANKING_MAX) {
            dayBlanking = DAY_BLANKING_MIN;
          }
        }
        loadNumberArrayConfInt(dayBlanking, displayMode);
        displayConfig();
        break;
      }
    case MODE_HR_BLNK_START: {
        if (button1.isButtonPressedAndReleased()) {
          blankHourStart++;
          if (blankHourStart > HOURS_MAX) {
            blankHourStart = 0;
          }
        }
        loadNumberArrayConfInt(blankHourStart, displayMode);
        displayConfig();
        break;
      }
    case MODE_HR_BLNK_END: {
        if (button1.isButtonPressedAndReleased()) {
          blankHourEnd++;
          if (blankHourEnd > HOURS_MAX) {
            blankHourEnd = 0;
          }
        }
        loadNumberArrayConfInt(blankHourEnd, displayMode);
        displayConfig();
        break;
      }
    case MODE_SUPPRESS_ACP: {
        if (button1.isButtonPressedAndReleased()) {
          suppressACP = !suppressACP;
        }
        loadNumberArrayConfBool(suppressACP, displayMode);
        displayConfig();
        break;
      }
    case MODE_BLANK_MODE: {
        if (button1.isButtonPressedAndReleased()) {
          blankMode++;
          if (blankMode > BLANK_MODE_MAX) {
            blankMode = BLANK_MODE_MIN;
          }
        }
        loadNumberArrayConfInt(blankMode, displayMode);
        displayConfig();
        break;
      }
    case MODE_USE_LDR: {
        if (button1.isButtonPressedAndReleased()) {
          useLDR = !useLDR;
        }
        loadNumberArrayConfBool(useLDR, displayMode);
        displayConfig();
        break;
      }
    case MODE_FADE_STEPS_UP: {
        if (button1.isButtonPressedAndReleased()) {
          fadeSteps++;
          if (fadeSteps > FADE_STEPS_MAX) {
            fadeSteps = FADE_STEPS_MIN;
          }
        }
        loadNumberArrayConfInt(fadeSteps, displayMode);
        displayConfig();
        fadeStep = DIGIT_DISPLAY_COUNT / fadeSteps;
        break;
      }
    case MODE_FADE_STEPS_DOWN: {
        if (button1.isButtonPressedAndReleased()) {
          fadeSteps--;
          if (fadeSteps < FADE_STEPS_MIN) {
            fadeSteps = FADE_STEPS_MAX;
          }
        }
        loadNumberArrayConfInt(fadeSteps, displayMode);
        displayConfig();
        fadeStep = DIGIT_DISPLAY_COUNT / fadeSteps;
        break;
      }
    case MODE_DISPLAY_SCROLL_STEPS_DOWN: {
        if (button1.isButtonPressedAndReleased()) {
          scrollSteps--;
          if (scrollSteps < SCROLL_STEPS_MIN) {
            scrollSteps = SCROLL_STEPS_MAX;
          }
        }
        loadNumberArrayConfInt(scrollSteps, displayMode);
        displayConfig();
        break;
      }
    case MODE_DISPLAY_SCROLL_STEPS_UP: {
        if (button1.isButtonPressedAndReleased()) {
          scrollSteps++;
          if (scrollSteps > SCROLL_STEPS_MAX) {
            scrollSteps = SCROLL_STEPS_MIN;
          }
        }
        loadNumberArrayConfInt(scrollSteps, displayMode);
        displayConfig();
        break;
      }
    case MODE_SLOTS_MODE: {
        if (button1.isButtonPressedAndReleased()) {
          slotsMode++;
          if (slotsMode > SLOTS_MODE_MAX) {
            slotsMode = SLOTS_MODE_MIN;
          }
        }
        loadNumberArrayConfInt(slotsMode, displayMode);
        displayConfig();
        break;
      }
    case MODE_BACKLIGHT_MODE: {
        if (button1.isButtonPressedAndReleased()) {
          backlightMode++;
          if (backlightMode > BACKLIGHT_MAX) {
            backlightMode = BACKLIGHT_MIN;
          }
        }
        loadNumberArrayConfInt(backlightMode, displayMode);
        displayConfig();
        break;
      }
    case MODE_RED_CNL: {
        if (button1.isButtonPressedAndReleased()) {
          redCnl++;
          if (redCnl > COLOUR_CNL_MAX) {
            redCnl = COLOUR_CNL_MIN;
          }
        }
        loadNumberArrayConfInt(redCnl, displayMode);
        displayConfig();
        break;
      }
    case MODE_GRN_CNL: {
        if (button1.isButtonPressedAndReleased()) {
          grnCnl++;
          if (grnCnl > COLOUR_CNL_MAX) {
            grnCnl = COLOUR_CNL_MIN;
          }
        }
        loadNumberArrayConfInt(grnCnl, displayMode);
        displayConfig();
        break;
      }
    case MODE_BLU_CNL: {
        if (button1.isButtonPressedAndReleased()) {
          bluCnl++;
          if (bluCnl > COLOUR_CNL_MAX) {
            bluCnl = COLOUR_CNL_MIN;
          }
        }
        loadNumberArrayConfInt(bluCnl, displayMode);
        displayConfig();
        break;
      }
    case MODE_CYCLE_SPEED: {
        if (button1.isButtonPressedAndReleased()) {
          cycleSpeed = cycleSpeed + 2;
          if (cycleSpeed > CYCLE_SPEED_MAX) {
            cycleSpeed = CYCLE_SPEED_MIN;
          }
        }
        loadNumberArrayConfInt(cycleSpeed, displayMode);
        displayConfig();
        break;
      }
    case MODE_TARGET_HV_UP: {
        if (button1.isButtonPressedAndReleased()) {
          hvTargetVoltage += 5;
          if (hvTargetVoltage > HVGEN_TARGET_VOLTAGE_MAX) {
            hvTargetVoltage = HVGEN_TARGET_VOLTAGE_MIN;
          }
        }
        loadNumberArrayConfInt(hvTargetVoltage, displayMode);
        rawHVADCThreshold = getRawHVADCThreshold(hvTargetVoltage);
        displayConfig();
        break;
      }
    case MODE_TARGET_HV_DOWN: {
        if (button1.isButtonPressedAndReleased()) {
          hvTargetVoltage -= 5;
          if (hvTargetVoltage < HVGEN_TARGET_VOLTAGE_MIN) {
            hvTargetVoltage = HVGEN_TARGET_VOLTAGE_MAX;
          }
        }
        loadNumberArrayConfInt(hvTargetVoltage, displayMode);
        rawHVADCThreshold = getRawHVADCThreshold(hvTargetVoltage);
        displayConfig();
        break;
      }
    case MODE_PULSE_UP: {
        if (button1.isButtonPressedAndReleased()) {
          pwmOn += 10;
          if (pwmOn > PWM_PULSE_MAX) {
            pwmOn = PWM_PULSE_MAX;
          }
          setPWMOnTime(pwmOn);
        }
        loadNumberArrayConfInt(pwmOn, displayMode);
        displayConfig();
        break;
      }
    case MODE_PULSE_DOWN: {
        if (button1.isButtonPressedAndReleased()) {
          pwmOn -= 10;
          if (pwmOn > PWM_PULSE_MAX) {
            pwmOn = PWM_PULSE_MAX;
          }
          setPWMOnTime(pwmOn);
        }
        loadNumberArrayConfInt(pwmOn, displayMode);
        displayConfig();
        break;
      }
    case MODE_MIN_DIM_UP: {
        if (button1.isButtonPressedAndReleased()) {
          minDim += 10;
          if (minDim > MIN_DIM_MAX) {
            minDim = MIN_DIM_MAX;
          }
        }
        loadNumberArrayConfInt(minDim, displayMode);
        displayConfig();
        break;
      }
    case MODE_MIN_DIM_DOWN: {
        if (button1.isButtonPressedAndReleased()) {
          minDim -= 10;
          if (minDim < MIN_DIM_MIN) {
            minDim = MIN_DIM_MIN;
          }
        }
        loadNumberArrayConfInt(minDim, displayMode);
        displayConfig();
        break;
      }
    case MODE_ANTI_GHOST_UP: {
        if (button1.isButtonPressedAndReleased()) {
          antiGhost += 1;
          if (antiGhost > ANTI_GHOST_MAX) {
            antiGhost = ANTI_GHOST_MAX;
          }
          dispCount = DIGIT_DISPLAY_COUNT + antiGhost;
        }
        loadNumberArrayConfInt(antiGhost, displayMode);
        displayConfig();
        break;
      }
    case MODE_ANTI_GHOST_DOWN: {
        if (button1.isButtonPressedAndReleased()) {
          antiGhost -= 1;
          if (antiGhost < ANTI_GHOST_MIN) {
            antiGhost = ANTI_GHOST_MIN;
          }
          dispCount = DIGIT_DISPLAY_COUNT + antiGhost;
        }
        loadNumberArrayConfInt(antiGhost, displayMode);
        displayConfig();
        break;
      }
    case MODE_TEMP: {
        loadNumberArrayTemp(displayMode);
        displayConfig();
        break;
      }
    case MODE_VERSION: {
        loadNumberArrayConfInt(SOFTWARE_VERSION, displayMode);
        displayConfig();
        break;
      }
    case MODE_TUBE_TEST: {
        allNormal();
        loadNumberArrayTestDigits();
        break;
      }
    case MODE_DIGIT_BURN: {
        if (button1.isButtonPressedAndReleased()) {
          digitBurnValue += 1;
          if (digitBurnValue > 9) {
            digitBurnValue = 0;

            digitOff();
            digitBurnDigit += 1;
            if (digitBurnDigit > 5) {
              digitBurnDigit = 0;
            }
          }
        }
      }
  }
}

// ************************************************************
// output a PWM LED channel, adjusting for dimming and PWM
// brightness:
// rawValue: The raw brightness value between 0 - 255
// ledPWMVal: The pwm factor between 0 - 1
// dimFactor: The dimming value between 0 - 1
// ************************************************************
byte getLEDAdjusted(float rawValue, float ledPWMVal, float dimFactor) {
  byte dimmedPWMVal = (byte)(rawValue * ledPWMVal * dimFactor);
  return dim_curve[dimmedPWMVal];
}

// ************************************************************
// Colour cycling 3: one colour dominates
// ************************************************************
void cycleColours3(int colors[3]) {
  cycleCount++;
  if (cycleCount > cycleSpeed) {
    cycleCount = 0;

    if (changeSteps == 0) {
      changeSteps = random(256);
      currentColour = random(3);
    }

    changeSteps--;

    switch (currentColour) {
      case 0:
        if (colors[0] < 255) {
          colors[0]++;
          if (colors[1] > 0) {
            colors[1]--;
          }
          if (colors[2] > 0) {
            colors[2]--;
          }
        } else {
          changeSteps = 0;
        }
        break;
      case 1:
        if (colors[1] < 255) {
          colors[1]++;
          if (colors[0] > 0) {
            colors[0]--;
          }
          if (colors[2] > 0) {
            colors[2]--;
          }
        } else {
          changeSteps = 0;
        }
        break;
      case 2:
        if (colors[2] < 255) {
          colors[2]++;
          if (colors[0] > 0) {
            colors[0]--;
          }
          if (colors[1] > 0) {
            colors[1]--;
          }
        } else {
          changeSteps = 0;
        }
        break;
    }
  }
}

//**********************************************************************************
//**********************************************************************************
//*                             Utility functions                                  *
//**********************************************************************************
//**********************************************************************************

// ************************************************************
// Break the time into displayable digits
// ************************************************************
void loadNumberArrayTime() {
  NumberArray[5] = second() % 10;
  NumberArray[4] = second() / 10;
  NumberArray[3] = minute() % 10;
  NumberArray[2] = minute() / 10;
  if (hourMode) {
    NumberArray[1] = hourFormat12() % 10;
    NumberArray[0] = hourFormat12() / 10;
  } else {
    NumberArray[1] = hour() % 10;
    NumberArray[0] = hour() / 10;
  }
}

// ************************************************************
// Break the time into displayable digits
// ************************************************************
void loadNumberArraySameValue(byte val) {
  NumberArray[5] = val;
  NumberArray[4] = val;
  NumberArray[3] = val;
  NumberArray[2] = val;
  NumberArray[1] = val;
  NumberArray[0] = val;
}

// ************************************************************
// Break the time into displayable digits
// ************************************************************
void loadNumberArrayDate() {
  switch (dateFormat) {
    case DATE_FORMAT_YYMMDD:
      NumberArray[5] = day() % 10;
      NumberArray[4] = day() / 10;
      NumberArray[3] = month() % 10;
      NumberArray[2] = month() / 10;
      NumberArray[1] = (year() - 2000) % 10;
      NumberArray[0] = (year() - 2000) / 10;
      break;
    case DATE_FORMAT_MMDDYY:
      NumberArray[5] = (year() - 2000) % 10;
      NumberArray[4] = (year() - 2000) / 10;
      NumberArray[3] = day() % 10;
      NumberArray[2] = day() / 10;
      NumberArray[1] = month() % 10;
      NumberArray[0] = month() / 10;
      break;
    case DATE_FORMAT_DDMMYY:
      NumberArray[5] = (year() - 2000) % 10;
      NumberArray[4] = (year() - 2000) / 10;
      NumberArray[3] = month() % 10;
      NumberArray[2] = month() / 10;
      NumberArray[1] = day() % 10;
      NumberArray[0] = day() / 10;
      break;
  }
}

// ************************************************************
// Break the temperature into displayable digits
// ************************************************************
void loadNumberArrayTemp(int confNum) {
  NumberArray[5] = (confNum) % 10;
  NumberArray[4] = (confNum / 10) % 10;
  float temp = getRTCTemp();
  int wholeDegrees = int(temp);
  temp = (temp - float(wholeDegrees)) * 100.0;
  int fractDegrees = int(temp);

  NumberArray[3] = fractDegrees % 10;
  NumberArray[2] =  fractDegrees / 10;
  NumberArray[1] =  wholeDegrees % 10;
  NumberArray[0] = wholeDegrees / 10;
}

// ************************************************************
// Break the LDR reading into displayable digits
// ************************************************************
void loadNumberArrayLDR() {
  NumberArray[5] = 0;
  NumberArray[4] = 0;

  NumberArray[3] = (digitOffCount / 1) % 10;
  NumberArray[2] = (digitOffCount / 10) % 10;
  NumberArray[1] = (digitOffCount / 100) % 10;
  NumberArray[0] = (digitOffCount / 1000) % 10;
}

// ************************************************************
// Test digits
// ************************************************************
void loadNumberArrayTestDigits() {
  NumberArray[5] =  second() % 10;
  NumberArray[4] = (second() + 1) % 10;
  NumberArray[3] = (second() + 2) % 10;
  NumberArray[2] = (second() + 3) % 10;
  NumberArray[1] = (second() + 4) % 10;
  NumberArray[0] = (second() + 5) % 10;
}

// ************************************************************
// Do the Anti Cathode Poisoning
// ************************************************************
void loadNumberArrayACP() {
  NumberArray[5] = (second() + acpOffset) % 10;
  NumberArray[4] = (second() / 10 + acpOffset) % 10;
  NumberArray[3] = (minute() + acpOffset) % 10;
  NumberArray[2] = (minute() / 10 + acpOffset) % 10;
  NumberArray[1] = (hour() + acpOffset)  % 10;
  NumberArray[0] = (hour() / 10 + acpOffset) % 10;
}

// ************************************************************
// Show an integer configuration value
// ************************************************************
void loadNumberArrayConfInt(int confValue, int confNum) {
  NumberArray[5] = (confNum) % 10;
  NumberArray[4] = (confNum / 10) % 10;
  NumberArray[3] = (confValue / 1) % 10;
  NumberArray[2] = (confValue / 10) % 10;
  NumberArray[1] = (confValue / 100) % 10;
  NumberArray[0] = (confValue / 1000) % 10;
}

// ************************************************************
// Show a boolean configuration value
// ************************************************************
void loadNumberArrayConfBool(boolean confValue, int confNum) {
  int boolInt;
  if (confValue) {
    boolInt = 1;
  } else {
    boolInt = 0;
  }
  NumberArray[5] = (confNum) % 10;
  NumberArray[4] = (confNum / 10) % 10;
  NumberArray[3] = boolInt;
  NumberArray[2] = 0;
  NumberArray[1] = 0;
  NumberArray[0] = 0;
}

// ************************************************************
// Show an integer configuration value
// ************************************************************
void loadNumberArrayIP(byte byte1, byte byte2) {
  NumberArray[5] = (byte2) % 10;
  NumberArray[4] = (byte2 / 10) % 10;
  NumberArray[3] = (byte2 / 100) % 10;
  NumberArray[2] = (byte1) % 10;
  NumberArray[1] = (byte1 / 10) % 10;
  NumberArray[0] = (byte1 / 100) % 10;
}

// ************************************************************
// Decode the value to send to the 74141 and send it
// We do this via the decoder to allow easy adaptation to
// other pin layouts.
// ************************************************************
void SetSN74141Chip(int num1)
{
  // Map the logical numbers to the hardware pins we send to the SN74141 IC
  int decodedDigit = decodeDigit[num1];

  // Mask all digit bits to 0
  byte portb = PORTB;
  portb = portb & B11001010;

  // Set the bits we need
  switch ( decodedDigit )
  {
    case 0:                             break; // a=0;b=0;c=0;d=0
    case 1:  portb = portb | B00100000; break; // a=1;b=0;c=0;d=0
    case 2:  portb = portb | B00000100; break; // a=0;b=1;c=0;d=0
    case 3:  portb = portb | B00100100; break; // a=1;b=1;c=0;d=0
    case 4:  portb = portb | B00000001; break; // a=0;b=0;c=1;d=0
    case 5:  portb = portb | B00100001; break; // a=1;b=0;c=1;d=0
    case 6:  portb = portb | B00000101; break; // a=0;b=1;c=1;d=0
    case 7:  portb = portb | B00100101; break; // a=1;b=1;c=1;d=0
    case 8:  portb = portb | B00010000; break; // a=0;b=0;c=0;d=1
    case 9:  portb = portb | B00110000; break; // a=1;b=0;c=0;d=1
    default: portb = portb | B00110101; break; // a=1;b=1;c=1;d=1
  }
  PORTB = portb;
}

// ************************************************************
// Do a single complete display, including any fading and
// dimming requested. Performs the display loop
// DIGIT_DISPLAY_COUNT times for each digit, with no delays.
// This is the heart of the display processing!
// ************************************************************
void outputDisplay()
{
  int digitOnTime;
  int digitOffTime;
  int digitSwitchTime;
  float digitSwitchTimeFloat;
  int tmpDispType;

  // used to blank all leading digits if 0
  boolean leadingZeros = true;

  for ( int i = 0 ; i < DIGIT_COUNT ; i ++ )
  {
    if (blankTubes) {
      tmpDispType = BLANKED;
    } else {
      tmpDispType = displayType[i];
    }

    switch (tmpDispType) {
      case BLANKED:
        {
          digitOnTime = DIGIT_DISPLAY_NEVER;
          digitOffTime = DIGIT_DISPLAY_ON;
          break;
        }
      case DIMMED:
        {
          digitOnTime = DIGIT_DISPLAY_ON;
          digitOffTime = DIM_VALUE;
          break;
        }
      case BRIGHT:
        {
          digitOnTime = DIGIT_DISPLAY_ON;
          digitOffTime = DIGIT_DISPLAY_OFF;
          break;
        }
      case FADE:
      case NORMAL:
        {
          digitOnTime = DIGIT_DISPLAY_ON;
          digitOffTime = digitOffCount;
          break;
        }
      case BLINK:
        {
          if (blinkState) {
            digitOnTime = DIGIT_DISPLAY_ON;
            digitOffTime = digitOffCount;
          } else {
            digitOnTime = DIGIT_DISPLAY_NEVER;
            digitOffTime = DIGIT_DISPLAY_ON;
          }
          break;
        }
      case SCROLL:
        {
          digitOnTime = DIGIT_DISPLAY_ON;
          digitOffTime = digitOffCount;
          break;
        }
    }

    // Do scrollback when we are going to 0
    if ((NumberArray[i] != currNumberArray[i]) &&
        (NumberArray[i] == 0) &&
        scrollback) {
      tmpDispType = SCROLL;
    }

    // manage scrolling, count the digits down
    if (tmpDispType == SCROLL) {
      digitSwitchTime = DIGIT_DISPLAY_OFF;
      if (NumberArray[i] != currNumberArray[i]) {
        if (fadeState[i] == 0) {
          // Start the fade
          fadeState[i] = scrollSteps;
        }

        if (fadeState[i] == 1) {
          // finish the fade
          fadeState[i] = 0;
          currNumberArray[i] = currNumberArray[i] - 1;
        } else if (fadeState[i] > 1) {
          // Continue the scroll countdown
          fadeState[i] = fadeState[i] - 1;
        }
      }
    // manage fading, each impression we show 1 fade step less of the old
    // digit and 1 fade step more of the new
    } else if (tmpDispType == FADE) {
      if (NumberArray[i] != currNumberArray[i]) {
        if (fadeState[i] == 0) {
          // Start the fade
          fadeState[i] = fadeSteps;
          digitSwitchTime = (int) fadeState[i] * fadeStep;
        }
      }

      if (fadeState[i] == 1) {
        // finish the fade
        fadeState[i] = 0;
        currNumberArray[i] = NumberArray[i];
        digitSwitchTime = DIGIT_DISPLAY_COUNT;
      } else if (fadeState[i] > 1) {
        // Continue the fade
        fadeState[i] = fadeState[i] - 1;
        digitSwitchTime = (int) fadeState[i] * fadeStep;
      }
    } else {
      digitSwitchTime = DIGIT_DISPLAY_COUNT;
      currNumberArray[i] = NumberArray[i];
    }

    for (int timer = 0 ; timer < dispCount ; timer++) {
      if (timer == digitOnTime) {
        digitOn(i, currNumberArray[i]);
      }

      if  (timer == digitSwitchTime) {
        SetSN74141Chip(NumberArray[i]);
      }

      if (timer == digitOffTime) {
        digitOff();
      }
    }
  }

  // Deal with blink, calculate if we are on or off
  blinkCounter++;
  if (blinkCounter == BLINK_COUNT_MAX) {
    blinkCounter = 0;
    blinkState = !blinkState;
  }
}

// ************************************************************
// Set a digit with the given value and turn the HVGen on
// Assumes that all digits have previously been turned off
// by a call to "digitOff"
// ************************************************************
void digitOn(int digit, int value) {
  switch (digit) {
    case 0: PORTC = PORTC | B00001000; break; // PC3 - equivalent to digitalWrite(ledPin_a_1,HIGH);
    case 1: PORTC = PORTC | B00000100; break; // PC2 - equivalent to digitalWrite(ledPin_a_2,HIGH);
    case 2: PORTD = PORTD | B00010000; break; // PD4 - equivalent to digitalWrite(ledPin_a_3,HIGH);
    case 3: PORTD = PORTD | B00000100; break; // PD2 - equivalent to digitalWrite(ledPin_a_4,HIGH);
    case 4: PORTD = PORTD | B00000010; break; // PD1 - equivalent to digitalWrite(ledPin_a_5,HIGH);
    case 5: PORTD = PORTD | B00000001; break; // PD0 - equivalent to digitalWrite(ledPin_a_6,HIGH);
  }
  SetSN74141Chip(value);
  TCCR1A = tccrOn;
}

// ************************************************************
// Finish displaying a digit and turn the HVGen on
// ************************************************************
void digitOff() {
  TCCR1A = tccrOff;
  //digitalWrite(anodePins[digit], LOW);

  // turn all digits off - equivalent to digitalWrite(ledPin_a_n,LOW); (n=1,2,3,4,5,6) but much faster
  PORTC = PORTC & B11110011;
  PORTD = PORTD & B11101000;
}

// ************************************************************
// Display preset - apply leading zero blanking
// ************************************************************
void applyBlanking() {
  // If we are not blanking, just get out
  if (blankLeading == false) {
    return;
  }

  // We only want to blank the hours tens digit
  if (NumberArray[0] == 0) {
    if (displayType[0] != BLANKED) {
      displayType[0] = BLANKED;
    }
  }
}

// ************************************************************
// Display preset
// ************************************************************
void allFadeOrNormal(boolean blanking) {
  if (fade) {
    allFade();
  } else {
    allNormal();
  }

  if (blanking) {
    applyBlanking();
  }
}

// ************************************************************
// Display preset
// ************************************************************
void allFade() {
  if (displayType[0] != FADE) displayType[0] = FADE;
  if (displayType[1] != FADE) displayType[1] = FADE;
  if (displayType[2] != FADE) displayType[2] = FADE;
  if (displayType[3] != FADE) displayType[3] = FADE;
  if (displayType[4] != FADE) displayType[4] = FADE;
  if (displayType[5] != FADE) displayType[5] = FADE;
}

// ************************************************************
// Display preset
// ************************************************************
void allBright() {
  if (displayType[0] != BRIGHT) displayType[0] = BRIGHT;
  if (displayType[1] != BRIGHT) displayType[1] = BRIGHT;
  if (displayType[2] != BRIGHT) displayType[2] = BRIGHT;
  if (displayType[3] != BRIGHT) displayType[3] = BRIGHT;
  if (displayType[4] != BRIGHT) displayType[4] = BRIGHT;
  if (displayType[5] != BRIGHT) displayType[5] = BRIGHT;
}

// ************************************************************
// highlight years taking into account the date format
// ************************************************************
void highlightYearsDateFormat() {
  switch (dateFormat) {
    case DATE_FORMAT_YYMMDD:
      highlight0and1();
      break;
    case DATE_FORMAT_MMDDYY:
      highlight4and5();
      break;
    case DATE_FORMAT_DDMMYY:
      highlight4and5();
      break;
  }
}

// ************************************************************
// highlight years taking into account the date format
// ************************************************************
void highlightMonthsDateFormat() {
  switch (dateFormat) {
    case DATE_FORMAT_YYMMDD:
      highlight2and3();
      break;
    case DATE_FORMAT_MMDDYY:
      highlight0and1();
      break;
    case DATE_FORMAT_DDMMYY:
      highlight2and3();
      break;
  }
}

// ************************************************************
// highlight days taking into account the date format
// ************************************************************
void highlightDaysDateFormat() {
  switch (dateFormat) {
    case DATE_FORMAT_YYMMDD:
      highlight4and5();
      break;
    case DATE_FORMAT_MMDDYY:
      highlight2and3();
      break;
    case DATE_FORMAT_DDMMYY:
      highlight0and1();
      break;
  }
}

// ************************************************************
// Display preset, highlight digits 0 and 1
// ************************************************************
void highlight0and1() {
  if (displayType[0] != BRIGHT) displayType[0] = BRIGHT;
  if (displayType[1] != BRIGHT) displayType[1] = BRIGHT;
  if (displayType[2] != DIMMED) displayType[2] = DIMMED;
  if (displayType[3] != DIMMED) displayType[3] = DIMMED;
  if (displayType[4] != DIMMED) displayType[4] = DIMMED;
  if (displayType[5] != DIMMED) displayType[5] = DIMMED;
}

// ************************************************************
// Display preset, highlight digits 2 and 3
// ************************************************************
void highlight2and3() {
  if (displayType[0] != DIMMED) displayType[0] = DIMMED;
  if (displayType[1] != DIMMED) displayType[1] = DIMMED;
  if (displayType[2] != BRIGHT) displayType[2] = BRIGHT;
  if (displayType[3] != BRIGHT) displayType[3] = BRIGHT;
  if (displayType[4] != DIMMED) displayType[4] = DIMMED;
  if (displayType[5] != DIMMED) displayType[5] = DIMMED;
}

// ************************************************************
// Display preset, highlight digits 4 and 5
// ************************************************************
void highlight4and5() {
  if (displayType[0] != DIMMED) displayType[0] = DIMMED;
  if (displayType[1] != DIMMED) displayType[1] = DIMMED;
  if (displayType[2] != DIMMED) displayType[2] = DIMMED;
  if (displayType[3] != DIMMED) displayType[3] = DIMMED;
  if (displayType[4] != BRIGHT) displayType[4] = BRIGHT;
  if (displayType[5] != BRIGHT) displayType[5] = BRIGHT;
}

// ************************************************************
// Display preset
// ************************************************************
void allNormal() {
  if (displayType[0] != NORMAL) displayType[0] = NORMAL;
  if (displayType[1] != NORMAL) displayType[1] = NORMAL;
  if (displayType[2] != NORMAL) displayType[2] = NORMAL;
  if (displayType[3] != NORMAL) displayType[3] = NORMAL;
  if (displayType[4] != NORMAL) displayType[4] = NORMAL;
  if (displayType[5] != NORMAL) displayType[5] = NORMAL;
}

// ************************************************************
// Display preset
// ************************************************************
void displayConfig() {
  if (displayType[0] != BRIGHT) displayType[0] = BRIGHT;
  if (displayType[1] != BRIGHT) displayType[1] = BRIGHT;
  if (displayType[2] != BRIGHT) displayType[2] = BRIGHT;
  if (displayType[3] != BRIGHT) displayType[3] = BRIGHT;
  if (displayType[4] != BLINK)  displayType[4] = BLINK;
  if (displayType[5] != BLINK)  displayType[5] = BLINK;
}

// ************************************************************
// Display preset
// ************************************************************
void displayConfig3() {
  if (displayType[0] != BLANKED) displayType[0] = BLANKED;
  if (displayType[1] != NORMAL) displayType[1] = BRIGHT;
  if (displayType[2] != NORMAL) displayType[2] = BRIGHT;
  if (displayType[3] != NORMAL) displayType[3] = BRIGHT;
  if (displayType[4] != BLINK)  displayType[4] = BLINK;
  if (displayType[5] != BLINK)  displayType[5] = BLINK;
}

// ************************************************************
// Display preset
// ************************************************************
void displayConfig2() {
  if (displayType[0] != BLANKED) displayType[0] = BLANKED;
  if (displayType[1] != BLANKED) displayType[1] = BLANKED;
  if (displayType[2] != NORMAL) displayType[2] = BRIGHT;
  if (displayType[3] != NORMAL) displayType[3] = BRIGHT;
  if (displayType[4] != BLINK)  displayType[4] = BLINK;
  if (displayType[5] != BLINK)  displayType[5] = BLINK;
}

// ************************************************************
// Display preset
// ************************************************************
void allBlanked() {
  if (displayType[0] != BLANKED) displayType[0] = BLANKED;
  if (displayType[1] != BLANKED) displayType[1] = BLANKED;
  if (displayType[2] != BLANKED) displayType[2] = BLANKED;
  if (displayType[3] != BLANKED) displayType[3] = BLANKED;
  if (displayType[4] != BLANKED) displayType[4] = BLANKED;
  if (displayType[5] != BLANKED) displayType[5] = BLANKED;
}

// ************************************************************
// Reset the seconds to 00
// ************************************************************
void resetSecond() {
  byte tmpSecs = 0;
  setTime(hour(), minute(), tmpSecs, day(), month(), year());
  setRTC();
}

// ************************************************************
// increment the time by 1 Sec
// ************************************************************
void incSecond() {
  byte tmpSecs = second();
  tmpSecs++;
  if (tmpSecs >= SECS_MAX) {
    tmpSecs = 0;
  }
  setTime(hour(), minute(), tmpSecs, day(), month(), year());
  setRTC();
}

// ************************************************************
// increment the time by 1 min
// ************************************************************
void incMins() {
  byte tmpMins = minute();
  tmpMins++;
  if (tmpMins >= MINS_MAX) {
    tmpMins = 0;
  }
  setTime(hour(), tmpMins, 0, day(), month(), year());
  setRTC();
}

// ************************************************************
// increment the time by 1 hour
// ************************************************************
void incHours() {
  byte tmpHours = hour();
  tmpHours++;

  if (tmpHours >= HOURS_MAX) {
    tmpHours = 0;
  }
  setTime(tmpHours, minute(), second(), day(), month(), year());
  setRTC();
}

// ************************************************************
// increment the date by 1 day
// ************************************************************
void incDays() {
  byte tmpDays = day();
  tmpDays++;

  int maxDays;
  switch (month())
  {
    case 4:
    case 6:
    case 9:
    case 11:
      {
        maxDays = 31;
        break;
      }
    case 2:
      {
        // we won't worry about leap years!!!
        maxDays = 28;
        break;
      }
    default:
      {
        maxDays = 31;
      }
  }

  if (tmpDays > maxDays) {
    tmpDays = 1;
  }
  setTime(hour(), minute(), second(), tmpDays, month(), year());
  setRTC();
}

// ************************************************************
// increment the month by 1 month
// ************************************************************
void incMonths() {
  byte tmpMonths = month();
  tmpMonths++;

  if (tmpMonths > 12) {
    tmpMonths = 1;
  }
  setTime(hour(), minute(), second(), day(), tmpMonths, year());
  setRTC();
}

// ************************************************************
// increment the year by 1 year
// ************************************************************
void incYears() {
  byte tmpYears = year() % 100;
  tmpYears++;

  if (tmpYears > 50) {
    tmpYears = 15;
  }
  setTime(hour(), minute(), second(), day(), month(), 2000 + tmpYears);
  setRTC();
}

// ************************************************************
// Check the blanking
// ************************************************************
boolean checkBlanking() {
  // Check day blanking, but only when we are in
  // normal time mode
  if (currentMode == MODE_TIME) {
    switch (dayBlanking) {
      case DAY_BLANKING_NEVER:
        return false;
      case DAY_BLANKING_HOURS:
        return getHoursBlanked();
      case DAY_BLANKING_WEEKEND:
        return ((weekday() == 1) || (weekday() == 7));
      case DAY_BLANKING_WEEKEND_OR_HOURS:
        return ((weekday() == 1) || (weekday() == 7)) || getHoursBlanked();
      case DAY_BLANKING_WEEKEND_AND_HOURS:
        return ((weekday() == 1) || (weekday() == 7)) && getHoursBlanked();
      case DAY_BLANKING_WEEKDAY:
        return ((weekday() > 1) && (weekday() < 7));
      case DAY_BLANKING_WEEKDAY_OR_HOURS:
        return ((weekday() > 1) && (weekday() < 7)) || getHoursBlanked();
      case DAY_BLANKING_WEEKDAY_AND_HOURS:
        return ((weekday() > 1) && (weekday() < 7)) && getHoursBlanked();
      case DAY_BLANKING_ALWAYS:
        return true;
    }
  }
}

// ************************************************************
// If we are currently blanked based on hours
// ************************************************************
boolean getHoursBlanked() {
  if (blankHourStart > blankHourEnd) {
    // blanking before midnight
    return ((hour() >= blankHourStart) || (hour() < blankHourEnd));
  } else if (blankHourStart < blankHourEnd) {
    // dim at or after midnight
    return ((hour() >= blankHourStart) && (hour() < blankHourEnd));
  } else {
    // no dimming if Start = End
    return false;
  }
}

// ************************************************************
// Set the tubes and LEDs blanking variables based on blanking mode and 
// blank mode settings
// ************************************************************
void setTubesAndLEDSBlankMode() {
  if (blanked) {
    switch(blankMode) {
      case BLANK_MODE_TUBES:
      {
        blankTubes = true;
        blankLEDs = false;
        break;
      }
      case BLANK_MODE_LEDS:
      {
        blankTubes = false;
        blankLEDs = true;
        break;
      }
      case BLANK_MODE_BOTH:
      {
        blankTubes = true;
        blankLEDs = true;
        break;
      }
    }
  } else {
    blankTubes = false;
    blankLEDs = false;
  }
}

// ************************************************************
// Show a random RGB colour: used to indicate a factory reset
// ************************************************************
void randomRGBFlash(int delayVal) {
  digitalWrite(tickLed, HIGH);
  if (random(3) == 0) {
    digitalWrite(RLed, HIGH);
  }
  if (random(3) == 0) {
    digitalWrite(GLed, HIGH);
  }
  if (random(3) == 0) {
    digitalWrite(BLed, HIGH);
  }
  delay(delayVal);
  digitalWrite(tickLed, LOW);
  digitalWrite(RLed, LOW);
  digitalWrite(GLed, LOW);
  digitalWrite(BLed, LOW);
  delay(delayVal);
}

// ************************************************************
// Used to set the LEDs during test mode
// ************************************************************
void setLedsTestPattern(unsigned long currentMillis) {
  unsigned long currentSec = currentMillis / 1000;
  byte phase = currentSec % 4;
  digitalWrite(tickLed, LOW);
  digitalWrite(RLed, LOW);
  digitalWrite(GLed, LOW);
  digitalWrite(BLed, LOW);

  if (phase == 0) {
    digitalWrite(tickLed, HIGH);
  }
  
  if (phase == 1) {
    digitalWrite(RLed, HIGH);
  }
  
  if (phase == 2) {
    digitalWrite(GLed, HIGH);
  }
  
  if (phase == 3) {
    digitalWrite(BLed, HIGH);
  }
}

// ************************************************************
// Jump to a new position in the menu - used to skip unused items
// ************************************************************
void setNewNextMode(int newNextMode) {
  nextMode = newNextMode;
  currentMode = newNextMode - 1;
}

//**********************************************************************************
//**********************************************************************************
//*                         RTC Module Time Provider                               *
//**********************************************************************************
//**********************************************************************************

// ************************************************************
// Get the time from the RTC
// ************************************************************
void getRTCTime() {
  // Start the RTC communication in master mode
  Wire.end();
  Wire.begin();

  // Set up the time provider
  // first try to find the RTC, if not available, go into slave mode
  Wire.beginTransmission(RTC_I2C_ADDRESS);
  useRTC = (Wire.endTransmission() == 0);
  if (useRTC) {
    bool PM;
    bool twentyFourHourClock;
    bool century = false;

    byte years = Clock.getYear() + 2000;
    byte months = Clock.getMonth(century);
    byte days = Clock.getDate();
    byte hours = Clock.getHour(twentyFourHourClock, PM);
    byte mins = Clock.getMinute();
    byte secs = Clock.getSecond();
    setTime(hours, mins, secs, days, months, years);

    // Make sure the clock keeps running even on battery
    if (!Clock.oscillatorCheck())
      Clock.enableOscillator(true, true, 0);
  }

  // Return back to I2C in slave mode
  Wire.end();
  Wire.begin(I2C_SLAVE_ADDR);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
}

// ************************************************************
// Set the date/time in the RTC from the internal time
// Always hold the time in 24 format, we convert to 12 in the
// display.
// ************************************************************
void setRTC() {
  if (useRTC) {
    // Start the RTC communication in master mode
    Wire.end();
    Wire.begin();

    // Set up the time provider
    // first try to find the RTC, if not available, go into slave mode
    Wire.beginTransmission(RTC_I2C_ADDRESS);
    Clock.setClockMode(false); // false = 24h
    Clock.setYear(year() % 100);
    Clock.setMonth(month());
    Clock.setDate(day());
    Clock.setDoW(weekday());
    Clock.setHour(hour());
    Clock.setMinute(minute());
    Clock.setSecond(second());

    Wire.endTransmission();
    Wire.end();
    Wire.begin(I2C_SLAVE_ADDR);
    Wire.onReceive(receiveEvent);
    Wire.onRequest(requestEvent);
  }
}

// ************************************************************
// Get the temperature from the RTC
// ************************************************************
float getRTCTemp() {
  if (useRTC) {
    return Clock.getTemperature();
  } else {
    return 0.0;
  }
}

//**********************************************************************************
//**********************************************************************************
//*                               EEPROM interface                                 *
//**********************************************************************************
//**********************************************************************************

// ************************************************************
// Save current values back to EEPROM
// ************************************************************
void saveEEPROMValues() {
  EEPROM.write(EE_12_24, hourMode);
  EEPROM.write(EE_FADE_STEPS, fadeSteps);
  EEPROM.write(EE_DATE_FORMAT, dateFormat);
  EEPROM.write(EE_DAY_BLANKING, dayBlanking);
  EEPROM.write(EE_DIM_DARK_LO, dimDark % 256);
  EEPROM.write(EE_DIM_DARK_HI, dimDark / 256);
  EEPROM.write(EE_BLANK_LEAD_ZERO, blankLeading);
  EEPROM.write(EE_SCROLLBACK, scrollback);
  EEPROM.write(EE_FADE, fade);
  EEPROM.write(EE_SCROLL_STEPS, scrollSteps);
  EEPROM.write(EE_DIM_BRIGHT_LO, dimBright % 256);
  EEPROM.write(EE_DIM_BRIGHT_HI, dimBright / 256);
  EEPROM.write(EE_DIM_SMOOTH_SPEED, sensorSmoothCountLDR);
  EEPROM.write(EE_RED_INTENSITY, redCnl);
  EEPROM.write(EE_GRN_INTENSITY, grnCnl);
  EEPROM.write(EE_BLU_INTENSITY, bluCnl);
  EEPROM.write(EE_BACKLIGHT_MODE, backlightMode);
  EEPROM.write(EE_HV_VOLTAGE, hvTargetVoltage);
  EEPROM.write(EE_SUPPRESS_ACP, suppressACP);
  EEPROM.write(EE_HOUR_BLANK_START, blankHourStart);
  EEPROM.write(EE_HOUR_BLANK_END, blankHourEnd);
  EEPROM.write(EE_CYCLE_SPEED, cycleSpeed);
  EEPROM.write(EE_PULSE_LO, pwmOn % 256);
  EEPROM.write(EE_PULSE_HI, pwmOn / 256);
  EEPROM.write(EE_PWM_TOP_LO, pwmTop % 256);
  EEPROM.write(EE_PWM_TOP_HI, pwmTop / 256);
  EEPROM.write(EE_MIN_DIM_LO, minDim % 256);
  EEPROM.write(EE_MIN_DIM_HI, minDim / 256);
  EEPROM.write(EE_ANTI_GHOST, antiGhost);
  EEPROM.write(EE_USE_LDR, useLDR);
  EEPROM.write(EE_BLANK_MODE, blankMode);
  EEPROM.write(EE_SLOTS_MODE, slotsMode);
}

// ************************************************************
// read EEPROM values
// ************************************************************
void readEEPROMValues() {
  hourMode = EEPROM.read(EE_12_24);

  fadeSteps = EEPROM.read(EE_FADE_STEPS);
  if ((fadeSteps < FADE_STEPS_MIN) || (fadeSteps > FADE_STEPS_MAX)) {
    fadeSteps = FADE_STEPS_DEFAULT;
  }

  dateFormat = EEPROM.read(EE_DATE_FORMAT);
  if ((dateFormat < DATE_FORMAT_MIN) || (dateFormat > DATE_FORMAT_MAX)) {
    dateFormat = DATE_FORMAT_DEFAULT;
  }

  dayBlanking = EEPROM.read(EE_DAY_BLANKING);
  if ((dayBlanking < DAY_BLANKING_MIN) || (dayBlanking > DAY_BLANKING_MAX)) {
    dayBlanking = DAY_BLANKING_DEFAULT;
  }

  dimDark = EEPROM.read(EE_DIM_DARK_HI) * 256 + EEPROM.read(EE_DIM_DARK_LO);
  if ((dimDark < SENSOR_LOW_MIN) || (dimDark > SENSOR_LOW_MAX)) {
    dimDark = SENSOR_LOW_DEFAULT;
  }

  blankLeading = EEPROM.read(EE_BLANK_LEAD_ZERO);
  scrollback = EEPROM.read(EE_SCROLLBACK);
  fade = EEPROM.read(EE_FADE);

  scrollSteps = EEPROM.read(EE_SCROLL_STEPS);
  if ((scrollSteps < SCROLL_STEPS_MIN) || (scrollSteps > SCROLL_STEPS_MAX)) {
    scrollSteps = SCROLL_STEPS_DEFAULT;
  }

  dimBright = EEPROM.read(EE_DIM_BRIGHT_HI) * 256 + EEPROM.read(EE_DIM_BRIGHT_LO);
  if ((dimBright < SENSOR_HIGH_MIN) || (dimBright > SENSOR_HIGH_MAX)) {
    dimBright = SENSOR_HIGH_DEFAULT;
  }

  sensorSmoothCountLDR = EEPROM.read(EE_DIM_SMOOTH_SPEED);
  if ((sensorSmoothCountLDR < SENSOR_SMOOTH_READINGS_MIN) || (sensorSmoothCountLDR > SENSOR_SMOOTH_READINGS_MAX)) {
    sensorSmoothCountLDR = SENSOR_SMOOTH_READINGS_DEFAULT;
  }

  backlightMode = EEPROM.read(EE_BACKLIGHT_MODE);
  if ((backlightMode < BACKLIGHT_MIN) || (backlightMode > BACKLIGHT_MAX)) {
    backlightMode = BACKLIGHT_DEFAULT;
  }

  redCnl = EEPROM.read(EE_RED_INTENSITY);
  if ((redCnl < COLOUR_CNL_MIN) || (redCnl > COLOUR_CNL_MAX)) {
    redCnl = COLOUR_RED_CNL_DEFAULT;
  }

  grnCnl = EEPROM.read(EE_GRN_INTENSITY);
  if ((grnCnl < COLOUR_CNL_MIN) || (grnCnl > COLOUR_CNL_MAX)) {
    grnCnl = COLOUR_GRN_CNL_DEFAULT;
  }

  bluCnl = EEPROM.read(EE_BLU_INTENSITY);
  if ((bluCnl < COLOUR_CNL_MIN) || (bluCnl > COLOUR_CNL_MAX)) {
    bluCnl = COLOUR_BLU_CNL_DEFAULT;
  }

  hvTargetVoltage = EEPROM.read(EE_HV_VOLTAGE);
  if ((hvTargetVoltage < HVGEN_TARGET_VOLTAGE_MIN) || (hvTargetVoltage > HVGEN_TARGET_VOLTAGE_MAX)) {
    hvTargetVoltage = HVGEN_TARGET_VOLTAGE_DEFAULT;
  }

  pwmOn = EEPROM.read(EE_PULSE_HI) * 256 + EEPROM.read(EE_PULSE_LO);
  if ((pwmOn < PWM_PULSE_MIN) || (pwmOn > PWM_PULSE_MAX)) {
    pwmOn = PWM_PULSE_DEFAULT;

    // Hmmm, need calibration
    EEPROM.write(EE_HVG_NEED_CALIB, true);
  }

  pwmTop = EEPROM.read(EE_PWM_TOP_HI) * 256 + EEPROM.read(EE_PWM_TOP_LO);
  if ((pwmTop < PWM_TOP_MIN) || (pwmTop > PWM_TOP_MAX)) {
    pwmTop = PWM_TOP_DEFAULT;

    // Hmmm, need calibration
    EEPROM.write(EE_HVG_NEED_CALIB, true);
  }

  suppressACP = EEPROM.read(EE_SUPPRESS_ACP);

  blankHourStart = EEPROM.read(EE_HOUR_BLANK_START);
  if ((blankHourStart < 0) || (blankHourStart > HOURS_MAX)) {
    blankHourStart = 0;
  }

  blankHourEnd = EEPROM.read(EE_HOUR_BLANK_END);
  if ((blankHourEnd < 0) || (blankHourEnd > HOURS_MAX)) {
    blankHourEnd = 7;
  }

  cycleSpeed = EEPROM.read(EE_CYCLE_SPEED);
  if ((cycleSpeed < CYCLE_SPEED_MIN) || (cycleSpeed > CYCLE_SPEED_MAX)) {
    cycleSpeed = CYCLE_SPEED_DEFAULT;
  }

  minDim = EEPROM.read(EE_MIN_DIM_HI) * 256 + EEPROM.read(EE_MIN_DIM_LO);
  if ((minDim < MIN_DIM_MIN) || (minDim > MIN_DIM_MAX)) {
    minDim = MIN_DIM_DEFAULT;
  }

  antiGhost = EEPROM.read(EE_ANTI_GHOST);
  if ((antiGhost < ANTI_GHOST_MIN) || (antiGhost > ANTI_GHOST_MAX)) {
    antiGhost = ANTI_GHOST_DEFAULT;
  }
  dispCount = DIGIT_DISPLAY_COUNT + antiGhost;

  blankMode= EEPROM.read(EE_BLANK_MODE);
  if ((blankMode < BLANK_MODE_MIN) || (blankMode > BLANK_MODE_MAX)) {
    blankMode = BLANK_MODE_DEFAULT;
  }

  useLDR = EEPROM.read(EE_USE_LDR);

  slotsMode= EEPROM.read(EE_SLOTS_MODE);
  if ((slotsMode < SLOTS_MODE_MIN) || (slotsMode > SLOTS_MODE_MAX)) {
    slotsMode = SLOTS_MODE_DEFAULT;
  }

}

// ************************************************************
// Reset EEPROM values back to what they once were
// ************************************************************
void factoryReset() {
  hourMode = HOUR_MODE_DEFAULT;
  blankLeading = LEAD_BLANK_DEFAULT;
  scrollback = SCROLLBACK_DEFAULT;
  fade = FADE_DEFAULT;
  fadeSteps = FADE_STEPS_DEFAULT;
  dateFormat = DATE_FORMAT_DEFAULT;
  dayBlanking = DAY_BLANKING_DEFAULT;
  dimDark = SENSOR_LOW_DEFAULT;
  scrollSteps = SCROLL_STEPS_DEFAULT;
  dimBright = SENSOR_HIGH_DEFAULT;
  sensorSmoothCountLDR = SENSOR_SMOOTH_READINGS_DEFAULT;
  dateFormat = DATE_FORMAT_DEFAULT;
  dayBlanking = DAY_BLANKING_DEFAULT;
  backlightMode = BACKLIGHT_DEFAULT;
  redCnl = COLOUR_RED_CNL_DEFAULT;
  grnCnl = COLOUR_GRN_CNL_DEFAULT;
  bluCnl = COLOUR_BLU_CNL_DEFAULT;
  hvTargetVoltage = HVGEN_TARGET_VOLTAGE_DEFAULT;
  suppressACP = SUPPRESS_ACP_DEFAULT;
  blankHourStart = 0;
  blankHourEnd = 7;
  cycleSpeed = CYCLE_SPEED_DEFAULT;
  pwmOn = PWM_PULSE_DEFAULT;
  pwmTop = PWM_TOP_DEFAULT;
  minDim = MIN_DIM_DEFAULT;
  antiGhost = ANTI_GHOST_DEFAULT;
  useLDR = USE_LDR_DEFAULT;
  blankMode = BLANK_MODE_DEFAULT;
  slotsMode = SLOTS_MODE_DEFAULT;

  saveEEPROMValues();
}

//**********************************************************************************
//**********************************************************************************
//*                          High Voltage generator                                *
//**********************************************************************************
//**********************************************************************************

// ************************************************************
// Adjust the HV gen to achieve the voltage we require
// Pre-calculate the threshold value of the ADC read and make
// a simple comparison against this for speed
// We control only the PWM "off" time, because the "on" time
// affects the current consumption and MOSFET heating
// ************************************************************
void checkHVVoltage() {
  if (getSmoothedHVSensorReading() > rawHVADCThreshold) {
    setPWMTopTime(pwmTop + getInc());
  } else {
    setPWMTopTime(pwmTop - getInc());
  }
}

// Get the increment value we are going to use based on the magnitude of the 
// difference we have measured
int getInc() {
  int diffValue = abs(getSmoothedHVSensorReading() - rawHVADCThreshold);
  int incValue = 1;
  if (diffValue > 20) incValue = 50;
  else if (diffValue > 10) incValue = 5;
  return incValue;  
}

// ************************************************************
// Calculate the target value for the ADC reading to get the
// defined voltage
// ************************************************************
int getRawHVADCThreshold(double targetVoltage) {
  double externalVoltage = targetVoltage * 4.7 / 394.7 * 1023 / 5;
  int rawReading = (int) externalVoltage;
  return rawReading;
}

//**********************************************************************************
//**********************************************************************************
//*                          Light Dependent Resistor                              *
//**********************************************************************************
//**********************************************************************************

// ******************************************************************
// Check the ambient light through the LDR (Light Dependent Resistor)
// Smooths the reading over several reads.
//
// The LDR in bright light gives reading of around 50, the reading in
// total darkness is around 900.
//
// The return value is the dimming count we are using. 999 is full
// brightness, 100 is very dim.
//
// Because the floating point calculation may return more than the
// maximum value, we have to clamp it as the final step
// ******************************************************************
int getDimmingFromLDR() {
  if (useLDR) {
    int rawSensorVal = 1023 - analogRead(LDRPin);
    double sensorDiff = rawSensorVal - sensorLDRSmoothed;
    sensorLDRSmoothed += (sensorDiff / sensorSmoothCountLDR);

    double sensorSmoothedResult = sensorLDRSmoothed - dimDark;
    if (sensorSmoothedResult < dimDark) sensorSmoothedResult = dimDark;
    if (sensorSmoothedResult > dimBright) sensorSmoothedResult = dimBright;
    sensorSmoothedResult = (sensorSmoothedResult - dimDark) * sensorFactor;

    int returnValue = sensorSmoothedResult;

    if (returnValue < minDim) returnValue = minDim;
    if (returnValue > DIGIT_DISPLAY_OFF) returnValue = DIGIT_DISPLAY_OFF;
    return returnValue;
  } else {
    return DIGIT_DISPLAY_OFF;  
  }
}

// ******************************************************************
// Routine to check the PWM LEDs
// brightens and dims a PWM capable LED
// - 0 to 255 ramp up
// - 256 to 511 plateau
// - 512 to 767 ramp down
// ******************************************************************
void checkLEDPWM(byte LEDPin, int step) {
  if (step > 767) {
    analogWrite(LEDPin, getLEDAdjusted(0, 1, 1));
  } else if (step > 512) {
    analogWrite(LEDPin, getLEDAdjusted(255 - (step - 512), 1, 1));
  } else if (step > 255) {
    analogWrite(LEDPin, getLEDAdjusted(255, 1, 1));
  } else if (step > 0) {
    analogWrite(LEDPin, getLEDAdjusted(step, 1, 1));
  }
}

// ******************************************************************
// Calibrate the HV generator
// The idea here is to get the right combination of PWM on and top
// time to provide the right high voltage with the minimum power
// Consumption.
//
// Every combination of tubes and external power supply is different
// and we need to pick the right PWM total duration ("top") and
// PWM on time ("on") to match the power supply and tubes.
// Once we pick the "on" time, it is not adjusted during run time.
// PWM top is adjusted during run.
//
// The PWM on time is picked so that we reach just the point that the
// inductor goes into saturation - any more time on is just being used
// to heat the MOSFET and the inductor, but not provide any voltage.
//
// We go through two cycles: each time we decrease the PWM top
// (increase frequency) to give more voltage, then reduce PWM on
// until we notice a drop in voltage.
// ******************************************************************
void calibrateHVG() {

  // *************** first pass - get approximate frequency *************
  rawHVADCThreshold = getRawHVADCThreshold(hvTargetVoltage + 5);

  setPWMOnTime(PWM_PULSE_DEFAULT);
  // Calibrate HVGen at full
  for (int i = 0 ; i < 768 ; i++ ) {
    loadNumberArraySameValue(8);
    allBright();
    outputDisplay();
    checkHVVoltage();
    checkLEDPWM(tickLed, i);
  }

  // *************** second pass - get on time minimum *************
  rawHVADCThreshold = getRawHVADCThreshold(hvTargetVoltage);

  // run up the on time from the minimum to where we reach the required voltage
  setPWMOnTime(PWM_PULSE_MIN);
  for (int i = 0 ; i < 768 ; i++ ) {
    //loadNumberArray8s();
    loadNumberArrayConfInt(pwmOn, 0);
    allBright();
    outputDisplay();

    if (getSmoothedHVSensorReading() < rawHVADCThreshold) {
      if ((i % 8) == 0 ) {
        incPWMOnTime();
      }
    }
    checkLEDPWM(RLed, i);
  }

  int bottomOnValue = pwmOn;

  // *************** third pass - get on time maximum *************
  setPWMOnTime(pwmOn + 50);
  for (int i = 0 ; i < 768 ; i++ ) {
    //loadNumberArray8s();
    loadNumberArrayConfInt(pwmOn, 0);
    allBright();
    outputDisplay();

    if (getSmoothedHVSensorReading() > rawHVADCThreshold) {
      if ((i % 8) == 0 ) {
        decPWMOnTime();
      }
    }
    checkLEDPWM(GLed, i);
  }

  int topOnValue = pwmOn;

  int aveOnValue = (bottomOnValue + topOnValue) / 2;
  setPWMOnTime(aveOnValue);

  // *************** fourth pass - adjust the frequency *************
  rawHVADCThreshold = getRawHVADCThreshold(hvTargetVoltage + 5);

  // Calibrate HVGen at full
  for (int i = 0 ; i < 768 ; i++ ) {
    loadNumberArraySameValue(8);
    allBright();
    outputDisplay();
    checkHVVoltage();
    checkLEDPWM(BLed, i);
  }
}

/**
   Set the PWM top time. Bounds check it so that it stays
   between the defined minimum and maximum, and that it
   does not go under the PWM On time (plus a safety margin).

   Set both the internal "pwmTop" value and the register.
*/
void setPWMTopTime(int newTopTime) {
  if (newTopTime < PWM_TOP_MIN) {
    newTopTime = PWM_TOP_MIN;
  }

  if (newTopTime > PWM_TOP_MAX) {
    newTopTime = PWM_TOP_MAX;
  }

  if (newTopTime < (pwmOn + PWM_OFF_MIN)) {
    newTopTime = pwmOn + PWM_OFF_MIN;
  }

  ICR1 = newTopTime;
  pwmTop = newTopTime;
}

/**
   Set the new PWM on time. Bounds check it to make sure
   that is stays between pulse min and max, and that it
   does not get bigger than PWM top, less the safety margin.

   Set both the internal "pwmOn" value and the register.
*/
void setPWMOnTime(int newOnTime) {
  if (newOnTime < PWM_PULSE_MIN) {
    newOnTime = PWM_PULSE_MIN;
  }

  if (newOnTime > PWM_PULSE_MAX) {
    newOnTime = PWM_PULSE_MAX;
  }

  if (newOnTime > (pwmTop - PWM_OFF_MIN)) {
    newOnTime = pwmTop - PWM_OFF_MIN;
  }

  OCR1A = newOnTime;
  pwmOn = newOnTime;
}

void incPWMOnTime() {
  setPWMOnTime(pwmOn + 1);
}

void decPWMOnTime() {
  setPWMOnTime(pwmOn - 1);
}

/**
   Get the HV sensor reading. Smooth it using a simple
   moving average calculation.
*/
int getSmoothedHVSensorReading() {
  int rawSensorVal = analogRead(sensorPin);
  double sensorDiff = rawSensorVal - sensorHVSmoothed;
  sensorHVSmoothed += (sensorDiff / sensorSmoothCountHV);
  int sensorHVSmoothedInt = (int) sensorHVSmoothed;
  return sensorHVSmoothedInt;
}

//**********************************************************************************
//**********************************************************************************
//*                                 I2C interface                                  *
//**********************************************************************************
//**********************************************************************************

/**
 * receive information from the master
 */
void receiveEvent(int bytes) {
  // the operation tells us what we are getting
  int operation = Wire.read();

  if (operation == I2C_TIME_UPDATE) {
    // If we're getting time from the WiFi module, mark that we have an active WiFi with a 5 min time out
    useWiFi = MAX_WIFI_TIME;

    int newYears = Wire.read();
    int newMonths = Wire.read();
    int newDays = Wire.read();

    int newHours = Wire.read();
    int newMins = Wire.read();
    int newSecs = Wire.read();

    setTime(newHours, newMins, newSecs, newDays, newMonths, newYears);
  } else if (operation == I2C_SET_OPTION_12_24) {
    byte readByte1224 = Wire.read();
    hourMode = (readByte1224 == 1);
    EEPROM.write(EE_12_24, hourMode);
  } else if (operation == I2C_SET_OPTION_BLANK_LEAD) {
    byte readByteBlank = Wire.read();
    blankLeading = (readByteBlank == 1);
    EEPROM.write(EE_BLANK_LEAD_ZERO, blankLeading);
  } else if (operation == I2C_SET_OPTION_SCROLLBACK) {
    byte readByteSB = Wire.read();
    scrollback = (readByteSB == 1);
    EEPROM.write(EE_SCROLLBACK, scrollback);
  } else if (operation == I2C_SET_OPTION_SUPPRESS_ACP) {
    byte readByteSA = Wire.read();
    suppressACP = (readByteSA == 1);
    EEPROM.write(EE_SUPPRESS_ACP, suppressACP);
  } else if (operation == I2C_SET_OPTION_DATE_FORMAT) {
    dateFormat = Wire.read();
    EEPROM.write(EE_DATE_FORMAT, dateFormat);
  } else if (operation == I2C_SET_OPTION_DAY_BLANKING) {
    dayBlanking = Wire.read();
    EEPROM.write(EE_DAY_BLANKING, dayBlanking);
  } else if (operation == I2C_SET_OPTION_BLANK_START) {
    blankHourStart = Wire.read();
    EEPROM.write(EE_HOUR_BLANK_START, blankHourStart);
  } else if (operation == I2C_SET_OPTION_BLANK_END) {
    blankHourEnd = Wire.read();
    EEPROM.write(EE_HOUR_BLANK_END, blankHourEnd);
  } else if (operation == I2C_SET_OPTION_FADE_STEPS) {
    fadeSteps = Wire.read();
    EEPROM.write(EE_FADE_STEPS, fadeSteps);
  } else if (operation == I2C_SET_OPTION_SCROLL_STEPS) {
    scrollSteps = Wire.read();
    EEPROM.write(EE_SCROLL_STEPS, scrollSteps);
  } else if (operation == I2C_SET_OPTION_BACKLIGHT_MODE) {
    backlightMode = Wire.read();
    EEPROM.write(EE_BACKLIGHT_MODE, backlightMode);
  } else if (operation == I2C_SET_OPTION_RED_CHANNEL) {
    redCnl = Wire.read();
    EEPROM.write(EE_RED_INTENSITY, redCnl);
  } else if (operation == I2C_SET_OPTION_GREEN_CHANNEL) {
    grnCnl = Wire.read();
    EEPROM.write(EE_GRN_INTENSITY, grnCnl);
  } else if (operation == I2C_SET_OPTION_BLUE_CHANNEL) {
    bluCnl = Wire.read();
    EEPROM.write(EE_BLU_INTENSITY, bluCnl);
  } else if (operation == I2C_SET_OPTION_CYCLE_SPEED) {
    cycleSpeed = Wire.read();
    EEPROM.write(EE_CYCLE_SPEED, cycleSpeed);
  } else if (operation == I2C_SHOW_IP_ADDR) {
    ourIP[0] = Wire.read();
    ourIP[1] = Wire.read();
    ourIP[2] = Wire.read();
    ourIP[3] = Wire.read();
  } else if (operation == I2C_SET_OPTION_FADE) {
    fade = Wire.read();
    EEPROM.write(EE_FADE, fade);
  } else if (operation == I2C_SET_OPTION_USE_LDR) {
    byte readByteUseLDR = Wire.read();
    useLDR = (readByteUseLDR == 1);
    EEPROM.write(EE_USE_LDR, useLDR);
  } else if (operation == I2C_SET_OPTION_BLANK_MODE) {
    blankMode = Wire.read();
    EEPROM.write(EE_BLANK_MODE, blankMode);
  } else if (operation == I2C_SET_OPTION_SLOTS_MODE) {
    slotsMode = Wire.read();
    EEPROM.write(EE_SLOTS_MODE, slotsMode);
  } else if (operation == I2C_SET_OPTION_MIN_DIM) {
    byte dimHI = Wire.read();
    byte dimLO = Wire.read();
    minDim = dimHI * 256 + dimLO;
    EEPROM.write(EE_MIN_DIM_HI, dimHI);
    EEPROM.write(EE_MIN_DIM_LO, dimLO);
  }
}

/**
   send information to the master
*/
void requestEvent() {
  byte configArray[I2C_DATA_SIZE];
  int idx = 0;
  configArray[idx++] = I2C_PROTOCOL_NUMBER;  // protocol version
  configArray[idx++] = encodeBooleanForI2C(hourMode);
  configArray[idx++] = encodeBooleanForI2C(blankLeading);
  configArray[idx++] = encodeBooleanForI2C(scrollback);
  configArray[idx++] = encodeBooleanForI2C(suppressACP);
  configArray[idx++] = encodeBooleanForI2C(fade);
  configArray[idx++] = dateFormat;
  configArray[idx++] = dayBlanking;
  configArray[idx++] = blankHourStart;
  configArray[idx++] = blankHourEnd;
  configArray[idx++] = fadeSteps;
  configArray[idx++] = scrollSteps;
  configArray[idx++] = backlightMode;
  configArray[idx++] = redCnl;
  configArray[idx++] = grnCnl;
  configArray[idx++] = bluCnl;
  configArray[idx++] = cycleSpeed;
  configArray[idx++] = encodeBooleanForI2C(useLDR);
  configArray[idx++] = blankMode;
  configArray[idx++] = slotsMode;
  configArray[idx++] = minDim / 256;
  configArray[idx++] = minDim % 256;

  Wire.write(configArray, I2C_DATA_SIZE);
}

byte encodeBooleanForI2C(boolean valueToProcess) {
  if (valueToProcess) {
    byte byteToSend = 1;
    return byteToSend;
  } else {
    byte byteToSend = 0;
    return byteToSend;
  }
}

 

таймер обратного отсчета ( на бочке с динамитом и все это в комнате из которой надо найти выход – квест)

4-Digit Wemos Clock
поиск часы на цифровых лампах – переход с главной странички

DA-2000 6-Digit Numitron Clock

This is a simple to build kit based on a Wemos micro controller, packed full of features and able to drive 6 Numitron tubes. Numitrons are long life filament displays and provide a warm glow and a restful display. The clock gets the time from an Internet NTP (Network Time Protocol) and is fully configurable via a browser.

This clock runs off a 5V DC USB power supply – an old one from a phone with a USB-B is perfect.

IV-9 6-Digit Numitron Clock

This is a simple to build kit based on a Wemos micro controller, packed full of features and able to drive 6 Numitron tubes. Numitrons are long life filament displays and provide a warm glow and a restful display. The clock gets the time from an Internet NTP (Network Time Protocol) and is fully configurable via a browser.

This clock runs off a 5V DC USB power supply – an old one from a phone with a USB-B is perfect.

All-In-One Clock for IN-14 / IN-8-2 / Z570M

This is a simple to build kit based on Arduino (Atmel ATMega micro controller), packed full of features for use with 6 IN-14 tubes. You don’t need to own an Arduino to use this kit! It is self contained and runs without an external Arduino board.

This kit is ideal if you want to make a traditional “6 in a row” eye catching clock in a custom case. You will need to add IN-14 tubes and an external power supply (9VDC centre positive, 5.5mm x 2.1mm barrel jack).

4-Digit Wemos Clock

A fully WiFi enabled clock using four IN-12 or IN-2 tubes. Fully configurable using a browser!

and the time is set from Internet atomic clock sources. The time also changes to respect daylight savings time. An external power supply (9VDC centre positive) is needed.

There is also a 3D printable case available for this clock for you to print at home!

ClassicWiFi Rev5

This is the new Classic Nixie Clock with an integrated ESP8266 WiFi module. This is our “Classic” board, but in a more compact form for the WiFi time provider. Note this this kit needs a small amount of SMD soldering!

You need to add the components, the tubes (any kind) and an external power supply (7.5VDC – 12VDC)

Modular Nixie Clock IN-14 tubes

This is a simple to build kit based on Arduino (Atmel ATMega micro controller), packed full of features and able to drive any combination of 6 tubes. It is the same as the “All-In-One” clock, but the tubes are mounted on removable tube holder boards.

This makes replacing a tube as simple as unplugging it and putting a new one in. You need to add IN-14 tubes and an external power supply (7.5VDC – 12VDC).

ClassicWiFi Rev6

This is the new Classic Nixie Clock with an integrated ESP8266 WiFi module. This is our “ClassicRev5” board, but updated to drive NeoPixels and tube decimal points, and to use a PIR or motion detector. Note this this kit needs a small amount of SMD soldering if you select the WiFi option!

You need to add the components, the tubes (any kind) and an external power supply (7.5VDC – 12VDC)

Single Decatron Clock (A-101 or OG-4)

A innovative and entertaining way of using a single Decatron to show the time, by rapidly animating the clock face on the round face of the Decatron. It is fully WiFi connected and supports a motion detector (PIR or microwave) to tun off the display when no one is there to see it.

You need to add the components, the Decatron (A-101 or OG-4) and an external power supply (7.5VDC – 12VDC)

Про подключение декатрона смотрим – Часы на цифровых лампах – там arduino dekatron shield Decatron A101 они дорогие десяток на 80 долл или 5000р и похоже почти все скупил. в Брянске не делают уже.

перевод будет

Separator LEDs/NeonsParts List:R381k or 270k (see note)R391k or 270k (see note)LED8LED 5mm (see note)LED9LED 5mm (see note)Neon1Neon indicator lamp(see note)Neon2Neon indicator lampP1Connect COM to HV or VCCNow that we have the tubes mounted, we can mount the separators. We do this after the tubesare mounted because now we can judge the height that the separators should be.Notes:•You can install either LEDs or neons as the colon indicators. There is a minor change to the components depending on which you choose.•For LEDs: You need to connect P1 “VIN” to “COM” and choose 1k values for R38 and R39. You need to take care of the polarity of the LEDs, following what is marked on the board.•For neons: You need to connect “170V” to “COM” and choose 270k values for R38 and R39. The polarity of the neons does not matter.•Be careful to adequately insulate the LED leads using some heat shrink or excess plasticsleeving.•Don’t use a pluggable “jumper” header for setting P1: Solder a wire between the pins you want to jump directly. If you use a header, someone later will want to change it.TroubleshootingThe separators do not light up.Make sure that you set the jumper P1 according to the separators you are using.Set to “VIN” for LEDs, or “170V” for neons.Front Panel componentsWhen all the components are installed, you are finished with the main build of the clock. You can now add the “front panel” components. These will usually be added on the outside of the clock case.SV24 pin connectorLDRLDRS1SWITCHThe switch connects to ground when closed. It uses the internal pull-up resistor provided by theAtmega on the input pin to pull the input to VCC when the switch is not closed.The switch is de-bounced in software, so practically any switch you want to use is suitable. A simple switch is provided in the kit, but you might want to substitute this switch with one that suits your case.The LDR forms a potential divider with R17. One leg of the LDR must be connected to the input pin, the other leg must be connected to GND.The LDR should be mounted in such a way that the flat face of the LDR is exposed to the ambient light. This will allow it to detect the ambient light and adjust the brightness for it.Please see the connection diagram in the schematics to understand how to wire the front panelcomponents.WiFi moduleParts List:R4010kR4110kR4210kR4310kC9100nFC10100nFC11220uF 10VQ72N7000 Q82N7000U178L33P22x4 Female HeaderWiFiPreprogrammed ESP-8266If you have the WiFi module, you need to install the level shifter components on the board. Thisprovides an interface between the 5V Atmel controller and the 3V3 ESP circuitry.Notes:•Be careful of the orientation of the polarity of the components Q7, Q8, U1 and C11•The ESP 8266 must be mounted so that the body of the board sits over the K155ID1, NOT pointing outwardsWarning!The ESP 8266 must point inwards to the board. It must not point outwards over the edge of the board.If you mount the ESP 8266 incorrectly, you will destroy it! The ESP 8266 does not have any key to make sure it cannot be mounted incorrectly, so you have totake care.RTC moduleParts List:RTCRTCSV3CONN_RTCThe RTC module is a traditional Real Time Clock module. Unfortunately we are no longer permitted to post the battery with the kit, so you will have to get your own. The battery you need to get is a CR2032, or a LIR20132 (rechargeable). You can mount the RTC module directly on the board, or as a separate board connected by flying leads.The markings on the board need to match up with the markings on the module. In particular, the VCC and GND need to be in the right orientation.The WiFi module has it’s own instruction manual. Please refer to that if you have the WiFi option.•The RTC module has two sets of contacts on it. You can use either the side with the pins on it or wire up the other side with flying wires. If you use the side with pins, you should carefully remove the two unused pins (see hint). If you use the contacts on the module, you should leave CONN_RTC off the board.•If you receive the connector header as a single strip, break off 4 pins for SV3.Hint: Trimming the extra pins on the RTC moduleONLY if you want to mount the RTC module directly onto the main board (you can also do it via flying leads), trim off the pins “32K” and “SQW” using a pair of precision side cutters.If you want to mount using flying leads, you can skip this step and use the four holes on the other side of the board.RTC Module with pins removedTroubleshootingIf not everything goes as you expect, please refer to the test steps during the construction and the associated troubleshooting tips. If that does not cover the problem you have, please see below. If you still can’t find the answer, contact us!TroubleshootingThe tubes flash (or blink) on and off.This could be a symptom that the external power supply can’t deliver the powerneeded to drive the circuit.On start up, the High Voltage generator needs to draw significantly more power than when it is running normally, and in some cases this might overload the external power supply.Try a different external power supply and see if the problem persists.TroubleshootingThe tube display brightness is not constant, and appears to “pulse” rapidly.This is a symptom that the High Voltage generator or the external power supply is overloaded.First perform a factory reset to make sure that no strange values have been left in the EEPROM.Next, check the value of the PWM On Timeconfiguration. Try increasing this until the brightness is constant, but be careful not to set the value too high. Thelonger the On Time, the more the MOSFET has to conduct current, and this will cause it to heat up. A good value for small tubes is 120-150, larger tubes may require 150-200.TroubleshootingThe display is too dim.Check if the auto-dimming is working. If the display does not change in low or high ambient light, your LDR does not appear to be working. Check the connections to the LDR.If the LDR is correct, perform a factory reset to make sure that no strange values have been left in the EEPROM.Check the LDR reading by pressing the button three times in quick succession when the clock is on. You should see a value between “01 00 00” and “09 99 00”. Changing the light conditions should change this value. It is normal that the value is not stable when it is in the middle of the range. We read the LDR many times a second, and it is unusual that two readings are identical.TroubleshootingThe display does not come on, but I do have a high voltage.Try pressing the button. If the display comes on, you probably have display blanking mode set. Check the configuration.Check the orientation of the opto-couplers.Check the LDR connection. In some cases, the dimming algorithm does not startup as expected when no LDR is present. Shine a bright light on the LDR.In some cases, a factory reset can help.TroubleshootingOne or more of the RGB LEDs will not go out completelyOne of the LEDs (either one of the R, G or B channels, or the TICK LED) does notgo out completely when it is configured to be dark or at the darkest point of the “pulse” flash.The FETs which drive the LEDs are very sensitive, and can pick up the stray voltages which are carried by excess flux on the board. Carefully clean with a non-scratching instrument between the three pins on the FET of the affected channel. Alternatively, use a solvent to remove excess flux.TroubleshootingThe MOSFET gets really hot.Try a factory reset. There is a setting about how hard the IRF740 should be driven “PWM On Time”. Perhaps the value has not been set properly. The defaultvalue should be OK most of the time, but depending on the tubes and power supply, this might need adjustment. The lower the value, the less power will be used and the less hot the MOSFET will run, but also the less power will be available to drive the tubes.Check the power supply. If the power supply is too “strong” (too much voltage or too much current capacity), the MOSFET will have to carry high currents. Try a different power supply. 9V and 500mA is ideal.Change the settings for the “PWM On Time”. Adjust it to be as small as possible without a loss of brightness. This also reduces the power consumption of the module: normally it should not consume more than 3W.TroubleshootingI can see some “ghosting”.“Ghosting” is where you can see a very faint image of another number at the same time as the one that should be shown. Some tubes are more sensitive than others, and depending on the construction and components, it might show up more.If you see ghosting, increase the “anti-ghosting” setting, but only to the point where the ghosting is no longer visible or irritating.The “anti-ghosting” setting decreases the overall brightness of the display slightly, and not all tubes (even of the same sort) need it, so anti-ghosting should only be used when there is a real need to use it.TroubleshootingThe clock sometimes just stops showing the digits.In especially noisy environments with clocks that have no grounding, external interference can cause the controller to lock up. In this case, please either put the clock in a grounded enclosure or select a higher quality power supply. (Poor quality power supplies don’t provide any path to ground).In extreme cases, there is a grounding point close to the crystal and you can ground the clock circuit.Programming the micro-controllerThe micro-controller comes preprogrammed. You don’t need to program it, but you might want to, especially if you want to change the code.You can update the micro-controller with a newer version of the software, or even create your own software, and load it onto the chip. We have gone to a lot of trouble to make this as easy as possible.Programming with an Arduino UnoWe supply the 328P micro-controller chips with a standard Arduino boot loader, so you don’t need to have a special programmer in order to update the software, a standard Arduino UNO is enough.To program the 328P, simply remove it from the clock board, and place it in the Arduino UNO. Then you will be able to program the controller as you would any other Arduino UNO, simply upload the software onto the controller. Put the 328P back into the clock board and you are done.You can also program the 328P micro-controller with a programmer, but you will lose the possibility to program in the Arduino UNO, unless you remember to burn the boot loader again.That’s it!One last thing: The controller used in the IN-14 All-In-One clock Rev is completely compatible with the Classic Nixie Clock module Rev2, Rev3 and Rev4. They can be interchanged without danger.:)Parts list / BOMHere is the list of the parts needed for the main board:#IDQtyValue1C5,C6222pF2LED9,LED82LED 5mm3C112.2uF4C3,C42220uF5LED2,LED7,LED6,LED5,LED4,LED36LED RAGB (common anode)6Q2116MHz7D1,D22UF40078IC2174141N9R214.7k10R11390k11R14,R7,R8,R28,R27,R26,R6,R1083k12R13,R12,R9,R11,R3,R19,R18,R23,R20,R21,R22,R24,R25,R29,R31,R32, R33,R34,R35,R36,R37,R4,R5,R15,R16271k13R38,R3921k for LEDs,270k for neons14R30,R17210k15IC31MEGA8-P 328P16OK1,OK2,OK3,OK4,OK5,OK66EL81717SV1,SV2,SV334 pin header18Q3,Q4,Q532N700019Q61MPSA4220C7,C2,C83100nF21LED11LED 3mm22L11100uH23IC11780524Q11IRF64025N2,N5,N4,N3,N6,N16IN-1426CON_11Barrel JackParts needed for the optional WiFi module:#IDQtyValue1R40, 41, 42, 43410k2C9, C102100nF3C111220uF 10V4Q7, Q822N70005U11AP1117-3V36P212x4 Female HeaderRevisions:V0001: 12Jan2018: New manual for Revision 3V0002: 05Dec2018: Correct External Components drawing, thanks Kent!V0003: 14May2020: Correct Wifi BoM, and separators section.

я не перевожу – на поиск этой комплектухи у нас уйдет месяца 2 – без поездки к Павелецкому в крупную компанию будет трудновато да и туда доставят от 500 штук за месяц. Ардуино уно делается с помощью кристалла atmega328p – подсоединить к wileem eprom или к другой ардуинке и прошить загрузчик ( купить на радио рынке или Али готовые ). Для опытов нужен комп нормальный – можно ноутбук, параллельный порт не обязательно. Держу для программатора 2 системника года 2008 со всеми портами – на одном материнка asus p5w dh deluxe он покупался за 3000 американских с корпусом и видеоплатой. Примерно 2006 года , там памяти много, 16 вроде и винда 10-ка установилась, а диск пришлось поменять – нового типа ssd не стал работать но запустился прекрасно toshiba на 3 терабайта ( найти в крупном розничном магазине а то сейчас эпидемия – компьютерщики заразились майнером chia , я не очень поддерживаю, этот диск не ходовой). Хорошо подойдут советские ex-ussr детальки, но они сейчас дороже так раз в пяток – оказалось делали очень хорошо, да еще буковки военной приемки. вариант анодных ключей кт3157а кт940 (mpsa92 mpsa42).

програмка на ассемблере часы ( буду разбирать еще прошивку с какой то – залоченой – платы, раньше увлекались)

;**************************************************************************
; Jon's Mods 12/2006 for B7971 alphanumeric nixie tube
; Earl's Mods 7/2005 for standard nixie tube driven by Port A and Port B
; Original program was David Tait's CLOCK.ASM
;
; Displays time on a single B7971 Nixie display.
; Timing Source: 4 MHz crystal

; A B7971 Nixie display is attached as follows:
;
;	PIN	SEGMENT			B7971 PIN #
;	RB0       1			    8
;	RB1       2			    5
;	RB2       3			    6
;	RB3       4			    9
;	RB4       5			    14
;	RB5       6			    15
;	RB6       7		  	    12 (SEE NOTE 1)\
;	RB7       8			    7		    } Both active = pin 1, 10 on
;	RA0	  11			    16 (SEE NOTE 1)/
;	RA1	  9			    3  (SEE NOTE 2)\
;	RA2	  14			    4  (SEE NOTE 2)/ Both active = pin 17 on
;   
;	RA3 Chime Output
;	RA4 Switch Input
;
; * Note 1:
; RB6 and RA0 feed into logic gate system, RB6 feeds segment 7 via NOR
; and RA0 feeds segment 11 via NOR. When both RB6 and RA0 are active, segment 10/13 on.
;
; * Note 2:
; RA1 and RA2 feed into logic gate system, RA1 feeds segment 9 via NOR
; and RA2 feeds segment 14 via NOR. When both RA1 and RA2 are active, segment 12 on.
;
; Each output is connected via a resistor to a transistor base.
; The common anode is connected to high voltage (200-300V) through a resistor. 
; Holding button down during power up runs a Nixie Test program.
;
; The time is displayed digit by digit over a four second period
; every 10 seconds and the display is blank otherwise.  
;
; A normally open switch is connected from RA4 to GND via a 470 ohm 
; resistor; no pull-up resistor is needed as an internal weak pull-up 
; is enabled by the program.  To set the clock press the switch when
; the appropriate digit is displayed; the digit can then be incremented 
; by pressing the switch repeatedly.  If the switch is open for about 
; 2 seconds the clock display is restarted allowing another digit to be 
; changed.  It is up to the user to put in sensible times as no checking 
; is done by the program.
;
;**************************************************************************
	processor	16f84
	include		<p16f84.inc>
	__config	_XT_OSC & _WDT_OFF & _PWRTE_ON

	ERRORLEVEL      -302    ; suppress bank selection messages

PCL     EQU     2               ; registers are defined here to make this
STATUS  EQU     3               ; program self contained.
PORTA   EQU     5
PORTB   EQU     6
INTCON  EQU     0BH
TRISA   EQU     85H
TRISB   EQU     86H
OPTREG  EQU     81H

C       EQU     0               ; bits in STATUS
Z       EQU     2
RP0     EQU     5

T0IF    EQU     2               ; bit in INTCON


; Below are ticks for 4 MHz crystal

TCK0    EQU     .122
TCK1    EQU     .123
TCK2    EQU     .121
TCK3    EQU     .123
TCK4    EQU     .124
TCK5    EQU     .125
TCK6    EQU     .125


	CBLOCK  0CH             ; define variables required

	TICKS                   ; decremented every tick (9.15 ms or 8.19 ms)
	SEGS                    ; one bit per segment
	SEC
	SEC10
	MIN
	MIN10
	HOUR
	HOUR10
	FRAME                   ; used to decide when to display time
	HHMM                    ; one bit per digit displayed
	COUNT                   ; scratch register
	DIGIT                   ; last digit displayed
	CHIME			; Number of chimes needed
	ENDC


	;*********************************;
	;         Initialization          ;
	;*********************************;

	ORG     0

INIT    CLRF    SEC
	CLRF    SEC10
	CLRF    MIN
	CLRF    MIN10
	CLRF    HOUR
	movlw	D'1'
	movwf	HOUR10
	CLRF    FRAME
	CLRF    PORTA
	CLRF    PORTB
	CLRF	CHIME
	BSF     STATUS,RP0      ; select register bank 1
	CLRF    TRISB		; all of port B are outputs
	MOVLW   10H
	MOVWF   TRISA           ; only RA4 is an input
	MOVLW   4
	MOVWF   OPTREG          ; assign prescaler (1:32) to TMR0
	BCF     STATUS,RP0      ; reselect bank 0

	BTFSS   PORTA,4         ; switch on RA4 open?
	GOTO    TEST         	; no, go to test mode
	;*********************************;
	;          Main Program           ;
	;*********************************;

MAIN    CALL    CLOCK           		; real-time clock algo called every second
		CALL    SHOW            	; display the time
		MOVF	CHIME,F			; check if chimes needed
		BZ	WAIT			; No Chime now
		BSF	PORTA,3			; Chime on RA3
		DECF	CHIME,F
WAIT    BTFSS   INTCON,	T0IF     		; wait for TMR0 to roll over
		GOTO    WAIT
		CLRF    INTCON
		CALL    CHKSW           	; check for button press
		MOVF	TICKS,W
		XORLW	D'20'			;Check for 20 ticks
		BTFSS	STATUS,Z
		GOTO	J1
		CLRF	PORTB			;Turn off Display at 100/120 ticks
		CLRF	PORTA
J1		DECFSZ  TICKS,F
		GOTO    WAIT
		GOTO    MAIN            	; get here every second


	;*********************************;
	;  Convert digit to segment form  ;
	;*********************************;

SEGTAB  ANDLW   0FH 
	ADDWF   PCL,F

	DT      0xFC,0x02,0xD1,0xB1,0x67,0x96,0x9E,0x80,0xFF,0xF1,0x00 ; Port B lookup table

SEGTABA  ANDLW   0FH 
	ADDWF   PCL,F

	DT      00h,01h,01h,04h,00h,02h,02h,05h,00h,06h,00h ; Port A lookup table

	;*********************************;
	;  Real-time clock algorithm      ;
	;*********************************;

CLOCK   MOVLW   TCK0            ; increment digits as appropriate
	MOVWF   TICKS           ; and define number of ticks per
	INCF    SEC,F           ; second for next second
	MOVF    SEC,W
	SUBLW   9
	BTFSC   STATUS,C
	 RETURN

	MOVLW   TCK1			;per 10 sec
	MOVWF   TICKS
	CLRF    SEC				;clear seconds, add 10 seconds
	INCF    SEC10,F
	MOVF    SEC10,W
	SUBLW   5				;check for a 6
	BTFSC   STATUS,C
	 RETURN

	MOVLW   TCK2			;per min
	MOVWF   TICKS
	CLRF    SEC10			;clear 10sec, add 1 minute
	INCF    MIN,F
	MOVF    MIN,W
	SUBLW   9				;check for 10
	BTFSC   STATUS,C
	 RETURN

	MOVLW   TCK3			;per 10 minutes
	MOVWF   TICKS
	CLRF    MIN				;clear minute, add 10 minute
	INCF    MIN10,F
	MOVF    MIN10,W
	SUBLW   5
	BTFSC   STATUS,C
	 RETURN

	MOVLW   TCK4
	MOVWF   TICKS
	CLRF    MIN10
	MOVF    HOUR10,W
	SUBLW   1
	BTFSC   STATUS,C
	 GOTO   INCHR
	INCF    HOUR,F
	MOVF    HOUR,W
	SUBLW   3
	BTFSC   STATUS,C
	goto SETCHIME

	MOVLW   TCK6
	MOVWF   TICKS
	CLRF    HOUR
	CLRF    HOUR10
	goto SETCHIME


INCHR   INCF    HOUR,F			;Add to single digit hour
		MOVF    HOUR,W
		SUBLW   9		; did it go from 9 to 10 ?
		BTFSC   STATUS,C
		goto SETCHIME		; no, it's  less than 10

		MOVLW   TCK5		;deal with 9 to 10 carry
		MOVWF   TICKS		;each 10 hour
		CLRF    HOUR
		INCF    HOUR10,F
		GOTO SETCHIME

SETCHIME 	MOVF	HOUR,W		;On hour change set chimes
		MOVWF	CHIME
		MOVF	HOUR10,F
		BZ	J2
		MOVLW	D'10'
		ADDWF	CHIME,F
J2		RETURN
	;*********************************;
	; Displays the time digit by digit;
	;*********************************;

SHOW    CLRF    HHMM            
		INCF    FRAME,F         ; increment place in frame
		MOVF    FRAME,W
		SUBLW   D'06'			; Display cycle time
		BTFSS   STATUS,C
		CLRF    FRAME
		MOVF    FRAME,W
		XORLW   1               
		BTFSC   STATUS,Z
		GOTO    DHOUR10        ; display 10s of hours when frame is 1
		XORLW   1^2
		BTFSC   STATUS,Z
		GOTO    DHOUR          ; display hour when frame is 2
		XORLW   2^3
		BTFSC   STATUS,Z
		GOTO    DMIN10         ; display 10s of mins when frame is 3
		XORLW   3^4
		BTFSC   STATUS,Z
		GOTO    DMIN           ; display mins when frame is 4 
		MOVLW   H'00'              ; otherwise blank display
		MOVWF   PORTB
		MOVLW   H'00'
		MOVWF   PORTA
		RETURN

DHOUR10 BSF     HHMM,3
		MOVF    HOUR10,W
		BTFSS	STATUS,Z		;check for leading zero
		GOTO    DISPLAY        
		RETURN					;skip display of zero

DHOUR   BSF     HHMM,2
		MOVF    HOUR,W
		GOTO    DISPLAY

DMIN10  BSF     HHMM,1
		MOVF    MIN10,W
		GOTO    DISPLAY

DMIN    BSF     HHMM,0
		MOVF    MIN,W           ; falls through to DISPLAY

	
	;*********************************;
	;       Displays digit in W       ;
	;*********************************;

DISPLAY MOVWF   DIGIT           ; save number to be displayed in DIGIT
	CALL    SEGTAB          ; call bit allocation table
	MOVWF   PORTB           ; and display bits for port B
	MOVF	DIGIT,W
	CALL	SEGTABA		; call bit allocation table
	MOVWF   PORTA           ; and display bits for port A
	RETURN


	;*********************************;
	; Checks for a switch press and   ;
	; updates digit if displayed      ;
	;*********************************;

CHKSW   BTFSC   PORTA,4         ; switch closed on RA4?
		RETURN                 ; no
		MOVF    HHMM,F
		BTFSC   STATUS,Z        ; digit displayed?
		RETURN                 ; no

INCDIG  INCF    DIGIT,F         ; DIGIT is the currently displayed number
		MOVF    DIGIT,W
		SUBLW   9
		BTFSS   STATUS,C
		CLRF    DIGIT
		MOVF    DIGIT,W
		CALL    DISPLAY
		CALL    DELAY           ; wait for switch to settle
CHKSW0  BTFSS   PORTA,4         ; switch open?
		GOTO    CHKSW0         ; no
		CALL    DELAY
		BTFSS   PORTA,4         ; still open?
		GOTO    CHKSW0         ; no
		MOVLW   D'20'
		MOVWF   COUNT
CHKSW1  BTFSS   PORTA,4         ; switch open?
		GOTO    INCDIG         ; no
		CALL    DELAY
		DECFSZ  COUNT,F
		GOTO    CHKSW1
		MOVF    DIGIT,W         ; switch was open for around 2 secs
		CLRF    FRAME  
		CLRF    SEC10
		CLRF    SEC
		BTFSC   HHMM,3          ; update correct digit
		MOVWF  HOUR10
		BTFSC   HHMM,2
		MOVWF  HOUR
		BTFSC   HHMM,1
		MOVWF  MIN10
		BTFSC   HHMM,0
		MOVWF   MIN
		GOTO    MAIN            ; restart the program


	;*********************************;
	; Delay used by switch routine    ;
	;*********************************;

DELAY   MOVLW   D'12'           ; roughly 100ms delay
		MOVWF   TICKS
DEL1    BTFSS   INTCON,T0IF     ; wait for TMR0 to roll over
	 	GOTO    DEL1
		CLRF    INTCON
		DECFSZ  TICKS,F
	 	GOTO    DEL1
		RETURN

TEST	CLRF	COUNT			;exercise Nixie by cycling through
		MOVF	COUNT,W			;all numbers
		CALL	DISPLAY
		CALL	DELAY
		CALL	DELAY		;1 second delay
		INCF	COUNT,F
		MOVF	COUNT,W
		XORLW   D'10'			;Test for 10             
		BTFSS   STATUS,Z
		GOTO    TEST+1
		GOTO	TEST   		;Reset COUNT to 0 
		END

 

програмка на бейсике (pic basic)

пограмма часов на другой плате . Без статики – но там выбираются 3 выхода на аноды ламп – каждый на 2 и 2 дешифратора К155ИД1, подобрал же 185 вольт для всех ИН1 без засветов лишних. Будет 4 штуки ИН18 – самые большие из доступных и у них такие же резисторы как у ИН1, 15 килоом или 18 , 175 -195 вольт.

////////// Hardware outputs //////////
//This clock is 2x3 multiplexed: two tubes powered at a time.
//The anode channel determines which two tubes are powered,
//and the two SN74141 cathode driver chips determine which digits are lit.
//4 pins out to each SN74141, representing a binary number with values [1,2,4,8]
byte binOutA[4] = {2,3,4,5};
byte binOutB[4] = {6,7,8,9};
//3 pins out to anode channel switches
byte anodes[3] = {11,12,13};

 

полностью код программы будет с изменениями – arduinix 6 , 4, https://github.com/isparkes?tab=repositories https://github.com/isparkes/ArdunixNix6 RLB Design’s плата UNDB https://github.com/clockspot/arduino-clock это оригинал. Я хорошо разогнал под статический дисплей програмку и яркость не потерялась и не моргает – 56 больших циклов в секунду : убрал настройку последовательных регистров в подпрограмму, все через ShiftOut библиотеку. Это на моей страничке – есть в записи Часы на цифровых лампах. Не моргает, свет яркий, 3 эффекта работают включая затухание плавное и смена сразу 2-х цифр, на определенное время включаются 53-56 раз в секунду. Взял еще один эффект – и сделал – сменяемая цифра _сгорает_ со вспышкой – надо в конфигурации включить режим fade настройка 11.

добавляю устройство фонарик – используется открытая матрица led cob на 10 ватт на хорошем радиаторе – как бы катушка намотана и ее обмотка позволяет 5 ампер снять. Выпрямитель конечно будет Шоттки на 40А от блока питания.

штука вообще инопланетная – часы -включаются кнопочкой на секунд 20, 5 ламп ИН12 , мощный фонарь и батарейка плоская (от ноутбука) – для вылазок на природу пойдет, не хватает только зеленого лазера * его аргонка требует мощности неслабой лгн406 например 380 вольт ампер 100 вот на дискотеке включали давно было не вру на секунды, пожарка не понадобилась.

подсветка из одного led cob – то есть это на самом деле кристалл на котором 9 светодиодов по 1 ватту, у него характеристика – 12 вольт питание 3 Ома ток 0,4 напряжение на сборке 9,8 – 50 процентов а 2 ома ток 0,6 и 10,4 в – не проверял больше а то погорит : 300 mA на светодиод и 900 на сборку может и можно но яркости не прибавит а дым скорее всего пойдет. 3,6 вольта на каждый кристаллик это на пределе, такие же только из 3 кристалликов ставятся на светодиодную ленту, там превышать напряжение нельзя здохнут. ( а из 50 штук по 0,1 ватта только чуть желтоватые лампочка – намного ярче на глазок). Снимок из пещеры на Imgur – это вот такой фонарь из 50 кристаллов как прожектор, он 4 ватта всего.

проверка лампочки ( есть видео на ю тюбе – поиск – проверка ламп и декатронов) просто приблизить к Катушке Теслы. Поле высокой частоты зажигает неон внутри лампы и не только неон – голубым загораются наполненные водородом тиратрон и стабилитрон газовый, а это нужно 1000 вольт. А они там есть. Катушка Теслы есть на страничках – она не совсем резонансная но близко – на частоте удвоенной от резонанса на самом деле – особенность транзистора КТ817 при токе 2 ампера а частота 6 мегагерц, 16 вольт. Эффект начинается при большой катушке от 1700 а если еще больше то от 145 – 150 килогерц. Кстати ВЧ разряд не бьет током но может обжечь – уколоть палец.