ATmega8/16: шим, управляемый энкодером

Обсуждаем контроллеры компании Atmel.
Ответить
Поставщик валерьянки для Кота
Сообщения: 2466
Зарегистрирован: Сб май 07, 2011 17:52:59

Сообщение charchyard »

Здравствуйте! Я полнейший лохопэт в мк и в Си. Есть Атмел Студио и Протеус на пц. Отладочных плат и места , чтоб это всё запустить пока нет. Необходимо в симуляторе как-то выполнить такую задачку: два инкрементальных энкодера с кнопками должны управлять двумя каналами таймеров-счётчиков Т0 и Т1 в режиме шим. Диапазон заполнения шим сигнала должен плавно меняться от 0 до 100% вращением энкодера по часовой и обратно от 100% до 0 с шагом 0.25...1%. Блокировка и разблокировка энкодеров кнопками на оси. Запомнить заполнение при отключении питания и вернуться к установкам при включении... я уверен, многие делали подобное, ткните в подобные темы, пожалуйста...
душа человеческая темна и с легкостью обращается ко злу
Реклама
Поставщик валерьянки для Кота
Сообщения: 2466
Зарегистрирован: Сб май 07, 2011 17:52:59

Сообщение charchyard »

нашёл в сети схему аппаратного разделения сигналов направления вращения вала энкодера и подавления дребезга. при вращении в одну сторону, импульсы появляются на одном выходе (верхний 2and), при вращении в противоположную - на нижнем 2and. т.е. кусман кода дешифровки направления и подавления можно скомкать и пульнуть в корзиночку... а вот что дальше делать? как инкрементировать/декрементировать ширину шим импульсов по сигналам А и В?
СпойлерИзображение
если импульсы в канале А опережают импульсы в канале В, то сигнал появляется на нижнем выходе, а на верхнем 0
СпойлерИзображение
если наоборот, то
СпойлерИзображение
т.е. как двумя кнопками + и - сделать шим 0...100% с шагом 0.5...1% по каждому щелчку энкодера?
душа человеческая темна и с легкостью обращается ко злу
Реклама
Модератор
Аватара пользователя
Сообщения: 19055
Зарегистрирован: Сб авг 14, 2010 15:05:51
Откуда: г. Озерск, Челябинская обл.

Сообщение Starichok51 »

а что это ты за огород нагородил на логике?
всю работу по определению направления вращения легко делает сам МК по сигналам А и В.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Сообщение 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;
      }
 }
Изображение
Без комментариев.
Реклама
Эиком - электронные компоненты и радиодетали
Поставщик валерьянки для Кота
Сообщения: 2466
Зарегистрирован: Сб май 07, 2011 17:52:59

Сообщение charchyard »

легко делает сам МК
я догадываюсь, но не знаю как
не скомпилировался код
СпойлерИзображение
душа человеческая темна и с легкостью обращается ко злу
Реклама
Поставщик валерьянки для Кота
Сообщения: 1978
Зарегистрирован: Ср июл 17, 2013 13:55:57

Сообщение NStorm »

Сверху дописать:

Код: Выделить всё

#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

Сообщение charchyard »

когда создаю проект в Атмел, то прописываю в нём ATmega168P и вставляю код из блокнота заместо начальной надписи int main (void) на пищет вот что
СпойлерИзображение
душа человеческая темна и с легкостью обращается ко злу
Друг Кота
Аватара пользователя
Сообщения: 7016
Зарегистрирован: Вс июл 12, 2009 19:15:29
Откуда: Ижевск

Сообщение pyzhman »

#include <avr/cpufunc.h>
Лапками что-ль набиваешь? А как же скопипастить?
Docendo discimus
Контактная информация:
Поставщик валерьянки для Кота
Сообщения: 2466
Зарегистрирован: Сб май 07, 2011 17:52:59

Сообщение charchyard »

Лапками что-ль набиваешь?
:roll: вах!!! слешь упустил :dont_know: собрался :)) протеусь вкл!
душа человеческая темна и с легкостью обращается ко злу
Поставщик валерьянки для Кота
Сообщения: 1978
Зарегистрирован: Ср июл 17, 2013 13:55:57

Сообщение NStorm »

Там еще eeprom_*(), сразу не разглядел. Надо еще строку #include <avr/eeprom.h> добавить.
Это не хвост, это антенна
Сообщения: 1480
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

