КРУТИЛКА ЭНКОДЕРА И СЕМИСЕГМЕНТНЫЙ ИНДИКАТОР НА ОСНОВЕ МИКРОСХЕМЫ TM1637
/* * TM1637 Control by encoder program * С version * https://madmentat.ru * 2024 */ #define F_CPU 16000000UL // Определяем частоту процессора как 16 МГц #include <avr/io.h> #include <util/delay.h> #include <stdbool.h> #include <avr/interrupt.h> #define CLK_PIN PC0 #define DIO_PIN PC1 #define BUTTON_PIN PD2 #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 DEFAULT_BIT_DELAY 100 #define TM1637_I2C_COMM1 0x40 #define TM1637_I2C_COMM2 0xC0 #define TM1637_I2C_COMM3 0x80 // Настройки UART #define BAUD 9600 #define MYUBRR F_CPU/16/BAUD-1 // Define encoder pins #define ENCODER_PIN1 PB1 #define ENCODER_PIN2 PB2 #define LATCH0 0 // input state at position 0 #define LATCH3 3 // input state at position 3 long pos = 0; long newPos; char buffer[16]; // Буфер для хранения строки // Rotary encoder variables volatile int8_t oldState; volatile long position; volatile long positionExt; volatile long positionExtPrev; unsigned long positionExtTime; unsigned long positionExtTimePrev; // Global millisecond counter volatile unsigned long milliseconds = 0; const int8_t KNOBDIR[] = { 0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0 }; int number = 1982; // Начальное число // Преобразование числа в строку 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) { value = -value; *ptr++ = '-'; } tmp_value = value; do { int remainder = tmp_value % base; *ptr++ = (remainder < 10) ? (remainder + '0') : (remainder - 10 + 'a'); } while (tmp_value /= base); if (*buffer == '-') { ptr1++; } *ptr-- = '\0'; while (ptr1 < ptr) { tmp_char = *ptr; *ptr-- = *ptr1; *ptr1++ = tmp_char; } return buffer; } // Функция для инициализации UART void UART_init(unsigned int ubrr) { // Устанавливаем скорость передачи UBRR0H = (unsigned char)(ubrr >> 8); UBRR0L = (unsigned char)ubrr; // Разрешаем передачу UCSR0B = (1 << TXEN0); // Устанавливаем формат кадра: 8 бит данных, 1 стоп-бит UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); } // Функция для передачи одного байта данных void UART_transmit(unsigned char data) { // Ожидаем, пока освободится буфер передачи while (!(UCSR0A & (1 << UDRE0))); // Помещаем данные в буфер UDR0 = data; } // Функция для передачи строки void UART_putstring(const char *s) { while (*s) { UART_transmit(*s++); } } // Millis function unsigned long millis() { unsigned long ms; // Disable interrupts temporarily to prevent variable corruption cli(); ms = milliseconds; sei(); return ms; } // Initialize rotary encoder void rotary_encoder_init() { // Set encoder pins as inputs with pull-ups DDRB &= ~(1 << ENCODER_PIN1); // Set PB1 as input DDRB &= ~(1 << ENCODER_PIN2); // Set PB2 as input PORTB |= (1 << ENCODER_PIN1); // Enable pull-up on PB1 PORTB |= (1 << ENCODER_PIN2); // Enable pull-up on PB2 // Initialize state uint8_t sig1 = PINB & (1 << ENCODER_PIN1); uint8_t sig2 = PINB & (1 << ENCODER_PIN2); oldState = (sig1 >> ENCODER_PIN1) | ((sig2 >> ENCODER_PIN2) << 1); // Initialize position position = 0; positionExt = 0; positionExtPrev = 0; positionExtTime = 0; positionExtTimePrev = 0; } // Read the encoder and update position void rotary_encoder_tick() { 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 (oldState != thisState) { position += KNOBDIR[thisState | (oldState << 2)]; oldState = thisState; if (thisState == LATCH0 || thisState == LATCH3) { positionExt = position >> 2; positionExtTimePrev = positionExtTime; positionExtTime = millis(); } } } // Get the current position long rotary_encoder_get_position() { return positionExt; } // Get time between rotations unsigned long rotary_encoder_get_millis_between_rotations() { return (positionExtTime - positionExtTimePrev); } // Get RPM unsigned long rotary_encoder_get_rpm() { unsigned long timeBetweenLastPositions = positionExtTime - positionExtTimePrev; unsigned long timeToLastPosition = millis() - positionExtTime; unsigned long t = (timeBetweenLastPositions > timeToLastPosition) ? timeBetweenLastPositions : timeToLastPosition; return 60000UL / (t * 20); } const uint8_t digitToSegment[] = { 0b00111111, // 0 0b00000110, // 1 0b01011011, // 2 0b01001111, // 3 0b01100110, // 4 0b01101101, // 5 0b01111101, // 6 0b00000111, // 7 0b01111111, // 8 0b01101111, // 9 0b01110111, // A 0b01111100, // b 0b00111001, // C 0b01011110, // d 0b01111001, // E 0b01110001 // F }; static const uint8_t minusSegments = 0b01000000; 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 showNumberDec(int num, bool leading_zero, uint8_t length, uint8_t pos); void showNumberDecEx(int num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos); void showNumberHexEx(uint16_t num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos); void showNumberBaseEx(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 bitDelay(void) { _delay_us(DEFAULT_BIT_DELAY); } void start(void) { DDRC |= (1 << DIO_PIN); // Установить DIO_PIN как выход bitDelay(); } void stop(void) { DDRC |= (1 << DIO_PIN); // Установить DIO_PIN как выход bitDelay(); DDRC &= ~(1 << CLK_PIN); // Установить CLK_PIN как вход bitDelay(); DDRC &= ~(1 << DIO_PIN); // Установить DIO_PIN как вход bitDelay(); } bool writeByte(uint8_t b) { uint8_t data = b; for (uint8_t i = 0; i < 8; i++) { DDRC |= (1 << CLK_PIN); // Установить CLK_PIN как выход bitDelay(); if (data & 0x01) DDRC &= ~(1 << DIO_PIN); // Установить DIO_PIN как вход else DDRC |= (1 << DIO_PIN); // Установить DIO_PIN как выход bitDelay(); DDRC &= ~(1 << CLK_PIN); // Установить CLK_PIN как вход bitDelay(); data >>= 1; } DDRC |= (1 << CLK_PIN); // Установить CLK_PIN как выход DDRC &= ~(1 << DIO_PIN); // Установить DIO_PIN как вход bitDelay(); DDRC &= ~(1 << CLK_PIN); // Установить CLK_PIN как вход bitDelay(); uint8_t ack = PINC & (1 << DIO_PIN); if (ack == 0) DDRC |= (1 << DIO_PIN); // Установить DIO_PIN как выход bitDelay(); DDRC |= (1 << CLK_PIN); // Установить CLK_PIN как выход bitDelay(); return ack; } void setBrightness(uint8_t brightness, bool on) { //uint8_t m_brightness = (brightness & 0x7) | (on ? 0x08 : 0x00); // Здесь можно добавить код для применения яркости } 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 showNumberDec(int num, bool leading_zero, uint8_t length, uint8_t pos) { showNumberDecEx(num, 0, leading_zero, length, pos); } void showNumberDecEx(int num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos) { showNumberBaseEx(num < 0 ? -10 : 10, num < 0 ? -num : num, dots, leading_zero, length, pos); } void showNumberHexEx(uint16_t num, uint8_t dots, bool leading_zero, uint8_t length, uint8_t pos) { showNumberBaseEx(16, num, dots, leading_zero, length, pos); } void showNumberBaseEx(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 setup() { UART_init(MYUBRR); // Инициализация UART rotary_encoder_init(); // Инициализация энкодера // Установить начальное значение position для начала с 1982 pos = 0; newPos = 1982; position = 1982 * 4; // Если каждый шаг энкодера соответствует одному числу // Настройка таймера для millis() TCCR0A = (1 << WGM01); // CTC режим TCCR0B = (1 << CS01) | (1 << CS00); // Предделитель 64 OCR0A = 249; // Установка значения для сравнения, чтобы прерывание срабатывало каждую 1 мс на 16 МГц TIMSK0 = (1 << OCIE0A); // Включение прерывания таймера sei(); // Разрешение глобальных прерываний // Установка пинов для CLK и DIO как выходы DDRC |= (1 << CLK_PIN) | (1 << DIO_PIN); // Установка пина кнопки как вход с подтяжкой DDRD &= ~(1 << BUTTON_PIN); // Установить PD2 как вход PORTD |= (1 << BUTTON_PIN); // Включить внутренний подтягивающий резистор на PD2 setBrightness(0x0f, true); // Установка максимальной яркости // Обновление дисплея начальным значением showNumberDec(newPos, true, 4, 0); // Отображение начального числа с ведущими нулями // Вывод начального значения на UART itoa(newPos, buffer, 10); UART_putstring(buffer); UART_putstring("\n"); } void loop() { //static bool buttonPressed = false; // Для отслеживания состояния кнопки while (1) { // Обновление позиции энкодера rotary_encoder_tick(); newPos = rotary_encoder_get_position(); // Получение текущей позиции // Ограничение значения if (newPos < 0) newPos = 0; if (newPos > 9999) newPos = 9999; // Проверка на изменение позиции if (pos != newPos) { pos = newPos; number = newPos; // Теперь `number` и `pos` синхронизированы showNumberDec(number, true, 4, 0); // Обновление дисплея itoa(number, buffer, 10); // Вывод значения в UART UART_putstring(buffer); UART_putstring("\n"); } } } int main(void) { setup(); loop(); return 0; } // Timer0 Compare Match A ISR ISR(TIMER0_COMPA_vect) { // Increment the millisecond counter milliseconds++; }