ЭКСПЕРИМЕНТАЛЬНАЯ СТАТЬЯ

 

#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;
    }

}