[uquote="charchyard",url="/forum/viewtopic.php?p=3966257#p3966257"]ткните в подобные темы, пожалуйста...[/uquote]
Для начала разделить все ТЗ на логические блоки. Запись чтение EEPROM, энкодер. Это примеры. Плевать что. Проектирование сверху вниз. Задумывается все устройство или концепция, потом мы начинаем есть мамонта кусочками. Мамонта как сожрать. Только кусочками. Иначе как в том анекдоте. Мне б такое хлебало, медок наворачивать. Модульное программирование. Весь проект дробится. Декомпозиция, анализ, синтез. Когда раздербанили весь проект на кусочки и отладили каждый кусочек отдельно, начинаем продумывать взаимодействие между кусочками. Дальше сами. Уж простите, крайне занят. Как делать, удочку дал.
Контактная информация:
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Сообщение Dimon456 »

Demiurg писал(а):Как делать, удочку дал.
А я думал, ну как бы удочку кинуть. А то все мимо и мимо этой темы проходите.
Вам что 30 минут времени своего жалко? У меня на этот код ушло 30 минут.
Покажите вариант из цикла статей Татарчевского, SWITCH технологии или еще чего-то, декомпозиция, анализ, синтез.
Поставщик валерьянки для Кота
Сообщения: 2466
Зарегистрирован: Сб май 07, 2011 17:52:59

Сообщение charchyard »

накарябал в протеусе и залил код в мк
СпойлерИзображение
... вместо реальных энкодеров поставил четыре генератора со сдвигом в 1/4 периода. на выходы шимов воткнул по вольтметру. нажал на кнопку. отжал кнопку. вольтметры по нулям...
СпойлерИзображение Изображение
библиотеку eeprom подключил... интересно, на отладочной заработало бы?
Последний раз редактировалось charchyard Вт янв 26, 2021 18:16:46, всего редактировалось 1 раз.
душа человеческая темна и с легкостью обращается ко злу
Это не хвост, это антенна
Сообщения: 1480
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

[uquote="Dimon456",url="/forum/viewtopic.php?p=3967882#p3967882"]...[/uquote]
А не надо ерничать. Изучение архитектуры. Чтение даташитов. Программирование микроконтроллеров это игры со временем. Временем выполнения кода. Всё.
Контактная информация:
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Сообщение 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 позволил бы выжать максимум частоты, но это пусть ТС изучает. А еще мне код опроса энкодера не нравится, ну да ладно, на китайской х работает как-то, завтра осциллографом гляну еще, для интереса. Минус отсутствие индикации блокировки энкодера, в слепую.
Это не хвост, это антенна
Сообщения: 1480
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

Я на форуме выкладывал схему на логике, чтобы логикой а не программно различать направление вращения энкодера. Кстати, надо будет как нибудь сесть, попробовать реализовать программную модель этой схемы.
Изображение
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 7077
Зарегистрирован: Пт ноя 11, 2016 05:48:09
Откуда: Сердце Пармы

Сообщение Ivanoff-iv »

если есть прерывание по одной из ног енкодера, то в момент прихода прерывания сравнивай состояние выходов енкоденра: одинаковые - вращение в одну сторону, разные - в другую...
если прерывания нет, то просто регулярно опрашивай выходы енкодера (50-200 Гц) и сравнивай с предыдущим - по этой разнице можно определить вращение енкодера.
Для тех, кто не учил магию мир полон физики :)
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Сообщение Dimon456 »

Demiurg, таким образом можно и до специализированных микросхем дойти.

В моем коде направление вращения определяет функция enc_relative, на выходе этой функции всего три значения, <0 0 >0.
Этого достаточно для принятия решения, -1 0 +1.
Если этого вам мало, можете использовать параметр enc_diff который возвращает функция enc_relative.
А опрос энкодера, специально даже указал, Timer Period: 0,5 ms.

Проверил на осциллографе ширину импульса, даже прибором DM90A посмотрел, все ОК, а то по светодиоду как-то не очень видно было.

Ivanoff-iv, главная задача, которая не была выполнена мной, борьба с дребезгом. Ну не нравятся вам по чему-то конденсаторы, все боитесь за контакты энкодера.
Друг Кота
Аватара пользователя
Сообщения: 7077
Зарегистрирован: Пт ноя 11, 2016 05:48:09
Откуда: Сердце Пармы

Сообщение Ivanoff-iv »

[uquote="Dimon456",url="/forum/viewtopic.php?p=3968285#p3968285"]А опрос энкодера, специально даже указал, Timer Period: 0,5 ms.[/uquote] и ему мешает дребезг? странно...
Для тех, кто не учил магию мир полон физики :)
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Сообщение 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.
Энкодеры.zip
(148.6 КБ) 261 скачивание
В протеусе на Атмега8 подключал к выше коду и проверял - работает.
Ответить

Вернуться в «AVR»