|
|
  |
Генератор прямоугольных импульсов на AVR |
|
|
|
Nov 11 2008, 04:21
|

Местный
  
Группа: Свой
Сообщений: 473
Регистрация: 10-09-06
Из: Тольятти. Самарская обл.
Пользователь №: 20 249

|
Добрый день, необходимо управлять скоростью вращения шагового двигателя, для этого на плату управления двигателем нужно подавать прямоугольные импульсы с платы микроконтроллера. Для начала написал это: Код #include <avr/io.h> #include <avr/interrupt.h>
#define Frq 20 //Частота импульсов Гц
// Конфигурирование Timer1 void Timer1_init(void) { TCCR1B |= 0x04; //Частота счета таймера Sclk/256 TCCR1B |= 0x08; //Вкл CTC TCCR1A |= 0x40; //Активировать выход OC1A OCR1A = F_CPU/(256*Frq); //Определяем соответствующее значение для Frq }
void Inter_init(void) { TIMSK = 0x00; //Запрещаем все прерывания GIMSK = 0x00; //Запрет внешних прерываний cli(); //Общее запрещение прерываний; }
// Конфигурирование портов В/В void Ports_init(void) { DDRB = 0x0F; // PB1 Выход } int main (void) { Ports_init(); Timer1_init(); Inter_init(); while(1) {} return 1; } Это работает нормально. Теперь хочу плавно регулировать частоту: Код #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h>
#define Frq 20 //Частота импульсов Гц
//Функция задания частоты void SetSpeed(const unsigned int w) { OCR1A = (F_CPU/w)>>8; }
// Конфигурирование Timer1 void Timer1_init(void) { TCCR1B |= 0x04; //Частота счета таймера Sclk/256 TCCR1B |= 0x08; //Вкл CTC TCCR1A |= 0x40; //Активировать выход OC1A OCR1A = F_CPU/(256*Frq); //Определяем соответствующее значение для Frq }
void Inter_init(void) { TIMSK = 0x00; //Запрещаем все прерывания GIMSK = 0x00; //Запрет внешних прерываний cli(); //Общее запрещение прерываний; }
// Конфигурирование портов В/В void Ports_init(void) { DDRB = 0x0F; // PB1 Выход } int main (void) { unsigned int i; Ports_init(); Timer1_init(); Inter_init(); while(1) { while (i<5000) { SetSpeed(i); i++; _delay_ms(10); } _delay_ms(1000); while (i>2) { SetSpeed(i); i--; _delay_ms(10); } } return 1; } Это работает весьма странно... Иногда нормально, а иногда импульсы прекращаются и на ноге OC1A устанавливается 0 или 1.  Прерывания вроде бы отключены.
--------------------
Если все, то не я...
|
|
|
|
|
Nov 11 2008, 05:51
|

Местный
  
Группа: Свой
Сообщений: 473
Регистрация: 10-09-06
Из: Тольятти. Самарская обл.
Пользователь №: 20 249

|
Цитата(ARV @ Nov 11 2008, 09:41)  ...
Эскизы прикрепленных изображений
--------------------
Если все, то не я...
|
|
|
|
|
Nov 12 2008, 09:10
|

nofb
  
Группа: Свой
Сообщений: 430
Регистрация: 18-05-06
Из: Москва, Зеленоград
Пользователь №: 17 218

|
Цитата(GDI @ Nov 12 2008, 12:01)  Вообще то регистры OCRx реально перезаписываются в момент переполнения таймера, т.е. они с двойной буферизацией. Это всего лишь влияет на фазу.
--------------------
Это не то что вы подумали ...
|
|
|
|
|
Nov 12 2008, 09:11
|

Местный
  
Группа: Свой
Сообщений: 473
Регистрация: 10-09-06
Из: Тольятти. Самарская обл.
Пользователь №: 20 249

|
Цитата(GDI @ Nov 12 2008, 13:01)  Вообще то регистры OCRx реально перезаписываются в момент переполнения таймера, т.е. они с двойной буферизацией. Вот это как раз то, что нужно! Как же это реализовать? Видимо строчка вроде: OCR1A = A; этого не обеспечивает.
--------------------
Если все, то не я...
|
|
|
|
|
Nov 12 2008, 09:23
|
Профессионал
    
Группа: Свой
Сообщений: 1 235
Регистрация: 14-05-05
Из: Санкт-Петербург
Пользователь №: 5 008

|
Именно это она и обеспечивает, т.е. реальная запись в регистр таймера произойдет только после того как предыдущая фаза будет закончена. Цитата Проблема похоже в том, что когда OCR1A устанавливается ниже текущего значения TCNT1 таймер продолжает долго и упорно считать до FFFF и уже затем до OCR1A... Не будет такого. таймер досчитает до предыдущего значения OCR, и в этот момент произойдет перезапись нового значения в OCR, а таймер обнулится.
--------------------
|
|
|
|
|
Nov 12 2008, 10:11
|
Участник

Группа: Участник
Сообщений: 17
Регистрация: 24-10-08
Пользователь №: 41 157

|
тоже нужно было генерить меандр, а генератора под рукой не было... вот что пришлось сваять. Код ..... unsigned char state_encoder=0; unsigned int timer_tmp=4000; //начальное значение для OCR1A
void timer_init(void) { TIMSK=(1<<TOIE0)|(1<<OCIE1A); TCNT0=TCNT0_const; TCCR0=TCCR0_const;
TCNT1=0; TCCR1A=(0<<COM1A1)|(1<<COM1A0)|(0<<WGM11)|(0<<WGM10); TCCR1B=(0<<WGM13)|(0<<WGM12)|(0<<CS12)|(0<<CS11)|(1<<CS10); OCR1A=timer_tmp; }
....
int main( void ) { port_init(); Init_Encoder(); timer_init(); asm("sei");
while(1) { if((state_encoder)!=0) { if (state_encoder==0x01) {timer_tmp-=10;} else { if (state_encoder==0xff) {timer_tmp+=10;} } state_encoder=0; } }
return 0; }
//опрос энкодера #pragma vector=TIMER0_OVF_vect __interrupt void timer0_ovf_my(void) { TCNT0=TCNT0_const; state_encoder=Read_Encoder(); }
#pragma vector=TIMER1_COMPA_vect __interrupt void timer1_compa_my(void) { TCNT1=0; OCR1A=timer_tmp; } значение для timer_tmp устанавливается энкодером в основном цикле программы, а обновление OCR1A происходит в прерывании таймера1. прямоугольный сигнал генерится на выводе PD5(OC1A)
|
|
|
|
|
Nov 12 2008, 14:11
|

4 синих кубика
   
Группа: Участник
Сообщений: 526
Регистрация: 19-09-08
Из: полупроводника, металла и стекла
Пользователь №: 40 326

|
При записи в регистры Таймера1, например, в OCR1A, есть ньюансы. Эти ньюансы описаны в даташите, указан порядок заполнения старшего и младшего байтов регистра, правда это для ассемблера, а Си вроде как должен знать эту особенность (так написано). Я на асме писал в регистры не в том порядке и долго не мог добиться, чтобы срабатывало прерывание по совпадению, хотя в AVRStudio всё пошагово отрабатывалось как надо. Кстати, в Proteus'е тоже ерунду показывало, точно как на макете было, покуда не учёл этот ньюанс. Казалось бы мелочь, а времени потерял вагон.
--------------------
p-n-p-p-n-p-n-n-p-n-p структура однако очень эффективна
|
|
|
|
|
Nov 12 2008, 15:12
|

Местный
  
Группа: Свой
Сообщений: 473
Регистрация: 10-09-06
Из: Тольятти. Самарская обл.
Пользователь №: 20 249

|
Вообщем GDI был прав, но почему-то поправил пост...  Постеснялся наверное... Код #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h>
#define Frq 10 //×åñòîòà ïåðåêëþ÷åíèÿ Ãö
void SetSpeed(uint16_t w) { uint8_t sreg; sreg = SREG; cli(); OCR1A = (F_CPU>>3)/w; SREG = sreg; } // Êîíôèãóðèðîâàíèå Timer1 void Timer1_init(void) { TCCR1B |= 0x02; //×àñòîòà ñ÷åòà òàéìåðà Sclk/256 TCCR1B |= 0x18; //Âêë fast PWM TCCR1A |= 0x03; //ñ 50% ïðîäîëæèòåëüíîñòüþ öèêëà TCCR1A |= 0x40; //Àêòèâèðîâàòü âûõîä OC1A OCR1A = (F_CPU>>3)/Frq; //Îïðåäåëÿåì ñîîòâåòñòâóþùåå çíà÷åíèå äëÿ Frq }
void Inter_init(void) { TIMSK = 0x00; //Çàïðåùàåì âñå ïðåðûâàíèÿ GIMSK = 0x00; //Çàïðåò âíåøíèõ ïðåðûâàíèé cli(); //Îáùåå çàïðåùåíèå ïðåðûâàíèé; }
// Êîíôèãóðèðîâàíèå ïîðòîâ Â/Â void Ports_init(void) { DDRB = 0x0F; // PB1 Âûõîä } int main(void) { uint16_t i=10; Ports_init(); Timer1_init(); Inter_init(); while(1) { for (i=10; i<10000; i=i+10) { SetSpeed(i); _delay_ms(20); } for (; i>10; i=i-10) { SetSpeed(i); _delay_ms(20); } } return 1; } А вот решение проблемы:
Эскизы прикрепленных изображений
--------------------
Если все, то не я...
|
|
|
|
|
Nov 14 2008, 21:18
|
Участник

Группа: Участник
Сообщений: 69
Регистрация: 27-06-06
Пользователь №: 18 383

|
От себя могу добавить что програматор запустился под вистой с драйверами выложеными несколько странци назад, единственно с прошивкой была, нет protoss, а выложенной ранее. вроде так на стр 5, mail.hex.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|