Считаем ширину импульса ШИМ (вариант 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); } |