ЭЛЕКТРОНИКА Attiny13 прошивка на C. Прошу помощи в реализации ШИМ.

Strannik

✩✩✩✩✩✩✩
5 Дек 2018
15
0
Доброго дня! Учусь писать прошивки для AtTiny13. Проект предполагает, что при нажатии кнопки сначала загориться 1 светодиод, через 20 секунд он помигает и загорится другой светодиод. Возник вопрос, а как вместо мигания сделать затухание через ШИМ? Так как памяти в attiny очень мало, я попытался писать на СИ, что дало мне существенную экономию места, но как реализовать ШИМ я не разобрался.

Привожу код:
C:
#include <avr/io.h>
#include <util/delay.h>

int timer_protect = 20; //активация 1 режима
int timer_zombie = 20; //активация 2 режима

int main(void)
{
  DDRB &= ~ (1 << 2); // устанавливаем вывод PB2 как вход кнопки (не забыть подтянуть её на землю через резистор)
  DDRB |= (1 << 0); // устанавливаем вывод PB0 как выход
  DDRB |= (1 << 1); // устанавливаем вывод PB1 как выход

  while (1) {
    if (PINB & (1 << PINB2)) { //если нажали кнопку
      PORTB |= (1 << 0); // устанавливаем высокий уровень на выводе PB0
      PORTB &= ~(1 << 1); // устанавливаем низкий уровень на выводе PB1
      _delay_ms(timer_protect * 1000); // ждем 1й таймер
      for (int i = 0; i < 10; i++) {
        PORTB &= ~(1 << 0); // попеременно мигаем двумя светодиодами
        PORTB |= (1 << 1);
        _delay_ms(300);
        PORTB |= (1 << 0);
        PORTB &= ~(1 << 1);
        _delay_ms(300);
      }
      PORTB &= ~(1 << 0); // устанавливаем низкий уровень на выводе PB4
      PORTB |= (1 << 1); // устанавливаем высокий уровень на выводе PB3
      _delay_ms(timer_zombie * 1000);  // ждем 2й таймер
    } else {
      PORTB &= ~(1 << 0);
      PORTB &= ~(1 << 1); //если ничего не нажато либо истек 2 таймер - выключаем все.
    }
  }
  return 0;
}
 

Sergo_ST

★★★★★★✩
15 Мар 2020
959
825
Вот попробуйте:
C:
#include <avr/io.h>
#include <util/delay.h>

#define SPEED 2 //скорость вспышек(быстрее 1..2..3..4..5 медленнее)
#define TIME 10 //количество циклов мерцания(1..255)

#define timer_protect 20 //активация 1 режима
#define timer_zombie 20 //активация 2 режима

int main(void)
{
  DDRB &= ~ (1 << 2); // устанавливаем вывод PB2 как вход кнопки (не забыть подтянуть её на землю через резистор)
  DDRB |= (1 << 0); // устанавливаем вывод PB0 как выход
  DDRB |= (1 << 1); // устанавливаем вывод PB1 как выход

  TCCR0A = (0x01 << WGM01 | 0x01 << WGM00); //режим fastPWM
  TCCR0B = SPEED; //пределитель

  while (1) {
    if (PINB & (1 << PINB2)) { //если нажали кнопку
      PORTB |= (1 << 0); // устанавливаем высокий уровень на выводе PB0
      PORTB &= ~(1 << 1); // устанавливаем низкий уровень на выводе PB1
      _delay_ms(timer_protect * 1000); // ждем 1й таймер
      _flash(); // попеременно мигаем двумя светодиодами
      PORTB &= ~(1 << 0); // устанавливаем низкий уровень на выводе PB0
      PORTB |= (1 << 1); // устанавливаем высокий уровень на выводе PB1
      _delay_ms(timer_zombie * 1000);  // ждем 2й таймер
    } else {
      PORTB &= ~(1 << 0);
      PORTB &= ~(1 << 1); //если ничего не нажато либо истек 2 таймер - выключаем все.
    }
  }
  return 0;
}
//----------------------------------------------------------------------------------
void _flash(void)
{
  boolean drv = 0;
  TCCR0A |= (0x01 << COM0A1 | 0x01 << COM0B1); //подключаем OCR0A и OCR0B

  for (uint8_t timer = 0; timer < TIME;) { //таймер циклов переключений
    TIFR0 |= (0x01 << OCF0A ); //сбрасываем флаг переполнения таймера
    while (!(TIFR0 & (0x01 << OCF0A ))); //ждем установки флага переполнения
    switch (drv) {
      case 0: //режим прибавления
        if (OCR0A < 255) OCR0B = (++OCR0A ^ 0xFF); //если регистр шим OCR0A не переполнен, прибавляем единицу и записываем инверсное значение в OCR0B
        else {
          drv = 1; //переводим в режим убавления
          timer++; //добавляем цикл таймеру переключений
        }
        break;
      case 1: //режим убавления
        if (OCR0A > 0) OCR0B = (--OCR0A ^ 0xFF); //если регистр шим OCR0A не переполнен, убавляем единицу и записываем инверсное значение в OCR0B
        else {
          drv = 0; //переводим в режим прибавления
          timer++; //добавляем цикл таймеру переключений
        }
        break;
    }
  }
  TCCR0A &= ~(0x01 << COM0A1 | 0x01 << COM0B1); //отключаем OCR0A и OCR0B
  PORTB &= ~(0x01 << 0 | 0x01 << 1); //переводим пины PB0 и PB1 в низкий уровень
}
 
Изменено:
  • Красота! +2
Реакции: Strannik

Sergo_ST

★★★★★★✩
15 Мар 2020
959
825
Пожалуйста)

Не гаснет во время мерцания?
 
Изменено:

Strannik

✩✩✩✩✩✩✩
5 Дек 2018
15
0
Пожалуйста)

Не гаснет во время мерцания?
Да, не гаснет во время мерцания. Тоесть затухает, но не гаснет до конца. Возможно связано с тем что синий светодиод ярче из-за чего тайминги может и правильные, но визуально не видно что синий погас до конца. Я думаю просто делей повесить на 100мс
 

Sergo_ST

★★★★★★✩
15 Мар 2020
959
825
Да такое может быть, тк светодиоды полностью отключаются только на 1 такт из 256.
Дилей можно добавить после:
C++:
timer++; //добавляем цикл таймеру переключений
 
  • Лойс +1
Реакции: Strannik

Strannik

✩✩✩✩✩✩✩
5 Дек 2018
15
0
Да такое может быть, тк светодиоды полностью отключаются только на 1 такт из 256.
Дилей можно добавить после:
C++:
timer++; //добавляем цикл таймеру переключений
Добавил
C++:
case 1: if (OCR0A > 0) OCR0B = (--OCR0A ^ 255); else {drv = 0; timer++; _delay_ms(500);} break;
и стало даже лучше! Зеленый задерживается на полсекунды что позволяет лучше его считать.
 

Sergo_ST

★★★★★★✩
15 Мар 2020
959
825
Для симметрии можно добавить задержку после каждого прибавления таймера.