ЭКСПЕРИМЕНТАЛЬНАЯ СТАТЬЯ
#define F_CPU 16000000UL #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #include <stdlib.h> #include <stdio.h> #include <avr/wdt.h> #include <stdint.h> #include "string.h" #include "fram.h" // ------------------------ RS?485 -------------------------------- #define RS485_DE_DDR DDRC #define RS485_DE_PORT PORTC #define RS485_DE_PIN PC0 #define CLK_PIN PB0 #define DIO_PIN PC4 #define R1 PD3 #define R2 PD4 #define KNC PD2 #define DisplayBUTTON PC5 #define SHOW_EIGHTS_BUTTON PC3 #define SEG_A 0b00000001 #define SEG_B 0b00000010 #define SEG_C 0b00000100 #define SEG_D 0b00001000 #define SEG_E 0b00010000 #define SEG_F 0b00100000 #define SEG_G 0b01000000 #define SEG_P 0b01110011 #define DEFAULT_BIT_DELAY 100 #define TM1637_I2C_COMM1 0x40 #define TM1637_I2C_COMM2 0xC0 #define TM1637_I2C_COMM3 0x80 #define BAUD 9600 #define MYUBRR F_CPU/16/BAUD-1 #define ENCODER_PIN1 PB1 #define ENCODER_PIN2 PB2 #define LATCH0 0 #define LATCH3 3 #define FRAM_SDA_PORT PORTD #define FRAM_SDA_DDR DDRD #define FRAM_SDA_PINR PIND #define FRAM_SDA_BIT PD7 #define FRAM_SCL_PORT PORTD #define FRAM_SCL_DDR DDRD #define FRAM_SCL_PINR PIND #define FRAM_SCL_BIT PD5 #define FRAM_SIGNATURE 0xA5A5 #define SR_SER PB4 #define SR_RCLK PC1 #define SR_SRCLK PC2 #ifndef MAX6675_SO_PORT #define MAX6675_SO_PORT PORTB #define MAX6675_SO_DDR DDRB #define MAX6675_SO_PIN PINB #define MAX6675_SO_BIT PB3 #endif #ifndef MAX6675_CS_PORT #define MAX6675_CS_PORT PORTD #define MAX6675_CS_DDR DDRD #define MAX6675_CS_PIN PIND #define MAX6675_CS_BIT PD6 #endif #ifndef MAX6675_SCK_PORT #define MAX6675_SCK_PORT PORTB #define MAX6675_SCK_DDR DDRB #define MAX6675_SCK_PIN PINB #define MAX6675_SCK_BIT PB5 #endif #define BUFFER_SIZE 64 // bytes per buffer #define TIMEOUT_LIMIT 5 // ms of silence to finish a message static char rx_buffer1[BUFFER_SIZE]; static char rx_buffer2[BUFFER_SIZE]; volatile char *current_rx_buffer = rx_buffer1; volatile char *sending_buffer = NULL; volatile uint8_t rx_index = 0; // write index in active buffer volatile uint8_t message_length = 0; // bytes to send back volatile uint8_t message_complete = 0; // flag: buffer ready to echo volatile uint8_t timeout_counter = 0; // ms since last byte struct FRAMData { uint16_t signature; uint16_t resetCount; uint16_t num; uint16_t chDelay; uint16_t heatInCamera; uint16_t Airflow; uint16_t iteration; }; volatile uint8_t oldState; volatile long position; volatile long positionExt; volatile long positionExtPrev; unsigned long positionExtTime; unsigned long positionExtTimePrev; volatile long number = 500; volatile unsigned long milliseconds1 = 0; volatile unsigned long milliseconds2 = 0; volatile uint16_t milliseconds3 = 0; volatile uint16_t milliseconds4 = 0; volatile uint16_t milliseconds5 = 0; volatile unsigned long Timer3 = 10; volatile uint16_t Timer4 = 1000; volatile uint8_t exitFlag = 0; volatile int KNC_count = 0; volatile bool eightsButtonPressed = false; volatile bool eightsButtonWasPressed = false; volatile unsigned long eightsLastDebounceTime = 0; volatile int displayMODE = 0; volatile bool eightsMode = false; bool buttonKNC_STATE = true; bool buttonFlagUp = false; bool buttonFlagDwn = false; volatile uint16_t Decrease = 0; volatile uint16_t Increase = 0; static const uint8_t minusSegments = 0b01000000; volatile int Mu = 1; volatile long tickCount = 0; volatile uint8_t lastTickCount = 0; volatile bool tickFlag = false; volatile uint16_t resetCount = 0; volatile uint16_t num = 8888; volatile uint16_t chDelay = 15; volatile uint16_t heatInCamera = 150; volatile uint16_t Airflow = 0; volatile uint16_t iteration = 0; long pos = 0; long newPos; char buffer[32]; char KNC_iterationStr[10]; char iterationStr[10]; char numStr[6]; char heatStr[6]; char chDelayStr[6]; char AirflowStr[6]; char resetCountStr[12]; int8_t KNOBDIR[16]; const uint8_t digitToSegment[] = { 0b00111111,0b00000110,0b01011011,0b01001111,0b01100110,0b01101101,0b01111101,0b00000111,0b01111111,0b01101111,0b01110111,0b01111100,0b00111001,0b01011110,0b01111001,0b01110001 }; bool buttonPressed = false; bool buttonWasPressed = false; unsigned long lastDebounceTime = 0; unsigned long debounceDelay = 50; void fram_write(uint16_t addr, uint8_t value); uint8_t fram_read(uint16_t addr); void fram_fill(uint8_t value); void fram_dump(uint16_t start, uint16_t len, void (*print_byte)(uint8_t)); bool fram_test(uint16_t addr, uint8_t value); void sendFRAMData(void); void fram_write_uint16(uint16_t addr, uint16_t value); uint16_t fram_read_uint16(uint16_t addr); void writeToFRAM(void); void readFromFRAM(void); void UART_init(unsigned int ubrr); void UART_transmit(unsigned char data); void UART_putstring(const char *s); void uart_send_char(char UARTmessage); void uart_send_string(const char *s); void rotary_encoder_init(void); void rotary_encoder_tick(void); long rotary_encoder_get_position(void); unsigned long rotary_encoder_get_millis_between_rotations(void); unsigned long rotary_encoder_get_rpm(void); void updateKnobDir(void); void bitDelay(void); void start(void); void stop(void); bool writeByte(uint8_t b); void setBrightness(uint8_t brightness, bool on); void setSegments(const uint8_t segments[], uint8_t length, uint8_t pos); void clear(void); void shownumDec(int num, bool leading_zero, uint8_t length, uint8_t pos); void shownumDecEx(int num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos); void shownumHexEx(uint16_t num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos); void shownumBaseEx(int8_t base, uint16_t num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos); void showDots(uint8_t dots, uint8_t* digits); void showEights(void); void init_MAX_pins(void); uint16_t read_max6675(void); float get_temperature(void); void print_temperature(float temp); void comeOnBabyLightMyFire(void); void buttonCheck(void); void EN(void); void shiftOut(uint8_t data); char* utoa(unsigned int value, char* str, int base); char* itoa(int value, char* buffer, int base); void setupTimer0(void); void setupTimer1(void); void WDT_init(void); void io_init(void); void interrupts_init(void); void setup(void); void loop(void); int main(void); uint32_t millis(void); void shiftOut(uint8_t data) { for (uint8_t i=0;i<8;i++) { if (data & 0x80) PORTB |= (1 << SR_SER); else PORTB &= ~(1 << SR_SER); PORTC |= (1 << SR_SRCLK); PORTC &= ~(1 << SR_SRCLK); data <<= 1; } PORTC |= (1 << SR_RCLK); PORTC &= ~(1 << SR_RCLK); } void sendFRAMData(void) { utoa(resetCount, resetCountStr, 10); uart_send_string("resetCount = "); uart_send_string(resetCountStr); uart_send_string("\n"); utoa(chDelay, chDelayStr, 10); uart_send_string("chDelay = "); uart_send_string(chDelayStr); uart_send_string("\n"); utoa(heatInCamera, heatStr, 10); uart_send_string("heatInCamera = "); uart_send_string(heatStr); uart_send_string("\n"); utoa(Airflow, AirflowStr, 10); uart_send_string("Airflow = "); uart_send_string(AirflowStr); uart_send_string("\n"); utoa(iteration, iterationStr, 10); uart_send_string("iteration = "); uart_send_string(iterationStr); uart_send_string("\n"); } void fram_write_uint16(uint16_t addr, uint16_t value) { fram_write(addr, (uint8_t)(value >> 8)); fram_write(addr + 1, (uint8_t)value); } uint16_t fram_read_uint16(uint16_t addr) { uint8_t high = fram_read(addr); uint8_t low = fram_read(addr + 1); return ((uint16_t)high << 8) | low; } void writeToFRAM(void) { struct FRAMData data = { .signature = FRAM_SIGNATURE, .resetCount = resetCount, .num = num, .chDelay = chDelay, .heatInCamera = heatInCamera, .Airflow = Airflow, .iteration = iteration }; uint16_t addr = 0; fram_write_uint16(addr, data.signature); addr += 2; fram_write_uint16(addr, data.resetCount); addr += 2; fram_write_uint16(addr, data.num); addr += 2; fram_write_uint16(addr, data.chDelay); addr += 2; fram_write_uint16(addr, data.heatInCamera); addr += 2; fram_write_uint16(addr, data.Airflow); addr += 2; fram_write_uint16(addr, data.iteration); addr += 2; } void readFromFRAM(void) { struct FRAMData data; uint16_t addr = 0; data.signature = fram_read_uint16(addr); addr += 2; data.resetCount = fram_read_uint16(addr); addr += 2; data.num = fram_read_uint16(addr); addr += 2; data.chDelay = fram_read_uint16(addr); addr += 2; data.heatInCamera = fram_read_uint16(addr); addr += 2; data.Airflow = fram_read_uint16(addr); addr += 2; data.iteration = fram_read_uint16(addr); addr += 2; if (data.signature == FRAM_SIGNATURE) { resetCount = data.resetCount; num = data.num; chDelay = data.chDelay; heatInCamera = data.heatInCamera; Airflow = data.Airflow; iteration = data.iteration; } else { writeToFRAM(); } } static inline void rs485_begin_tx(void) { RS485_DE_PORT |= (1 << RS485_DE_PIN); // DE = 1 (driver enable) _delay_us(30); // t(EN) for SN75176B } static inline void rs485_end_tx(void) { while (!(UCSR0A & (1 << TXC0))); // wait for last stop?bit UCSR0A |= (1 << TXC0); // clear TXC flag RS485_DE_PORT &= ~(1 << RS485_DE_PIN); // DE = 0 (receive) } void UART_init(unsigned int ubrr) { UBRR0H = (unsigned char)(ubrr >> 8); UBRR0L = (unsigned char)ubrr; UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0); // RX, TX, RX interrupt UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); // 8N1 } static inline void uart_send_byte(uint8_t data) { while (!(UCSR0A & (1 << UDRE0))); // wait TX register empty UDR0 = data; } static void uart_send_buf(const char *buf, uint8_t len) { rs485_begin_tx(); for (uint8_t i = 0; i < len; i++) { uart_send_byte(buf[i]); } rs485_end_tx(); } void uart_send_string(const char *s) { rs485_begin_tx(); while (*s) { uart_send_byte(*s++); } rs485_end_tx(); } void rotary_encoder_init(void) { DDRB &= ~(1 << ENCODER_PIN1); DDRB &= ~(1 << ENCODER_PIN2); PORTB |= (1 << ENCODER_PIN1); PORTB |= (1 << ENCODER_PIN2); PCICR |= (1 << PCIE0); PCMSK0 |= (1 << PCINT1) | (1 << PCINT2); uint8_t sig1 = PINB & (1 << ENCODER_PIN1); uint8_t sig2 = PINB & (1 << ENCODER_PIN2); oldState = (sig1 >> ENCODER_PIN1) | ((sig2 >> ENCODER_PIN2) << 1); position = 0; positionExt = 0; positionExtPrev = 0; positionExtTime = 0; positionExtTimePrev = 0; } void rotary_encoder_tick(void) { uint8_t sig1 = PINB & (1 << ENCODER_PIN1); uint8_t sig2 = PINB & (1 << ENCODER_PIN2); int8_t thisState = (sig1 >> ENCODER_PIN1) | ((sig2 >> ENCODER_PIN2) << 1); if (thisState != oldState) { position += KNOBDIR[thisState | (oldState << 2)]; oldState = thisState; if (thisState == LATCH0 || thisState == LATCH3) { positionExt = position >> 2; positionExtTimePrev = positionExtTime; positionExtTime = milliseconds1; } } } long rotary_encoder_get_position(void) { return positionExt; } void updateKnobDir(void) { KNOBDIR[0] = 0; KNOBDIR[1] = -Mu; KNOBDIR[2] = Mu; KNOBDIR[3] = 0; KNOBDIR[4] = Mu; KNOBDIR[5] = 0; KNOBDIR[6] = 0; KNOBDIR[7] = -Mu; KNOBDIR[8] = -Mu; KNOBDIR[9] = 0; KNOBDIR[10] = 0; KNOBDIR[11] = Mu; KNOBDIR[12] = 0; KNOBDIR[13] = Mu; KNOBDIR[14] = -Mu; KNOBDIR[15] = 0; } void bitDelay(void) { _delay_us(DEFAULT_BIT_DELAY); } void start(void) { DDRC |= (1 << DIO_PIN); bitDelay(); } void stop(void) { DDRC |= (1 << DIO_PIN); bitDelay(); DDRB &= ~(1 << CLK_PIN); bitDelay(); DDRC &= ~(1 << DIO_PIN); bitDelay(); } bool writeByte(uint8_t b) { for (uint8_t i = 0; i < 8; i++) { DDRB |= (1 << CLK_PIN); bitDelay(); if (b & 0x01) DDRC &= ~(1 << DIO_PIN); else DDRC |= (1 << DIO_PIN); bitDelay(); DDRB &= ~(1 << CLK_PIN); bitDelay(); b >>= 1; } DDRB |= (1 << CLK_PIN); DDRC &= ~(1 << DIO_PIN); bitDelay(); DDRB &= ~(1 << CLK_PIN); bitDelay(); uint8_t ack = PINC & (1 << DIO_PIN); if (ack == 0) DDRC |= (1 << DIO_PIN); bitDelay(); DDRB |= (1 << CLK_PIN); bitDelay(); return ack; } void setBrightness(uint8_t brightness, bool on) { } void setSegments(const uint8_t segments[], uint8_t length, uint8_t pos) { start(); writeByte(TM1637_I2C_COMM1); stop(); start(); writeByte(TM1637_I2C_COMM2 + (pos & 0x03)); for (uint8_t k = 0; k < length; k++) writeByte(segments[k]); stop(); start(); writeByte(TM1637_I2C_COMM3 + (0x0f & 0x0f)); stop(); } void clear(void) { uint8_t data[] = { 0, 0, 0, 0 }; setSegments(data, 4, 0); } void shownumDec(int num, bool leading_zero, uint8_t length, uint8_t pos) { shownumDecEx(num, 0, leading_zero, length, pos); } void shownumDecEx(int num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos) { shownumBaseEx(num < 0 ? -10 : 10, num < 0 ? -num : num, dots, leading_zero, length, pos); } void shownumHexEx(uint16_t num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos) { shownumBaseEx(16, num, dots, leading_zero, length, pos); } void shownumBaseEx(int8_t base, uint16_t num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos) { bool negative = false; if (base < 0) { base = -base; negative = true; } uint8_t digits[4]; if (num == 0 && !leading_zero) { for (uint8_t i = 0; i < (length - 1); i++) digits[i] = 0; digits[length - 1] = digitToSegment[0]; } else { for (int i = length - 1; i >= 0; --i) { uint8_t digit = num % base; if (digit == 0 && num == 0 && !leading_zero) digits[i] = 0; else digits[i] = digitToSegment[digit & 0x0f]; if (digit == 0 && num == 0 && negative) { digits[i] = minusSegments; negative = false; } num /= base; } if (dots != 0) showDots(dots, digits); } setSegments(digits, length, pos); } void showDots(uint8_t dots, uint8_t* digits) { for (int i = 0; i < 4; ++i) { digits[i] |= (dots & 0x80); dots <<= 1; } } void showEights(void) { uint8_t segments[6] = {0}; for (uint8_t i = 0; i < 4; i++) { segments[i] = digitToSegment[8]; } segments[4] = digitToSegment[14]; segments[5] = digitToSegment[14]; setSegments(segments, 6, 0); } void init_MAX_pins(void) { MAX6675_SO_DDR &= ~(1 << MAX6675_SO_BIT); MAX6675_SO_PORT &= ~(1 << MAX6675_SO_BIT); MAX6675_SCK_DDR |= (1 << MAX6675_SCK_BIT); MAX6675_SCK_PORT &= ~(1 << MAX6675_SCK_BIT); MAX6675_CS_DDR |= (1 << MAX6675_CS_BIT); MAX6675_CS_PORT |= (1 << MAX6675_CS_BIT); } uint16_t read_max6675(void) { uint16_t data = 0; MAX6675_CS_PORT &= ~(1 << MAX6675_CS_BIT); _delay_us(10); for (uint8_t i = 0; i < 16; i++) { MAX6675_SCK_PORT |= (1 << MAX6675_SCK_BIT); _delay_us(1); data <<= 1; if (MAX6675_SO_PIN & (1 << MAX6675_SO_BIT)) { data |= 1; } MAX6675_SCK_PORT &= ~(1 << MAX6675_SCK_BIT); _delay_us(1); } MAX6675_CS_PORT |= (1 << MAX6675_CS_BIT); return data; } float get_temperature(void) { uint16_t data = read_max6675(); char buffer[32]; snprintf(buffer, sizeof(buffer), "Raw data: 0x%04X\n", data); uart_send_string(buffer); if (data & (1 << 2)) { return -1; } if (data & (1 << 1)) { return -2; } data >>= 3; float temp = (data * 0.25) * (41.0 / 55.0); temp += 25.3; return temp; } void print_temperature(float temp) { char buffer[16]; if (temp == -1) { uart_send_string("Error: Thermocouple disconnected\n"); } else { int whole = (int)temp; int frac = (int)((temp - whole) * 100); snprintf(buffer, sizeof(buffer), "Temp: %d.%02d C\n", whole, frac); uart_send_string(buffer); } } void comeOnBabyLightMyFire(void) { KNC_count++; Timer3 = number; uart_send_string("\n"); uart_send_string("Relay2 ON!\n"); uint8_t segments[] = { 0, 0, 0, SEG_P }; setSegments(segments, 4, 0); milliseconds3 = 0; PORTD |= (1 << R2); while (milliseconds3 < Timer3) { } PORTD &= ~(1 << R2); exitFlag = 0; uart_send_string("Relay2 OFF!\n"); milliseconds4 = 0; uart_send_string("Relay1 ON!\n"); PORTD |= (1 << R1); milliseconds3 = 0; while (milliseconds3 < chDelay) { } PORTD &= ~(1 << R1); uart_send_string("Relay1 OFF!\n"); } void buttonCheck(void) { sei(); if ((PIND & (1 << KNC))) { if (!buttonKNC_STATE) { buttonKNC_STATE = true; comeOnBabyLightMyFire(); utoa(KNC_count, KNC_iterationStr, 10); uart_send_string("Cycle started! Count: "); uart_send_string(KNC_iterationStr); uart_send_string("\n"); } } else { if (milliseconds4 > Timer4) { buttonKNC_STATE = false; } } bool currentButtonState = !(PINC & (1 << DisplayBUTTON)); if (currentButtonState != buttonPressed) { lastDebounceTime = millis(); } if ((millis() - lastDebounceTime) > debounceDelay) { if (currentButtonState != buttonWasPressed) { buttonWasPressed = currentButtonState; if (buttonWasPressed) { uint8_t oldMode = displayMODE; displayMODE = (displayMODE + 1) % 4; if (oldMode != displayMODE) { Mu = 1; updateKnobDir(); tickCount = 0; lastTickCount = 0; tickFlag = false; milliseconds2 = 0; switch (displayMODE) { case 0: newPos = number; break; case 2: newPos = chDelay; break; case 3: newPos = Airflow; break; } pos = newPos; position = (long)newPos * 4; uart_send_string("Encoder state reset for mode: "); utoa(displayMODE, KNC_iterationStr, 10); uart_send_string(KNC_iterationStr); uart_send_string("\n"); } utoa(displayMODE, KNC_iterationStr, 10); uart_send_string("Display mode: "); uart_send_string(KNC_iterationStr); switch (displayMODE) { case 0: uart_send_string(" (Number)\n"); shiftOut(0b00000010); break; case 1: uart_send_string(" (KNC Counter)\n"); shiftOut(0b00000100); break; case 2: uart_send_string(" (chDelay)\n"); shiftOut(0b00001000); break; case 3: uart_send_string(" (Airflow)\n"); shiftOut(0b00010000); break; } writeToFRAM(); } } } buttonPressed = currentButtonState; } void EN(void) { if (displayMODE == 0 || displayMODE == 2 || displayMODE == 3) { tickFlag = true; tickCount++; rotary_encoder_tick(); newPos = rotary_encoder_get_position(); if (pos != newPos) { switch (displayMODE) { case 0: if (position <= 0) { position = 0; newPos = 0; tickFlag = false; Mu = 1; } else if (position >= (long)9999 * 4) { position = (long)9999 * 4; newPos = 9999; tickFlag = false; Mu = 1; } number = newPos; break; case 2: if (position <= 0) { position = 0; newPos = 0; tickFlag = false; Mu = 1; } else if (position >= (long)9999 * 4) { position = (long)9999 * 4; newPos = 9999; tickFlag = false; Mu = 1; } chDelay = newPos; break; case 3: if (position <= 0) { position = 0; newPos = 0; tickFlag = false; Mu = 1; } else if (position >= (long)9999 * 4) { position = (long)9999 * 4; newPos = 9999; tickFlag = false; Mu = 1; } Airflow = newPos; break; } pos = newPos; } } } char* utoa(unsigned int value, char* str, int base) { char* rc; char* ptr; char* low; static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; if (base < 2 || base > 36) { *str = '\0'; return str; } rc = ptr = str; low = ptr; do { *ptr++ = digits[value % base]; value /= base; } while (value); *ptr-- = '\0'; while (low < ptr) { char tmp = *low; *low++ = *ptr; *ptr-- = tmp; } return rc; } char* itoa(int value, char* buffer, int base) { if (base < 2 || base > 36) { *buffer = '\0'; return buffer; } char* ptr = buffer, *ptr1 = buffer, tmp_char; int tmp_value; if (value < 0 && base == 10) { *ptr++ = '-'; ptr1++; value = -value; } tmp_value = value; do { int remainder = tmp_value % base; *ptr++ = (remainder < 10) ? (remainder + '0') : (remainder - 10 + 'a'); } while (tmp_value /= base); *ptr-- = '\0'; while (ptr1 < ptr) { tmp_char = *ptr; *ptr-- = *ptr1; *ptr1++ = tmp_char; } return buffer; } uint32_t millis(void) { uint32_t ms; cli(); ms = milliseconds1; sei(); return ms; } static inline void timer2_reset(void) { TCNT2 = 0; } void setupTimer0(void) { TCCR0A = (1 << WGM01); TCCR0B = (1 << CS01) | (1 << CS00); OCR0A = 249; TIMSK0 = (1 << OCIE0A); } void setupTimer1(void) { TCCR1A = 0; TCCR1B = (1 << WGM12) | (1 << CS12); OCR1A = 62499; TIMSK1 |= (1 << OCIE1A); } static void setupTimer2(void) { TCCR2A = (1 << WGM21); // CTC mode (OCR2A top) OCR2A = 249; // 1 ms = 16MHz / 64 / 1000 - 1 TCCR2B = (1 << CS22); // prescaler = 64 TIMSK2 = (1 << OCIE2A); // enable compare match A interrupt } void io_init(void) { RS485_DE_DDR |= (1 << RS485_DE_PIN); // PC0 output RS485_DE_PORT &= ~(1 << RS485_DE_PIN); // default: receive DDRD |= (1 << R1) | (1 << R2); PORTD &= ~((1 << R1) | (1 << R2)); DDRC &= ~(1 << DisplayBUTTON); PORTC |= (1 << DisplayBUTTON); DDRD &= ~(1 << KNC); DDRC &= ~(1 << SHOW_EIGHTS_BUTTON); PORTC |= (1 << SHOW_EIGHTS_BUTTON); DDRB |= (1 << CLK_PIN); DDRC |= (1 << DIO_PIN); init_MAX_pins(); DDRB |= (1 << PB4); DDRC |= (1 << PC1) | (1 << PC2); PORTB &= ~(1 << PB4); PORTC &= ~((1 << PC1) | (1 << PC2)); } void interrupts_init(void) { setupTimer0(); setupTimer1(); setupTimer2(); PCICR |= (1 << PCIE0); PCMSK0 |= (1 << PCINT1) | (1 << PCINT2); PCICR |= (1 << PCIE1); PCMSK1 |= (1 << PCINT11); sei(); } void WDT_init(void) { wdt_enable(WDTO_2S); } void setup(void) { io_init(); interrupts_init(); updateKnobDir(); UART_init(MYUBRR); rotary_encoder_init(); pos = number; newPos = number; position = (long)number * 4; uart_send_string("ARD Systems 2025\r\n"); // Send first part with CRLF _delay_ms(10); // Small delay to ensure receiver processes the first part uart_send_string("Initializing timer...\r\n"); // Send second part with CRLF setBrightness(0x0f, true); WDT_init(); shiftOut(0b00000010); readFromFRAM(); resetCount++; pos = number; newPos = number; position = (long)number * 4; writeToFRAM(); uint8_t resetReason = MCUSR; MCUSR = 0; uart_send_string("Last reset cause: "); if (resetReason & (1 << WDRF)) { uart_send_string("Watchdog reset\r\n"); } else if (resetReason & (1 << EXTRF)) { uart_send_string("External reset\r\n"); } else if (resetReason & (1 << PORF)) { uart_send_string("Power-on reset\r\n"); } else if (resetReason & (1 << BORF)) { uart_send_string("Brown-out reset\r\n"); } else { uart_send_string("Unknown reset\r\n"); } sendFRAMData(); memset(rx_buffer1, 0, BUFFER_SIZE); memset(rx_buffer2, 0, BUFFER_SIZE); DDRD &= ~(1 << PD0) | (1 << PD1); PORTD &= ~(1 << PD0) | (1 << PD1); } void loop(void) { static unsigned long lastFramWrite = 0; while (1) { wdt_reset(); if (message_complete) { cli(); // protect shared vars volatile char *buf = sending_buffer; uint8_t len = message_length; message_complete = 0; sending_buffer = NULL; sei(); if (len && buf) { uart_send_string("echo: "); uart_send_buf((const char *)buf, len); uart_send_string("\r\n"); memset((void *)buf, 0, BUFFER_SIZE); } } buttonCheck(); switch (displayMODE) { case 0: shownumDec(number, false, 4, 0); break; case 1: shownumDec(KNC_count, false, 4, 0); break; case 2: shownumDec(chDelay, false, 4, 0); break; case 3: shownumDec(Airflow, false, 4, 0); break; } if (tickFlag == true) { itoa(newPos, buffer, 10); uart_send_string("Encoder value: "); uart_send_string(buffer); uart_send_string("\n"); if (millis() - lastFramWrite > 500) { writeToFRAM(); lastFramWrite = millis(); } tickFlag = false; milliseconds2 = 0; } if (tickCount > 80) { if (milliseconds2 < 200) { Mu = Mu * 10; if (Mu > 100) { Mu = 100; } updateKnobDir(); tickCount = 0; milliseconds2 = 0; } } if (milliseconds2 > 150) { milliseconds2 = 0; if (tickCount == lastTickCount) { tickFlag = false; tickCount = 0; Mu = 1; updateKnobDir(); } lastTickCount = tickCount; } } } int main(void) { setup(); loop(); return 0; } ISR(TIMER0_COMPA_vect) { milliseconds1++; milliseconds2++; milliseconds3++; milliseconds4++; milliseconds5++; Decrease++; Increase++; } ISR(TIMER1_COMPA_vect) { wdt_reset(); } ISR(TIMER2_COMPA_vect) { if (rx_index) { timeout_counter++; if (timeout_counter >= TIMEOUT_LIMIT) { sending_buffer = current_rx_buffer; message_length = rx_index; current_rx_buffer = (current_rx_buffer == rx_buffer1) ? rx_buffer2 : rx_buffer1; rx_index = 0; timeout_counter = 0; message_complete = 1; } } else { timeout_counter = 0; } } // ------------------------ INTERRUPTS ---------------------------- ISR(USART_RX_vect) { static const uint8_t XON = 0x11; static const uint8_t XOFF = 0x13; uint8_t data = UDR0; // simple ignore of software flow?control if (data == XON || data == XOFF) return; if (rx_index < (BUFFER_SIZE - 1)) { current_rx_buffer[rx_index++] = data; timeout_counter = 0; timer2_reset(); } } ISR(PCINT0_vect) { EN(); } ISR(PCINT1_vect) { float temp = get_temperature(); print_temperature(temp); heatInCamera = temp; writeToFRAM(); }
// Наша DWIN-панель подключена напрямую к UART PD0/1, без RS485, однако механизм управления RE/DE оставлен для дальнейших разработок. #define F_CPU 16000000UL #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #include <stdlib.h> #include <stdio.h> #include <avr/wdt.h> #include <stdint.h> #include <stdbool.h> #include "string.h" #include "fram.h" #define DEBUG 1 // Раскомментируй для включения дебага, иначе дебаг отключён // ------------------------ RS-485 / Пины / Дисплей ------------------------ #define RS485_DE_DDR DDRC #define RS485_DE_PORT PORTC #define RS485_DE_PIN PC0 #define CLK_PIN PB0 #define DIO_PIN PC4 #define R1 PD3 #define R2 PD4 #define KNC PD2 #define DisplayBUTTON PC5 #define SHOW_EIGHTS_BUTTON PC3 // TM1637 сегменты #define SEG_A 0b00000001 #define SEG_B 0b00000010 #define SEG_C 0b00000100 #define SEG_D 0b00001000 #define SEG_E 0b00010000 #define SEG_F 0b00100000 #define SEG_G 0b01000000 #define SEG_P 0b01110011 #define DEFAULT_BIT_DELAY 100 #define TM1637_I2C_COMM1 0x40 #define TM1637_I2C_COMM2 0xC0 #define TM1637_I2C_COMM3 0x80 // ------------------------ Скорости UART ------------------------ #define MYUBRR 8 // Изменено: Для 115200 бод с U2X=0 (error -3.5%, лучше для DWIN) #define USE_U2X 0 // Изменено: 0 — стандартный режим (/16) #define DWIN_BAUD 115200UL #define DWIN_UBRR (F_CPU/16/DWIN_BAUD - 1) // /16 для U2X=0 // ------------------------ Энкодер ------------------------ #define ENCODER_PIN1 PB1 #define ENCODER_PIN2 PB2 #define LATCH0 0 #define LATCH3 3 // ------------------------ FRAM I2C бит-бэнг ------------------------ #define FRAM_SDA_PORT PORTD #define FRAM_SDA_DDR DDRD #define FRAM_SDA_PINR PIND #define FRAM_SDA_BIT PD7 #define FRAM_SCL_PORT PORTD #define FRAM_SCL_DDR DDRD #define FRAM_SCL_PINR PIND #define FRAM_SCL_BIT PD5 #define FRAM_SIGNATURE 0xA5A5 // ------------------------ 74HC595 ------------------------ #define SR_SER PB4 #define SR_RCLK PC1 #define SR_SRCLK PC2 // ------------------------ MAX6675 ------------------------ #ifndef MAX6675_SO_PORT #define MAX6675_SO_PORT PORTB #define MAX6675_SO_DDR DDRB #define MAX6675_SO_PIN PINB #define MAX6675_SO_BIT PB3 #endif #ifndef MAX6675_CS_PORT #define MAX6675_CS_PORT PORTD #define MAX6675_CS_DDR DDRD #define MAX6675_CS_PIN PIND #define MAX6675_CS_BIT PD6 #endif #ifndef MAX6675_SCK_PORT #define MAX6675_SCK_PORT PORTB #define MAX6675_SCK_DDR DDRB #define MAX6675_SCK_PIN PINB #define MAX6675_SCK_BIT PB5 #endif #ifndef MAX6675_CS_INACTIVE_LEVEL #define MAX6675_CS_INACTIVE_LEVEL 1 // 1 = HIGH неактивно, 0 = LOW неактивно #endif #define READ_MAX6675_CS() ((MAX6675_CS_PIN & (1 << MAX6675_CS_BIT)) ? 1 : 0) #define CS_IS_INACTIVE() (READ_MAX6675_CS() == (MAX6675_CS_INACTIVE_LEVEL ? 1 : 0)) // ------------------------ DWIN адреса ------------------------ #define DWIN_VP_NUMBER 0x1000 // VP для переменной number // ------------------------ Глобальные переменные для DWIN ------------------------ volatile uint16_t number = 500; // наше основное число volatile uint16_t chDelay = 15; volatile uint16_t heatInCamera = 150; volatile uint16_t Airflow = 0; volatile uint16_t iteration = 0; volatile uint16_t resetCount = 0; volatile uint16_t KNC_count = 0; // ------------------------ DWIN переменные ------------------------ #define VP_COUNT 8 typedef struct { uint16_t vp_addr; // VP-адрес (например, 0x1000) volatile uint16_t *value; // Указатель на переменную (например, &number) uint8_t dirty; // Флаг изменения } DWIN_VP; DWIN_VP vp_array[VP_COUNT] = { {0x1000, &number, 0}, // number {0x1001, &chDelay, 0}, // chDelay {0x1002, &heatInCamera, 0}, // heatInCamera {0x1003, &Airflow, 0}, // Airflow {0x1004, &iteration, 0}, // iteration {0x1005, &resetCount, 0}, // resetCount {0x1006, &KNC_count, 0}, // KNC_count {0x1007, NULL, 0} // Резерв }; // ------------------------ Прочие глобальные переменные ------------------------ volatile uint8_t button_event = 0; // Флаг для отладки: 1 = кнопка +1, 2 = кнопка -1, 0 = нет struct FRAMData { uint16_t signature; uint16_t resetCount; uint16_t num; uint16_t chDelay; uint16_t heatInCamera; uint16_t Airflow; uint16_t iteration; }; volatile uint8_t oldState; volatile long position; volatile long positionExt; volatile long positionExtPrev; unsigned long positionExtTime; unsigned long positionExtTimePrev; volatile uint8_t number_dirty = 0; // флаг «надо отправить на DWIN» volatile unsigned long milliseconds1 = 0; volatile unsigned long milliseconds2 = 0; volatile uint16_t milliseconds3 = 0; volatile uint16_t milliseconds4 = 0; volatile uint16_t milliseconds5 = 0; volatile unsigned long Timer3 = 10; volatile uint16_t Timer4 = 1000; volatile uint8_t exitFlag = 0; volatile bool eightsButtonPressed = false; volatile bool eightsButtonWasPressed = false; volatile unsigned long eightsLastDebounceTime = 0; volatile int displayMODE = 0; volatile bool eightsMode = false; bool buttonKNC_STATE = true; bool buttonFlagUp = false; bool buttonFlagDwn = false; volatile uint16_t Decrease = 0; volatile uint16_t Increase = 0; static const uint8_t minusSegments = 0b01000000; volatile int Mu = 1; volatile long tickCount = 0; volatile uint8_t lastTickCount = 0; volatile bool tickFlag = false; volatile uint16_t num = 8888; long pos = 0; long newPos; char buffer[32]; char KNC_iterationStr[10]; char iterationStr[10]; char numStr[6]; char heatStr[6]; char chDelayStr[6]; char AirflowStr[6]; char resetCountStr[12]; int8_t KNOBDIR[16]; const uint8_t digitToSegment[] = { 0b00111111, 0b00000110, 0b01011011, 0b01001111, 0b01100110, 0b01101101, 0b01111101, 0b00000111, 0b01111111, 0b01101111, 0b01110111, 0b01111100, 0b00111001, 0b01011110, 0b01111001, 0b01110001 }; bool buttonPressed = false; bool buttonWasPressed = false; unsigned long lastDebounceTime = 0; unsigned long debounceDelay = 50; // ------------------------ Буфер для UART RX ------------------------ #define RX_BUFFER_SIZE 16 volatile uint8_t rx_buffer[RX_BUFFER_SIZE]; volatile uint8_t rx_head = 0; volatile uint8_t rx_tail = 0; // ------------------------ Флаг для конфликта с MAX6675 ------------------------ volatile bool max6675_busy = false; // ------------------------ Прототипы ------------------------ void fram_write(uint16_t addr, uint8_t value); uint8_t fram_read(uint16_t addr); void fram_fill(uint8_t value); void fram_dump(uint16_t start, uint16_t len, void (*print_byte)(uint8_t)); bool fram_test(uint16_t addr, uint8_t value); void sendFRAMData(void); void fram_write_uint16(uint16_t addr, uint16_t value); uint16_t fram_read_uint16(uint16_t addr); void writeToFRAM(void); void readFromFRAM(void); void UART_init(unsigned int ubrr); static inline void uart_send_byte(uint8_t data); void uart_send_buf(const char *buf, uint8_t len); void rotary_encoder_init(void); void rotary_encoder_tick(void); long rotary_encoder_get_position(void); void updateKnobDir(void); void bitDelay(void); void start(void); void stop(void); bool writeByte(uint8_t b); void setBrightness(uint8_t brightness, bool on); void setSegments(const uint8_t segments[], uint8_t length, uint8_t pos); void clear(void); void shownumDec(int num, bool leading_zero, uint8_t length, uint8_t pos); void shownumDecEx(int num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos); void shownumHexEx(uint16_t num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos); void shownumBaseEx(int8_t base, uint16_t num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos); void showDots(uint8_t dots, uint8_t* digits); void showEights(void); void init_MAX_pins(void); uint16_t read_max6675(void); float get_temperature(void); void print_temperature(float temp); void comeOnBabyLightMyFire(void); void buttonCheck(void); void EN(void); void shiftOut(uint8_t data); char* utoa(unsigned int value, char* str, int base); char* itoa(int value, char* buffer, int base); void setupTimer0(void); void setupTimer1(void); void WDT_init(void); void io_init(void); void interrupts_init(void); void setup(void); void loop(void); int main(void); uint32_t millis(void); // ---- DWIN функции ---- static void dwin_write16(uint16_t vp_addr, uint16_t value); void dwin_set_page(uint8_t page); static void soft_uart_send_byte(uint8_t data); void send_buffered_data(void); // ------------------------ Реализация ------------------------ void shiftOut(uint8_t data) { for (uint8_t i = 0; i < 8; i++) { if (data & 0x80) PORTB |= (1 << SR_SER); else PORTB &= ~(1 << SR_SER); PORTC |= (1 << SR_SRCLK); PORTC &= ~(1 << SR_SRCLK); data <<= 1; } PORTC |= (1 << SR_RCLK); PORTC &= ~(1 << SR_RCLK); } void fram_write_uint16(uint16_t addr, uint16_t value) { fram_write(addr, (uint8_t)(value >> 8)); fram_write(addr + 1, (uint8_t)value); } uint16_t fram_read_uint16(uint16_t addr) { uint8_t high = fram_read(addr); uint8_t low = fram_read(addr + 1); return ((uint16_t)high << 8) | low; } void writeToFRAM(void) { struct FRAMData data = { .signature = FRAM_SIGNATURE, .resetCount = resetCount, .num = num, .chDelay = chDelay, .heatInCamera = heatInCamera, .Airflow = Airflow, .iteration = iteration }; uint16_t addr = 0; fram_write_uint16(addr, data.signature); addr += 2; fram_write_uint16(addr, data.resetCount); addr += 2; fram_write_uint16(addr, data.num); addr += 2; fram_write_uint16(addr, data.chDelay); addr += 2; fram_write_uint16(addr, data.heatInCamera); addr += 2; fram_write_uint16(addr, data.Airflow); addr += 2; fram_write_uint16(addr, data.iteration); addr += 2; } void readFromFRAM(void) { struct FRAMData data; uint16_t addr = 0; data.signature = fram_read_uint16(addr); addr += 2; data.resetCount = fram_read_uint16(addr); addr += 2; data.num = fram_read_uint16(addr); addr += 2; data.chDelay = fram_read_uint16(addr); addr += 2; data.heatInCamera = fram_read_uint16(addr); addr += 2; data.Airflow = fram_read_uint16(addr); addr += 2; data.iteration = fram_read_uint16(addr); addr += 2; if (data.signature == FRAM_SIGNATURE) { resetCount = data.resetCount; num = data.num; chDelay = data.chDelay; heatInCamera = data.heatInCamera; Airflow = data.Airflow; iteration = data.iteration; } else { writeToFRAM(); } } // ------------------------ RS-485 TX control (закомментировано для direct подключения) ------------------------ /* static inline void rs485_begin_tx(void) { RS485_DE_PORT |= (1 << RS485_DE_PIN); // DE = 1 (driver enable) _delay_us(30); // tEN для SN75176B } static inline void rs485_end_tx(void) { while (!(UCSR0A & (1 << TXC0))); // ждём окончания стоп-бита UCSR0A |= (1 << TXC0); // сброс TXC RS485_DE_PORT &= ~(1 << RS485_DE_PIN); // DE = 0 (приём) } */ // ------------------------ HW UART (DWIN, 115200) ------------------------ void UART_init(unsigned int ubrr) { #if USE_U2X UCSR0A |= (1 << U2X0); // делитель /8 #else UCSR0A &= ~(1 << U2X0); // делитель /16 #endif UBRR0H = (uint8_t)(ubrr >> 8); UBRR0L = (uint8_t)(ubrr & 0xFF); UCSR0B = (1 << TXEN0) | (1 << RXEN0) | (1 << RXCIE0); // TX, RX и прерывание RX UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); // 8N1 } static inline void uart_send_byte(uint8_t data) { while (!(UCSR0A & (1 << UDRE0))); // ждём буфер UDR0 = data; } void uart_send_buf(const char *buf, uint8_t len) { // rs485_begin_tx(); // Закомментировано для direct for (uint8_t i = 0; i < len; i++) { uart_send_byte((uint8_t)buf[i]); } // rs485_end_tx(); // Закомментировано для direct } // ------------------------ Софтварный UART на PB3 ------------------------ static void soft_uart_send_byte(uint8_t data) { cli(); // Отключить прерывания для точного тайминга if (max6675_busy) { sei(); return; } PORTD |= (1 << PD6); // Установить PD6 в 1 перед отправкой DDRB |= (1 << PB3); // PB3 как выход // Старт-бит (0) PORTB &= ~(1 << PB3); _delay_us(1000000.0 / DWIN_BAUD); // Данные (LSB first) for (uint8_t i = 0; i < 8; i++) { if (data & 0x01) { PORTB |= (1 << PB3); } else { PORTB &= ~(1 << PB3); } data >>= 1; _delay_us(1000000.0 / DWIN_BAUD); } // Стоп-бит (1) PORTB |= (1 << PB3); _delay_us(1000000.0 / DWIN_BAUD); DDRB &= ~(1 << PB3); // PB3 обратно как вход sei(); // Включить прерывания } // ------------------------ Отправка буферизованных данных ------------------------ void send_buffered_data(void) { while (rx_tail != rx_head) { soft_uart_send_byte(rx_buffer[rx_tail]); rx_tail = (rx_tail + 1) % RX_BUFFER_SIZE; } } // ------------------------ Энкодер ------------------------ void rotary_encoder_init(void) { DDRB &= ~(1 << ENCODER_PIN1); DDRB &= ~(1 << ENCODER_PIN2); PORTB |= (1 << ENCODER_PIN1); PORTB |= (1 << ENCODER_PIN2); PCICR |= (1 << PCIE0); PCMSK0 |= (1 << PCINT1) | (1 << PCINT2); uint8_t sig1 = PINB & (1 << ENCODER_PIN1); uint8_t sig2 = PINB & (1 << ENCODER_PIN2); oldState = (sig1 >> ENCODER_PIN1) | ((sig2 >> ENCODER_PIN2) << 1); position = 0; positionExt = 0; positionExtPrev = 0; positionExtTime = 0; positionExtTimePrev = 0; } void rotary_encoder_tick(void) { uint8_t sig1 = PINB & (1 << ENCODER_PIN1); uint8_t sig2 = PINB & (1 << ENCODER_PIN2); int8_t thisState = (sig1 >> ENCODER_PIN1) | ((sig2 >> ENCODER_PIN2) << 1); if (thisState != oldState) { position += KNOBDIR[thisState | (oldState << 2)]; oldState = thisState; if (thisState == LATCH0 || thisState == LATCH3) { positionExt = position >> 2; positionExtTimePrev = positionExtTime; positionExtTime = milliseconds1; } } } long rotary_encoder_get_position(void) { return positionExt; } void updateKnobDir(void) { KNOBDIR[0] = 0; KNOBDIR[1] = -Mu; KNOBDIR[2] = Mu; KNOBDIR[3] = 0; KNOBDIR[4] = Mu; KNOBDIR[5] = 0; KNOBDIR[6] = 0; KNOBDIR[7] = -Mu; KNOBDIR[8] = -Mu; KNOBDIR[9] = 0; KNOBDIR[10] = 0; KNOBDIR[11] = Mu; KNOBDIR[12] = 0; KNOBDIR[13] = Mu; KNOBDIR[14] = -Mu; KNOBDIR[15] = 0; } // ------------------------ TM1637 ------------------------ void bitDelay(void) { _delay_us(DEFAULT_BIT_DELAY); } void start(void) { DDRC |= (1 << DIO_PIN); bitDelay(); } void stop(void) { DDRC |= (1 << DIO_PIN); bitDelay(); DDRB &= ~(1 << CLK_PIN); bitDelay(); DDRC &= ~(1 << DIO_PIN); bitDelay(); } bool writeByte(uint8_t b) { for (uint8_t i = 0; i < 8; i++) { DDRB |= (1 << CLK_PIN); bitDelay(); if (b & 0x01) DDRC &= ~(1 << DIO_PIN); else DDRC |= (1 << DIO_PIN); bitDelay(); DDRB &= ~(1 << CLK_PIN); bitDelay(); b >>= 1; } DDRB |= (1 << CLK_PIN); DDRC &= ~(1 << DIO_PIN); bitDelay(); DDRB &= ~(1 << CLK_PIN); bitDelay(); uint8_t ack = PINC & (1 << DIO_PIN); if (ack == 0) DDRC |= (1 << DIO_PIN); bitDelay(); DDRB |= (1 << CLK_PIN); bitDelay(); return ack; } void setBrightness(uint8_t brightness, bool on) { (void)brightness; (void)on; // можно доработать при необходимости } void setSegments(const uint8_t segments[], uint8_t length, uint8_t pos) { start(); writeByte(TM1637_I2C_COMM1); stop(); start(); writeByte(TM1637_I2C_COMM2 + (pos & 0x03)); for (uint8_t k = 0; k < length; k++) writeByte(segments[k]); stop(); start(); writeByte(TM1637_I2C_COMM3 + (0x0f & 0x0f)); stop(); } void clear(void) { uint8_t data[] = { 0, 0, 0, 0 }; setSegments(data, 4, 0); } void shownumDec(int num, bool leading_zero, uint8_t length, uint8_t pos) { shownumDecEx(num, 0, leading_zero, length, pos); } void shownumDecEx(int num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos) { shownumBaseEx(num < 0 ? -10 : 10, num < 0 ? -num : num, dots, leading_zero, length, pos); } void shownumHexEx(uint16_t num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos) { shownumBaseEx(16, num, dots, leading_zero, length, pos); } void shownumBaseEx(int8_t base, uint16_t num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos) { bool negative = false; if (base < 0) { base = -base; negative = true; } uint8_t digits[4]; if (num == 0 && !leading_zero) { for (uint8_t i = 0; i < (length - 1); i++) digits[i] = 0; digits[length - 1] = digitToSegment[0]; } else { for (int i = length - 1; i >= 0; --i) { uint8_t digit = num % base; if (digit == 0 && num == 0 && !leading_zero) digits[i] = 0; else digits[i] = digitToSegment[digit & 0x0f]; if (digit == 0 && num == 0 && negative) { digits[i] = minusSegments; negative = false; } num /= base; } if (dots != 0) showDots(dots, digits); } setSegments(digits, length, pos); } void showDots(uint8_t dots, uint8_t* digits) { for (int i = 0; i < 4; ++i) { digits[i] |= (dots & 0x80); dots <<= 1; } } void showEights(void) { uint8_t segments[6] = {0}; for (uint8_t i = 0; i < 4; i++) { segments[i] = digitToSegment[8]; } segments[4] = digitToSegment[14]; segments[5] = digitToSegment[14]; setSegments(segments, 6, 0); } // ------------------------ MAX6675 ------------------------ void init_MAX_pins(void) { MAX6675_SO_DDR &= ~(1 << MAX6675_SO_BIT); // вход MAX6675_SO_PORT &= ~(1 << MAX6675_SO_BIT); // без подтяжки MAX6675_SCK_DDR |= (1 << MAX6675_SCK_BIT); MAX6675_SCK_PORT &= ~(1 << MAX6675_SCK_BIT); MAX6675_CS_DDR |= (1 << MAX6675_CS_BIT); if (MAX6675_CS_INACTIVE_LEVEL) MAX6675_CS_PORT |= (1 << MAX6675_CS_BIT); else MAX6675_CS_PORT &= ~(1 << MAX6675_CS_BIT); } uint16_t read_max6675(void) { if (max6675_busy) return 0; // Значение ошибки, если занято max6675_busy = true; uint16_t data = 0; if (MAX6675_CS_INACTIVE_LEVEL) MAX6675_CS_PORT &= ~(1 << MAX6675_CS_BIT); else MAX6675_CS_PORT |= (1 << MAX6675_CS_BIT); _delay_us(10); for (uint8_t i = 0; i < 16; i++) { MAX6675_SCK_PORT |= (1 << MAX6675_SCK_BIT); _delay_us(1); data <<= 1; if (MAX6675_SO_PIN & (1 << MAX6675_SO_BIT)) { data |= 1; } MAX6675_SCK_PORT &= ~(1 << MAX6675_SCK_BIT); _delay_us(1); } if (MAX6675_CS_INACTIVE_LEVEL) MAX6675_CS_PORT |= (1 << MAX6675_CS_BIT); else MAX6675_CS_PORT &= ~(1 << MAX6675_CS_BIT); max6675_busy = false; return data; } float get_temperature(void) { uint16_t data = read_max6675(); if (data & (1 << 2)) { return -1; } if (data & (1 << 1)) { return -2; } data >>= 3; float temp = (data * 0.25f) * (41.0f / 55.0f); temp += 25.3f; return temp; } void print_temperature(float temp) { } // ------------------------ Логика реле и кнопок ------------------------ void comeOnBabyLightMyFire(void) { KNC_count++; vp_array[6].dirty = 1; // Пометить KNC_count для отправки Timer3 = number; uint8_t segments[] = { 0, 0, 0, SEG_P }; setSegments(segments, 4, 0); milliseconds3 = 0; PORTD |= (1 << R2); while (milliseconds3 < Timer3) { } PORTD &= ~(1 << R2); exitFlag = 0; PORTD |= (1 << R1); milliseconds3 = 0; while (milliseconds3 < chDelay) { } PORTD &= ~(1 << R1); } void buttonCheck(void) { sei(); if ((PIND & (1 << KNC))) { if (!buttonKNC_STATE) { buttonKNC_STATE = true; comeOnBabyLightMyFire(); } } else { if (milliseconds4 > Timer4) { buttonKNC_STATE = false; } } bool currentButtonState = !(PINC & (1 << DisplayBUTTON)); if (currentButtonState != buttonPressed) { lastDebounceTime = millis(); } if ((millis() - lastDebounceTime) > debounceDelay) { if (currentButtonState != buttonWasPressed) { buttonWasPressed = currentButtonState; if (buttonWasPressed) { uint8_t oldMode = displayMODE; displayMODE = (displayMODE + 1) % 4; if (oldMode != displayMODE) { Mu = 1; updateKnobDir(); tickCount = 0; lastTickCount = 0; tickFlag = false; milliseconds2 = 0; switch (displayMODE) { case 0: newPos = number; break; case 2: newPos = chDelay; break; case 3: newPos = Airflow; break; default: break; } pos = newPos; position = (long)newPos * 4; } switch (displayMODE) { case 0: shiftOut(0b00000010); break; case 1: shiftOut(0b00000100); break; case 2: shiftOut(0b00001000); break; case 3: shiftOut(0b00010000); break; } writeToFRAM(); } } } buttonPressed = currentButtonState; } void EN(void) { if (displayMODE == 0 || displayMODE == 2 || displayMODE == 3) { tickFlag = true; tickCount++; rotary_encoder_tick(); newPos = rotary_encoder_get_position(); if (pos != newPos) { switch (displayMODE) { case 0: if (position <= 0) { position = 0; newPos = 0; tickFlag = false; Mu = 1; } else if (position >= (long)9999 * 4) { position = (long)9999 * 4; newPos = 9999; tickFlag = false; Mu = 1; } number = newPos; vp_array[0].dirty = 1; // Пометить number для отправки break; case 2: if (position <= 0) { position = 0; newPos = 0; tickFlag = false; Mu = 1; } else if (position >= (long)9999 * 4) { position = (long)9999 * 4; newPos = 9999; tickFlag = false; Mu = 1; } chDelay = newPos; vp_array[1].dirty = 1; // Пометить chDelay для отправки break; case 3: if (position <= 0) { position = 0; newPos = 0; tickFlag = false; Mu = 1; } else if (position >= (long)9999 * 4) { position = (long)9999 * 4; newPos = 9999; tickFlag = false; Mu = 1; } Airflow = newPos; vp_array[3].dirty = 1; // Пометить Airflow для отправки break; } pos = newPos; } } } // ------------------------ Утилиты ------------------------ char* utoa(unsigned int value, char* str, int base) { char* rc; char* ptr; char* low; static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz"; if (base < 2 || base > 36) { *str = '\0'; return str; } rc = ptr = str; low = ptr; do { *ptr++ = digits[value % base]; value /= base; } while (value); *ptr-- = '\0'; while (low < ptr) { char tmp = *low; *low++ = *ptr; *ptr-- = tmp; } return rc; } char* itoa(int value, char* buffer, int base) { if (base < 2 || base > 36) { *buffer = '\0'; return buffer; } char* ptr = buffer, *ptr1 = buffer, tmp_char; int tmp_value; if (value < 0 && base == 10) { *ptr++ = '-'; ptr1++; value = -value; } tmp_value = value; do { int remainder = tmp_value % base; *ptr++ = (remainder < 10) ? (remainder + '0') : (remainder - 10 + 'a'); } while (tmp_value /= base); *ptr-- = '\0'; while (ptr1 < ptr) { tmp_char = *ptr; *ptr-- = *ptr1; *ptr1++ = tmp_char; } return buffer; } uint32_t millis(void) { uint32_t ms; cli(); ms = milliseconds1; sei(); return ms; } // ------------------------ Таймеры ------------------------ void setupTimer0(void) { TCCR0A = (1 << WGM01); TCCR0B = (1 << CS01) | (1 << CS00); OCR0A = 249; TIMSK0 = (1 << OCIE0A); } void setupTimer1(void) { TCCR1A = 0; TCCR1B = (1 << WGM12) | (1 << CS12); OCR1A = 62499; TIMSK1 |= (1 << OCIE1A); } // ------------------------ DWIN: запись VP и страницы ------------------------ static void dwin_write16(uint16_t vp_addr, uint16_t value) { uint8_t pkt[8]; pkt[0] = 0x5A; pkt[1] = 0xA5; pkt[2] = 0x05; pkt[3] = 0x82; pkt[4] = (uint8_t)(vp_addr >> 8); pkt[5] = (uint8_t)(vp_addr & 0xFF); pkt[6] = (uint8_t)(value >> 8); pkt[7] = (uint8_t)(value & 0xFF); uart_send_buf((const char *)pkt, sizeof(pkt)); } void dwin_set_page(uint8_t page) { if (page > 8) page = 8; uint8_t pkt[10] = {0x5A, 0xA5, 0x07, 0x82, 0x00, 0x84, 0x5A, 0x01, 0x00, page}; uart_send_buf((const char *)pkt, sizeof(pkt)); } // ------------------------ IO / Прерывания / WDT ------------------------ void io_init(void) { //RS485_DE_DDR |= (1 << RS485_DE_PIN); // PC0 output //RS485_DE_PORT &= ~(1 << RS485_DE_PIN); // default: receive DDRD |= (1 << R1) | (1 << R2); PORTD &= ~((1 << R1) | (1 << R2)); DDRC &= ~(1 << DisplayBUTTON); PORTC |= (1 << DisplayBUTTON); DDRD &= ~(1 << KNC); DDRC &= ~(1 << SHOW_EIGHTS_BUTTON); PORTC |= (1 << SHOW_EIGHTS_BUTTON); DDRB |= (1 << CLK_PIN); DDRC |= (1 << DIO_PIN); init_MAX_pins(); DDRB |= (1 << PB4); DDRC |= (1 << PC1) | (1 << PC2); PORTB &= ~(1 << PB4); PORTC &= ~((1 << PC1) | (1 << PC2)); DDRD &= ~((1 << PD0) | (1 << PD1)); PORTD &= ~((1 << PD0) | (1 << PD1)); } void interrupts_init(void) { setupTimer0(); setupTimer1(); PCICR |= (1 << PCIE0); PCMSK0 |= (1 << PCINT1) | (1 << PCINT2); PCICR |= (1 << PCIE1); PCMSK1 |= (1 << PCINT11); sei(); } void WDT_init(void) { wdt_enable(WDTO_2S); } // ------------------------ Точка старта ------------------------ void setup(void) { io_init(); interrupts_init(); updateKnobDir(); UART_init(MYUBRR); rotary_encoder_init(); pos = number; newPos = number; position = (long)number * 4; setBrightness(0x0f, true); WDT_init(); shiftOut(0b00000010); readFromFRAM(); resetCount++; vp_array[5].dirty = 1; // Пометить resetCount для отправки pos = number; newPos = number; position = (long)number * 4; writeToFRAM(); dwin_set_page(0); for (uint8_t i = 0; i < VP_COUNT; i++) { if (vp_array[i].value != NULL) { dwin_write16(vp_array[i].vp_addr, *(vp_array[i].value)); } } } void loop(void) { static unsigned long lastFramWrite = 0; static unsigned long lastTestSend = 0; while (1) { wdt_reset(); buttonCheck(); switch (displayMODE) { case 0: shownumDec(number, false, 4, 0); break; case 1: shownumDec(KNC_count, false, 4, 0); break; case 2: shownumDec(chDelay, false, 4, 0); break; case 3: shownumDec(Airflow, false, 4, 0); break; } if (tickFlag == true) { if (millis() - lastFramWrite > 500) { writeToFRAM(); lastFramWrite = millis(); } tickFlag = false; milliseconds2 = 0; } if (tickCount > 80) { if (milliseconds2 < 200) { Mu = Mu * 10; if (Mu > 100) { Mu = 100; } updateKnobDir(); tickCount = 0; milliseconds2 = 0; } } if (milliseconds2 > 150) { milliseconds2 = 0; if (tickCount == lastTickCount) { tickFlag = false; tickCount = 0; Mu = 1; updateKnobDir(); } lastTickCount = tickCount; } for (uint8_t i = 0; i < VP_COUNT; i++) { if (vp_array[i].dirty && vp_array[i].value != NULL) { dwin_write16(vp_array[i].vp_addr, *(vp_array[i].value)); vp_array[i].dirty = 0; } } //send_buffered_data(); // Debug: Периодическая отправка команды для провоцирования ответа от DWIN (каждые 1 сек) if (millis() - lastTestSend > 1000) { dwin_set_page(0); // Простая команда lastTestSend = millis(); } } } int main(void) { setup(); loop(); return 0; } // ------------------------ Прерывания ------------------------ ISR(TIMER0_COMPA_vect) { milliseconds1++; milliseconds2++; milliseconds3++; milliseconds4++; milliseconds5++; Decrease++; Increase++; } ISR(TIMER1_COMPA_vect) { wdt_reset(); } ISR(PCINT0_vect) { EN(); } ISR(PCINT1_vect) { float temp = get_temperature(); heatInCamera = (uint16_t)temp; vp_array[2].dirty = 1; // Пометить heatInCamera для отправки writeToFRAM(); } ISR(USART_RX_vect) { uint8_t data = UDR0; uint8_t next_head = (rx_head + 1) % RX_BUFFER_SIZE; if (next_head != rx_tail) { // Проверяем, не переполнен ли буфер rx_buffer[rx_head] = data; rx_head = next_head; } }