Контроллер освещения и сирены аэросаней/лодки

Контроллер освещения и сирены аэросаней/лодки
Задумывался как услада для глаз и слуха 😁. Подключается к трем каналам управления - CH1 влево-вправо, CH2 - вперед-назад, CH3 - дополнительный (AUX). Имеет 9 выходов - 8 светодиодных через ULN2308 и один для мощной сирены через FET.
Симуляция выполнена в PROTEUS, после переноса в реал все заработало сразу - фантастика. К контроллеру можно подключить отладочный LCD, за счет некоторых выходных каналов (см. исходник), но я это делал только в PROTEUS. При симуляции лучше выкинуть FET, из-за него PROTEUS глючит и не может моделировать в нем переходные процессы.

Алгоритм работы понятен из программы (на PICC), но вы целом таков:

Алгоритм работы

При включении мигается всеми каналами несколько раз;
Определяется исходное положение канала CH1 (влево-вправо) и запоминается, как его центральная точка;
Входит в цикл;

Каналы считываются по прерыванию;
Если в каналах CH1 или CH2 ошибка - мигает всеми лампочками и верещит. Эти каналы считаются обязательными.
Ошибка в канале CH3 (AUX) просто сохраняет старое значение. Можно его не подключать вообще.

Если CH2 в нейтрали, горят задние красные светодиоды (тормоз).
Если CH2 в положении Взад, горят задние белые светодиоды (задний ход).
Если CH1 в положении Влево, мигает задний красный Левый светодиод.
Если CH1 в положении Вправо, мигает задний красный Правый светодиод.
Если CH3 на ~1/3 от минимума, включается белый ореол спереди на тоннеле вокруг мотора.
Если CH3 на ~2/3 от минимума, включается дополнительно сине-красная мигалка сверху на тоннеле над мотором.
Если CH3 на на максимуме, включается дополнительно сирена ФБР из набора MasterKit c 50ом 0.5W динамиком.
Бортовые светодиодные ленты имитируют “бегущий огонь”, зависящий от скорости и направления, если CH2 не в нейтрали.

Файлы

Схема, исходник, прошивка: BoatController.rar.html

Схема полностью: (также на fotki.yandex.ru/users/prikupets/album/43467/?p=0)

Просьба к понимающим тонкости обработки RC-каналов
Я старался сдалать так, чтобы порядок следования каналов был не важен - хоть все одновременно. Но иногда алгоритм сбоит и проскакивают ложные значения каналов. Побороть проблему я не смог и сделал пост-фильтрацию - чтобы логическое состояние внутренней переменной, связанной с каналом, изменилось, надо чтобы она несколько циклов подряд была в одном значени (подавление дребезга).
Кстати, ложные срабатывания удобно отлавливать в PROTEUS. 😒
Кто знает, как модифицировать обработчик прерываний, чтобы он не давал ложных считываний? 😵

  • 2719
Comments
Syberian

Кто знает, как модифицировать обработчик прерываний,
 Нужно первым делом после входа в процедуру считать значение порта в 8-битную переменную, затем слить таймер, затем уже работать с ними. У вас условия напрямую с портом работают, хотя значение могло давно поменяться.
И не используйте непрямую адресацию (массивы) в прерываниях! только глобальные переменные. Времени жрет дико.

Prikupets

Спасибо за советы, все верно, но я не могу согласиться, что у меня не так, как Вы написали.

Я первым делом считываю TMR1 в t. Цикл do нужен для повторного выполнения обработчика прерывания если за время первого его выполнения пришло еще одно прерывание - при этом оно обрабатывается без потерь времени на вход/выход в прерывание. С портов я при этом ничего не читаю, только с памятью.

Дальше - массивы. Я работаю с константными индексами, следовательно адрес ячейки массива вычисляется при компиляции, и обращение к элементу массива в результате не отличается от обычной переменной.

Syberian

С таймером у Вас как раз все нормально.
if (RX1_LEFT_RIGHT != RX1_signal) - вот что я имею ввиду, таймер считан один раз, а работа с физ. пинами разбросана по всему обработчику. Это означает, что определенному значению пина может соответствовать уже другая метка времени, с разницей в пару мкс. В статике ничего страшного, но при изменении состояния будет “дребезг”. Надо вычитывать TRISA в переменную одновременно с таймером, а потом работать уже только с ней. Тогда, кстати, и цикл не нужен, можно все условия пробегать за один раз.
В сигнале с приемника дребезг отсутствует как класс.

Prikupets

Хмм. Наверное про пару мкс верно, попробую так сделать. Но там скачки были на 200 мкс, что уж совсем не укладывается в это объяснение.

P.S. Наверное имелось ввиду PORTA а не TRISA…

Prikupets

Переделал как Syberian написал, вроде стало получше (в Proteus). Дрожание не больше 7 мкс.
Спасибо, отличный совет!

Вот изменения:

#define RX3_AUX 0b00001000 // RA3
#define RX2_FORWARD_BACKWARD 0b00010000 // RA4
#define RX1_LEFT_RIGHT 0b00100000 // RA5

static volatile byte _PORTA;

static void interrupt
isr(void)   // Here be interrupt function - the name is unimportant.
{
  // handle the interrupt (insert application code here)
// needs 3-4 cycles to get into interrupt;
  uint16 t;
  byte portA = PORTA;
  byte delta = portA ^ _PORTA;
  // Reading TMR1 16 bit value in the safe manner;
  t = TMR1;
  if (TMR1H != *((byte*)(&t)+1) )
    t = TMR1;

  if (delta & RX1_LEFT_RIGHT) {
    if (portA & RX1_LEFT_RIGHT) { // front edge;
      RX_timer[CH1] = t;
    } else {
      RX_channel[CH1] = t-RX_timer[CH1];
    }
  }
  if (delta & RX2_FORWARD_BACKWARD) {
    if (portA & RX2_FORWARD_BACKWARD) { // front edge;
      RX_timer[CH2] = t;
    } else {
      RX_channel[CH2] = t-RX_timer[CH2];
    }
  }
  if (delta & RX3_AUX) {
    if (portA & RX3_AUX) { // front edge;
      RX_timer[CH3] = t;
    } else {
      RX_channel[CH3] = t-RX_timer[CH3];
    }
  }
  input_error = 0;
  _PORTA = portA;
  RAIF = 0;  // reset flag
}
Syberian

Рад, что получилось 😃

Тут еще поправочку внесу:

byte portA = PORTA;
byte delta = portA ^ _PORTA;
// Reading TMR1 16 bit value in the safe manner;
t = TMR1;
if (TMR1H != *((byte*)(&t)+1) )
t = TMR1;

желательно переставить так:

portA = PORTA;
// Reading TMR1 16 bit value in the safe manner;
t = TMR1;
if (TMR1H != *((byte*)(&t)+1) )
t = TMR1;
delta = portA ^ _PORTA;

Тогда джиттер будет еще меньше. Причем portA и delta определить как глобальные в шапке.
Потому что иначе память на них будет выделяться из data stack каждый раз заново, а это - лишние микросекунды.
Если в прерывании не используются локальные переменные,
вход будет проводиться быстрее.

Prikupets

Супер, спасибо за поправку!

Кстати, на PIC16 стек для переменных не используется, все сохраняется в глобальных переменных и так. Даже регистр W и флаги. А поскольку переменные, используемые в прерывании, описаны как volatile, они не будут делить один и тот же адрес с другими переменными из других областей видимости, даже если оптимизатор этого захочет. Так что выносить их в статические вроде смысла нет 😃

Prikupets

Выложено на GCode code.google.com/p/…/list
BoatController.rar