Страница 1 из 4
ATmega8/16: шим, управляемый энкодером
Добавлено: Вс янв 24, 2021 11:19:18
charchyard
Здравствуйте! Я полнейший лохопэт в мк и в Си. Есть Атмел Студио и Протеус на пц. Отладочных плат и места , чтоб это всё запустить пока нет. Необходимо в симуляторе как-то выполнить такую задачку: два инкрементальных энкодера с кнопками должны управлять двумя каналами таймеров-счётчиков Т0 и Т1 в режиме шим. Диапазон заполнения шим сигнала должен плавно меняться от 0 до 100% вращением энкодера по часовой и обратно от 100% до 0 с шагом 0.25...1%. Блокировка и разблокировка энкодеров кнопками на оси. Запомнить заполнение при отключении питания и вернуться к установкам при включении... я уверен, многие делали подобное, ткните в подобные темы, пожалуйста...
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Пн янв 25, 2021 19:03:42
charchyard
нашёл в сети схему аппаратного разделения сигналов направления вращения вала энкодера и подавления дребезга. при вращении в одну сторону, импульсы появляются на одном выходе (верхний 2and), при вращении в противоположную - на нижнем 2and. т.е. кусман кода дешифровки направления и подавления можно скомкать и пульнуть в корзиночку... а вот что дальше делать? как инкрементировать/декрементировать ширину шим импульсов по сигналам А и В?
Спойлер

если импульсы в канале А опережают импульсы в канале В, то сигнал появляется на нижнем выходе, а на верхнем 0
Спойлер

если наоборот, то
Спойлер

т.е. как двумя кнопками + и - сделать шим 0...100% с шагом 0.5...1% по каждому щелчку энкодера?
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Пн янв 25, 2021 19:21:59
Starichok51
а что это ты за огород нагородил на логике?
всю работу по определению направления вращения легко делает сам МК по сигналам А и В.
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Пн янв 25, 2021 19:31:20
Dimon456
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;
}
}

Без комментариев.
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Вт янв 26, 2021 11:32:13
charchyard
легко делает сам МК
я догадываюсь, но не знаю как
не скомпилировался код
Спойлер

Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Вт янв 26, 2021 13:03:08
NStorm
Сверху дописать:
Код: Выделить всё
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <avr/io.h>
#include <avr/cpufunc.h>
#include <util/delay.h>
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Вт янв 26, 2021 15:10:32
charchyard
когда создаю проект в Атмел, то прописываю в нём ATmega168P и вставляю код из блокнота заместо начальной надписи int main (void) на пищет вот что
Спойлер

Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Вт янв 26, 2021 15:12:44
pyzhman
#include <avr/cpufunc.h>
Лапками что-ль набиваешь? А как же скопипастить?
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Вт янв 26, 2021 15:58:07
charchyard
Лапками что-ль набиваешь?

вах!!! слешь упустил

собрался

протеусь вкл!
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Вт янв 26, 2021 16:24:11
NStorm
Там еще eeprom_*(), сразу не разглядел. Надо еще строку #include <avr/eeprom.h> добавить.
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Вт янв 26, 2021 17:03:35
Demiurg
[uquote="charchyard",url="/forum/viewtopic.php?p=3966257#p3966257"]ткните в подобные темы, пожалуйста...[/uquote]
Для начала разделить все ТЗ на логические блоки. Запись чтение EEPROM, энкодер. Это примеры. Плевать что. Проектирование сверху вниз. Задумывается все устройство или концепция, потом мы начинаем есть мамонта кусочками. Мамонта как сожрать. Только кусочками. Иначе как в том анекдоте. Мне б такое хлебало, медок наворачивать. Модульное программирование. Весь проект дробится. Декомпозиция, анализ, синтез. Когда раздербанили весь проект на кусочки и отладили каждый кусочек отдельно, начинаем продумывать взаимодействие между кусочками. Дальше сами. Уж простите, крайне занят. Как делать, удочку дал.
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Вт янв 26, 2021 17:26:08
Dimon456
Demiurg писал(а):Как делать, удочку дал.
А я думал, ну как бы удочку кинуть. А то все мимо и мимо этой темы проходите.
Вам что 30 минут времени своего жалко? У меня на этот код ушло 30 минут.
Покажите вариант из цикла статей Татарчевского, SWITCH технологии или еще чего-то, декомпозиция, анализ, синтез.
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Вт янв 26, 2021 17:51:06
charchyard
накарябал в протеусе и залил код в мк
Спойлер

