ATmega8/16: шим, управляемый энкодером
- Сообщения: 2466
- Зарегистрирован: Сб май 07, 2011 17:52:59
Здравствуйте! Я полнейший лохопэт в мк и в Си. Есть Атмел Студио и Протеус на пц. Отладочных плат и места , чтоб это всё запустить пока нет. Необходимо в симуляторе как-то выполнить такую задачку: два инкрементальных энкодера с кнопками должны управлять двумя каналами таймеров-счётчиков Т0 и Т1 в режиме шим. Диапазон заполнения шим сигнала должен плавно меняться от 0 до 100% вращением энкодера по часовой и обратно от 100% до 0 с шагом 0.25...1%. Блокировка и разблокировка энкодеров кнопками на оси. Запомнить заполнение при отключении питания и вернуться к установкам при включении... я уверен, многие делали подобное, ткните в подобные темы, пожалуйста...
душа человеческая темна и с легкостью обращается ко злу
- Реклама
- Сообщения: 2466
- Зарегистрирован: Сб май 07, 2011 17:52:59
нашёл в сети схему аппаратного разделения сигналов направления вращения вала энкодера и подавления дребезга. при вращении в одну сторону, импульсы появляются на одном выходе (верхний 2and), при вращении в противоположную - на нижнем 2and. т.е. кусман кода дешифровки направления и подавления можно скомкать и пульнуть в корзиночку... а вот что дальше делать? как инкрементировать/декрементировать ширину шим импульсов по сигналам А и В?
если импульсы в канале А опережают импульсы в канале В, то сигнал появляется на нижнем выходе, а на верхнем 0
если наоборот, то
т.е. как двумя кнопками + и - сделать шим 0...100% с шагом 0.5...1% по каждому щелчку энкодера?
душа человеческая темна и с легкостью обращается ко злу
а что это ты за огород нагородил на логике?
всю работу по определению направления вращения легко делает сам МК по сигналам А и В.
всю работу по определению направления вращения легко делает сам МК по сигналам А и В.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
- Сообщения: 1849
- Зарегистрирован: Вс дек 25, 2016 08:34:54
charchyard писал(а):Необходимо в симуляторе как-то выполнить такую задачку: два инкрементальных энкодера с кнопками должны управлять двумя каналами таймеров-счётчиков Т0 и Т1 в режиме шим. Диапазон заполнения шим сигнала должен плавно меняться от 0 до 100% вращением энкодера по часовой и обратно от 100% до 0 с шагом 0.25...1%. Блокировка и разблокировка энкодеров кнопками на оси. Запомнить заполнение при отключении питания и вернуться к установкам при включении...
Спойлер
Atmega168 16МГцКод: Выделить всё
uint8_t EEMEM pwm1;
uint8_t EEMEM pwm2;
void init_pwm (void)
{
DDRD |= (1<<DDD6) ;
PORTD &= ~(1<<PORTD6);
DDRB |= (1<<DDB1);
PORTB &= ~ (1<<PORTB1);
TCCR0A=(1<<COM0A1) | (0<<COM0A0) | (0<<COM0B1) | (0<<COM0B0) | (1<<WGM01) | (1<<WGM00);
TCCR0B=(0<<WGM02) | (0<<CS02) | (0<<CS01) | (1<<CS00);
TCNT0=0x00;
OCR0A = eeprom_read_byte (&pwm1);
OCR0B=0x00;
TCCR1A=(1<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (1<<WGM10);
TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (1<<WGM12) | (0<<CS12) | (0<<CS11) | (1<<CS10);
TCNT1=0x00;
ICR1=0x00;
OCR1A = eeprom_read_byte (&pwm2);
OCR1B=0x00;
}
typedef struct soft_enc
{
bool status;
volatile uint8_t enc_state;
volatile int8_t enc_data;
volatile int8_t enc_last;
volatile int8_t enc_new;
volatile int8_t enc_diff;
volatile uint8_t enc_pin;
} soft_enc;
soft_enc encoder1;
soft_enc encoder2;
#define encoder_init(a) _encoder_init(&a)
void _encoder_init (soft_enc *_soft_enc)
{
DDRD &= ~(_soft_enc -> enc_pin);
PORTD |= (_soft_enc -> enc_pin);
}
uint8_t konvert (uint8_t data)
{
switch (data)
{
case 0: return 0;
case 1: return 1;
case 2: return 2;
case 3: return 3;
case 4: return 1;
case 8: return 2;
case 12: return 3;
default: return 0;
}
}
#define encoder_scan(a) _encoder_scan(&a)
void _encoder_scan (soft_enc *_soft_enc)
{
uint8_t New;
if (!_soft_enc -> status)
{
New = konvert (PIND & (_soft_enc -> enc_pin));
switch(_soft_enc -> enc_state)
{
case 2:
{
if(New == 3) _soft_enc -> enc_data++;
if(New == 0) _soft_enc -> enc_data--;
break;
}
case 0:
{
if(New == 2) _soft_enc -> enc_data++;
if(New == 1) _soft_enc -> enc_data--;
break;
}
case 1:
{
if(New == 0) _soft_enc -> enc_data++;
if(New == 3) _soft_enc -> enc_data--;
break;
}
case 3:
{
if(New == 1) _soft_enc -> enc_data++;
if(New == 2) _soft_enc -> enc_data--;
break;
}
}
_soft_enc -> enc_state = New;
}
}
#define KEY_NULL 0
#define KEY_1 1
#define KEY_2 2
#define THRESHOLD 20
volatile uint8_t pressedKey = 0;
volatile uint8_t comp = 0;
void skan_kn (void)
{
uint8_t key;
if ((PINC & (1<<PINC1))==0)
key = KEY_1;
else if ((PINC & (1<<PINC2))==0)
key = KEY_2;
else {
key = KEY_NULL;
}
if (key) {
if (comp == THRESHOLD) {
comp = THRESHOLD + 10;
pressedKey = key;
return;
}
else if (comp < (THRESHOLD+5)) comp++;
}
else comp=0;
}
uint8_t get_key(void)
{
uint8_t key = pressedKey;
pressedKey = KEY_NULL;
return key;
}
void kn_init (void)
{
DDRC &= ~((1<<DDC2) | (1<<DDC1) | (1<<DDC0));
PORTC |= (1<<PORTC2) | (1<<PORTC1);
PORTC &= ~(1<<PORTC0);
EICRA=(0<<ISC11) | (0<<ISC10) | (0<<ISC01) | (0<<ISC00);
EIMSK=(0<<INT1) | (0<<INT0);
PCICR=(0<<PCIE2) | (1<<PCIE1) | (0<<PCIE0);
PCMSK1=(0<<PCINT14) | (0<<PCINT13) | (0<<PCINT12) | (0<<PCINT11) | (0<<PCINT10) | (0<<PCINT9) | (1<<PCINT8);
PCIFR=(0<<PCIF2) | (1<<PCIF1) | (0<<PCIF0);
sei();
}
void init_timer2 (void)
{
ASSR=(0<<EXCLK) | (0<<AS2);
TCCR2A=(0<<COM2A1) | (0<<COM2A0) | (0<<COM2B1) | (0<<COM2B0) | (1<<WGM21) | (0<<WGM20);
TCCR2B=(0<<WGM22) | (1<<CS22) | (1<<CS21) | (1<<CS20);
TCNT2=0x00;
OCR2A=0xFF;
OCR2B=0x00;
TIMSK2=(0<<OCIE2B) | (1<<OCIE2A) | (0<<TOIE2);
sei();
}
ISR(TIMER2_COMPA_vect)
{
encoder_scan(encoder1);
encoder_scan(encoder2);
}
ISR(PCINT1_vect)
{
if((PINC & (1<<(PINC0))) == 0)
{
cli();
eeprom_update_byte(&pwm1,OCR0A);
eeprom_update_byte(&pwm2,OCR1A);
OCR0A = 0;
OCR1A = 0;
while (1) {};
}
}
#define enc_relative(a) _enc_relative(&a)
int8_t _enc_relative(soft_enc *_soft_enc)
{
_soft_enc -> enc_new = _soft_enc -> enc_data / 4;
_soft_enc -> enc_diff = (int8_t)(_soft_enc -> enc_new - _soft_enc -> enc_last);
_soft_enc -> enc_last = _soft_enc -> enc_new;
return _soft_enc -> enc_diff;
}
int main()
{ uint8_t as; int8_t tm;
encoder1.enc_pin = (1<<PIND1) | (1<<PIND0);
encoder2.enc_pin = (1<<PIND3) | (1<<PIND2);
encoder_init(encoder1);
encoder_init(encoder2);
kn_init();
init_pwm();
init_timer2();
while (1)
{
tm = enc_relative(encoder1);
if(tm > 0) { if(OCR0A != 254) OCR0A++; }
if(tm < 0) { if(OCR0A != 0) OCR0A--; }
tm = enc_relative(encoder2);
if(tm > 0) { if(OCR1A != 254) OCR1A++; }
if(tm < 0) { if(OCR1A != 0) OCR1A--; }
skan_kn();
as = get_key();
if (as == KEY_1) encoder1.status = !encoder1.status;
if (as == KEY_2) encoder2.status = !encoder2.status;
}
}Без комментариев.
- Сообщения: 2466
- Зарегистрирован: Сб май 07, 2011 17:52:59
душа человеческая темна и с легкостью обращается ко злу
- Реклама
- Сообщения: 1978
- Зарегистрирован: Ср июл 17, 2013 13:55:57
Сверху дописать:
Код: Выделить всё
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <avr/io.h>
#include <avr/cpufunc.h>
#include <util/delay.h>
- Сообщения: 2466
- Зарегистрирован: Сб май 07, 2011 17:52:59
когда создаю проект в Атмел, то прописываю в нём ATmega168P и вставляю код из блокнота заместо начальной надписи int main (void) на пищет вот что
душа человеческая темна и с легкостью обращается ко злу
#include <avr/cpufunc.h>
Лапками что-ль набиваешь? А как же скопипастить?
Лапками что-ль набиваешь? А как же скопипастить?
Docendo discimus
- Сообщения: 2466
- Зарегистрирован: Сб май 07, 2011 17:52:59
Лапками что-ль набиваешь?
душа человеческая темна и с легкостью обращается ко злу
- Сообщения: 1978
- Зарегистрирован: Ср июл 17, 2013 13:55:57
Там еще eeprom_*(), сразу не разглядел. Надо еще строку #include <avr/eeprom.h> добавить.
- Сообщения: 1480
- Зарегистрирован: Ср июн 25, 2008 15:19:44
[uquote="charchyard",url="/forum/viewtopic.php?p=3966257#p3966257"]ткните в подобные темы, пожалуйста...[/uquote]
Для начала разделить все ТЗ на логические блоки. Запись чтение EEPROM, энкодер. Это примеры. Плевать что. Проектирование сверху вниз. Задумывается все устройство или концепция, потом мы начинаем есть мамонта кусочками. Мамонта как сожрать. Только кусочками. Иначе как в том анекдоте. Мне б такое хлебало, медок наворачивать. Модульное программирование. Весь проект дробится. Декомпозиция, анализ, синтез. Когда раздербанили весь проект на кусочки и отладили каждый кусочек отдельно, начинаем продумывать взаимодействие между кусочками. Дальше сами. Уж простите, крайне занят. Как делать, удочку дал.
Для начала разделить все ТЗ на логические блоки. Запись чтение EEPROM, энкодер. Это примеры. Плевать что. Проектирование сверху вниз. Задумывается все устройство или концепция, потом мы начинаем есть мамонта кусочками. Мамонта как сожрать. Только кусочками. Иначе как в том анекдоте. Мне б такое хлебало, медок наворачивать. Модульное программирование. Весь проект дробится. Декомпозиция, анализ, синтез. Когда раздербанили весь проект на кусочки и отладили каждый кусочек отдельно, начинаем продумывать взаимодействие между кусочками. Дальше сами. Уж простите, крайне занят. Как делать, удочку дал.
- Сообщения: 1849
- Зарегистрирован: Вс дек 25, 2016 08:34:54
А я думал, ну как бы удочку кинуть. А то все мимо и мимо этой темы проходите.Demiurg писал(а):Как делать, удочку дал.
Вам что 30 минут времени своего жалко? У меня на этот код ушло 30 минут.
Покажите вариант из цикла статей Татарчевского, SWITCH технологии или еще чего-то, декомпозиция, анализ, синтез.
- Сообщения: 2466
- Зарегистрирован: Сб май 07, 2011 17:52:59
накарябал в протеусе и залил код в мк
... вместо реальных энкодеров поставил четыре генератора со сдвигом в 1/4 периода. на выходы шимов воткнул по вольтметру. нажал на кнопку. отжал кнопку. вольтметры по нулям...
библиотеку eeprom подключил... интересно, на отладочной заработало бы?
Последний раз редактировалось charchyard Вт янв 26, 2021 18:16:46, всего редактировалось 1 раз.
душа человеческая темна и с легкостью обращается ко злу
- Сообщения: 1480
- Зарегистрирован: Ср июн 25, 2008 15:19:44
[uquote="Dimon456",url="/forum/viewtopic.php?p=3967882#p3967882"]...[/uquote]
А не надо ерничать. Изучение архитектуры. Чтение даташитов. Программирование микроконтроллеров это игры со временем. Временем выполнения кода. Всё.
А не надо ерничать. Изучение архитектуры. Чтение даташитов. Программирование микроконтроллеров это игры со временем. Временем выполнения кода. Всё.
- Сообщения: 1849
- Зарегистрирован: Вс дек 25, 2016 08:34:54
Нет, надо поиграться с таймером TIMER2.charchyard писал(а):интересно, на отладочной заработало бы?
Проверил в реальном железе, вот с таким энкодером
Спойлер

Спойлер
Для частоты 16МГцКод: Выделить всё
void init_timer2 (void)
{
// Timer Period: 0,5 ms
ASSR=(0<<EXCLK) | (0<<AS2);
TCCR2A=(0<<COM2A1) | (0<<COM2A0) | (0<<COM2B1) | (0<<COM2B0) | (1<<WGM21) | (0<<WGM20);
TCCR2B=(0<<WGM22) | (0<<CS22) | (1<<CS21) | (1<<CS20);
TCNT2=0x00;
OCR2A=0xF9;
OCR2B=0x00;
// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=(0<<OCIE2B) | (1<<OCIE2A) | (0<<TOIE2);
sei();
}Моя задача была в один лист уместить, я уместил, а на блоки пусть сам дробит, это просто черновой набросок.
Тем более эти два канала можно было бы сделать на 1 таймере, регистр ICR1 позволил бы выжать максимум частоты, но это пусть ТС изучает. А еще мне код опроса энкодера не нравится, ну да ладно, на китайской х работает как-то, завтра осциллографом гляну еще, для интереса. Минус отсутствие индикации блокировки энкодера, в слепую.
- Сообщения: 1480
- Зарегистрирован: Ср июн 25, 2008 15:19:44
Я на форуме выкладывал схему на логике, чтобы логикой а не программно различать направление вращения энкодера. Кстати, надо будет как нибудь сесть, попробовать реализовать программную модель этой схемы.


если есть прерывание по одной из ног енкодера, то в момент прихода прерывания сравнивай состояние выходов енкоденра: одинаковые - вращение в одну сторону, разные - в другую...
если прерывания нет, то просто регулярно опрашивай выходы енкодера (50-200 Гц) и сравнивай с предыдущим - по этой разнице можно определить вращение енкодера.
если прерывания нет, то просто регулярно опрашивай выходы енкодера (50-200 Гц) и сравнивай с предыдущим - по этой разнице можно определить вращение енкодера.
Для тех, кто не учил магию мир полон физики 
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
- Сообщения: 1849
- Зарегистрирован: Вс дек 25, 2016 08:34:54
Demiurg, таким образом можно и до специализированных микросхем дойти.
В моем коде направление вращения определяет функция enc_relative, на выходе этой функции всего три значения, <0 0 >0.
Этого достаточно для принятия решения, -1 0 +1.
Если этого вам мало, можете использовать параметр enc_diff который возвращает функция enc_relative.
А опрос энкодера, специально даже указал, Timer Period: 0,5 ms.
Проверил на осциллографе ширину импульса, даже прибором DM90A посмотрел, все ОК, а то по светодиоду как-то не очень видно было.
Ivanoff-iv, главная задача, которая не была выполнена мной, борьба с дребезгом. Ну не нравятся вам по чему-то конденсаторы, все боитесь за контакты энкодера.
В моем коде направление вращения определяет функция enc_relative, на выходе этой функции всего три значения, <0 0 >0.
Этого достаточно для принятия решения, -1 0 +1.
Если этого вам мало, можете использовать параметр enc_diff который возвращает функция enc_relative.
А опрос энкодера, специально даже указал, Timer Period: 0,5 ms.
Проверил на осциллографе ширину импульса, даже прибором DM90A посмотрел, все ОК, а то по светодиоду как-то не очень видно было.
Ivanoff-iv, главная задача, которая не была выполнена мной, борьба с дребезгом. Ну не нравятся вам по чему-то конденсаторы, все боитесь за контакты энкодера.
[uquote="Dimon456",url="/forum/viewtopic.php?p=3968285#p3968285"]А опрос энкодера, специально даже указал, Timer Period: 0,5 ms.[/uquote] и ему мешает дребезг? странно...
Для тех, кто не учил магию мир полон физики 
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
- Сообщения: 1849
- Зарегистрирован: Вс дек 25, 2016 08:34:54
а я хочу оптимальный вариант, как на dvd-плеерах, музыкальных центрах что там еще.Ivanoff-iv писал(а):и ему мешает дребезг? странно...
Более-менее оптимальный вариант дает вот этот код
Спойлер
Код: Выделить всё
//функция опроса энкодера
static unsigned char stateEnc;
unsigned char tmp;
unsigned char currentState = 0;
if ((PIN_Enc & (1<<Pin1_Enc))!= 0) {SetBit(currentState,0);}
if ((PIN_Enc & (1<<Pin2_Enc))!= 0) {SetBit(currentState,1);}
tmp = stateEnc;
if (currentState == (tmp & b00000011)) return;
tmp = (tmp<<2)|currentState;
stateEnc = tmp;
if (tmp == b11100001) data_enc--;
if (tmp == b11010010) data_enc++;
return;
оптимальным оказался 1мс, проверено на реальном железе с китайским энкодером
Вот вам модели энкодеров найденные на просторах инета, один из библиотек самого протеуса, один с симуляцией дребезга на Атмега8. В протеусе на Атмега8 подключал к выше коду и проверял - работает.


