Если Вы зададите этот вопрос гуглу - найдете массу вариантов, советов и реализаций. Так получилось, что ни одна из имеющихся мне не подошла и я сделал свою.
Задача:
Имеется приемник системы радиоуправления. Хотим узнать ширину импульса в микросекундах для канала Х.
Использованное оборудование:
МК - клон Arduino на базе ATmega168(вполне подойдет и
такая плата или любой другой клон).
Шилд - нужен только для удобства подключения. Можете обойтись и без него, пожертвовав удобством.
Система р/у -
Турнига 9х или любая другая.
Экран - для контроля значения. Опять же использовал для удобства.
Серва - просто первая подвернувшаяся под руку.
ПО:
Arduino IDE 1.0.2Библиотека
PinChangeInt для удобства работы с прерываниями. Я пробовал и без нее - результат тот же, а с ней нагляднее. Потеря памяти меня устроила.
Библиотека для работы с экраном в архиве с исходниками. Уже не помню где нашел, но остальные память кушали совершенно нищадно и были отложены.
Библиотека
RCArduinoFastLib для управления сервой.
Подключение:
1. Питание МК берем с USB.
2. Соединяем экран посредством стандартного шлейфа с колодкой 3х2 через последовательный порт. На шилде разъем помечен как LCD Serial.
3. Приемник соединяем с пятым пином МК при помощи обычного 3х-шнура (как у сервы). Я не долго думая воткнул в первый канал. Опять же удобство шилда - без каких бы то ни было проблем приемник получает питание с МК.
4. На восьмой пин вешаем серву.
Все провода стандартные и ничего паять и обжимать не надо - спасибо шилду.
Алгоритм:
1. Вешаем на входной пин прерывание на изменение уровня.
2. В прерывании проверяем пин. Если состояние 1, то запускаем таймер. Если состояние 0, то останавливаем таймер и вычисляем значение ширины импульса.
3. Вешаем на таймер прерывание на совпадение с некоторым калибровочным значением. Пробовал прерывание по переполнению, но не устроила точность. В перывании увеличиваем на 1 некоторую переменную.
Таймеры 0 и 1 мне нужны для других целей и я решил использовать таймер 2. Но если первые - 16 бит, то этот - 8 бит. Именно поэтому и нужен пункт 3.
Скетч (меня результат устроил):
Скачать можно тутКод
#include <PinChangeInt.h>
#include "LCD12864RSPI.h"
#include <RCArduinoFastLib.h>
// Сюда подаем сигнал
#define PWM_PIN 5
// Сюда цепляем серву
#define SERVO_PIN 8
//калибровка таймера
#define TIMER_COMP_VAL 0xfa
volatile uint16_t ovf_cnt;
uint16_t val;
uint16_t val2;
volatile uint16_t temp;
volatile uint16_t temp_cnt;
String temp_str;
char cstr[16];
uint16_t val_prev;
void stop_timer()
{
TCCR2B = 0;
}
void restart_timer()
{
// обнуляем
TCNT2 = 0;
ovf_cnt = 0;
// запускаем
TCCR2B = 0<<CS22 | 1<<CS21 | 1<<CS20;
}
// Прерывание таймера как досчитает до 255
ISR(TIMER2_COMPA_vect) {
// сброс счетчика
TCNT2 = 0;
// увеличение счетчика верхних разрядов
ovf_cnt++;
}
void setup()
{
// Подключаем экран
LCDA.Initialise(); // INIT SCREEN
delay(100);
LCDA.CLEAR();//Clear screen
// Инициализируем библиотеку управления сервой
CRCArduinoFastServos::setFrameSpaceA(3,7*2000);
CRCArduinoFastServos::begin();
// Подключаем серву
CRCArduinoFastServos::attach(0, SERVO_PIN);
// Настройка таймера 2
stop_timer();
val = 0;
val_prev = 0;
TCCR2A = 0;
OCR2A = TIMER_COMP_VAL;
TIMSK2 = (1 << OCIE2A);
//Подключение прерывания по изменению сигнала на входе
PCintPort::attachInterrupt(PWM_PIN, calc, CHANGE);
//Запуск таймера
restart_timer();
}
void loop()
{
val = (temp_cnt*TIMER_COMP_VAL + temp);
val = map(val,0,TIMER_COMP_VAL*4,0,2000);
if (val != val_prev) {
val_prev = val;
// вывод значения на экран
temp_str = String(val_prev, DEC);
temp_str.toCharArray(cstr, temp_str.length()+1);
LCDA.DisplayString(0,0,(unsigned char*)cstr, temp_str.length());//
// установка позиции сервы
CRCArduinoFastServos::writeMicroseconds(0,val_prev);
}
}
void calc()
{
stop_timer();
if(PCintPort::pinState)
{
// начало импульса - стартуем таймер
restart_timer();
}
else
{
// Конец импульса - считаем что получилось
temp = TCNT2;
temp_cnt = ovf_cnt;
}
}