Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Генератор прямоугольных импульсов на AVR
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
PhX
Добрый день, необходимо управлять скоростью вращения шагового двигателя, для этого на плату управления двигателем нужно подавать прямоугольные импульсы с платы микроконтроллера.
Для начала написал это:
Код
#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. 07.gif Прерывания вроде бы отключены.
ARV
...
PhX
Цитата(ARV @ Nov 11 2008, 09:41) *
...
=GM=
Цитата(PhX @ Nov 11 2008, 04:21) *
Это работает весьма странно... Иногда нормально, а иногда импульсы прекращаются и на ноге OC1A устанавливается 0 или 1

Возможно, при больших i вы устанавливаете OCR1A = (F_CPU/i)>>8 в 0, при этом CTC не срабатывает.
PhX
Проблема похоже в том, что когда OCR1A устанавливается ниже текущего значения TCNT1 таймер продолжает долго и упорно считать до FFFF и уже затем до OCR1A...
Можно конечно перед изменением OCR1A изменять TCNT1=0, но мне кажется это не самый лучший путь. Я почти уверен, что задача варьирования частоты прямоугольных импульсов решалась на avr множество раз. Если кто знает, поделитесь решением.
GDI
Вообще то регистры OCRx реально перезаписываются в момент переполнения таймера, т.е. они с двойной буферизацией.
gormih
Цитата(GDI @ Nov 12 2008, 12:01) *
Вообще то регистры OCRx реально перезаписываются в момент переполнения таймера, т.е. они с двойной буферизацией.

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

Вот это как раз то, что нужно! Как же это реализовать?
Видимо строчка вроде:
OCR1A = A;
этого не обеспечивает.
GDI
Именно это она и обеспечивает, т.е. реальная запись в регистр таймера произойдет только после того как предыдущая фаза будет закончена.

Цитата
Проблема похоже в том, что когда OCR1A устанавливается ниже текущего значения TCNT1 таймер продолжает долго и упорно считать до FFFF и уже затем до OCR1A...

Не будет такого. таймер досчитает до предыдущего значения OCR, и в этот момент произойдет перезапись нового значения в OCR, а таймер обнулится.
gormih
Цитата(PhX @ Nov 11 2008, 07:21) *
Это работает весьма странно... Иногда нормально, а иногда импульсы прекращаются и на ноге OC1A устанавливается 0 или 1. 07.gif Прерывания вроде бы отключены.

Иногда и иногда - это когда? Если исходник не меняешь, а эффект наступает случайно - то это не программный сбой.
sansnotfor
тоже нужно было генерить меандр, а генератора под рукой не было... вот что пришлось сваять.

Код
.....
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)
МП41
При записи в регистры Таймера1, например, в OCR1A, есть ньюансы. Эти ньюансы описаны в даташите, указан порядок заполнения старшего и младшего байтов регистра, правда это для ассемблера, а Си вроде как должен знать эту особенность (так написано). Я на асме писал в регистры не в том порядке и долго не мог добиться, чтобы срабатывало прерывание по совпадению, хотя в AVRStudio всё пошагово отрабатывалось как надо. Кстати, в Proteus'е тоже ерунду показывало, точно как на макете было, покуда не учёл этот ньюанс. Казалось бы мелочь, а времени потерял вагон.
PhX
Вообщем GDI был прав, но почему-то поправил пост... 05.gif Постеснялся наверное...
Код
#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;
}

А вот решение проблемы:
Roger
От себя могу добавить что програматор запустился под вистой с драйверами выложеными несколько странци назад, единственно с прошивкой была, нет protoss, а выложенной ранее. вроде так на стр 5, mail.hex.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.