Redfern_89

led_clock.c

Jan 27th, 2019
289
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 32.05 KB | None | 0 0
  1. /*
  2.  * led_clock.c
  3.  *
  4.  * Created: 24.12.2018 19:24:51
  5.  * Author : Redfern.89
  6.  *
  7.  *      ========== ПРОЕКТ ЕЩЕ В СТАДИИ РАЗРАБОТКИ !!! ==========
  8.  *
  9.  * Для работы с NEC я использовал следующие источники
  10.  *      http://radiohlam.ru/?p=1018
  11.  *      https://habr.com/ru/post/257041/
  12.  * Для работы с i2c и DS1307
  13.  *      https://radioparty.ru/prog-avr/program-c/430-lesson-ds1307
  14.  * Мои вопросы на форумах, касательно этого проекта
  15.  *      http://www.cyberforum.ru/avr/thread2384452.html
  16.  *      http://www.cyberforum.ru/avr/thread2387596.html
  17.  *      http://forum.amperka.ru/threads/atmega328-nec-%D0%BF%D1%80%D0%BE%D1%82%D0%BE%D0%BA%D0%BE%D0%BB.17370/
  18.  *      http://forum.amperka.ru/threads/%D0%96%D0%B5%D1%81%D1%82%D1%8C-%D1%81-%D0%BF%D1%80%D0%B5%D1%80%D1%8B%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%D0%BC%D0%B8-%D0%BD%D0%B0-atmega328.17355/
  19.  */
  20.  
  21. // Частота проца в герцах
  22. #ifndef F_CPU
  23. #define F_CPU   16000000UL
  24. #endif
  25.  
  26. #include <stdio.h>
  27. #include <avr/io.h>
  28. #include <util/delay.h>
  29. #include <avr/interrupt.h>
  30. #include <math.h>
  31. #include <avr/eeprom.h>
  32.  
  33. // Настройки портов ввода-вывода
  34. #define RCLK_DDR    DDRD        //         
  35. #define SCLK_DDR    DDRD        //           SSSSSSSSSS      PPPPPPPPP    III
  36. #define DATA_DDR    DDRD        //          S               P         P    I
  37. #define RCLK_PORT   PORTD       //          S               P         P    I
  38. #define SCLK_PORT   PORTD       //           SSSSSSSSSS     P PPPPPPPP     I
  39. #define DATA_PORT   PORTD       //                     S    P              I
  40. #define RCLK        1           //                     S    P              I
  41. #define SCLK        3           //           SSSSSSSSSS     P             III
  42. #define DATA        0           //
  43.  
  44. /* ДИНАМИК БУДИЛЬНИКА */
  45. #define SPEAKER_DDR     DDRD
  46. #define SPEAKER_PORT    PORTD
  47. #define SPEAKER_OUT     PD5
  48.  
  49. /*  ~~~~~~~~~~~~~~~~~~~~ МАКРОСЫ ~~~~~~~~~~~~~~~~~~~~ */
  50.  
  51. // Упарвление защелкой и сдвигом регистров
  52. #define _74hc595_RegisterLatch(code)    {   RCLK_PORT &= ~(1 << RCLK); code; RCLK_PORT |= (1 << RCLK);  }
  53. #define _74hc595_RegisterShift()        {   SCLK_PORT &= ~(1 << SCLK); SCLK_PORT |= (1 << SCLK);        }
  54.    
  55. // Запуск и стоп таймера 0
  56. #define T0_START    {    TCCR0B |= (1 << CS01) | (1 << CS00);   }
  57. #define T0_STOP     {    TCCR0B = 0x00;                         }
  58.  
  59. // Таймер для работы интерфеса (отсчет прошедших милисекунд с момента старта системы)
  60. volatile unsigned long long int millis = 0;
  61.  
  62. // Таймер без delay
  63. #define setInterval(n, tmr, code) { if ((millis - tmr) >= n) { tmr = millis; code; }}
  64.  
  65. /* Кое-какие глобальные переменные */
  66.  
  67. //  ( это ааааадовый костыль, который, я надеюсь потом уберу )
  68. // Переменные-таймеры/счетчики...так думаю, что нахуй они не нужны,
  69. // ибо более одной задержки одновременно не планирую в программе ...
  70. unsigned long long int tmr0 = 0;
  71. unsigned long long int tmr1 = 0;
  72. unsigned long long int tmr2 = 0;
  73. unsigned long long int tmr3 = 0;
  74.  
  75. // Данные о сегментах индикатора (LSBFIRST)
  76. // H, A, F, B, D, C, G, E
  77. unsigned char digits[11] = { 0x7D, 0x14, 0x5B, 0x5E, 0x36, 0x6E, 0x6F, 0x54, 0x7F, 0x7E, 0xFF };
  78.  
  79. // Данные о группах светодиодов
  80. unsigned char groups[4] = { 0xF7, 0xFD, 0xFB, 0xEF };
  81.  
  82. unsigned char NC                        = 0xFF;
  83. volatile unsigned char display_data[4]  = { 0x00, 0x00, 0x00, 0x00 };
  84. volatile const static int max_groups    = 4;
  85. volatile unsigned int display_pos       = 0;
  86.  
  87. /* Протокол NEC */
  88. volatile int NEC_REPEAT_FLAG            = 0;
  89. volatile int NEC_START_FLAG             = 0;
  90. volatile int NEC_IR_DONE                = 0;
  91. volatile unsigned long int NEC_SCLK     = 0;        // Тактовые синхроимпульсы (64 мкс)
  92. volatile unsigned long int NEC_RECV_CNT = 0;        // Кол-во принятых битов
  93. const int NEC_MIN_CLK = 5;                          // Минимальное значение, при котором следует начинать захват
  94. /*
  95.     Минимальные м максимальные интервалы. Значения были вычесленны при 100 нажатиях на кнопки пульта,
  96.     полученные значения были запёханы в массив и найдены минимумы и максимумы
  97. */
  98. const int NEC_MIN_HEADER_MESSAGE_CLK = 190;
  99. const int NEC_MAX_HEADER_MESSAGE_CLK = 245;
  100. const int NEC_MIN_ONE_BIT_CLK = 30;
  101. const int NEC_MAX_ONE_BIT_CLK = 40;
  102. const int NEC_MIN_NUL_BIT_CLK = 15;
  103. const int NEC_MAX_NUL_BIT_CLK = 25;
  104. // Задержки для определения повтора сообщения (не используется)
  105. const int NEC_MIN_REPEAT_CLK = 170;
  106. const int NEC_MAX_REPEAT_CLK = 180;
  107. const static int NEC_MAX_RESET_OVF      = 1200; // Столько тиков должно пройти, что-бы система перешла вновь в режим ожидания (1200 * 64 = 76.8ms)
  108. const static int NEC_PACKET_LENGTH      = 32;
  109. volatile unsigned char addr1 = 0x00;    // Адрес
  110. volatile unsigned char addr2 = 0x00;    // Инверсия адреса
  111. volatile unsigned char cmd1 = 0x00;     // Команда
  112. volatile unsigned char cmd2 = 0x00;     // Инверсия команды
  113. volatile uint16_t command = 0x0000;     // Команда пульта
  114.  
  115. // Смещения для создания битовой маски сообщенй (адрес1, адрес2, команда1, команда2)
  116. const int offset1_addr1 = 0;
  117. const int offset2_addr1 = 9;
  118. const int offset1_addr2 = 9;
  119. const int offset2_addr2 = 17;
  120. const int offset1_cmd1 = 17;
  121. const int offset2_cmd1 = 25;
  122. const int offset1_cmd2 = 25;
  123. const int offset2_cmd2 = 33;   
  124.  
  125. // Макросы для USART'а (использовал только при отладке и щас они нах ненужны)
  126. #define FOSC 16000000L
  127. #define BAUD 9600L
  128. #define MYUBRR FOSC / 16 / BAUD - 1
  129.  
  130. // Коды с пульта
  131. const uint16_t POWER_OFF = 0xFF45;
  132. const uint16_t MENU = 0xFF47;
  133. const uint16_t TEST = 0xFF44;
  134. const uint16_t PLUS = 0xFF40;
  135. const uint16_t RETURN = 0xFF43;
  136. const uint16_t RWND = 0xFF07;
  137. const uint16_t PLAY = 0xFF15;
  138. const uint16_t FWND = 0xFF09;
  139. const uint16_t MINUS = 0xFF19;
  140. const uint16_t CLEAR = 0xFF0D;
  141. const uint16_t D0 = 0xFF16;
  142. const uint16_t D1 = 0xFF0C;
  143. const uint16_t D2 = 0xFF18;
  144. const uint16_t D3 = 0xFF5E;
  145. const uint16_t D4 = 0xFF08;
  146. const uint16_t D5 = 0xFF1C;
  147. const uint16_t D6 = 0xFF5A;
  148. const uint16_t D7 = 0xFF42;
  149. const uint16_t D8 = 0xFF52;
  150. const uint16_t D9 = 0xFF4A;
  151.  
  152. // Структура DateTime (для удобства работы)
  153. typedef struct {
  154.     int Sec;
  155.     int Min;
  156.     int Hour;
  157.     int Month;
  158.     int Day;
  159.     int Year;
  160.     int WeekDay;
  161.     int AMPM;
  162.     int H12_24;
  163. } TDateTime;
  164. TDateTime DateTime;
  165.  
  166. // Структура TAlarmData
  167. typedef struct {
  168.     int Hour;
  169.     int Min;
  170.     int enabled;
  171. } TAlarmData;
  172. TAlarmData AlarmData;
  173.  
  174. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  175.                                 USART functions
  176.  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  177. void USORT_Init(unsigned char ubrr) {
  178.     UBRR0H = (unsigned char)(ubrr >> 8);
  179.     UBRR0L = (unsigned char)ubrr;
  180.     UCSR0B = (1 << RXEN0) | (1 << TXEN0);
  181.     UCSR0C = (1 << USBS0) | (3 << UCSZ00);
  182. }
  183.  
  184. void USORT_Transmit( unsigned char data ) {
  185.     while ( !( UCSR0A & (1 << UDRE0)) );
  186.     UDR0 = data;
  187. }
  188.  
  189. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  190.                         Shift registers functions
  191.  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  192.  
  193. // Инициализация портов ввода-вывода SPI
  194. void _74hc595_SPI_Init() {
  195.     RCLK_DDR |= (1 << RCLK);
  196.     SCLK_DDR |= (1 << SCLK);
  197.     DATA_DDR |= (1 << DATA);
  198. }
  199.  
  200. // Отправка данных в регистр
  201. void _74hc595_SPI_send(char data) {
  202.     int i;
  203.     unsigned char val;
  204.    
  205.     for (i = 0; i < 8; i++) {
  206.         val = !!(data & (1 << (7 - i)));
  207.         if (val) DATA_PORT |= 1 << DATA;
  208.             else DATA_PORT &= ~(1 << DATA);
  209.         _74hc595_RegisterShift();
  210.     }
  211. }
  212.  
  213. // Отправка данных в регистры с защелкиванием (2.0)
  214. void _74hc595_RegisterWrite2(unsigned char* data, int registrs_count) {
  215.     unsigned int i = 0;
  216.     _74hc595_RegisterLatch({
  217.         for (i = 0; i < registrs_count; i++) {
  218.             _74hc595_SPI_send(data[i]);
  219.         }
  220.     });
  221. }
  222.  
  223. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  224.                             NEC 2.0+ functions
  225.  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  226.  
  227. // Таймер обработки длительности импульсов
  228. ISR (TIMER0_OVF_vect) {
  229.     TCNT0 = 0xF0; // Настроенный таймер тикает так, что-бы каждый тик совершался через каждые 64us
  230.     if (++NEC_SCLK >= NEC_MAX_RESET_OVF) { // Если в течении 1200 тиков ничего не пришло, переходим в режим ожидания
  231.         T0_STOP; // Останавливаем таймер
  232.         NEC_SCLK = 0; // Обнуляем счетчик синхроимпульсов
  233.         NEC_START_FLAG = 0;  // Обнуляем флаг приема стартового сообщения
  234.         command = 0x0000;
  235.     }
  236.    
  237.     // Это не обязательно, но для перестраховки оставлю тут
  238.     // Если в течении 1200 тиков пришло менее 32 бит, обнуляем счетчики и переходим в режим ожидания
  239.     if (NEC_SCLK >= NEC_MAX_RESET_OVF && NEC_RECV_CNT < NEC_PACKET_LENGTH) {
  240.         T0_STOP;
  241.         NEC_SCLK = 0;
  242.         NEC_RECV_CNT = 0;
  243.         command = 0x0000;
  244.     }
  245.    
  246. }
  247.  
  248. /*
  249.  
  250. Вот так выглядит сообщение - 0x00FF453A (если что, это кнопка выключения)
  251. Описание:
  252.     1. Header - стартовое сообщение. Опорный импульс. Нужен для того, что-бы система определила, когда начинать отсчет
  253.     2. Pause - пауза. После паузы идет прерывание на спад.
  254.     3. 0 и 1 - Биты данных. Всего сообщение имеет 32 бита. 0 длится ~1,12мс, 1 длится ~2,25мс
  255.  
  256.   Header  Pause 0 0 0 0 0 0 0 0  1   1   1   1   1   1   1   1   1  0 0 0  1  0  1  0 0  1   1   1  0  1  0  1
  257. |________|_____|_|_|_|_|_|_|_|_|___|___|___|___|___|___|___|___|___|_|_|_|___|_|___|_|_|___|___|___|_|___|_|___|_
  258.    9ms    4.5ms
  259. */
  260.  
  261. // Обработчик внешнего прерывания
  262. ISR (INT0_vect) {
  263.     T0_START; // Начинаем отсчет
  264.    
  265.     if (NEC_SCLK > NEC_MIN_CLK) { // Не обробатываем то, что меньше ожидаемого
  266.        
  267.         // Тут определяем стартовое сообщение (преамбулу)
  268.         if (NEC_SCLK >= NEC_MIN_HEADER_MESSAGE_CLK && NEC_SCLK < NEC_MAX_HEADER_MESSAGE_CLK) {
  269.             NEC_START_FLAG = 1;
  270.             NEC_REPEAT_FLAG = 0;
  271.             NEC_RECV_CNT = 0;
  272.         }
  273.        
  274.         /* Знаю, по идиотски, Но умнее лень было придумывать */
  275.        
  276.         // Тут определяем биты нулевого значения
  277.         if ((NEC_SCLK >= NEC_MIN_NUL_BIT_CLK && NEC_SCLK < NEC_MAX_NUL_BIT_CLK) && NEC_START_FLAG) {
  278.             NEC_RECV_CNT++; // Инкрементируем колличество принятых нулей
  279.             // ну а тут мутнаые процедуры записи значений в переменные
  280.             if (NEC_RECV_CNT >= offset1_addr1 && NEC_RECV_CNT < offset2_addr1) { // Если мы в диапозоне 1-8, ...
  281.                 addr1 &= ~(1 << (NEC_RECV_CNT - offset1_addr1)); // добавляем в addr1 нули в нужные места
  282.             }
  283.             // Остальные диапозоны тоже самое
  284.             if (NEC_RECV_CNT >= offset1_addr2 && NEC_RECV_CNT < offset2_addr2) {
  285.                 addr2 &= ~(1 << (NEC_RECV_CNT - offset1_addr2));
  286.             }
  287.             if (NEC_RECV_CNT >= offset1_cmd1 && NEC_RECV_CNT < offset2_cmd1) {
  288.                 cmd1 &= ~(1 << (NEC_RECV_CNT - offset1_cmd1));
  289.             }
  290.             if (NEC_RECV_CNT >= offset1_cmd2 && NEC_RECV_CNT < offset2_cmd2) {
  291.                 cmd2 &= ~(1 << (NEC_RECV_CNT - offset1_cmd2));
  292.             }
  293.         }
  294.        
  295.         // Тут определяем биты положительного значения (такая-же хуйня как и с нулями, только интервалы у NEC_SCLK больше)
  296.         if ((NEC_SCLK >= NEC_MIN_ONE_BIT_CLK && NEC_SCLK < NEC_MAX_ONE_BIT_CLK) && NEC_START_FLAG) {
  297.             NEC_RECV_CNT++; // Инкрементируем колличество принятых едениц
  298.            
  299.             if (NEC_RECV_CNT >= offset1_addr1 && NEC_RECV_CNT < offset2_addr1) {
  300.                 addr1 |= (1 << (NEC_RECV_CNT - offset1_addr1));
  301.             }
  302.             if (NEC_RECV_CNT >= offset1_addr2 && NEC_RECV_CNT < offset2_addr2) {
  303.                 addr2 |= (1 << (NEC_RECV_CNT - offset1_addr2));
  304.             }
  305.             if (NEC_RECV_CNT >= offset1_cmd1 && NEC_RECV_CNT < offset2_cmd1) {
  306.                 cmd1 |= (1 << (NEC_RECV_CNT - offset1_cmd1));
  307.             }
  308.             if (NEC_RECV_CNT >= offset1_cmd2 && NEC_RECV_CNT < offset2_cmd2) {
  309.                 cmd2 |= (1 << (NEC_RECV_CNT - offset1_cmd2));
  310.             }
  311.         }
  312.        
  313.         // Тут обнуляем счетчик синхроимульсов, что-бы отсчитывать время следующего прерывания с нуля
  314.         NEC_SCLK = 0;
  315.        
  316.         // Колличество нулей и едениц в конечном счете должно быть 32, на этом и остановимся
  317.         if (NEC_RECV_CNT == NEC_PACKET_LENGTH) {
  318.             // Выставляем в стартовое положение все счетчики и останавлиеваем подсчет
  319.             NEC_RECV_CNT = 0;
  320.             NEC_START_FLAG = 0;
  321.             T0_STOP;
  322.             // Проверка сообщения на целостность
  323.             // В версии протокола 2.0 адрес имеет расширеное пространство и не имеет инверсии
  324.             // Значит проверяем либо сложив обе инверсии (адреса и команды), либо только инверсии команды
  325.             if ((((addr1 + addr2 == 0xFF) && (cmd1 + cmd2) == 0xFF)) || (cmd1 + cmd2 == 0xFF)) {
  326.                 NEC_IR_DONE = 1; // Сообщаем системе, что чтение завершено
  327.                 // Команду склеиваем с адресом (16 бит)
  328.                 command = ((addr1 + addr2) << 8) | cmd1;
  329.             }
  330.         }
  331.        
  332.     }
  333.  
  334. }
  335.  
  336. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  337.                             i2c Functions
  338.  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  339. void i2c_init(void) {
  340.     TWBR = 2;   // Настройка частоты шины
  341.     TWSR = (1 << TWPS1) | (1 << TWPS0); // Предделитель на 64
  342.     TWCR |= (1 << TWEN);    // Включение модуля TWI
  343. }
  344.  
  345. void i2c_start() {
  346.     TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTA); // Передача условия START
  347.     while (!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT
  348. }
  349.  
  350. void i2c_stop() {
  351.     TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); // Передача условия STOP
  352.     while (TWCR & (1 << TWSTO)); // Ожидание завршения передачи условия STOP
  353. }
  354.  
  355. unsigned char i2c_write(unsigned char data) {
  356.     TWDR = data; // Загрузка данных в регистр TWDR
  357.     TWCR = (1 << TWEN) | (1 << TWINT);  // Сброс флага TWINT для начала передачи данных
  358.     while (!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT
  359.    
  360.     // Проверка статуса
  361.     if((TWSR & 0xF8) == 0x18 || (TWSR & 0xF8) == 0x40 || (TWSR & 0xF8) == 0x28) return 1;
  362.     else return 0;
  363. }
  364.  
  365. unsigned char i2c_read(unsigned char *data, unsigned char ack) {
  366.     // Возвращаем "подтверждение" после приема
  367.     if (ack) TWCR |= (1 << TWEA);
  368.     // Возвращаем "неподтверждение" после приема
  369.     // Ведомое устройство не получает больше данных
  370.     // обычно используется для распознования последнего байта
  371.     else TWCR &= ~(1 << TWEA);
  372.     // Разрешение приема данных после сброса TWINT
  373.     TWCR |= (1 << TWINT);
  374.     while (!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT
  375.     // Проверка статуса
  376.     // Если принят байт данных и возвращается "подтверждение"(0x50)
  377.     // или принят байт данных и возвращается "ненеподтверждение"(0x58)
  378.     if ((TWSR & 0xF8) == 0x50 || (TWSR & 0xF8) == 0x58) {
  379.         *data = TWDR; // Читаем данные из TWDR
  380.         return 1; // OK
  381.     } else {
  382.         return 0; // ОШИБКА
  383.     }
  384. }
  385.  
  386. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  387.                             DS1307 Functions
  388.  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  389. unsigned char DS1307_Read(unsigned char address, unsigned char *data) {
  390.     unsigned char res;
  391.     i2c_start(); // Старт
  392.     res = i2c_write(0b11010000); // Адрес DS1307+W
  393.     if (!res) return 0; // Ошибка
  394.     res = i2c_write(address); // Передача адреса необходимого регистра
  395.     if (!res) return 0; // Ошибка
  396.     i2c_start(); // Повторный старт
  397.     res = i2c_write(0b11010001); // Адрес DS1307+R
  398.     if (!res) return 0; // Ошибка
  399.     res = i2c_read(data, 0); // чтение данных
  400.     if (!res) return 0; // Ошибка
  401.     i2c_stop(); // стоп
  402.     return 1; // ОК
  403. }
  404.  
  405. unsigned char DS1307_Write(unsigned char address, unsigned char data) {
  406.     unsigned char res;
  407.     i2c_start(); // старт
  408.     res = i2c_write(0b11010000); // адреc DS1307+W
  409.     if (!res) return 0; // ошибка
  410.     res = i2c_write(address); // Передача адреса необходимого регистра
  411.     if (!res) return 0; // ошибка
  412.     res = i2c_write(data); // Запись данных
  413.     if (!res) return 0; // ошибка
  414.     i2c_stop(); // стоп
  415.     return 1; // ОК
  416. }
  417.  
  418. // Процедура чтения данных из DS1307 в структуру TDateTime (пока не доработал ее, да и лень как-то)
  419. void DS1307_ReadDateTime(void) {
  420.     unsigned char tmp;
  421.    
  422.     /* Читаем данные и преобразуем из BCD в двоичную систему */
  423.     // Секунды
  424.     DS1307_Read(0x00, &tmp);
  425.     DateTime.Sec = (((tmp & 0xF0) >> 4) * 10) + (tmp & 0x0F);
  426.     // Минуты
  427.     DS1307_Read(0x01, &tmp);
  428.     DateTime.Min = (((tmp & 0xF0) >> 4) * 10) + (tmp & 0x0F);
  429.     // Часы
  430.     DS1307_Read(0x02, &tmp);
  431.     DateTime.Hour = (((tmp & 0xF0) >> 4) * 10) + (tmp & 0x0F);
  432.     // День недели
  433.     DS1307_Read(0x03, &tmp);
  434.     DateTime.WeekDay = (((tmp & 0xF0) >> 4) * 10) + (tmp & 0x0F);
  435. }
  436.  
  437. // Процедура записи в микросхему из структуры TDateTime
  438. void DS1307_WriteDateTime() {
  439.     unsigned char tmp;
  440.     tmp = ((DateTime.Sec / 10) << 4) | (DateTime.Sec % 10);
  441.     DS1307_Write(0x00, tmp);
  442.     tmp = ((DateTime.Min / 10) << 4) | (DateTime.Min % 10);
  443.     DS1307_Write(0x01, tmp);
  444.     tmp = ((DateTime.Hour / 10) << 4) | (DateTime.Hour % 10);
  445.     DS1307_Write(0x02, tmp);
  446.     tmp = ((DateTime.WeekDay / 10) << 4) | (DateTime.WeekDay % 10);
  447.     DS1307_Write(0x03, tmp);
  448. }
  449.  
  450. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  451.                             Main Functions
  452.  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  453.  
  454. // Запись данных будильника
  455. void AlarmWriteTime(void) {
  456.     eeprom_write_byte((uint8_t*)0x00, AlarmData.Hour);
  457.     eeprom_write_byte((uint8_t*)0x01, AlarmData.Min);
  458.     eeprom_write_byte((uint8_t*)0x02, (AlarmData.enabled ? (1 << 0) : (0 << 0)));
  459. }
  460.  
  461. // Чтение данных будильника
  462. void AlarmReadTime(void) {
  463.     AlarmData.Hour = eeprom_read_byte((uint8_t*)0x00);
  464.     AlarmData.Min = eeprom_read_byte((uint8_t*)0x01);
  465.     AlarmData.enabled = (eeprom_read_byte((uint8_t*)0x02) & (1 << 0)) != 0;
  466. }
  467.  
  468. // Динамическая индикация
  469. ISR (TIMER2_OVF_vect) {
  470.     TCNT2 = 0xF0;
  471.            
  472.     /* Отсылаем текущие данные в сдвиговые регистры */
  473.     display_pos = (display_pos + 1) % max_groups;
  474.     unsigned char display[2] = { groups[display_pos], display_data[display_pos] }; // Содержит текущие данные о сегментах и группе светодиодов
  475.    
  476.     _74hc595_RegisterWrite2(display, 2);
  477. }
  478.  
  479. // Таймер для работы интерфейса системы (подсчет милисекунд с момента старта системы)
  480. ISR (TIMER1_OVF_vect) {
  481.     TCNT1 = 0xFFF0;
  482.     millis++;
  483. }
  484.  
  485. // Отрисовка цифр на дисплее
  486. // Я ебал что-то тут расписывать, но все-же попробую
  487. void print_display(int hours, int minutes, int colon_blink, int hours_blink, int min_blink) {
  488.     // переменные colon_blink, hours_blink и min_blink нужны для мигания цифр или двоеточия
  489.     // в основной программе эти значения переключаются через каждые N ms
  490.     if (!hours_blink) { // Если мы хотим отображать часы, то...
  491.         if (hours == 0) { // Если часы равны 0, ...
  492.             display_data[0] = digits[0]; // Рисуем в первом разряде ведущий ноль
  493.             // к первому разряду подключено двоеточие, если надо, отобразим и его и ноль на текущем разряде
  494.             display_data[1] = colon_blink ? digits[0] | (1 << 7) : digits[0];
  495.             } else if (hours >= 1 && hours <= 9) { // Если часы больше 1 и меньше 9, ...
  496.             display_data[0] = digits[0]; // Отобразим ведущий ноль
  497.             display_data[1] = colon_blink ? digits[hours] | (1 << 7) : digits[hours]; // И отобразим текущую цифру (и если надо двоеточие)
  498.             } else if (hours >= 10 && hours <= 99) { // Если часы больше 10 и меньше 99, ...
  499.             // Тупо разбиваем число на разрядыи эти разряды пихаем в нужные участки массива
  500.             display_data[0] = digits[(unsigned int)round(hours % 100) / 10]; // Перввый разряд это остаток от деления на 100, разделенный на 10
  501.             // Второй разряд это остаток от деления на 10 и опять-же, если надо, то зажечь двоеточие
  502.             display_data[1] = colon_blink ? digits[(unsigned int)round(hours % 10)] | (1 << 7) : digits[(unsigned int)round(hours % 10)];
  503.         }
  504.     } else { // Если мы не хотим отображать часы, то заполняем массив нулями
  505.         display_data[0] = ~NC;
  506.         display_data[1] = ~NC;
  507.     }
  508.    
  509.     /* Обсалютно такая-же хуйня и с минутами, но без двоеточия, т.к. оно подключенно к 1му разряду */
  510.     if (!min_blink) {
  511.         if (minutes == 0) {
  512.             display_data[2] = digits[0];
  513.             display_data[3] = digits[0];
  514.             } else if (minutes >= 1 && minutes <= 9) {
  515.             display_data[2] = digits[0];
  516.             display_data[3] = digits[minutes];
  517.             } else if (minutes >= 10 && minutes <= 99) {
  518.             display_data[2] = digits[(unsigned int)round(minutes % 100) / 10];
  519.             display_data[3] = digits[(unsigned int)round(minutes % 10)];
  520.         }
  521.     } else {
  522.         display_data[2] = ~NC;
  523.         display_data[3] = ~NC;
  524.     }
  525. }
  526.  
  527. // Инициализация микроконтроллера и переферии
  528. void MCU_init() {
  529.     // Динамик
  530.     SPEAKER_DDR |= (1 << SPEAKER_OUT);
  531.    
  532.     // Инициализация портов ввода-вывода SPI
  533.     _74hc595_SPI_Init();
  534.    
  535.     // Инициализация внешнего прерывания (на спад)
  536.     EICRA |= (1 << ISC01) | (0 << ISC00);
  537.     EIMSK |= (1 << INT0);
  538.     EIFR |= (1 << INTF0);
  539.     PCICR = 0x00;
  540.    
  541.     // Инициализация таймера/счестчика 0 для подсчета временного интервала между импульсами
  542.     TCCR0B = 0x00;//(1 << CS00) | (1 << CS01);
  543.     TIMSK0 |= (1 << TOIE0);
  544.     TCNT0 = 0xF0;
  545.    
  546.     // Инициализация таймера/счестчика 1 для работы интерфейса системы
  547.     TCCR1B |= (1 << CS12) | (0 << CS11) | (1 << CS10);
  548.     TIMSK1 |= (1 << TOIE1);
  549.     TCNT1 = 0xFFF0;
  550.    
  551.     // Инициализация таймера/счестчика 2 для динамической индикации
  552.     TCCR2B |= (1 << CS22);
  553.     TIMSK2 |= (1 << TOIE2);
  554.     TCNT2 = 0xFF;
  555.    
  556.     // Инициализация i2c
  557.     i2c_init();
  558. }
  559.  
  560. int main(void) {
  561.     asm("CLI");
  562.     MCU_init();
  563.     asm("SEI");
  564.    
  565.     // Переменные для работы интерфейса
  566.     int menu_level = 0; // позиция в главном меню
  567.     int menu_flag = 0; // Говорит о том, что открыто меню в данный момент или нет
  568.     int digits_blink_flag = 0; // Это переключающиеся значение для мигания настраиваемых значений
  569.     int colon_blink_flag = 0; // Это тоже переключающиеся значение для мигания двоеточия
  570.     int submenu_level = 0; // Позиция в подменю, клавиши на пульте: "<<" и ">>"
  571.     int s_hour = 0, s_min = 0; // Настраиваемые значения
  572.     int enter_menu_flag = 0; // Флаг входа в меню. выполняется каждый раз при входе в меню для приравнивания значений
  573.     int alarm_enabled_flag = 0, alarm_flag = 0; // Флаги будильника.
  574.     int blink_speed = 0; // Скорость мигания двоеточия. я идиот набитый забыл предусмотреть индикатор включенного будильника. хоть так будет видно
  575.    
  576.     while (1) {
  577.        
  578.         if (NEC_IR_DONE) { // Если пришла команда с пульта
  579.             NEC_IR_DONE = 0; // уже нет
  580.            
  581.             // Кнопка POWER_OFF включает/выключает будильник и останавливает его
  582.             if (command == POWER_OFF) {
  583.                 alarm_enabled_flag ^= 1;
  584.                 eeprom_write_byte((uint8_t*)0x03, alarm_enabled_flag ? (1 << 0) : (0 << 0));
  585.                 if (alarm_flag) alarm_flag = 0; // Если пищит будильник - вырубаем его
  586.             }
  587.            
  588.             // Заходим в меню
  589.             if (command == MENU) {
  590.                 menu_level++; // Прибавляем уровень меню
  591.                 menu_flag = 1; // Говорим системе, что мы в меню
  592.                 submenu_level = 0; // Обнуляем позицию подменю
  593.                 if (menu_level > 2) menu_level = 1; // Пунктов меню всего два
  594.                 enter_menu_flag = 1; // Говорим системе, что вошли в меню
  595.             }
  596.             // Если мы в меню и нажали на RETURN
  597.             if (command == RETURN && menu_flag) {
  598.                 // Выходим из меню и обнуляем счетчики позиций
  599.                 menu_flag = 0;
  600.                 menu_level = 0;
  601.                 submenu_level = 0;
  602.             }
  603.             // При нажатии на ">>" перемещаемся по подменю
  604.             if (command == FWND && menu_flag) {
  605.                 submenu_level++;
  606.                 if (submenu_level > 1) submenu_level = 0;
  607.             }
  608.             // Тоже самое для "<<"
  609.             if (command == RWND && menu_flag) {
  610.                 submenu_level--;
  611.                 if (submenu_level < 0) submenu_level = 1;
  612.             }
  613.             // Если мы в меню и нажали на "+"
  614.             if (command == PLUS && menu_flag) {
  615.                 if (submenu_level == 0) { // Если настраиваем часы
  616.                     s_hour++; // прибавляем часы
  617.                     if (s_hour > 23) s_hour = 0; // но не больше 23
  618.                     print_display(s_hour, s_min, 0, 0, 0); // так как настраиваемые цифры мигают, задержим их во время настройки
  619.                 }
  620.                 // тоже самое и с настройками минут
  621.                 if (submenu_level == 1) {
  622.                     s_min++;
  623.                     if (s_min > 59) s_min = 0;
  624.                     print_display(s_hour, s_min, 0, 0, 0);
  625.                 }
  626.             }
  627.             // Тоже самое, что и в "+", только значения убавляем и соблюдаем пороги
  628.             if (command == MINUS && menu_flag) {
  629.                 if (submenu_level == 0) {
  630.                     s_hour--;
  631.                     if (s_hour < 0) s_hour = 23;
  632.                     print_display(s_hour, s_min, 0, 0, 0);
  633.                 }
  634.                 if (submenu_level == 1) {
  635.                     s_min--;
  636.                     if (s_min < 0) s_min = 59;
  637.                     print_display(s_hour, s_min, 0, 0, 0);
  638.                 }
  639.             }
  640.             // Кнопка PLAY - это кнопка OK, при ее нажатии сохраняем текущие настройки
  641.             if (command == PLAY && menu_flag) {
  642.                 if (menu_level == 1) { // Если настраиваем время
  643.                     DateTime.Sec = 0; // Устанавливаем такой формат XX:XX:00
  644.                     DateTime.Min = s_min;
  645.                     DateTime.Hour = s_hour;
  646.                     DS1307_WriteDateTime(); // Пишем новое время в микросхему DS1307
  647.                 }
  648.                 if (menu_level == 2) { // Если настраиваем будильник
  649.                     AlarmData.Hour = s_hour;
  650.                     AlarmData.Min = s_min;
  651.                     AlarmWriteTime(); // Записываем настройки в EEPROM
  652.                 }
  653.                 // И выходим из меню после сохранения всех настроек
  654.                 menu_flag = 0;
  655.                 menu_level = 0;
  656.                 submenu_level = 0;
  657.             }
  658.            
  659.         }
  660.        
  661.         // Поведение системы, когда открыто меню
  662.         if (menu_flag) {
  663.             // Приравниваем текущие значения к настраиваемым
  664.             if (enter_menu_flag) {
  665.                 // Обнуляем флаг входа в меню, что-бы не было глюков
  666.                 enter_menu_flag = 0;
  667.                
  668.                 if (menu_level == 1) { // Если настраиваем время
  669.                     DS1307_ReadDateTime();
  670.                     s_hour = DateTime.Hour;
  671.                     s_min = DateTime.Min;
  672.                 }
  673.                
  674.                 if (menu_level == 2) { // Если настраиваем будильник
  675.                     AlarmReadTime();
  676.                     s_hour = AlarmData.Hour;
  677.                     s_min = AlarmData.Min;
  678.                 }
  679.             }
  680.            
  681.             setInterval(540, tmr0, { // Мигаем настраиваемыми значениями
  682.                 digits_blink_flag ^= 1; // Переключение
  683.                 if (submenu_level == 0) // Если 0й уровень - настройка часов
  684.                     print_display(s_hour, s_min, 0, digits_blink_flag, 0);
  685.                 if (submenu_level == 1) // Если 1й уровень - настройка минут
  686.                     print_display(s_hour, s_min, 0, 0, digits_blink_flag);
  687.             });
  688.        
  689.         // Если мы не в меню
  690.         } else {
  691.             if (!alarm_flag) { // Если будильник не активен
  692.                 if (!alarm_enabled_flag) blink_speed = 540; // Если будильник выключен - частота мигания двоеточия 540мс
  693.                 if (alarm_enabled_flag) blink_speed = 300; // Если включен - 300мс
  694.                 setInterval(blink_speed, tmr0, { // Таймер мигания двоеточия
  695.                     DS1307_ReadDateTime(); // Считываем время из микросхемы DS1307
  696.                     colon_blink_flag ^= 1; // Переключаем флаг мигания
  697.                     print_display(DateTime.Hour, DateTime.Min, colon_blink_flag, 0, 0); // Отображаем текущее время на дисплее
  698.                 });
  699.             }
  700.         }
  701.         // Если включен будильник
  702.         if (alarm_enabled_flag) {
  703.             AlarmReadTime(); // Читаем время будильника и сравниваем с текущим
  704.             if ((DateTime.Hour == AlarmData.Hour) && (DateTime.Min == AlarmData.Min)) {
  705.                 alarm_flag = 1; // Говорим системе, что сработал будильник
  706.                
  707.                 // а тут будет код работы будильника, пока писать его дико в падлу
  708.                 // будут типа мигающие цифры, громкий писк и тд, но пока мне пищалки
  709.                 // не пришли с китая. ждемс)))))) 04.02.2019 06:16 AM
  710.             }
  711.         }
  712.     }
  713.  
  714.     return 0;
  715. }
Advertisement
Add Comment
Please, Sign In to add comment