/** * My Nixie Clock IN-4x6 * Vladimir N. Shilov * 2020.01.06 */ /* Compiler libs */ #include #include #include #include #include #include #include #include /* Project libs */ #include "i2c.h" #include "ds3231.h" #include "rtos.h" #include "event-system.h" #include "common.h" #include "main.h" /* Defines */ /* Timer2 settings */ #define TIMER2_HZ 400 #define TIMER2_PRESCALER 1024 #define TIMER2_CS (1< BRIGHT_IDX_MAX) { brightIdx = BRIGHT_IDX_MAX; } #endif // USE_BRIGHT_CONTROL /** * Инициализация, настройка... */ Board_Init(); /* Initialize Scheduler */ RTOS_Init(); tdelay_ms(2000); lampTest(); tdelay_ms(5000); /* Initialize I2C Bus and RTC */ I2C_Init(); RTC_Init(); RTC_ReadAll(&RTC); /* Initialize Event State Machine */ ES_Init(stShowTime); showTime(); RTOS_SetTask(btnProcess, 3, BTN_SCAN_PERIOD); #ifdef USE_DHT RTOS_SetTask(dhtStart, 2000, 15000); #endif // USE_DHT /** main loop */ do { /* new second interrupt from RTC */ if (Flag.RTC_Int != 0) { Flag.RTC_Int = 0; ES_PlaceEvent(evNewSecond); RTC_ReadTime(&RTC); if (RTC.Sec == 0 && RTC.Min == 0) { // begin of new hour if (RTC.Hr == 0) { // begin of new day RTC_ReadCalendar(&RTC); ES_PlaceEvent(evRefreshCal); } #ifdef USE_BRIGHT_CONTROL if (RTC.Hr >= FULL_BRIGHT_ON && RTC.Hr < FULL_BRIGHT_OFF) { OCR2 = pgm_read_byte(&brightConv[BRIGHT_IDX_MAX]); } else { OCR2 = pgm_read_byte(&brightConv[brightIdx]); } #endif // USE_BRIGHT_CONTROL setSummerWinterTime(); } // begin new hour if (DISP_WDT != 0) { DISP_WDT --; if (DISP_WDT == 0) { ES_PlaceEvent(evDisplayWDT); if (Flag.saveCal != 0) { Flag.saveCal = 0; RTC_WriteCalendar(&RTC); } if (Flag.saveEEP != 0) { Flag.saveEEP = 0; eeprom_update_byte(&EEP_BrightIdx, brightIdx); } } } #ifdef USE_DHT switch(RTC.Sec) { case 0x20: case 0x50: ES_PlaceEvent(evShTemp); break; case 0x22: case 0x52: ES_PlaceEvent(evShHum); break; case 0x24: case 0x54: ES_PlaceEvent(evShTime); break; } #endif // USE_DHT } // End of New Second event = ES_GetEvent(); if (event) { ES_Dispatch(event); } // крутим диспетчер RTOS_DispatchTask(); // делать нечего -- спим, ждём прерывание set_sleep_mode(SLEEP_MODE_IDLE); sleep_mode(); } while(1); } /** * П о д п р о г р а м м ы */ /** * @brief Initializy perephireal */ static void Board_Init(void) { /* power off Analog Comparator */ ACSR = ACD; /* GPIO */ DDRB = ANODB_PINS; // as output PORTB = BUTTON_PINS; // enable pull-up DDRC = DIGIT_PINS; // as output DDRD = (DOT_PIN | ANODD_PINS); // as output #ifdef USE_DHT /* Timer1, IC negative edge, CTC mode, 64 prescaler, 4 mks one tick */ TCCR1B = ((0<> 8); UBRRL = BAUD_PRESCALE; #endif // USE_UART /* Enable Interrupts */ sei(); } /** * @brief Correct current time for Sun or Winter */ static void setSummerWinterTime(void) { uint8_t sunTime = eeprom_read_byte(&EEP_SummerTime); /* Переход на летнее время */ if ((RTC.Mon == 3) && (RTC.WD == 7) && (RTC.Hr == 3) && (sunTime != 0)) { if ((RTC.Day + 7) > 31) { RTC.Hr = 4; RTC_WriteHHMM(&RTC); sunTime = 0; eeprom_update_byte(&EEP_SummerTime, sunTime); } } /* Переход на зимнее время */ if ((RTC.Mon == 10) && (RTC.WD == 7) && (RTC.Hr == 4) && (sunTime == 0)) { if ((RTC.Day + 7) > 31) { RTC.Hr = 3; RTC_WriteHHMM(&RTC); sunTime = 1; eeprom_update_byte(&EEP_SummerTime, sunTime); } } } void dotOn(void) { PORTD |= DOT_PIN; } void dotOff(void) { PORTD &= ~(DOT_PIN); } void dotOnPersistent(void) { RTOS_DeleteTask(dotOff); PORTD |= DOT_PIN; } #ifdef USE_DHT static void dhtStart(void) { RTOS_SetTask(dhtProcess, 2, 0); DHT_PIN_LOW; } static void dhtProcess(void) { uint8_t cnt1, cnt2, buf; uint16_t tcnt_old, hmdt, tmprtr; DHT_PIN_INPUT; TCNT1 = 0; // ждём первого "0" while(bit_is_set(PINB, PB0) && TCNT1= DHT_TOUT1) { RTOS_SetTask(dhtNoAck, 0, 0); return; } // white for end of preamble while(bit_is_clear(PINB, PB0)); while(bit_is_set(PINB, PB0) && TCNT1= DHT_TIMEOUT) { RTOS_SetTask(dhtTimeout, 0, 0); } hmdt = 0; tmprtr = 0; for (cnt1=0; cnt1<32; cnt1+=8) { // 0 8 16 24 32 buf = 0; for (cnt2=0; cnt2<8; cnt2++) { buf <<= 1; // "0", начало периода while(bit_is_clear(PINB, PB0)); // ждём начало импульса tcnt_old = TCNT1; // начало импульса while(bit_is_set(PINB, PB0) && TCNT1 10) { buf |= 1; } } switch (cnt1) { case 0: hmdt = buf << 8; break; case 8: hmdt |= buf; break; case 16: tmprtr = buf << 8; break; case 24: tmprtr |= buf; break; } } if (TCNT1 >= DHT_TIMEOUT) { RTOS_SetTask(dhtTimeout, 0, 0); return; } dhtData.Humidity = (uint8_t)((hmdt + 5) / 10); dhtData.Temperature = tmprtr; RTOS_SetTask(dhtEnd, 0, 0); } static void dhtEnd(void) { #ifdef USE_UART char buffer[6]; usart_puts("Humidity: "); itoa(dhtData.Humidity, buffer, 10); usart_puts(buffer); usart_puts(" %\t\t"); usart_puts("Temperature: "); itoa(dhtData.Temperature/10, buffer, 10); usart_puts(buffer); usart_putc('.'); itoa(dhtData.Temperature%10, buffer, 10); usart_puts(buffer); usart_puts("oC\r\n"); #endif // USE_UART } static void dhtNoAck(void) { #ifdef USE_UART usart_puts("DHT22 no ACK occurred.\r\n"); #endif // USE_UART } static void dhtTimeout(void) { #ifdef USE_UART usart_puts("DHT22 Timeout occurred.\r\n"); #endif // USE_UART } void showTemperature(void) { uint8_t a = dhtData.Temperature / 10; uint8_t b = dhtData.Temperature % 10; Digit[0] = a / 10; Digit[1] = a % 10; Digit[2] = b; Digit[3] = DIGIT_BLANK; } void showHumidity(void) { Digit[0] = DIGIT_BLANK; Digit[1] = DIGIT_BLANK; Digit[2] = dhtData.Humidity / 10; Digit[3] = dhtData.Humidity % 10;} #endif // USE_DHT /** * @brief Обработка кнопок. * @param : None * @retval : None */ static void btnProcess(void) { uint8_t i; for (i=0; i= BTN_TIME_HOLDED) { Button[i].time -= BTN_TIME_REPEATED; if (Button[i].holded == Button[i].pressed) { // if pressed and holded - same function, then button pressed auto repeat ES_PlaceEvent(Button[i].pressed); } } } else { // button released if (Button[i].time >= (BTN_TIME_HOLDED - BTN_TIME_REPEATED)) { ES_PlaceEvent(Button[i].holded); // process long press } else if (Button[i].time >= BTN_TIME_PRESSED) { ES_PlaceEvent(Button[i].pressed); // process short press } Button[i].time = 0; RTOS_SetTask(btnProcess, BTN_TIME_PAUSE, BTN_SCAN_PERIOD); } } /* end (pin == 0) */ } /* end FOR */ } void showTime(void) { dotOn(); RTOS_SetTask(dotOff, 500, 0); if (RTC.Hr > 0x09) { Digit[0] = RTC.Hr >> 4; } else { Digit[0] = DIGIT_BLANK; } Digit[1] = RTC.Hr & 0x0F; Digit[2] = RTC.Min >> 4; Digit[3] = RTC.Min & 0x0F; Digit[4] = RTC.Sec >> 4; Digit[5] = RTC.Sec & 0x0F; } void showWDM(void) { DISP_WDT = DISP_WDT_TIME; Digit[0] = RTC.WD & 0x0F; Digit[1] = DIGIT_BLANK; Digit[2] = RTC.Day >> 4; Digit[3] = RTC.Day & 0x0F; Digit[4] = RTC.Mon >> 4; Digit[5] = RTC.Mon & 0x0F; } void showWDay(void) { DISP_WDT = DISP_WDT_TIME; Digit[0] = DIGIT_BLANK; Digit[1] = DIGIT_BLANK; Digit[2] = RTC.WD & 0x0F; Digit[3] = DIGIT_BLANK; Digit[4] = DIGIT_BLANK; Digit[5] = DIGIT_BLANK; } void showMDay(void) { DISP_WDT = DISP_WDT_TIME; Digit[0] = RTC.Day >> 4; Digit[1] = RTC.Day & 0x0F; Digit[2] = DIGIT_BLANK; Digit[3] = DIGIT_BLANK; Digit[4] = DIGIT_BLANK; Digit[5] = DIGIT_BLANK; } void showMonth(void) { DISP_WDT = DISP_WDT_TIME; Digit[0] = DIGIT_BLANK; Digit[1] = DIGIT_BLANK; Digit[2] = RTC.Mon >> 4; Digit[3] = RTC.Mon & 0x0F; } void showYear(void) { DISP_WDT = DISP_WDT_TIME; Digit[0] = DIGIT_BLANK; Digit[1] = DIGIT_BLANK; Digit[2] = 0x02; Digit[3] = 0x00; Digit[4] = RTC.Year >> 4; Digit[5] = RTC.Year & 0x0F; } void incWDay(void) { if (RTC.WD < 7) { RTC.WD ++; } else { RTC.WD = 1; } Flag.saveCal = 1; } void decWDay(void) { if (RTC.WD > 1) { RTC.WD --; } else { RTC.WD = 7; } Flag.saveCal = 1; } void incMDay(void) { valIncrease(&RTC.Day, 31); Flag.saveCal = 1; } void decMDay(void) { valDecrease(&RTC.Day, 31); Flag.saveCal = 1; } void incMonth(void) { valIncrease(&RTC.Mon, 12); if (RTC.Mon == 0) { RTC.Mon = 1; } Flag.saveCal = 1; } void decMonth(void) { valDecrease(&RTC.Mon, 12); if (RTC.Mon == 0) { RTC.Mon = 0x12; } Flag.saveCal = 1; } void incYear(void) { valIncrease(&RTC.Year, 99); Flag.saveCal = 1; } void decYear(void) { valDecrease(&RTC.Year, 99); Flag.saveCal = 1; } #ifdef USE_BRIGHT_CONTROL void showBright(void) { DISP_WDT = DISP_WDT_TIME; Digit[0] = DIGIT_BLANK; Digit[1] = DIGIT_BLANK; Digit[2] = brightIdx; Digit[3] = DIGIT_BLANK; } void incBright(void) { if (brightIdx < BRIGHT_IDX_MAX) { brightIdx ++; OCR2 = pgm_read_byte(&brightConv[brightIdx]); Flag.saveEEP = 1; } } void decBright(void) { if (brightIdx > 0 ) { brightIdx --; OCR2 = pgm_read_byte(&brightConv[brightIdx]); Flag.saveEEP = 1; } } #endif // USE_BRIGHT_CONTROL static void blink(void) { static uint8_t s = 0; switch (s) { case 0: Flag.blinkC = 0; RTOS_SetTask(blink, 750, 0); s = 1; break; case 1: Flag.blinkC = 1; RTOS_SetTask(blink, 250, 0); s = 0; break; default: s = 0; } } void setTimeShow(void) { dotOn(); RTOS_SetTask(dotOff, 500, 0); Digit[0] = setRTC.Hr >> 4; Digit[1] = setRTC.Hr & 0x0F; Digit[2] = setRTC.Min >> 4; Digit[3] = setRTC.Min & 0x0F; } void setTimeBegin(void) { RTC_ReadTime(&setRTC); RTOS_SetTask(btnProcess, 500, BTN_SCAN_PERIOD); } void setHHBegin(void) { Flag.blink0 = 1; Flag.blink1 = 0; Flag.blink2 = 0; RTOS_SetTask(blink, 0, 0); setTimeShow(); } void setHHInc(void) { valIncrease(&setRTC.Hr, 23); } void setHHDec(void) { valDecrease(&setRTC.Hr, 23); } void setMMBegin(void) { Flag.blink0 = 0; Flag.blink1 = 1; Flag.blink2 = 0; RTOS_SetTask(blink, 0, 0); setTimeShow(); } void setMMInc(void) { valIncrease(&setRTC.Min, 59); } void setMMDec(void) { valDecrease(&setRTC.Min, 59); } void setTimeEnd(void) { RTOS_SetTask(btnProcess, 500, BTN_SCAN_PERIOD); setRTC.Sec = 0; RTC_WriteTime(&setRTC); RTOS_DeleteTask(blink); Flag.blink0 = 0; Flag.blink1 = 0; Flag.blink2 = 0; Flag.blinkC = 0; RTC_ReadTime(&RTC); } /** * @brief Increase BCD value. * @param : val, max * @retval : None */ static void valIncrease(uint8_t * val, uint8_t max) { uint8_t bin = 10 * (*val >> 4) + (*val & 0x0f); if (bin < max) { bin ++; } else { bin = 0; } *val = ((bin / 10 ) << 4) | (bin % 10); } /** * @brief Decrease BCD value. * @param : value, max * @retval : None */ static void valDecrease(uint8_t * val, uint8_t max) { uint8_t bin = 10 * (*val >> 4) + (*val & 0x0f); if (bin > 0) { bin --; } else { bin = max; } *val = ((bin / 10 ) << 4) | (bin % 10); } #ifdef USE_UART void usart_putc (char send) { // Do nothing for a bit if there is already // data waiting in the hardware to be sent while ((UCSRA & (1 << UDRE)) == 0) {}; UDR = send; } void usart_puts (const char *send) { // Cycle through each character individually while (*send) { usart_putc(*send++); } } #endif // USE_UART /** * Lamp Test */ static void lampValInc(uint8_t n) { if (n < LAMP_NUM) { if (Digit[n] != DIGIT_BLANK) { Digit[n] ++; } else { Digit[n] = 1; } if (Digit[n] > 9) { Digit[n] = 0; lampValInc(n+1); } } } static void lampTest(void) { uint8_t i=0; uint8_t k, x; dotOn(); for (k = 0; k 9) { Digit[0] = 0; dotOn(); lampValInc(1); } tdelay_ms(200); if (i == 5) { dotOff(); } x = 1; for (k = 0; k