/** * My Nixie Clock IN-12 * Vladimir N. Shilov * 2019.01.18 */ /* 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(1000); // warm up /* 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) { 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); } } } } // 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 Initialize peripheral */ static void Board_Init(void) { /* power off Analog Comparator */ ACSR = ACD; /* GPIO */ PORTB = BUTTON_PINS; // enable pull-up DDRC = DIGIT_PINS; // as output DDRD = (DOT_PIN | ANOD_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; } } /* Переход на зимнее время */ 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); } static 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) { dotOn(); 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) { dotOff(); 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) { switch(RTC.Sec) { #ifdef USE_DHT case 0x20: case 0x50: showTemperature(); break; case 0x22: case 0x52: showHumidity(); break; #endif // USE_DHT case 0x27: case 0x28: case 0x29: case 0x57: case 0x58: case 0x59: dotOn(); showMMSS(); break; default: 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; } } void showMMSS(void) { Digit[0] = RTC.Min >> 4; Digit[1] = RTC.Min & 0x0F; Digit[2] = RTC.Sec >> 4; Digit[3] = RTC.Sec & 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; } 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; } 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] = 0x02; Digit[1] = 0x00; Digit[2] = RTC.Year >> 4; Digit[3] = 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 = 1; Flag.blink2 = 0; Flag.blink3 = 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 = 0; Flag.blink2 = 1; Flag.blink3 = 1; 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.blink3 = 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 /** * П р е р ы в а н и я */ /** * @brief RTC one seconds interrupt */ ISR (INT1_vect) { Flag.RTC_Int = 1; } /** * @brief Refresh Nixie output * @note Digit[] must be in range 0x00 - 0x0F */ #pragma GCC optimize ("O3") ISR(TIMER2_OVF_vect) { static uint8_t idx = 0; // reload timer TCNT2 = TIMER2_CNT; // read current register value and clean bits uint8_t pd = PORTD & ~ANOD_PINS; uint8_t pc = PORTC & ~DIGIT_PINS; #ifndef USE_BRIGHT_CONTROL // power off lamps PORTD = pd; PORTC = pc; #endif switch (idx) { case 0: // output lamp value PORTC = pc | Digit[0]; // power on lamp if (Digit[0] != DIGIT_BLANK) { if (Flag.blink0 == 0 || Flag.blinkC == 0) { PORTD = pd | ANOD1; } } idx = 1; break; case 1: PORTC = pc | Digit[1]; if (Digit[1] != DIGIT_BLANK) { if (Flag.blink1 == 0 || Flag.blinkC == 0) { PORTD = pd | ANOD2; } } idx = 2; break; case 2: PORTC = pc | Digit[2]; if (Digit[2] != DIGIT_BLANK) { if (Flag.blink2 == 0 || Flag.blinkC == 0) { PORTD = pd | ANOD3; } } idx = 3; break; case 3: PORTC = pc | Digit[3]; if (Digit[3] != DIGIT_BLANK) { if (Flag.blink3 == 0 || Flag.blinkC == 0) { PORTD = pd | ANOD4; } } idx = 0; break; default: idx = 0; break; } } #ifdef USE_BRIGHT_CONTROL /** * @brief Power Off Nixie output * @note For Brightnes dimming */ #pragma GCC optimize ("O3") ISR(TIMER2_COMP_vect) { // power off lamps PORTD &= ~ANOD_PINS; PORTC &= ~DIGIT_PINS; } #endif // USE_BRIGHT_CONTROL /** * @brief заглушка для неиспользуемых прерываний */ ISR(__vector_default,ISR_NAKED) { reti(); }