Вторник, 19.03.2024, 06:29
RC - Мастерская
Главная | Каталог статей | Регистрация | Вход
Меню
Статистика
Главная » Статьи » Обмен опытом » Микроконтроллеры

Считаем ширину импульса ШИМ (вариант 2)
Считаем ширину импульса ШИМ (вариант 2)

Оборудование:

Пульт р/у - Turnigy 9XR
ВЧ-модуль - FrSky DJT
Приемник - FrSky V8FR-II
Микроконтроллер - Arduino Nano v3 втыкаем в шилд для удобства подключения. Скетч подойдет для любого AVR микроконтроллера от 168 до 2560. В принципе влезет и на 48-ю мегу. Но места там больше почти ни на что не останется.

Задача:
Хотим получить значения ширины канальных импульсов для первых шести каналов.

Решение:
AVR микроконтроллеры умеют генерить программные прерывания по изменению состояния входа. Именно этим и стоит воспользоваться. Казалось бы все достаточно просто:
- Вешаем обработчик прерывания на каждый интересующий нас вход.
- Запоминаем время перехода из 0 в 1.
- При переходе из 1 в 0 из текущего времени вычитаем запомненное и так получаем результат.
Вот только есть один подвох - микроконтроллер генерит прерывания не на каждый пин, а в целом на порт.

Для теста была взята 328-я мега. Все ее входы объединены в 3 порта: PORTB - пины 8-13 (+2 используются для подключения внешнего кварцевого резонатора), PORTC пины 14-19 (два последних пина на ардуине не распаяны для совместимости мк в разных корпусах), PORTD - пины 0-7. Мне как-то больше приглянулся PORTD(хотя по сути это не имеет значения).Для каждого порта есть соответствующий бит в регистре PCICR, разрешающий прерывания по изменению уровня на входе. Для порта D это PCIE2. Регистр DDRD задает конфигурацию пинов: 1 в соответствующем разряде задает пин как выход, а 0 - как вход. Так же есть три регистра PCMSK0:2(по одному для каждого порта), которые определяют маску - по изменению состояния каких пинов генерить прерывание.

С учетом всего выше изложенного наш алгоритм придется видоизменить следующим образом:
- Устанавливаем нужные нам пины как входы
- Вешаем обработчик прерывания на интересующий нас порт
- Определяем маску пинов для прерывания
в прерывании:
- Получаем текущее время по таймеру
- Получаем состояние интересующих нас пинов
- Получаем пины, изменившие состояние
- В цикле для каждого пина
  - если он изменил состояние
    - если переход с 0 на 1, то запоминаем текущее время для канала
    - иначе получаем ширину импульса как разность текущего и ранее запомненного времени для канала
Так же стоит учесть, что при переполнении таймера вы получите ошибку. Поэтому стоит добавить контроль на то, что ширина импульса укладывается в разумные пределы. А ошибочные значения таким образом будут игнорироваться.

Собственно ниже код тестового скетча:

void setup()
{
  // Все пины порта D кроме нулевого(TX) устанавливаем как входы
  DDRD = 1;
  // Настраиваем таймер 1 на отсчет 0.5 мкс
  TCCR1B   =   0;  
  TCCR1A   =   0;
  TCNT1   =    0;  
  TCCR1A   =   0;  
  TCCR1B   =   0<<CS12 | 1<<CS11 | 0<<CS10;//0x1A; //start timer with 1/8
  // Разрешаем прерывания порта D по изменению уровня
  PCICR |= (1 << PCIE2);
  // настраиваем маску прерывания - пины 2-7
  PCMSK2 = 0xFC;
  // открываем UART для вывода результата
  Serial.begin(9600);
}

typedef struct {
  unsigned long riseTime; // время перехода 0->1
  unsigned int  lastWidth; // ширина импульса
} tPinTimingData;
volatile static tPinTimingData pinData[6]; //массив каналов
volatile static uint8_t PCintLast;

uint8_t bit;
uint8_t curr;
uint8_t mask;
uint16_t currentTime;
uint16_t time;

// обработчик прерывания порта D
ISR(PCINT2_vect)
{
  // текущее время по таймеру 1 в мкс
  currentTime = TCNT1>>1;
  // Получаем состояние интересующих нас пинов
  curr = PIND & 0xFC;
  // пины, изменившие состояние
  mask = curr ^ PCintLast;
  PCintLast = curr;
  // в цикле просматриваем каждый пин
  for (uint8_t i=0; i < 6; i++) {
    // маска текущего пина цыкла
    bit = 0x04 << i;
    if (bit & mask) {
      // если 0->1
      if (bit & PCintLast)
        // сохраняем текущее время для канала
        pinData[i].riseTime = currentTime;
      else {
        // рассчитываем ширину импульса
        time = currentTime - pinData[i].riseTime;
        // и сохраняем ее, если таймер не наврал
        if ((time >= 800) && (time <= 2200)) {
          pinData[i].lastWidth = time;
        }
      }
    }
  }
}

void loop()
{
  // Вывод результата в UART каждые 0.5 сек.
  Serial.println();
  for (byte i = 0; i < 6; i++) {
    Serial.print("Ch");
    Serial.print((int)i + 1);
    Serial.print(": ");
    Serial.print(pinData[i].lastWidth);
    Serial.print(", ");
  }
  delay(500);
}

Категория: Микроконтроллеры | Добавил: Mactep (10.08.2013)
Просмотров: 4338 | Теги: Arduino, ширина импульса, ШИМ, PWM | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск