Проблема с выводом PWM-сигналом на ATtiny45

Тема в разделе "Схемотехника, компоненты, модули", создана пользователем Eirin, 30 май 2013.

  1. Eirin

    Eirin Нуб

    Приобрел микроконтроллер Attiny45, написал несколько простых программ, успешно загрузил их использовав Arduino Uno как ISP-программатор.
    В итоге столкнулся с проблемой, что мк выдает PWM-сигнал только на 2х выводах (Pin 0 и Pin 1), хотя на сайте Амперки и в даташите написано, что на 4х выводах может быть получен PWM-сигнал (так же еще и на Pin 3 и Pin 4).

    Мне для проекта необходимо 3 вывода с PWM сигналом, потому и хотелось бы узнать, можно ли получить более 2х сигналов с ATtiny45 или же я что-то не так понял, т.к. разбираюсь с этим совсем недавно, и с чем может быть связана проблема?
     
  2. Unixon

    Unixon Оракул Модератор

    По всей видимости, в программе. Код покажете?
     
  3. Eirin

    Eirin Нуб

    Написал программу, где при нажатии кнопки изменяется цвет светодиода. При работа с цифровым выводом, все работает. Если же использовать PWM, то Pin4 не выдает вообще никаких значений (код ниже). В других программах, например изменение яркости светодиода, эфект тот же, при работе с Pin0,Pin1 все в порядке, при работе с Pin3,Pin4 ничего не происходит.

    Светодиод RGB, поэтому управляется катодом. Т.е. при 255 - он отключен, при 204 светится при малой яркости.

     
  4. nailxx

    nailxx Официальный Нерд Администратор

    Хмммм… А для какой «платы» в Arduino IDE вы компилировали код?

    Подозреваю, что для Arduino Uno, т.е. на деле для ATmega328 у которого маппинг пинов на порты и другие штуки отличаются от ATtiny
     
  5. Eirin

    Eirin Нуб

    Схему подключение и необходимые файлы брал с вот этого сайта: http://hlt.media.mit.edu/?p=1229

    Соответственно после добавление файлов в папку со скетчами, появился дополнительный выбор плат, где и выбрал "ATtiny45 (w/Arduino as ISP)".

    На сайте, где я и брал файлы, указано, что только 2 вывода работают с PWM-сигналом, поэтому у меня изначально были подозрения, что может не заработать. И получается, что проблема находится в этих файлах, но мой уровень мне не позволяет разобраться в этом деле более подробно.
     
  6. nailxx

    nailxx Официальный Нерд Администратор

    Ух ты. Не знал про такую штуку. Уверен, что некая проблема кроется именно в ней. А именно ШИМ на пинах PB0 и PB1 завязаны на Timer0, с ним всё в порядке. А вот PB3/PB4 — это Timer1. Видимо он как-то неверно инициализируется. На ATmega’х Timer1 16-битный, на тиньках он 8-битный. Вероятно, в этом проблема.

    Нужно глубже копнуть суть происходящего при инициализации…
     
  7. ИгорьК

    ИгорьК Оракул Модератор

    Дело там вот в чем. В Т1 ШИМ контролируется одновременно двумя регистрами.
    Например, чтобы дергать ногу PB4, нужно писать как в регистр OCR1В, так и в регистр OCR1C.
    Контроллер, при этом, считает до числа, указанного в регистре OCR1C, выше которого заполнение достигает 100% .
    Чтобы не заморачиваться с этим, достаточно в регистр OCR1C записать 0xFF, а команду на ШИМ писать в регистр OCR1В.

    Кусочки кода, которые у меня работают:
    Код (C):


    #include <tiny85.h>
    #include <delay.h>

    void main(void)
    {
    //....
    int i;

    // Port B initialization
    // Function: Bit5=In Bit4=Out Bit3=In Bit2=In Bit1=Out Bit0=Out
    DDRB=(0<<DDB5) | (1<<DDB4) | (0<<DDB3) | (0<<DDB2) | (1<<DDB1) | (1<<DDB0);
    // State: Bit5=T Bit4=0 Bit3=T Bit2=T Bit1=0 Bit0=0
    PORTB=(0<<PORTB5) | (0<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);

    //...

    // Timer/Counter 1 initialization
    // Mode: PWMB top=OCR1C
    // OC1A output: Disconnected
    // OC1B output: OC1B=PWM, /OC1B disc.
    // Timer Period: 4 us
    // Output Pulse(s):
    // OC1B Period: 4 us
    // Timer1 Overflow Interrupt: Off
    // Compare A Match Interrupt: Off
    // Compare B Match Interrupt: Off
    PLLCSR=(0<<PCKE) | (0<<PLLE) | (0<<PLOCK);

    TCCR1=(0<<CTC1) | (0<<PWM1A) | (0<<COM1A1) | (0<<COM1A0) | (0<<CS13) | (1<<CS12) | (1<<CS11) | (0<<CS10);
    GTCCR=(0<<TSM) | (1<<PWM1B) | (1<<COM1B1) | (0<<COM1B0) | (0<<PSR1) | (0<<PSR0);
    TCNT1=0x00;
    OCR1A=0x00;
    OCR1B=0x00;
    OCR1C=0x00;
    //...

     OCR1C=255; // Указываем число, до которого считает таймер;

    while (1)
          {
       
         for (i = 0; i < 255; i++) // Развлекаемся с диодом на ноге PB4;
          {
              OCR1B=i;
              delay_ms(20);
          }
         
          for (i = 255; i > 0; i--)
          {
              OCR1B=i;
              delay_ms(20);
          }
          }
    }

     
     
    Megakoteyka, Nickstalker и nailxx нравится это.
  8. fr0ster

    fr0ster Гик

    Почему вы решили, что 255 это отключение?
    Что значит управляется катодом? RGB ведь это просто три разноцветных светодиода в одном корпусе.
    Это если подключите светодиоды через p-n-p транзистор, то будет так, как вы говорите.
    Вечером покажу код, работающий на отдельном ATtiny45.
    Использую только analogWrite.

    Вот код, что работает на ATtiny45

    Код (C):
    //обзываем выводы соответственно цвету
    int REDpin = 0;
    int GREENpin = 1;
    int BLUEpin = 2;


    void setup(){}


    void loop(){
      for(int value = 0 ; value <= 255; value +=1) {
        //яркость красного уменьшается
        analogWrite(REDpin, 255-value);
        //яркость зеленого увеличивается
        analogWrite(GREENpin, value);
        //синий не горит
        analogWrite(BLUEpin, 0);
        //пауза
        delay(30);
      }


      for(int value = 0 ; value <= 255; value +=1) {
        //красный не горит
        analogWrite(REDpin, 0);
        //яркость зеленого уменьшается
        analogWrite(GREENpin, 255-value);
        //яркость синего увеличивается
        analogWrite(BLUEpin, value);
        //пауза
        delay(30);
      }


      for(int value = 0 ; value <= 255; value +=1) {
        //яркость красного увеличивается
        analogWrite(REDpin, value);
        //зеленый не горит
        analogWrite(GREENpin, 0);
        //яркость синего уменьшается
        analogWrite(BLUEpin, 255-value);
        //пауза
        delay(30);
      }
    }
     
  9. Sarin

    Sarin Нуб

  10. fr0ster

    fr0ster Гик

    В даташитах встречаетс "Количество ШИМ (PWM) выходов: 4" например тут же в амперке в магазине.
    [​IMG]
     
  11. Sarin

    Sarin Нуб

    да, но тут есть вот какой нюанс

    при прошивке в контроллер заливается не только логика программы, но и множество различных настроек. осцилятора, например. среди прочего нужно сконфигурировать какой пин что делает. прерывания, аналоговый ввод, PWM. PWM тоже имеет различные настройки. можно использовать диапазон значений, отличный от 0..255, например. но это всё лирика, потому что процессинг скрывает такие тонкости. он настраивает контроллер неким более менее универсальным образом.

    для того чтобы решить проблему с тремя PWM-пинами можно переписать программу используя avr-gcc вместо Arduino IDE. это позволит добраться до тонких настроек пинов и сделать всё как хочется.
    в принципе, Arduino сама использует avr-gcc и avrdude, о чём радостно и подробно пишет если поставить нужную галку.
    возможно удастся переконфигурировать чип из программы и полностию на ардуино. вроде там достаточно проставить определённые значения в какие-то регистры.

    деталей я не знаю.
     
  12. Megakoteyka

    Megakoteyka Оракул Модератор

    Выше так и делали.
     
  13. fr0ster

    fr0ster Гик

    Вот у меня в проге используется ШИМ без изысков на трех выводах ATtiny45.
    Это если в МК прошит загрузчик ардуиновский.
    Выше это годится и для чистого МК.
    Насколько понимаю проблема именно в желании запустить ШИМ на трех лапах.
     
  14. fr0ster

    fr0ster Гик

    Коллеги, я тут разбирался с вопросом, так в ATtiny в действительности 2 ШИМ канала, но вывести их можно на 2 из 4 доступных пинов. Удалось запустить Таймер1 как PWM, Таймер0 как PWM.
    А вот вместе их запустить не удается пока.

    ЗЫ Я там несколько был неточен насчет 255 это погасить или зажечь светодиод на пине.
    Не учел, что светодиод от пина как к земле подключают, как привык, так и плюсу цепляют.

    ЗЗЫ Для прошивки ардуиновского кода типа analogWrite загрузчик ардуиновский не нужен. Код побольше и все.
     
    Последнее редактирование: 2 ноя 2013
  15. fr0ster

    fr0ster Гик

    Пример кода, в котором настраиваются оба таймера и мигают светодиодами с разной частотой.
    Выходит есть 2 таймера, которые можно использовать для аппаратного PWM, но выводов всего три, с которых можно снимать PWM аппаратный. При этом на трех выводах можно скважность независимо менять, а вот частоту независимо друг от друга можно на двух рулить. Точнее два из трех будут с одной и той же всегда частотой меняться, один из трех независимо.

    Код (Text):
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <avr/sleep.h>

    #define TIMER1_TOP 255    /* only 8-bit PWM possible */

    enum {
      UP, DOWN}
    ;

    ISR (TIM1_OVF_vect)
    {
      static uint16_t pwm;
      static uint8_t direction;

      switch (direction)
      {
      case UP:
        if (++pwm == TIMER1_TOP)
          direction = DOWN;
        break;

      case DOWN:
        if (--pwm == 0)
          direction = UP;
        break;
      }

      OCR1A = pwm;
      OCR1B = TIMER1_TOP-pwm;
    }

    ISR (TIM0_OVF_vect)
    {
      static uint16_t pwm;
      static uint8_t direction;

      switch (direction)
      {
      case UP:
        if (++pwm == TIMER1_TOP)
          direction = DOWN;
        break;

      case DOWN:
        if (--pwm == 0)
          direction = UP;
        break;
      }

      OCR0A = pwm;
      OCR0B = TIMER1_TOP-pwm;
    }

    void ioinit (void)
    {
      /* Timer 0 is 8-bit PWM. */
      TCCR0A = _BV(COM0A1) | _BV(WGM01) | _BV(WGM00);
      /* Timer 1 is 8-bit PWM. */
      TCCR1 = _BV(PWM1A) | _BV(COM1A1);
      GTCCR = _BV(PWM1B) | _BV(COM1B1);
      /*
        * Start timer 0.
      *
      * NB: TCCR1A and TCCR1B could actually be the same register, so
      * take care to not clobber it.
      */
      TCCR0B = _BV(CS01);
      /*
        * Start timer 1.
      *
      * NB: TCCR1A and TCCR1B could actually be the same register, so
      * take care to not clobber it.
      */
      TCCR1 |= _BV(CS12);
      GTCCR |= _BV(CS12);
      /*
        * Run any device-dependent timer 1 setup hook if present.
      */
      //OCR1C = 255;

      /* Set PWM value to 0. */
      OCR0A = 0;
      OCR0B = 0;
      OCR1A = 0;
      OCR1B = 0;
      /* Enable OC1 as output. */
      DDRB |= _BV (PB1);
      DDRB |= _BV (PB0);
      DDRB |= _BV (PB4);

      /* Enable timer 1 overflow interrupt. */
      TIMSK = _BV (TOIE1);
      /* Enable timer 0 overflow interrupt. */
      TIMSK |= _BV (TOIE0);
      sei ();
    }

    int main (void)
    {

      ioinit ();

      /* loop forever, the interrupts are doing the rest */

      for (;;){
        sleep_mode();
      }
      return (0);
    }
     
    Мопед не совсем мой, я переработал найденные в интернетах куски кода, основа взята с примера с сайта avr-lib кажется.
     
  16. fr0ster

    fr0ster Гик

    Одним словом все фигня кроме пчел, используя таймер и раздельное управление скважностью на три вывода, можно сделать ШИМ RGB управление на ATtiny45 и оно не будет софтовым.
    Вроде так сделать если, то оно будет работать.
    Кто может сказать, 100% ли это аппаратный ШИМ?
    Код (Text):
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>

    #define TIMER1_TOP 255    /* only 8-bit PWM possible */


    const int delay_tome = 1000;
    const int max_lumen = 255;
    const int min_lumen = 1;
    int pwm_pb0;
    int pwm_pb1;
    int pwm_pb4;
    enum { UP, DOWN };

    ISR (TIM1_OVF_vect)
    {
      OCR1A = pwm_pb1;
      OCR1B = pwm_pb4;
      OCR0A = pwm_pb0;
    }

    void ioinit (void)
    {
     /* Timer 0 is 8-bit PWM. */
     TCCR0A = _BV(COM0A1) | _BV(WGM01) | _BV(WGM00);
      /* Timer 1 is 8-bit PWM . */
      TCCR1 = _BV(PWM1A) | _BV(COM1A1);
      GTCCR = _BV(PWM1B) | _BV(COM1B1);
      /*
        * Start timer 0.
       */
      TCCR0B = _BV(CS00);
      /*
        * Start timer 1.
       */
      TCCR1 |= _BV(CS12);

      /* Enable output. */
      DDRB |= _BV (PB1);
      DDRB |= _BV (PB0);
      DDRB |= _BV (PB4);

      /* Enable timer 1 overflow interrupt. */
      TIMSK = _BV (TOIE1);
      sei ();
    }

    int main (void)
    {
      ioinit ();
      for (;;)   loop();
      return (0);
    }

    void loop(){
      static int st = 0;
      if (st <= 6) st++;
      pwm_pb4 = ch_red();
      if(st >= 3) pwm_pb1 = ch_green();
      if(st >= 6) pwm_pb0 = ch_blue();
      //пауза
      _delay_ms(delay_tome);
    }

    int ch_red(){
      static int direct;
      static int value = 1;
      if (value >= max_lumen){
        direct = DOWN;
      }
      else if (value <= min_lumen) {
        direct = UP;
      };
      if (direct == UP) {
        value *=2;
      }
      else {
        if (value >= min_lumen)
          value /=2;
        else value = min_lumen;
      };
      if (value >= max_lumen) value = max_lumen;
      if (value <= min_lumen) value = min_lumen;
      return value;
    }

    int ch_green(){
      static int direct;
      static int value = 1;
      if (value >= max_lumen){
        direct = DOWN;
      }
      else if (value <= min_lumen) {
        direct = UP;
      };
      if (direct == UP) {
        value *=2;
      }
      else {
        value /=2;
      };
      if (value >= max_lumen) value = max_lumen;
      if (value <= min_lumen) value = min_lumen;
      return value;
    }

    int ch_blue(){
      static int direct;
      static int value = 1;
      if (value >= max_lumen){
        direct = DOWN;
      }
      else if (value <= min_lumen) {
        direct = UP;
      };
      if (direct == UP) {
        value *=2;
      }
      else {
        value /=2;
      };
      if (value >= max_lumen) value = max_lumen;
      if (value <= min_lumen) value = min_lumen;
      return value;
    }
     
     
    Последнее редактирование: 3 ноя 2013
  17. ИгорьК

    ИгорьК Оракул Модератор

    Это аппаратный ШИМ безусловно. Да еще сдобренный прерываниями. Функция void ioinit (void) определяет режим работы двух счетчиков, которые и заведуют этим делом.
     
  18. fr0ster

    fr0ster Гик

    Собственно почему скважность таймеров ставится в прерывании одного из них, чтоб с синхронизацией не заморачиваться, Частоты таймеров одинаковые, 1/8 частоты МК. А если скважность устанавливать не из прерываний, то три светодиода яркость менять будут, но не в такт. А так все красиво вышло.
     
  19. ИгорьК

    ИгорьК Оракул Модератор

    Что Вы имеете в виду под тактом смены яркости? Вряд ли человеческий глаз заметит разницу между последовательной и одновременной сменой яркости на трех каналах. Решение, безусловно, интересное Вы нашли, но оно уже для продвинутых товарищей. То что там происходит можно сделать, ИМХО, проще и вряд ли это будет заметно на глаз.
     
  20. fr0ster

    fr0ster Гик

    Для того, чтобы изменение было заметным, яркость меняется в два раза от предыдущего значения.
    Так что все заметно человеческого глаза. Для плавного изменения цвета RGB-светодиода, процесс запускается для каждого канала со сдвигом по фазе на 120 градусов. Грубо говоря скважность ШИМ от 0 до 255, это 8 ступеней изменения яркости, соответственно пропускаем 3 цикла для второго и 6 для третьего каналов. И RGB-ШИМ работает.

    И вам отдельное мерси, с Вашего кода собственно и начался путь в разбирательстве с аппаратным ШИМ на ATtiny45 :)