Вторник, 19.03.2024, 12:23
RC - Мастерская
Главная | Каталог статей | Регистрация | Вход
Меню
Статистика
Главная » Статьи » Чертежи и проекты » Микроконтроллеры

Как получить числовое значение ширины ШИМ-сигнала?
Как получить числовое значение ширины ШИМ-сигнала?

Если Вы зададите этот вопрос гуглу - найдете массу вариантов, советов и реализаций. Так получилось, что ни одна из имеющихся мне не подошла и я сделал свою.

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

Использованное оборудование:
МК - клон 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;
  }
}

Категория: Микроконтроллеры | Добавил: Mactep (05.01.2013)
Просмотров: 3729 | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск