Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * led_clock.c
- *
- * Created: 24.12.2018 19:24:51
- * Author : Redfern.89
- *
- * ========== ПРОЕКТ ЕЩЕ В СТАДИИ РАЗРАБОТКИ !!! ==========
- *
- * Для работы с NEC я использовал следующие источники
- * http://radiohlam.ru/?p=1018
- * https://habr.com/ru/post/257041/
- * Для работы с i2c и DS1307
- * https://radioparty.ru/prog-avr/program-c/430-lesson-ds1307
- * Мои вопросы на форумах, касательно этого проекта
- * http://www.cyberforum.ru/avr/thread2384452.html
- * http://www.cyberforum.ru/avr/thread2387596.html
- * http://forum.amperka.ru/threads/atmega328-nec-%D0%BF%D1%80%D0%BE%D1%82%D0%BE%D0%BA%D0%BE%D0%BB.17370/
- * 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/
- */
- // Частота проца в герцах
- #ifndef F_CPU
- #define F_CPU 16000000UL
- #endif
- #include <stdio.h>
- #include <avr/io.h>
- #include <util/delay.h>
- #include <avr/interrupt.h>
- #include <math.h>
- #include <avr/eeprom.h>
- // Настройки портов ввода-вывода
- #define RCLK_DDR DDRD //
- #define SCLK_DDR DDRD // SSSSSSSSSS PPPPPPPPP III
- #define DATA_DDR DDRD // S P P I
- #define RCLK_PORT PORTD // S P P I
- #define SCLK_PORT PORTD // SSSSSSSSSS P PPPPPPPP I
- #define DATA_PORT PORTD // S P I
- #define RCLK 1 // S P I
- #define SCLK 3 // SSSSSSSSSS P III
- #define DATA 0 //
- /* ДИНАМИК БУДИЛЬНИКА */
- #define SPEAKER_DDR DDRD
- #define SPEAKER_PORT PORTD
- #define SPEAKER_OUT PD5
- /* ~~~~~~~~~~~~~~~~~~~~ МАКРОСЫ ~~~~~~~~~~~~~~~~~~~~ */
- // Упарвление защелкой и сдвигом регистров
- #define _74hc595_RegisterLatch(code) { RCLK_PORT &= ~(1 << RCLK); code; RCLK_PORT |= (1 << RCLK); }
- #define _74hc595_RegisterShift() { SCLK_PORT &= ~(1 << SCLK); SCLK_PORT |= (1 << SCLK); }
- // Запуск и стоп таймера 0
- #define T0_START { TCCR0B |= (1 << CS01) | (1 << CS00); }
- #define T0_STOP { TCCR0B = 0x00; }
- // Таймер для работы интерфеса (отсчет прошедших милисекунд с момента старта системы)
- volatile unsigned long long int millis = 0;
- // Таймер без delay
- #define setInterval(n, tmr, code) { if ((millis - tmr) >= n) { tmr = millis; code; }}
- /* Кое-какие глобальные переменные */
- // ( это ааааадовый костыль, который, я надеюсь потом уберу )
- // Переменные-таймеры/счетчики...так думаю, что нахуй они не нужны,
- // ибо более одной задержки одновременно не планирую в программе ...
- unsigned long long int tmr0 = 0;
- unsigned long long int tmr1 = 0;
- unsigned long long int tmr2 = 0;
- unsigned long long int tmr3 = 0;
- // Данные о сегментах индикатора (LSBFIRST)
- // H, A, F, B, D, C, G, E
- unsigned char digits[11] = { 0x7D, 0x14, 0x5B, 0x5E, 0x36, 0x6E, 0x6F, 0x54, 0x7F, 0x7E, 0xFF };
- // Данные о группах светодиодов
- unsigned char groups[4] = { 0xF7, 0xFD, 0xFB, 0xEF };
- unsigned char NC = 0xFF;
- volatile unsigned char display_data[4] = { 0x00, 0x00, 0x00, 0x00 };
- volatile const static int max_groups = 4;
- volatile unsigned int display_pos = 0;
- /* Протокол NEC */
- volatile int NEC_REPEAT_FLAG = 0;
- volatile int NEC_START_FLAG = 0;
- volatile int NEC_IR_DONE = 0;
- volatile unsigned long int NEC_SCLK = 0; // Тактовые синхроимпульсы (64 мкс)
- volatile unsigned long int NEC_RECV_CNT = 0; // Кол-во принятых битов
- const int NEC_MIN_CLK = 5; // Минимальное значение, при котором следует начинать захват
- /*
- Минимальные м максимальные интервалы. Значения были вычесленны при 100 нажатиях на кнопки пульта,
- полученные значения были запёханы в массив и найдены минимумы и максимумы
- */
- const int NEC_MIN_HEADER_MESSAGE_CLK = 190;
- const int NEC_MAX_HEADER_MESSAGE_CLK = 245;
- const int NEC_MIN_ONE_BIT_CLK = 30;
- const int NEC_MAX_ONE_BIT_CLK = 40;
- const int NEC_MIN_NUL_BIT_CLK = 15;
- const int NEC_MAX_NUL_BIT_CLK = 25;
- // Задержки для определения повтора сообщения (не используется)
- const int NEC_MIN_REPEAT_CLK = 170;
- const int NEC_MAX_REPEAT_CLK = 180;
- const static int NEC_MAX_RESET_OVF = 1200; // Столько тиков должно пройти, что-бы система перешла вновь в режим ожидания (1200 * 64 = 76.8ms)
- const static int NEC_PACKET_LENGTH = 32;
- volatile unsigned char addr1 = 0x00; // Адрес
- volatile unsigned char addr2 = 0x00; // Инверсия адреса
- volatile unsigned char cmd1 = 0x00; // Команда
- volatile unsigned char cmd2 = 0x00; // Инверсия команды
- volatile uint16_t command = 0x0000; // Команда пульта
- // Смещения для создания битовой маски сообщенй (адрес1, адрес2, команда1, команда2)
- const int offset1_addr1 = 0;
- const int offset2_addr1 = 9;
- const int offset1_addr2 = 9;
- const int offset2_addr2 = 17;
- const int offset1_cmd1 = 17;
- const int offset2_cmd1 = 25;
- const int offset1_cmd2 = 25;
- const int offset2_cmd2 = 33;
- // Макросы для USART'а (использовал только при отладке и щас они нах ненужны)
- #define FOSC 16000000L
- #define BAUD 9600L
- #define MYUBRR FOSC / 16 / BAUD - 1
- // Коды с пульта
- const uint16_t POWER_OFF = 0xFF45;
- const uint16_t MENU = 0xFF47;
- const uint16_t TEST = 0xFF44;
- const uint16_t PLUS = 0xFF40;
- const uint16_t RETURN = 0xFF43;
- const uint16_t RWND = 0xFF07;
- const uint16_t PLAY = 0xFF15;
- const uint16_t FWND = 0xFF09;
- const uint16_t MINUS = 0xFF19;
- const uint16_t CLEAR = 0xFF0D;
- const uint16_t D0 = 0xFF16;
- const uint16_t D1 = 0xFF0C;
- const uint16_t D2 = 0xFF18;
- const uint16_t D3 = 0xFF5E;
- const uint16_t D4 = 0xFF08;
- const uint16_t D5 = 0xFF1C;
- const uint16_t D6 = 0xFF5A;
- const uint16_t D7 = 0xFF42;
- const uint16_t D8 = 0xFF52;
- const uint16_t D9 = 0xFF4A;
- // Структура DateTime (для удобства работы)
- typedef struct {
- int Sec;
- int Min;
- int Hour;
- int Month;
- int Day;
- int Year;
- int WeekDay;
- int AMPM;
- int H12_24;
- } TDateTime;
- TDateTime DateTime;
- // Структура TAlarmData
- typedef struct {
- int Hour;
- int Min;
- int enabled;
- } TAlarmData;
- TAlarmData AlarmData;
- /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- USART functions
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
- void USORT_Init(unsigned char ubrr) {
- UBRR0H = (unsigned char)(ubrr >> 8);
- UBRR0L = (unsigned char)ubrr;
- UCSR0B = (1 << RXEN0) | (1 << TXEN0);
- UCSR0C = (1 << USBS0) | (3 << UCSZ00);
- }
- void USORT_Transmit( unsigned char data ) {
- while ( !( UCSR0A & (1 << UDRE0)) );
- UDR0 = data;
- }
- /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Shift registers functions
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
- // Инициализация портов ввода-вывода SPI
- void _74hc595_SPI_Init() {
- RCLK_DDR |= (1 << RCLK);
- SCLK_DDR |= (1 << SCLK);
- DATA_DDR |= (1 << DATA);
- }
- // Отправка данных в регистр
- void _74hc595_SPI_send(char data) {
- int i;
- unsigned char val;
- for (i = 0; i < 8; i++) {
- val = !!(data & (1 << (7 - i)));
- if (val) DATA_PORT |= 1 << DATA;
- else DATA_PORT &= ~(1 << DATA);
- _74hc595_RegisterShift();
- }
- }
- // Отправка данных в регистры с защелкиванием (2.0)
- void _74hc595_RegisterWrite2(unsigned char* data, int registrs_count) {
- unsigned int i = 0;
- _74hc595_RegisterLatch({
- for (i = 0; i < registrs_count; i++) {
- _74hc595_SPI_send(data[i]);
- }
- });
- }
- /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- NEC 2.0+ functions
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
- // Таймер обработки длительности импульсов
- ISR (TIMER0_OVF_vect) {
- TCNT0 = 0xF0; // Настроенный таймер тикает так, что-бы каждый тик совершался через каждые 64us
- if (++NEC_SCLK >= NEC_MAX_RESET_OVF) { // Если в течении 1200 тиков ничего не пришло, переходим в режим ожидания
- T0_STOP; // Останавливаем таймер
- NEC_SCLK = 0; // Обнуляем счетчик синхроимпульсов
- NEC_START_FLAG = 0; // Обнуляем флаг приема стартового сообщения
- command = 0x0000;
- }
- // Это не обязательно, но для перестраховки оставлю тут
- // Если в течении 1200 тиков пришло менее 32 бит, обнуляем счетчики и переходим в режим ожидания
- if (NEC_SCLK >= NEC_MAX_RESET_OVF && NEC_RECV_CNT < NEC_PACKET_LENGTH) {
- T0_STOP;
- NEC_SCLK = 0;
- NEC_RECV_CNT = 0;
- command = 0x0000;
- }
- }
- /*
- Вот так выглядит сообщение - 0x00FF453A (если что, это кнопка выключения)
- Описание:
- 1. Header - стартовое сообщение. Опорный импульс. Нужен для того, что-бы система определила, когда начинать отсчет
- 2. Pause - пауза. После паузы идет прерывание на спад.
- 3. 0 и 1 - Биты данных. Всего сообщение имеет 32 бита. 0 длится ~1,12мс, 1 длится ~2,25мс
- 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
- |________|_____|_|_|_|_|_|_|_|_|___|___|___|___|___|___|___|___|___|_|_|_|___|_|___|_|_|___|___|___|_|___|_|___|_
- 9ms 4.5ms
- */
- // Обработчик внешнего прерывания
- ISR (INT0_vect) {
- T0_START; // Начинаем отсчет
- if (NEC_SCLK > NEC_MIN_CLK) { // Не обробатываем то, что меньше ожидаемого
- // Тут определяем стартовое сообщение (преамбулу)
- if (NEC_SCLK >= NEC_MIN_HEADER_MESSAGE_CLK && NEC_SCLK < NEC_MAX_HEADER_MESSAGE_CLK) {
- NEC_START_FLAG = 1;
- NEC_REPEAT_FLAG = 0;
- NEC_RECV_CNT = 0;
- }
- /* Знаю, по идиотски, Но умнее лень было придумывать */
- // Тут определяем биты нулевого значения
- if ((NEC_SCLK >= NEC_MIN_NUL_BIT_CLK && NEC_SCLK < NEC_MAX_NUL_BIT_CLK) && NEC_START_FLAG) {
- NEC_RECV_CNT++; // Инкрементируем колличество принятых нулей
- // ну а тут мутнаые процедуры записи значений в переменные
- if (NEC_RECV_CNT >= offset1_addr1 && NEC_RECV_CNT < offset2_addr1) { // Если мы в диапозоне 1-8, ...
- addr1 &= ~(1 << (NEC_RECV_CNT - offset1_addr1)); // добавляем в addr1 нули в нужные места
- }
- // Остальные диапозоны тоже самое
- if (NEC_RECV_CNT >= offset1_addr2 && NEC_RECV_CNT < offset2_addr2) {
- addr2 &= ~(1 << (NEC_RECV_CNT - offset1_addr2));
- }
- if (NEC_RECV_CNT >= offset1_cmd1 && NEC_RECV_CNT < offset2_cmd1) {
- cmd1 &= ~(1 << (NEC_RECV_CNT - offset1_cmd1));
- }
- if (NEC_RECV_CNT >= offset1_cmd2 && NEC_RECV_CNT < offset2_cmd2) {
- cmd2 &= ~(1 << (NEC_RECV_CNT - offset1_cmd2));
- }
- }
- // Тут определяем биты положительного значения (такая-же хуйня как и с нулями, только интервалы у NEC_SCLK больше)
- if ((NEC_SCLK >= NEC_MIN_ONE_BIT_CLK && NEC_SCLK < NEC_MAX_ONE_BIT_CLK) && NEC_START_FLAG) {
- NEC_RECV_CNT++; // Инкрементируем колличество принятых едениц
- if (NEC_RECV_CNT >= offset1_addr1 && NEC_RECV_CNT < offset2_addr1) {
- addr1 |= (1 << (NEC_RECV_CNT - offset1_addr1));
- }
- if (NEC_RECV_CNT >= offset1_addr2 && NEC_RECV_CNT < offset2_addr2) {
- addr2 |= (1 << (NEC_RECV_CNT - offset1_addr2));
- }
- if (NEC_RECV_CNT >= offset1_cmd1 && NEC_RECV_CNT < offset2_cmd1) {
- cmd1 |= (1 << (NEC_RECV_CNT - offset1_cmd1));
- }
- if (NEC_RECV_CNT >= offset1_cmd2 && NEC_RECV_CNT < offset2_cmd2) {
- cmd2 |= (1 << (NEC_RECV_CNT - offset1_cmd2));
- }
- }
- // Тут обнуляем счетчик синхроимульсов, что-бы отсчитывать время следующего прерывания с нуля
- NEC_SCLK = 0;
- // Колличество нулей и едениц в конечном счете должно быть 32, на этом и остановимся
- if (NEC_RECV_CNT == NEC_PACKET_LENGTH) {
- // Выставляем в стартовое положение все счетчики и останавлиеваем подсчет
- NEC_RECV_CNT = 0;
- NEC_START_FLAG = 0;
- T0_STOP;
- // Проверка сообщения на целостность
- // В версии протокола 2.0 адрес имеет расширеное пространство и не имеет инверсии
- // Значит проверяем либо сложив обе инверсии (адреса и команды), либо только инверсии команды
- if ((((addr1 + addr2 == 0xFF) && (cmd1 + cmd2) == 0xFF)) || (cmd1 + cmd2 == 0xFF)) {
- NEC_IR_DONE = 1; // Сообщаем системе, что чтение завершено
- // Команду склеиваем с адресом (16 бит)
- command = ((addr1 + addr2) << 8) | cmd1;
- }
- }
- }
- }
- /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- i2c Functions
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
- void i2c_init(void) {
- TWBR = 2; // Настройка частоты шины
- TWSR = (1 << TWPS1) | (1 << TWPS0); // Предделитель на 64
- TWCR |= (1 << TWEN); // Включение модуля TWI
- }
- void i2c_start() {
- TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTA); // Передача условия START
- while (!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT
- }
- void i2c_stop() {
- TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); // Передача условия STOP
- while (TWCR & (1 << TWSTO)); // Ожидание завршения передачи условия STOP
- }
- unsigned char i2c_write(unsigned char data) {
- TWDR = data; // Загрузка данных в регистр TWDR
- TWCR = (1 << TWEN) | (1 << TWINT); // Сброс флага TWINT для начала передачи данных
- while (!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT
- // Проверка статуса
- if((TWSR & 0xF8) == 0x18 || (TWSR & 0xF8) == 0x40 || (TWSR & 0xF8) == 0x28) return 1;
- else return 0;
- }
- unsigned char i2c_read(unsigned char *data, unsigned char ack) {
- // Возвращаем "подтверждение" после приема
- if (ack) TWCR |= (1 << TWEA);
- // Возвращаем "неподтверждение" после приема
- // Ведомое устройство не получает больше данных
- // обычно используется для распознования последнего байта
- else TWCR &= ~(1 << TWEA);
- // Разрешение приема данных после сброса TWINT
- TWCR |= (1 << TWINT);
- while (!(TWCR & (1 << TWINT))); // Ожидание установки флага TWINT
- // Проверка статуса
- // Если принят байт данных и возвращается "подтверждение"(0x50)
- // или принят байт данных и возвращается "ненеподтверждение"(0x58)
- if ((TWSR & 0xF8) == 0x50 || (TWSR & 0xF8) == 0x58) {
- *data = TWDR; // Читаем данные из TWDR
- return 1; // OK
- } else {
- return 0; // ОШИБКА
- }
- }
- /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- DS1307 Functions
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
- unsigned char DS1307_Read(unsigned char address, unsigned char *data) {
- unsigned char res;
- i2c_start(); // Старт
- res = i2c_write(0b11010000); // Адрес DS1307+W
- if (!res) return 0; // Ошибка
- res = i2c_write(address); // Передача адреса необходимого регистра
- if (!res) return 0; // Ошибка
- i2c_start(); // Повторный старт
- res = i2c_write(0b11010001); // Адрес DS1307+R
- if (!res) return 0; // Ошибка
- res = i2c_read(data, 0); // чтение данных
- if (!res) return 0; // Ошибка
- i2c_stop(); // стоп
- return 1; // ОК
- }
- unsigned char DS1307_Write(unsigned char address, unsigned char data) {
- unsigned char res;
- i2c_start(); // старт
- res = i2c_write(0b11010000); // адреc DS1307+W
- if (!res) return 0; // ошибка
- res = i2c_write(address); // Передача адреса необходимого регистра
- if (!res) return 0; // ошибка
- res = i2c_write(data); // Запись данных
- if (!res) return 0; // ошибка
- i2c_stop(); // стоп
- return 1; // ОК
- }
- // Процедура чтения данных из DS1307 в структуру TDateTime (пока не доработал ее, да и лень как-то)
- void DS1307_ReadDateTime(void) {
- unsigned char tmp;
- /* Читаем данные и преобразуем из BCD в двоичную систему */
- // Секунды
- DS1307_Read(0x00, &tmp);
- DateTime.Sec = (((tmp & 0xF0) >> 4) * 10) + (tmp & 0x0F);
- // Минуты
- DS1307_Read(0x01, &tmp);
- DateTime.Min = (((tmp & 0xF0) >> 4) * 10) + (tmp & 0x0F);
- // Часы
- DS1307_Read(0x02, &tmp);
- DateTime.Hour = (((tmp & 0xF0) >> 4) * 10) + (tmp & 0x0F);
- // День недели
- DS1307_Read(0x03, &tmp);
- DateTime.WeekDay = (((tmp & 0xF0) >> 4) * 10) + (tmp & 0x0F);
- }
- // Процедура записи в микросхему из структуры TDateTime
- void DS1307_WriteDateTime() {
- unsigned char tmp;
- tmp = ((DateTime.Sec / 10) << 4) | (DateTime.Sec % 10);
- DS1307_Write(0x00, tmp);
- tmp = ((DateTime.Min / 10) << 4) | (DateTime.Min % 10);
- DS1307_Write(0x01, tmp);
- tmp = ((DateTime.Hour / 10) << 4) | (DateTime.Hour % 10);
- DS1307_Write(0x02, tmp);
- tmp = ((DateTime.WeekDay / 10) << 4) | (DateTime.WeekDay % 10);
- DS1307_Write(0x03, tmp);
- }
- /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Main Functions
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
- // Запись данных будильника
- void AlarmWriteTime(void) {
- eeprom_write_byte((uint8_t*)0x00, AlarmData.Hour);
- eeprom_write_byte((uint8_t*)0x01, AlarmData.Min);
- eeprom_write_byte((uint8_t*)0x02, (AlarmData.enabled ? (1 << 0) : (0 << 0)));
- }
- // Чтение данных будильника
- void AlarmReadTime(void) {
- AlarmData.Hour = eeprom_read_byte((uint8_t*)0x00);
- AlarmData.Min = eeprom_read_byte((uint8_t*)0x01);
- AlarmData.enabled = (eeprom_read_byte((uint8_t*)0x02) & (1 << 0)) != 0;
- }
- // Динамическая индикация
- ISR (TIMER2_OVF_vect) {
- TCNT2 = 0xF0;
- /* Отсылаем текущие данные в сдвиговые регистры */
- display_pos = (display_pos + 1) % max_groups;
- unsigned char display[2] = { groups[display_pos], display_data[display_pos] }; // Содержит текущие данные о сегментах и группе светодиодов
- _74hc595_RegisterWrite2(display, 2);
- }
- // Таймер для работы интерфейса системы (подсчет милисекунд с момента старта системы)
- ISR (TIMER1_OVF_vect) {
- TCNT1 = 0xFFF0;
- millis++;
- }
- // Отрисовка цифр на дисплее
- // Я ебал что-то тут расписывать, но все-же попробую
- void print_display(int hours, int minutes, int colon_blink, int hours_blink, int min_blink) {
- // переменные colon_blink, hours_blink и min_blink нужны для мигания цифр или двоеточия
- // в основной программе эти значения переключаются через каждые N ms
- if (!hours_blink) { // Если мы хотим отображать часы, то...
- if (hours == 0) { // Если часы равны 0, ...
- display_data[0] = digits[0]; // Рисуем в первом разряде ведущий ноль
- // к первому разряду подключено двоеточие, если надо, отобразим и его и ноль на текущем разряде
- display_data[1] = colon_blink ? digits[0] | (1 << 7) : digits[0];
- } else if (hours >= 1 && hours <= 9) { // Если часы больше 1 и меньше 9, ...
- display_data[0] = digits[0]; // Отобразим ведущий ноль
- display_data[1] = colon_blink ? digits[hours] | (1 << 7) : digits[hours]; // И отобразим текущую цифру (и если надо двоеточие)
- } else if (hours >= 10 && hours <= 99) { // Если часы больше 10 и меньше 99, ...
- // Тупо разбиваем число на разрядыи эти разряды пихаем в нужные участки массива
- display_data[0] = digits[(unsigned int)round(hours % 100) / 10]; // Перввый разряд это остаток от деления на 100, разделенный на 10
- // Второй разряд это остаток от деления на 10 и опять-же, если надо, то зажечь двоеточие
- display_data[1] = colon_blink ? digits[(unsigned int)round(hours % 10)] | (1 << 7) : digits[(unsigned int)round(hours % 10)];
- }
- } else { // Если мы не хотим отображать часы, то заполняем массив нулями
- display_data[0] = ~NC;
- display_data[1] = ~NC;
- }
- /* Обсалютно такая-же хуйня и с минутами, но без двоеточия, т.к. оно подключенно к 1му разряду */
- if (!min_blink) {
- if (minutes == 0) {
- display_data[2] = digits[0];
- display_data[3] = digits[0];
- } else if (minutes >= 1 && minutes <= 9) {
- display_data[2] = digits[0];
- display_data[3] = digits[minutes];
- } else if (minutes >= 10 && minutes <= 99) {
- display_data[2] = digits[(unsigned int)round(minutes % 100) / 10];
- display_data[3] = digits[(unsigned int)round(minutes % 10)];
- }
- } else {
- display_data[2] = ~NC;
- display_data[3] = ~NC;
- }
- }
- // Инициализация микроконтроллера и переферии
- void MCU_init() {
- // Динамик
- SPEAKER_DDR |= (1 << SPEAKER_OUT);
- // Инициализация портов ввода-вывода SPI
- _74hc595_SPI_Init();
- // Инициализация внешнего прерывания (на спад)
- EICRA |= (1 << ISC01) | (0 << ISC00);
- EIMSK |= (1 << INT0);
- EIFR |= (1 << INTF0);
- PCICR = 0x00;
- // Инициализация таймера/счестчика 0 для подсчета временного интервала между импульсами
- TCCR0B = 0x00;//(1 << CS00) | (1 << CS01);
- TIMSK0 |= (1 << TOIE0);
- TCNT0 = 0xF0;
- // Инициализация таймера/счестчика 1 для работы интерфейса системы
- TCCR1B |= (1 << CS12) | (0 << CS11) | (1 << CS10);
- TIMSK1 |= (1 << TOIE1);
- TCNT1 = 0xFFF0;
- // Инициализация таймера/счестчика 2 для динамической индикации
- TCCR2B |= (1 << CS22);
- TIMSK2 |= (1 << TOIE2);
- TCNT2 = 0xFF;
- // Инициализация i2c
- i2c_init();
- }
- int main(void) {
- asm("CLI");
- MCU_init();
- asm("SEI");
- // Переменные для работы интерфейса
- int menu_level = 0; // позиция в главном меню
- int menu_flag = 0; // Говорит о том, что открыто меню в данный момент или нет
- int digits_blink_flag = 0; // Это переключающиеся значение для мигания настраиваемых значений
- int colon_blink_flag = 0; // Это тоже переключающиеся значение для мигания двоеточия
- int submenu_level = 0; // Позиция в подменю, клавиши на пульте: "<<" и ">>"
- int s_hour = 0, s_min = 0; // Настраиваемые значения
- int enter_menu_flag = 0; // Флаг входа в меню. выполняется каждый раз при входе в меню для приравнивания значений
- int alarm_enabled_flag = 0, alarm_flag = 0; // Флаги будильника.
- int blink_speed = 0; // Скорость мигания двоеточия. я идиот набитый забыл предусмотреть индикатор включенного будильника. хоть так будет видно
- while (1) {
- if (NEC_IR_DONE) { // Если пришла команда с пульта
- NEC_IR_DONE = 0; // уже нет
- // Кнопка POWER_OFF включает/выключает будильник и останавливает его
- if (command == POWER_OFF) {
- alarm_enabled_flag ^= 1;
- eeprom_write_byte((uint8_t*)0x03, alarm_enabled_flag ? (1 << 0) : (0 << 0));
- if (alarm_flag) alarm_flag = 0; // Если пищит будильник - вырубаем его
- }
- // Заходим в меню
- if (command == MENU) {
- menu_level++; // Прибавляем уровень меню
- menu_flag = 1; // Говорим системе, что мы в меню
- submenu_level = 0; // Обнуляем позицию подменю
- if (menu_level > 2) menu_level = 1; // Пунктов меню всего два
- enter_menu_flag = 1; // Говорим системе, что вошли в меню
- }
- // Если мы в меню и нажали на RETURN
- if (command == RETURN && menu_flag) {
- // Выходим из меню и обнуляем счетчики позиций
- menu_flag = 0;
- menu_level = 0;
- submenu_level = 0;
- }
- // При нажатии на ">>" перемещаемся по подменю
- if (command == FWND && menu_flag) {
- submenu_level++;
- if (submenu_level > 1) submenu_level = 0;
- }
- // Тоже самое для "<<"
- if (command == RWND && menu_flag) {
- submenu_level--;
- if (submenu_level < 0) submenu_level = 1;
- }
- // Если мы в меню и нажали на "+"
- if (command == PLUS && menu_flag) {
- if (submenu_level == 0) { // Если настраиваем часы
- s_hour++; // прибавляем часы
- if (s_hour > 23) s_hour = 0; // но не больше 23
- print_display(s_hour, s_min, 0, 0, 0); // так как настраиваемые цифры мигают, задержим их во время настройки
- }
- // тоже самое и с настройками минут
- if (submenu_level == 1) {
- s_min++;
- if (s_min > 59) s_min = 0;
- print_display(s_hour, s_min, 0, 0, 0);
- }
- }
- // Тоже самое, что и в "+", только значения убавляем и соблюдаем пороги
- if (command == MINUS && menu_flag) {
- if (submenu_level == 0) {
- s_hour--;
- if (s_hour < 0) s_hour = 23;
- print_display(s_hour, s_min, 0, 0, 0);
- }
- if (submenu_level == 1) {
- s_min--;
- if (s_min < 0) s_min = 59;
- print_display(s_hour, s_min, 0, 0, 0);
- }
- }
- // Кнопка PLAY - это кнопка OK, при ее нажатии сохраняем текущие настройки
- if (command == PLAY && menu_flag) {
- if (menu_level == 1) { // Если настраиваем время
- DateTime.Sec = 0; // Устанавливаем такой формат XX:XX:00
- DateTime.Min = s_min;
- DateTime.Hour = s_hour;
- DS1307_WriteDateTime(); // Пишем новое время в микросхему DS1307
- }
- if (menu_level == 2) { // Если настраиваем будильник
- AlarmData.Hour = s_hour;
- AlarmData.Min = s_min;
- AlarmWriteTime(); // Записываем настройки в EEPROM
- }
- // И выходим из меню после сохранения всех настроек
- menu_flag = 0;
- menu_level = 0;
- submenu_level = 0;
- }
- }
- // Поведение системы, когда открыто меню
- if (menu_flag) {
- // Приравниваем текущие значения к настраиваемым
- if (enter_menu_flag) {
- // Обнуляем флаг входа в меню, что-бы не было глюков
- enter_menu_flag = 0;
- if (menu_level == 1) { // Если настраиваем время
- DS1307_ReadDateTime();
- s_hour = DateTime.Hour;
- s_min = DateTime.Min;
- }
- if (menu_level == 2) { // Если настраиваем будильник
- AlarmReadTime();
- s_hour = AlarmData.Hour;
- s_min = AlarmData.Min;
- }
- }
- setInterval(540, tmr0, { // Мигаем настраиваемыми значениями
- digits_blink_flag ^= 1; // Переключение
- if (submenu_level == 0) // Если 0й уровень - настройка часов
- print_display(s_hour, s_min, 0, digits_blink_flag, 0);
- if (submenu_level == 1) // Если 1й уровень - настройка минут
- print_display(s_hour, s_min, 0, 0, digits_blink_flag);
- });
- // Если мы не в меню
- } else {
- if (!alarm_flag) { // Если будильник не активен
- if (!alarm_enabled_flag) blink_speed = 540; // Если будильник выключен - частота мигания двоеточия 540мс
- if (alarm_enabled_flag) blink_speed = 300; // Если включен - 300мс
- setInterval(blink_speed, tmr0, { // Таймер мигания двоеточия
- DS1307_ReadDateTime(); // Считываем время из микросхемы DS1307
- colon_blink_flag ^= 1; // Переключаем флаг мигания
- print_display(DateTime.Hour, DateTime.Min, colon_blink_flag, 0, 0); // Отображаем текущее время на дисплее
- });
- }
- }
- // Если включен будильник
- if (alarm_enabled_flag) {
- AlarmReadTime(); // Читаем время будильника и сравниваем с текущим
- if ((DateTime.Hour == AlarmData.Hour) && (DateTime.Min == AlarmData.Min)) {
- alarm_flag = 1; // Говорим системе, что сработал будильник
- // а тут будет код работы будильника, пока писать его дико в падлу
- // будут типа мигающие цифры, громкий писк и тд, но пока мне пищалки
- // не пришли с китая. ждемс)))))) 04.02.2019 06:16 AM
- }
- }
- }
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment