KY-040 |
Энкодеры KY-040 часто применяют в различных проектах. KY-040 как правило выполнен в виде модуля, который состоит из небольшой платы в которую установлен энкодер и 3 подтягивающих (к +5 В) резистора номиналом 10 кОм. При вращении ручки модуля мы получаем два сигнала (A и B), которые противоположны по фазе. Каждый раз, когда сигнал А переходит от положительного уровня к нулю, мы считываем значение выхода В. Если В в этот момент находится в положительном состоянии, значит энкодер вращается по часовой стрелке, если В нуль, то энкодер вращается против часовой стрелки. Считывая оба выхода, можно определить направление вращения, и при помощи подсчета импульсов с А выхода — угол поворота.
Что бы безошибочно определять угол поворота и направление вращения необходимо постоянного опрашивать (считывать) состояние выходов A и B (CLK и DT) энкодера, при этом содержимое скетча не должно влиять на период опроса. Этого можно добиться двумя путями, использовать внешние прерывания или при помощи таймера.
Для считывания показаний энкодера лучше использовать внешнее прерывание, так как таймер может понадобится для других целей. Для управления энкодером можно использовать входы ATtiny2313 для внешнего прерывания PCINT0…7. (Работа с прерываниями PCINT0…7)
Ниже показан простой скетч который позволяет осуществлять чтение данных энкодера, в качестве индикатора будет использован модуль индикатора TM1637 который представляет собой 4-х разрядный семисегментный дисплей на базе драйвера TM1637. На индикатор будут выводится показания энкодера.
Для проверки работы энкодера необходимо собрать следующую схему:
Перед загрузкой скетча рекомендую ознакомится со статьей — ATtiny2313 + Arduino IDE
volatile uint8_t _prevValueAB = 0; volatile uint8_t _currValueAB = 0; volatile int16_t _counter = 0; int position = -999; int encoder; void setup(){ DDRB &= ~(1 << 2) | (1 << 3); GIMSK |= (1 << PCIE); PCMSK |= (1 << PCINT2) | (1 << PCINT3); } void loop() { if(getPosition() != position){ position = getPosition(); encoder = encoder+getPosition();setPosition(0);} if(encoder>=0)print_time(encoder, 0, 7, 0); if(encoder<0)print_time(abs(encoder), 0, 7, 2); } //////// encoder //////////////////////// ISR(PCINT_vect){ bool pinA = ((PINB >> 2) & 1); bool pinB = ((PINB >> 3) & 1); _currValueAB = pinA << 1; _currValueAB |= pinB; switch ((_prevValueAB | _currValueAB)){ case 0b0001: _counter++;break; case 0b0100: _counter--;break; } _prevValueAB = _currValueAB << 2; } int16_t getPosition(){ return _counter; } void setPosition(int16_t position){ _counter = position; } //////////// tm /////////////////// void tm_dec(byte dig) { for (int i = 0; i < 8; i++) { DDRB |= (1 << 0); del(); if (dig & 0x01) DDRB &= ~(1 << 1); else DDRB |= (1 << 1); del();DDRB &= ~(1 << 0); del(); dig = dig >> 1;} DDRB |= (1 << 0); DDRB &= ~(1 << 1); del(); DDRB &= ~(1 << 0); del(); if (((PINB >> 1) & 1) == 0) DDRB |= (1 << 1); del();DDRB |= (1 << 0); del(); } void tm_stop() { DDRB |= (1 << 1); del(); DDRB &= ~(1 << 0); del(); DDRB &= ~(1 << 1); del(); } void tm_start() { DDRB |= (1 << 1); del(); } void print_time(int t, byte pd_t, int br, byte mn) { tm_start(); tm_dec(0b10001000 + br); //tm_stop();tm_start(); tm_dec(0x40); tm_stop(); tm_start(); int data0; if(mn==1){data0 = 10;} else if(mn==2){data0 = 11;} else data0 = t / 1000; int data1 = t / 100 % 10; int data2 = t / 10 % 10; int data3 = t % 10; for (byte n = 0; n < 4; n++) { int data; switch (n) { case 0: data = data0; break; case 1: data = data1; break; case 2: data = data2; break; case 3: data = data3; break; } switch (data) { // XGFEDCBA case 0: data = 0b00111111; break; // 0 case 1: data = 0b00000110; break; // 1 case 2: data = 0b01011011; break; // 2 case 3: data = 0b01001111; break; // 3 case 4: data = 0b01100110; break; // 4 case 5: data = 0b01101101; break; // 5 case 6: data = 0b01111101; break; // 6 case 7: data = 0b00000111; break; // 7 case 8: data = 0b01111111; break; // 8 case 9: data = 0b01101111; break; // 9 case 10: data = 0b00000000; break; // пусто case 11: data = 0b01000000; break; // - } if (n == 0) {data0 = data;} if (n == 1) {data1 = data;} if (n == 2) {data2 = data;} if (n == 3) {data3 = data;} } switch (pd_t) { case 1 : data2 = data2 + 0b10000000; break; case 2 : data1 = data1 + 0b10000000; break; case 3 : data0 = data0 + 0b10000000; break; } tm_dec(0xC0); tm_dec(data0); tm_dec(data1); tm_dec(data2); tm_dec(data3); tm_stop(); } void del() {delayMicroseconds(200);}
Для считывания показаний энкодера используются входы внешнего прерывания PCINT2 и PCINT3, что соответствует входам микроконтроллера PB2 и PB3:
PCMSK |= (1 << PCINT2) | (1 << PCINT3);
Обработчик прерывания PCINT_vect при повороте ручки энкодера считывает логический уровень выходов энкодера CLK и DT, получая два сигнала (A и B).
Функция getPosition() выдает показания энкодера, а функция setPosition(int16_t position) позволяет установить любое первоначальное значение показаний энкодера.
Для примера использования энкодера на базе микроконтроллера ATtiny2313 можно собрать простой терморегулятор. В терморегуляторе в качестве датчика используется цифровой датчик температуры DS18B20, энкодер позволяет изменять температуру регулирования.
Основные параметры терморегулятора:
- Диапазон измеряемых температур от 0 до 125 °С
- Диапазон регулирования температуры от 0 до 125 °С
- Интервал измерения температуры 5 секунд
- Гистерезис 0,1 °С (можно изменить в скетче)
- Шаг регулировки и измерения температуры 0,1 °С
При включении терморегулятора на индикатор в течении 5 секунд выводится температура регулирования, далее выводится измеряемая температура. Если нажать на кнопку энкодера, то снова на 5 секунд выводятся показания температуры регулирования, если повернуть ручку энкодера, то можно изменить температуру регулирования. Температура регулирования заносится в энергонезависимую память. Выход микроконтроллера PD5 используется для управления нагревательным элементом.
byte bb,w; volatile uint8_t _prevValueAB = 0; volatile uint8_t _currValueAB = 0; volatile int16_t _counter = 0; int position = -999; int t_reg,t_dig; unsigned long time_i=0,time_i2=0,time_i3=0; const int gis = 1;//0.1 гр. Цельсия void setup(){ cli(); ////// timer 1 ////////// TCCR1A = 0; TCCR1B = 0; OCR1A = 18750; // 0.1 s TCCR1B |= (1 << WGM12); TCCR1B |= (1 << CS11) | (1 << CS10); // 64 TIMSK |= (1 << OCIE1A); ////////////////////////// DDRB &= ~(1 << 2) | (1 << 3); DDRD &= ~(1 << 6); DDRD |= (1 << 5); GIMSK |= (1 << PCIE); PCMSK |= (1 << PCINT2) | (1 << PCINT3); t_reg = EEPROM_read(0)*10+EEPROM_read(1); sei(); } void loop() { if(((PIND >> 6) & 1) == 0){time_i2=0;time_i3=0;} if(getPosition() != position){ position = getPosition(); t_reg = t_reg+getPosition();setPosition(0);w=1;time_i=0;time_i2=0;time_i3=0; if(t_reg<0){t_reg=0;}if(t_reg>1250){t_reg=1250;}} if(time_i2<=50){ if(t_reg>999){print_time(t_reg, 1, 7, 0);}else{print_time(t_reg, 1, 7, 1);}} if(time_i3>50){t_dig = read_temp();time_i3=0;} if(w==1&&time_i>50){w=0;EEPROM_write(0,t_reg/10);EEPROM_write(1,t_reg%10);delay(100);} if(time_i2>50){ if(t_dig>999){print_time(t_dig, 1, 7, 0);}else{print_time(t_dig, 1, 7, 1);}} if(t_reg >= t_dig + gis){PORTD |= (1 << 5);} if(t_reg <= t_dig - gis){PORTD &= ~(1 << 5);} } //////// encoder //////////////////////// ISR(PCINT_vect){ bool pinA = ((PINB >> 2) & 1); bool pinB = ((PINB >> 3) & 1); _currValueAB = pinA << 1; _currValueAB |= pinB; switch ((_prevValueAB | _currValueAB)){ case 0b0001: _counter++;break; case 0b0100: _counter--;break; } _prevValueAB = _currValueAB << 2; } int16_t getPosition(){ return _counter; } void setPosition(int16_t position){ _counter = position; } //////////// tm /////////////////// void tm_dec(byte dig) { for (int i = 0; i < 8; i++) { DDRB |= (1 << 0); del(); if (dig & 0x01) DDRB &= ~(1 << 1); else DDRB |= (1 << 1); del();DDRB &= ~(1 << 0); del(); dig = dig >> 1;} DDRB |= (1 << 0); DDRB &= ~(1 << 1); del(); DDRB &= ~(1 << 0); del(); if (((PINB >> 1) & 1) == 0) DDRB |= (1 << 1); del();DDRB |= (1 << 0); del(); } void tm_stop() { DDRB |= (1 << 1); del(); DDRB &= ~(1 << 0); del(); DDRB &= ~(1 << 1); del(); } void tm_start() { DDRB |= (1 << 1); del(); } void print_time(int t, byte pd_t, int br, byte mn) { tm_start(); tm_dec(0b10001000 + br); //tm_stop();tm_start(); tm_dec(0x40); tm_stop(); tm_start(); int data0; if(mn==1){data0 = 10;} else if(mn==2){data0 = 11;} else data0 = t / 1000; int data1 = t / 100 % 10; int data2 = t / 10 % 10; int data3 = t % 10; for (byte n = 0; n < 4; n++) { int data; switch (n) { case 0: data = data0; break; case 1: data = data1; break; case 2: data = data2; break; case 3: data = data3; break; } switch (data) { // XGFEDCBA case 0: data = 0b00111111; break; // 0 case 1: data = 0b00000110; break; // 1 case 2: data = 0b01011011; break; // 2 case 3: data = 0b01001111; break; // 3 case 4: data = 0b01100110; break; // 4 case 5: data = 0b01101101; break; // 5 case 6: data = 0b01111101; break; // 6 case 7: data = 0b00000111; break; // 7 case 8: data = 0b01111111; break; // 8 case 9: data = 0b01101111; break; // 9 case 10: data = 0b00000000; break; // пусто case 11: data = 0b01000000; break; // - } if (n == 0) {data0 = data;} if (n == 1) {data1 = data;} if (n == 2) {data2 = data;} if (n == 3) {data3 = data;} } switch (pd_t) { case 1 : data2 = data2 + 0b10000000; break; case 2 : data1 = data1 + 0b10000000; break; case 3 : data0 = data0 + 0b10000000; break; } tm_dec(0xC0); tm_dec(data0); tm_dec(data1); tm_dec(data2); tm_dec(data3); tm_stop(); } void del() {delayMicroseconds(200);} ////////// eeprom //////////////////// unsigned char EEPROM_read(unsigned int uiAddress){ while(EECR & (1<<EEPE)); EEAR = uiAddress; EECR |= (1<<EERE); return EEDR; } void EEPROM_write(unsigned int uiAddress, unsigned char ucData){ while(EECR & (1<<EEPE)); EEAR = uiAddress; EEDR = ucData; EECR |= (1<<EEMPE); EECR |= (1<<EEPE); } //////////// timer 1 ///////////// ISR(TIMER1_COMPA_vect){time_i++; time_i2++;time_i3++;} ///////////// 18b20 //////////////// uint8_t therm_reset(){ uint8_t i; PORTB &= ~(1 << 4); DDRB |= (1 << 4); delayMicroseconds(480); DDRB &= ~(1 << 4); delayMicroseconds(60); i=((PINB >> 4) & 1); delayMicroseconds(420); return i; } // write bit void therm_write_bit(uint8_t bit){ PORTB &= ~(1 << 4); DDRB |= (1 << 4); delayMicroseconds(1); if(bit) DDRB &= ~(1 << 4); delayMicroseconds(60); DDRB &= ~(1 << 4); } // read bit uint8_t therm_read_bit(void){ uint8_t bit=0; PORTB &= ~(1 << 4); DDRB |= (1 << 4); delayMicroseconds(1); DDRB &= ~(1 << 4); delayMicroseconds(14); if(PINB & (1 << 4)) bit=1; delayMicroseconds(45); return bit; } // read byte uint8_t therm_read_byte(void){ uint8_t i=8, n=0; while(i--){n>>=1;n|=(therm_read_bit()<<7);} return n; } // write byte void therm_write_byte(uint8_t byte){ uint8_t i=8; while(i--){therm_write_bit(byte&1);byte >>= 1; } } // read temp int read_temp(){ uint8_t temperature[2]; int temper; therm_reset(); therm_write_byte(0xCC); therm_write_byte(0x44); while(!therm_read_bit()); therm_reset(); therm_write_byte(0xCC); therm_write_byte(0xBE); temperature[0]=therm_read_byte(); temperature[1]=therm_read_byte(); therm_reset(); temper = (temperature[1] << 8 | temperature[0])*10/16; return temper; }
Скетч использует 1802 байт (87%) памяти устройства. Всего доступно 2048 байт.
Глобальные переменные используют 32 байт (25%) динамической памяти, оставляя 96 байт для локальных переменных. Максимум: 128 байт.Версия платы в Arduino IDE 1.2.5