БАНО на Arduino
может кому пригодиться.
Сразу предупреждаю, если Вас тошнит от языков программирования чуть более высокого уровня чем ассемблер, если Вы считаете, что за использование целой ардуины для “помигать лампочками” необходимо отрывать руки - поберегите свои нервы и просто не читайте это сообщение.
Итак. Данный проектик “одного вечера” был больше года назад реализован. Вчера я про него вспомнил, отрыл исходники, собрал на макетке с использованием подвернувшейся под руку Arduino Nano на базе 328-й атмеги, и оно заработало =)
Итак. что нам нужно:
Ардуина
6 светодиодов с резисторами
макетная плата - по желанию
подключаем светодиоды к портам D7, D8, D9, D10, D12 и D13
создаем в Arduino IDE новый скетч и копируем туда этот код
#define MSG_SERIAL 0
const int maxTaskCount = 16;
// базовый класс для запускаемой задачи
class TaskInfo {
private:
int _priority;
unsigned int _timer; // таймер-счетчик задачи. когда он равен нулю - будет вызван метод Proc() задачи
public:
inline TaskInfo() { _priority = 0; _timer=0; } // конструктор по умолчанию
inline TaskInfo(int priority) { _priority = priority; _timer=0; } // конструктор с параметром - задает приоритет задачи
inline int GetPriority () { return _priority; } // получить приоритет задачи
inline unsigned int GetTimer() { return _timer; } // возвращает количество тиков таймера до следующего вызова
inline void SetTimer ( unsigned int value ) { _timer = value; } // устанавливаем количество тиков таймера до следующего вызова
inline void Tick() { if(_timer > 0) _timer--; } // уменьшаем значение счетчика
virtual inline void HandleMsg (unsigned int msg, unsigned int arg ) { return; } // обработчик сообщений о системных событиях
virtual unsigned int Proc(void); // исполнемый метод
};
//----------------------------------------------------------
// менеджер задач
class TaskManager {
private:
int _taskCnt; // количество задач в списке
TaskInfo * _taskList[maxTaskCount]; // список задач
volatile int _tick; // признак сработавшего таймера
public:
inline TaskManager() { _taskCnt = 0; _tick = 0; } // конструктор
void AddTask (TaskInfo * task) ; // добавить задачу в очередь
void Tick(); // метод вызываемый при срабатывании таймера
void Run(); // запустить задания в очереди на выполнение
void HandleMsg (unsigned int msg, unsigned int arg ); // обработчик внешних событий. передает данные задачам в очереди
};
TaskManager taskManager; // глобальный объект - менеджер заданий
void TaskManager::AddTask (TaskInfo * task)
{
_taskList[_taskCnt] = task;
_taskCnt++;
}
void TaskManager::Tick()
{
_tick = 1;
}
void TaskManager::Run()
{
int i;
int taskIndex = -1;
int maxPriority = -1000;
int taskPriority;
// уменьшаем счетчики у задач
if (_tick)
{
for (i = 0; i < _taskCnt; i++)
{
( _taskList[i] )->Tick();
}
_tick = 0;
}
// поиск задачи с максимальным приоритетом
for ( i = 0; i < _taskCnt; i++)
{
taskPriority = (_taskList[i])->GetPriority();
if ((_taskList[i]->GetTimer() == 0) && (taskPriority > maxPriority))
{
taskIndex = i;
maxPriority = taskPriority;
}
}
// если нашли - запускаем
if (taskIndex >= 0)
{
_taskList[taskIndex]->SetTimer((_taskList[taskIndex])->Proc());
}
//
}
void TaskManager::HandleMsg (unsigned int msg, unsigned int arg )
{
int i;
for ( i = 0; i < _taskCnt; i++)
{
_taskList[i]->HandleMsg (msg, arg);
}
}
//----------------------------------------------------------
// Обычная "мигалка" c двумя состояниями и настраиваемыми интервалами
class BlinkTask : public TaskInfo {
private:
byte _state;
unsigned int _activeTime;
unsigned int _inactiveTime;
int _pin;
int _enabled;
public:
BlinkTask (int pin, unsigned int activeTime, unsigned int inactiveTime);
void HandleMsg (unsigned int msg, unsigned int arg );
unsigned int Proc(void);
};
BlinkTask::BlinkTask (int pin, unsigned int activeTime, unsigned int inactiveTime)
{
TaskInfo(0); //вызов конструктора базового класса
_activeTime = activeTime;
_inactiveTime = inactiveTime;
_state = 0;
_pin = pin;
_enabled = 1;
pinMode(_pin, OUTPUT);
}
unsigned int BlinkTask::Proc(void)
{
if (_enabled)
{
_state = ( _state + 1 ) & 0x01;
digitalWrite(_pin, _state);
return (_state == 0)?_inactiveTime:_activeTime;
}
else
{
digitalWrite(_pin, LOW);
return _inactiveTime;
}
}
void BlinkTask::HandleMsg (unsigned int msg, unsigned int arg )
{
if (msg == MSG_SERIAL)
{
if (arg == 49)
{
Serial.println("Blink ON");
_enabled = 1;
}
if (arg == 48)
{
Serial.println("Blink ON");
_enabled = 0;
}
}
}
//---------------------------------------------------------
// просто фары с функцией вкл-выкл (символы 2 и 3 с посл. порта)
class OnOffLightTask : public TaskInfo {
private:
byte _state;
unsigned int _pauseTime;
int _pin;
int _enabled;
public:
OnOffLightTask (int pin);
void HandleMsg (unsigned int msg, unsigned int arg );
unsigned int Proc(void);
};
OnOffLightTask::OnOffLightTask (int pin)
{
TaskInfo(0); //вызов конструктора базового класса
_state = 0;
_pauseTime = 50;
_pin = pin;
pinMode(_pin, OUTPUT);
}
unsigned int OnOffLightTask::Proc(void)
{
if(_state == 0)
{
digitalWrite(_pin, LOW);
}
else
{
digitalWrite(_pin, HIGH);
}
return _pauseTime;
}
void OnOffLightTask::HandleMsg (unsigned int msg, unsigned int arg )
{
if (msg == MSG_SERIAL)
{
if (arg == 51)
{
_state = 1;
Serial.println("OnOffLight ON");
}
if (arg == 50)
{
_state = 0;
Serial.println("OnOffLight OFF");
}
}
}
//---------------------------------------------------------
// стробоскоп с двумя вспышками и паузой
class StrobeTask : public TaskInfo {
private:
byte _state;
unsigned int _activeTime;
unsigned int _inactiveTime;
unsigned int _pauseTime;
int _pin;
int _enabled;
public:
StrobeTask (int pin, unsigned int activeTime, unsigned int inactiveTime, unsigned int pauseTime);
void HandleMsg (unsigned int msg, unsigned int arg );
unsigned int Proc(void);
};
StrobeTask::StrobeTask (int pin, unsigned int activeTime, unsigned int inactiveTime, unsigned int pauseTime)
{
TaskInfo(0); //вызов конструктора базового класса
_activeTime = activeTime;
_inactiveTime = inactiveTime;
_pauseTime = pauseTime;
_state = 0;
_pin = pin;
_enabled = 1;
pinMode(_pin, OUTPUT);
}
unsigned int StrobeTask::Proc(void)
{
unsigned int pause;
if (_enabled == 0)
{
digitalWrite(_pin, LOW);
_state=0;
return _pauseTime;
}
switch(_state) {
case 0: digitalWrite(_pin, HIGH);
pause = _activeTime;
break;
case 1: digitalWrite(_pin, LOW);
pause = _inactiveTime;
break;
case 2: digitalWrite(_pin, HIGH);
pause = _activeTime;
break;
case 3: digitalWrite(_pin, LOW);
pause = _pauseTime;
break;
}
_state = _state + 1 ;
if (_state == 4)
_state = 0;
return pause;
}
void StrobeTask::HandleMsg (unsigned int msg, unsigned int arg )
{
if (msg == MSG_SERIAL)
{
if (arg == 49)
{
_enabled = 1;
Serial.println("Strobe ON");
}
if (arg == 48)
{
_enabled = 0;
Serial.println("Strobe OFF");
}
}
}
//---------------------------------------------------------
// мигалка с плавным поджиганием. с настройкой скорости
class FadeTask : public TaskInfo {
private:
byte _state; // текущее состояние
int _pin;
unsigned int _sampleTime; // скорость работы. чем меньше - тем быстрее мигает
public:
FadeTask (int pin, unsigned int sampleTime); // конструктор
unsigned int Proc(void); // обработчик
};
FadeTask::FadeTask (int pin, unsigned int sampleTime)
{
TaskInfo(0);
_state = 0;
_pin = pin;
_sampleTime = sampleTime;
pinMode(_pin, OUTPUT);
}
unsigned int FadeTask::Proc(void)
{
switch (_state) {
case 0: analogWrite(_pin, 1);
break;
case 1: analogWrite(_pin, 2);
break;
case 2: analogWrite(_pin, 4);
break;
case 3: analogWrite(_pin, 6);
break;
case 4: analogWrite(_pin, 8);
break;
case 5: analogWrite(_pin, 10);
break;
case 6: analogWrite(_pin, 12);
break;
case 7: analogWrite(_pin, 15);
break;
case 8: analogWrite(_pin, 255); // максимум
break;
case 9: analogWrite(_pin, 15);
break;
case 10: analogWrite(_pin, 12);
break;
case 11: analogWrite(_pin, 10);
break;
case 12: analogWrite(_pin, 8);
break;
case 13: analogWrite(_pin, 6);
break;
case 14: analogWrite(_pin, 4);
break;
case 15: analogWrite(_pin, 2);
break;
}
_state++;
if (_state >= 16)
{
_state = 0;
}
return (_state == 9)?_sampleTime * 5:_sampleTime;
}
//---------------------------------------------------------
void setup()
{
Serial.begin(9600);
taskManager.AddTask(new FadeTask(10, 40)); // на D10 - мигалка PWM.
taskManager.AddTask(new FadeTask(9, 41)); // на D10 - мигалка PWM.
taskManager.AddTask(new BlinkTask(13, 100, 500)); // на D13 - обычная мигалка в возможностью включения/выключения (символы 1 и 0 через Serial port ) вкл - 100 тиков, выкл - 500 тиков
taskManager.AddTask(new StrobeTask(12, 100, 100 , 999)); // на D12 - двойной стробоскоп. вкл 2 раза 100 тиков с паузой между ними в 100 тиков. между сериями - 999 тиков
taskManager.AddTask(new BlinkTask(8, 99, 500)); // на D13 - обычная мигалка в возможностью включения/выключения (символы 1 и 0 через Serial port ) вкл - 100 тиков, выкл - 500 тиков
taskManager.AddTask(new OnOffLightTask(7)); // "фары" на D7. вкл/выкл символы 3 и 2 в терминале
// настройка таймера 2 (первый нельзя использоватьтак как он нужен для работы PWM analogWrite )
noInterrupts(); // disable all interrupts
//Timer2 Settings: Timer Prescaler
TCCR2B |= ((1<<CS22) | (0<<CS21) | (0<<CS20));
// Use normal mode
TCCR2B |= (0<<WGM21) | (0<<WGM20);
// Use internal clock - external clock not used in Arduino
ASSR |= (0<<AS2);
TIMSK2 |= (1<<TOIE2) | (0<<OCIE2A); //Timer2 Overflow Interrupt Enable
interrupts(); // разрешаем прерывания
}
ISR(TIMER2_OVF_vect) // обработка прерывания от таймера2
{
taskManager.Tick();
}
void loop() // главный цикл программы
{
byte incomingByte;
taskManager.Run();
if (Serial.available() > 0) { //если есть доступные данные
// считываем байт
incomingByte = Serial.read();
// отправляем сообщение в очередь задач
Serial.print("Command:");
Serial.println((int)incomingByte);
taskManager.HandleMsg(MSG_SERIAL, (int)incomingByte );
}
}
можем открыть окно терминала COM порта, и поиграться с включением/выключением светодиодов ( команды 1,2,3,4 )
Может быть кому-то будет полезно. Не вижу никаких преград чтобы вместо команд из компорта сделать захват ППМ от приемника.