... вместо реальных энкодеров поставил четыре генератора со сдвигом в 1/4 периода. на выходы шимов воткнул по вольтметру. нажал на кнопку. отжал кнопку. вольтметры по нулям...
Спойлер

библиотеку eeprom подключил... интересно, на отладочной заработало бы?
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Вт янв 26, 2021 18:14:14
Demiurg
[uquote="Dimon456",url="/forum/viewtopic.php?p=3967882#p3967882"]...[/uquote]
А не надо ерничать. Изучение архитектуры. Чтение даташитов. Программирование микроконтроллеров это игры со временем. Временем выполнения кода. Всё.
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Вт янв 26, 2021 22:22:45
Dimon456
charchyard писал(а):интересно, на отладочной заработало бы?
Нет, надо поиграться с таймером TIMER2.
Проверил в реальном железе, вот с таким энкодером
Спойлер

код таймера
Спойлер
Для частоты 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();
}
Demiurg, а я на блоки не делил, мне это не интересно было.
Моя задача была в один лист уместить, я уместил, а на блоки пусть сам дробит, это просто черновой набросок.
Тем более эти два канала можно было бы сделать на 1 таймере, регистр ICR1 позволил бы выжать максимум частоты, но это пусть ТС изучает. А еще мне код опроса энкодера не нравится, ну да ладно, на китайской х работает как-то, завтра осциллографом гляну еще, для интереса. Минус отсутствие индикации блокировки энкодера, в слепую.
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Ср янв 27, 2021 03:35:49
Demiurg
Я на форуме выкладывал схему на логике, чтобы логикой а не программно различать направление вращения энкодера. Кстати, надо будет как нибудь сесть, попробовать реализовать программную модель этой схемы.

Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Ср янв 27, 2021 06:31:45
Ivanoff-iv
если есть прерывание по одной из ног енкодера, то в момент прихода прерывания сравнивай состояние выходов енкоденра: одинаковые - вращение в одну сторону, разные - в другую...
если прерывания нет, то просто регулярно опрашивай выходы енкодера (50-200 Гц) и сравнивай с предыдущим - по этой разнице можно определить вращение енкодера.
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Ср янв 27, 2021 08:52:35
Dimon456
Demiurg, таким образом можно и до специализированных микросхем дойти.
В моем коде направление вращения определяет функция enc_relative, на выходе этой функции всего три значения, <0 0 >0.
Этого достаточно для принятия решения, -1 0 +1.
Если этого вам мало, можете использовать параметр enc_diff который возвращает функция enc_relative.
А опрос энкодера, специально даже указал, Timer Period: 0,5 ms.
Проверил на осциллографе ширину импульса, даже прибором DM90A посмотрел, все ОК, а то по светодиоду как-то не очень видно было.
Ivanoff-iv, главная задача, которая не была выполнена мной, борьба с дребезгом. Ну не нравятся вам по чему-то конденсаторы, все боитесь за контакты энкодера.
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Ср янв 27, 2021 10:41:31
Ivanoff-iv
[uquote="Dimon456",url="/forum/viewtopic.php?p=3968285#p3968285"]А опрос энкодера, специально даже указал, Timer Period: 0,5 ms.[/uquote] и ему мешает дребезг? странно...
Re: ATmega8/16: шим, управляемый энкодером
Добавлено: Ср янв 27, 2021 18:17:05
Dimon456
Ivanoff-iv писал(а):и ему мешает дребезг? странно...
а я хочу оптимальный вариант, как на dvd-плеерах, музыкальных центрах что там еще.
Более-менее оптимальный вариант дает вот этот код
Спойлер
Код: Выделить всё
//функция опроса энкодера
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мс, проверено на реальном железе с китайским энкодером charchyard, я ваши картинки моделей энкодеров не смотрел, но если они крутятся в обратную сторону то естественно нули будут, а у вас судя по настройкам генераторов они оба в какую-то одну сторону крутятся, надо было один так другой эдак настроить.
Вот вам модели энкодеров найденные на просторах инета, один из библиотек самого протеуса, один с симуляцией дребезга на Атмега8.
В протеусе на Атмега8 подключал к выше коду и проверял - работает.