Телескопы покупают здесь


A A A A Автор Тема: Платформа Arduino: полезные советы и решения  (Прочитано 4914 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн xdАвтор темы

  • *****
  • Сообщений: 17 977
  • Благодарностей: 378
    • Skype - deimos.belastro.net
  • Награды Открытие комет, астероидов, сверхновых звезд, научно значимые исследования.
    • Сообщения от xd
    • Белорусская любительская астрономическая сеть
Попробуем. Если что, новичковую тему можно отдельно отрезать.
У природы нет плохой погоды, у неё просто на нас аллергия.

Учение без размышления бесполезно, но и размышление без учения опасно /Конфуций/
Слово есть поступок. /Л. Толстой/

Оффлайн Ivan7enych

  • Модератор
  • *****
  • Сообщений: 12 054
  • Благодарностей: 762
  • Ионов Иван
  • Награды Победитель ежегодного конкурса астрофото
    • Сообщения от Ivan7enych
    • Астропроекты и астрософт
а зачем на атмеге делать многозадачность? ее прелесть именно в реалтайме, в аппаратных прерываниях, сажаешь функцию на прерывание по таймеру и получаешь строгую последовательность вызовов функции.

На счет среды разработки - есть бесплатная Atmel Studio, умеет конвертировать скетчи ардуины со всеми нужными библиотеками в отдельный проект где видны и доступны все компилируемые исходники.
Видео отчеты мастерской
телескопы - 230мм/4 самодельный ньютон для поездок, Televue NP101is на удаленке, 500мм ньютон в постройке.
Просьбы о ремонте пишите мне в телеграм, не в личку.

Оффлайн Александр (AnDom)

  • *****
  • Сообщений: 9 032
  • Благодарностей: 288
    • Сообщения от Александр (AnDom)
Попробуем. Если что, новичковую тему можно отдельно отрезать.

Было бы неплохо, т.к. вопросов, кмк, будет масса :)
Я видел М51 в Серегин 20"РК, а М42 в 650мм ньютон!!! :)

Оффлайн xdАвтор темы

  • *****
  • Сообщений: 17 977
  • Благодарностей: 378
    • Skype - deimos.belastro.net
  • Награды Открытие комет, астероидов, сверхновых звезд, научно значимые исследования.
    • Сообщения от xd
    • Белорусская любительская астрономическая сеть
Ну дык не каждый проект состоит из двух кнопочек :)
Если делать что-то, взаимодействующее с пользователем, то многозадачность очень даже то, что надо.
У природы нет плохой погоды, у неё просто на нас аллергия.

Учение без размышления бесполезно, но и размышление без учения опасно /Конфуций/
Слово есть поступок. /Л. Толстой/

Оффлайн Alexandrid

  • *****
  • Сообщений: 3 531
  • Благодарностей: 348
  • Есть умные, есть начитанные, но двух в одном нет..
    • Сообщения от Alexandrid
А из-за чего delay в прерываниях не работает как обычно?
Какой реально ресурс записи при использовании eeprom?
Avalon Instruments - Takahashi - QHY+ZWO - Leica - Swarovski Optic.

Оффлайн xdАвтор темы

  • *****
  • Сообщений: 17 977
  • Благодарностей: 378
    • Skype - deimos.belastro.net
  • Награды Открытие комет, астероидов, сверхновых звезд, научно значимые исследования.
    • Сообщения от xd
    • Белорусская любительская астрономическая сеть
Обработчик прерывания по таймеру обновляет глобальную переменную, которую читает delay. В atmega нет поддержки вложенных прерываний, поэтому, пока контроллер находитя в прерывании, нет возможности получить другие прерывания.
У природы нет плохой погоды, у неё просто на нас аллергия.

Учение без размышления бесполезно, но и размышление без учения опасно /Конфуций/
Слово есть поступок. /Л. Толстой/

Оффлайн Alexandrid

  • *****
  • Сообщений: 3 531
  • Благодарностей: 348
  • Есть умные, есть начитанные, но двух в одном нет..
    • Сообщения от Alexandrid
В прерывании delay работает как-то или вообще не работает?
Avalon Instruments - Takahashi - QHY+ZWO - Leica - Swarovski Optic.

Оффлайн xdАвтор темы

  • *****
  • Сообщений: 17 977
  • Благодарностей: 378
    • Skype - deimos.belastro.net
  • Награды Открытие комет, астероидов, сверхновых звезд, научно значимые исследования.
    • Сообщения от xd
    • Белорусская любительская астрономическая сеть
Если интервал ожидания короткий, и переполнения таймера TIMER0 не произошло за время ожидания, то должна сработать. Но лучше ней не пользоваться совсем.
Обычно в таких случаях в прерывании устанавливается volatile bool переменная, которая ожидается и сбрасывается в main (или loop).
У природы нет плохой погоды, у неё просто на нас аллергия.

Учение без размышления бесполезно, но и размышление без учения опасно /Конфуций/
Слово есть поступок. /Л. Толстой/

Оффлайн xdАвтор темы

  • *****
  • Сообщений: 17 977
  • Благодарностей: 378
    • Skype - deimos.belastro.net
  • Награды Открытие комет, астероидов, сверхновых звезд, научно значимые исследования.
    • Сообщения от xd
    • Белорусская любительская астрономическая сеть
volatile bool pin2trigger, pin3trigger;

void interruptCallback2() {
  pin2trigger = true;
}

void interruptCallback3() {
  pin3trigger = true;
}

void setup() {
  attachInterrupt(2, interruptCallback2, RISING);
  attachInterrupt(3, interruptCallback3, RISING);
}

void loop() {
  if (pin2trigger) {
    pin2trigger= false;
    // do something with interrupt
 }
 
 if (pin3trigger) {
   pin3trigger = false;
   // do something
 }
}
У природы нет плохой погоды, у неё просто на нас аллергия.

Учение без размышления бесполезно, но и размышление без учения опасно /Конфуций/
Слово есть поступок. /Л. Толстой/

Оффлайн xdАвтор темы

  • *****
  • Сообщений: 17 977
  • Благодарностей: 378
    • Skype - deimos.belastro.net
  • Награды Открытие комет, астероидов, сверхновых звезд, научно значимые исследования.
    • Сообщения от xd
    • Белорусская любительская астрономическая сеть
Продолжу свой монолог :)

В прошлый раз было описано нечто похожее на то, что в некоторых языках программирования называется сопрограммы (coroutine). Это по сути фукнция, которая может быть прервана, а затем продолжена с того же места. В языках, которые поддерживают её явно, для этого используются операторы или функции вроде yield, suspend и т.п.
У описанного подхода есть ограничение: поскольку происходит по сути вызов функции каждый раз заново, то и локальные переменные тоже создаются при входе и разрушаются при выходе. Это значит, что объявленная локальная переменная не сохраняется до следующего вызова. Вот если взять код вроде такого (псевдокод)
TASK SomeTask(int *state)
{
int a = 0;
print(a++);
YIELD; //!
print(a++);
}
то переменная a будет разрушена после первого выхода (помечено //! ) и создана заново при повторном. Таким образом, всё, что описывает состояние, необходимо вынести наружу. Куда - это отдельный вопрос, о нём ниже.
Разумеется, локальные переменные всё равно могут существовать в коде, однако они должны объявляться и использоваться только в одной секции, которая гарантированно выполняется как единое целое, то есть между вытеснениями задачи:
TASK SomeTask(int *state)
{
{
int a = 1;
print(a);
}
YIELD; //!
{
int b = 2;
print(b);
}
YIELD;
print("Finished");
}

Теперь надо решить, как разместить состояние.
Есть два подхода к решению этой задачи: в стиле С и в стиле С++.
В первом случае можно объявить что-то вроде такого:


struct TaskData {
TASK_STATE;
int a;
int b;
};
TASK(SomeTask)
{
INIT_TASK(TaskData);
for (data->a = 0; data->a < 100; ++data->a)
{
print(data->a);
YIELD;
}
TASK_END;
}
И код для передачи задачи планировщику будет выглядеть примено так:

int main()
{
struct Scheduler scheduler;
struct TaskData data = {0, 0};
AddTask(&scheduler, SomeTask, &data);

RunTasks(&scheduler);
}
Макросы будут имет примерно такой вид:
#define TASK(CALLBACK) int CALLBACK(int* _state, void* _data)
#define TASK_STATE int _state;
#define INIT_TASK(DATA) struct DATA *data = (struct DATA*)_data;
Переключение опять-таки пока не рассматриваем.

Решение в стиле С++ основано на наследовании
struct Task
{
int _state;
virtual bool Step() = 0;
}

class SomeTask : private Task
{
int a;

virtual bool Step() override
{
for (a = 0; a < 100; ++a)
{
print(a);
YIELD;
}
}
public:
SomeTask() : a(){}
}
Ну и планирование:
int main()
{
Scheduler scheduler;
SomeTask task;
scheduler << task;
scheduler.Run();
}
Код в таком виде пока работать не будет, надо ещё кое-что сделать, но идея будет именно такая. Дальше я буду писать весь код только в стиле С++.
У природы нет плохой погоды, у неё просто на нас аллергия.

Учение без размышления бесполезно, но и размышление без учения опасно /Конфуций/
Слово есть поступок. /Л. Толстой/

Оффлайн xdАвтор темы

  • *****
  • Сообщений: 17 977
  • Благодарностей: 378
    • Skype - deimos.belastro.net
  • Награды Открытие комет, астероидов, сверхновых звезд, научно значимые исследования.
    • Сообщения от xd
    • Белорусская любительская астрономическая сеть
Дальше нам надо как-то организовать вытеснение. Выше я писал конструкцию с обновлением состояния и оператором switch. Однако такой вариант будет утомительным.
К счастью, компилятор и С, и С++ поддерживает ряд встроенных макроопределений: __DATE__, __FUNCTION__, __FILE__, __LINE__ и т.п., которые разворачиваются в текущую дату, имя текущей функции, текущее имя файла, и то, что нам надо - номер строки в исходном файле. Самое важное его свойство состоит в том, что вхождения на одной строке будут всегда иметь одинаковое значение __LINE__, а в разных - разное, при этом номер строки будет строго положительным целым.
Перепишем код таким образом:

class SomeTask : private TaskBase
{
virtual bool TaskFunc() override
{
switch (_state)
{
case 0:
// step 1;
_state = __LINE__; return false; case __LINE__:
// step 2
_state = __LINE__; return false; case __LINE__:
// step 3
_state = __LINE__; return false; case __LINE__:
// step 4
default:
_state = -1;
return true;
}
}
}
При первом вызове state=0 и выполнится step 1, переменной state присвоится номер строки после первого шага и вернётся управление. При повторном вызове выполнение продожится с этой же точки. Так же с шагами 2 и 3. После 4 шага нет выхода из фукнции, зато есть state = -1; return true; - эта последовательность приведёт к тому, что state примет значение, которое гарантированно не совпадает ни с одним номером строки и не равно начальному состоянию 0. Возвращённое значение true укажет планировщику, что задачу следует снять с планирования. Каким образом это будет сделано - не так важно. И даже если функция будет вызвана после завершения, ничего страшного всё равно не произойдёт, ничего не сломается.

А теперь заворачиваем всё это дело в макросы.

DECLARE_TASK(SomeTask) CAN_SLEEP
int count;
bool IsPressed() { return digitalRead(BUTTON_PIN); }
BEGIN_TASK
count = 0;
pinMode(BUTTON_PIN, INPUT);

ALWAYS {
YIELD_WHILE(!IsPressed());
Serial.println(++count);
YIELD_WHILE(IsPressed());
SLEEP(500);
}
END_TASK
Выглядит симпатично, не так ли?
Займёмся макросами.
#define DECLARE_TASK(T) class T : private TaskBase {

#define BEGIN_TASK private: virtual bool Step() override { \
switch (_state) { \
case 0:

#define YIELD _state = __LINE__; return false; case __LINE__:

#define ALWAYS for(;;)

#define YIELD_WHILE(cond) case __LINE__: if (cond) {_state = __LINE__; return false;}

#define END_TASK default: _state = -1; return true; }}}; // завершить switch, метод Step и класс

По умолчанию все поля приватные, а конструктор по умолчанию отсутствует, то есть он реализуется по умолчанию. При необходимости можно сделать публичную секцию (public: )

Для того, чтобы реализовать задержку, нам надо получить текущее время и дождаться момента текущее время + интервал. Чтобы это реализовать, надо где-то хранить время.
Проще всего это сделать, если ввести специальное поле в класс задачи, но скрыть его за макросом. Итак:
#define CAN_SLEEP unsigned long _sleep;

#define SLEEP(time) _sleep = millis() + (time); case __LINE__:  if (millis() < _sleep) return false;
У природы нет плохой погоды, у неё просто на нас аллергия.

Учение без размышления бесполезно, но и размышление без учения опасно /Конфуций/
Слово есть поступок. /Л. Толстой/

Оффлайн xdАвтор темы

  • *****
  • Сообщений: 17 977
  • Благодарностей: 378
    • Skype - deimos.belastro.net
  • Награды Открытие комет, астероидов, сверхновых звезд, научно значимые исследования.
    • Сообщения от xd
    • Белорусская любительская астрономическая сеть
Планировщик предназначен для планирования выполнения задач. Рассмотрим самый простой вариант планирования - циклическое. Планировщик по очереди выбирает задачи и передаёт им управление. Можно рассмотреть два варианта организации последовательности задач: фиксированный массив и двусвязный динамический список.

#ifndef MAX_TASKS
#define MAX_TASKS 16
#endif

class Scheduler
{
TaskBase* tasks[MAX_TASKS];
byte usedTasks;

void AddTask(TaskBase* task)
{
if (usedTasks < MAX_TASKS)
{
tasks[usedTasks++] = task;
}
}

void RemoveTask(TaskBase* task)
{
for (byte i = 0; i < usedTasks; ++i)
{
if (task == tasks[i]) // найдена задача на удаление
{
RemoveTask(i);
break;
}
}
}

void RemoveTask(byte taskIndex)
{
for (int i = taskIndex + 1; i < usedTasks; ++i)
{
tasks[i - 1] = task[i];
}
--usedTasks;
}
public:
Scheduler() : usedTasks()
{}

Scheduler& operator<<(TaskBase* task)
{
AddTask(task);
return *this;
}

void Run()
{
byte activeTask = 0;
while (usedTasks)
{
if (tasks[activeTask].Step())
{
RemoveTask(activeTask);
}
else
{
++activeTask;
}
if (activeTask >= usedTasks)
{
activeTask = 0;
}
}
}
}

В общем-то простейший случай готов. Можно заменить массив в планировщике на односвязный или двусвязный список, но по моему мнению, это не имеет смысла, поскольку сильно усложняет код для обслуживания очереди задач, да и не имеет большого смысла, поскольку максимальное количество задач как правило известно на встраиваемой системе собственной разработки. Хотя в библиотеке, которую я собираюсь утворить, я обязательно сделаю разные алгоритмы управления очередями задач.
У природы нет плохой погоды, у неё просто на нас аллергия.

Учение без размышления бесполезно, но и размышление без учения опасно /Конфуций/
Слово есть поступок. /Л. Толстой/

Оффлайн Iovch

  • *****
  • Сообщений: 1 289
  • Благодарностей: 124
    • Сообщения от Iovch
А я то думал, что что-то понимаю в программировинии  :( ! Может ветку для начинающих откроешь Алексей? Здесь ведь не профессионалы собрались! Пару интересных моментов я увидел, но не в контексте повествования, а по написанию кода. И вопросов масса. Какую например оптимально среду разработки программ на С/С++ выбрать, чтобы программы и на ПК и на планшете потом шли например.
« Последнее редактирование: 06 Авг 2016 [19:08:05] от Iovch »
Ньютон SW150/750, Рефрактор триплет 100/365_H/M, CG4_GoTo_H/M, Datyson T7C, Levenhuk T510NG, Canon 450Da, БП2 10х50 Berkut

Оффлайн xdАвтор темы

  • *****
  • Сообщений: 17 977
  • Благодарностей: 378
    • Skype - deimos.belastro.net
  • Награды Открытие комет, астероидов, сверхновых звезд, научно значимые исследования.
    • Сообщения от xd
    • Белорусская любительская астрономическая сеть
Давайте создадим, мне не жалко :)
У природы нет плохой погоды, у неё просто на нас аллергия.

Учение без размышления бесполезно, но и размышление без учения опасно /Конфуций/
Слово есть поступок. /Л. Толстой/

Оффлайн xdАвтор темы

  • *****
  • Сообщений: 17 977
  • Благодарностей: 378
    • Skype - deimos.belastro.net
  • Награды Открытие комет, астероидов, сверхновых звезд, научно значимые исследования.
    • Сообщения от xd
    • Белорусская любительская астрономическая сеть
Прежде чем продолжить, я хотел бы немного рассказать о том, что можно сделать для работы с памятью и ссылками.
В том, что мы написали, если один маленький недостаток: сложно контролировать объём переменных, размещённых на стеке. Если включить при компилляции выдачу отчёта по использованию памяти, то локальные переменные, хранимые на стеке, не попадут в итоговый отчёт. Можно, конечно, использовать глобальные переменные, но тогда мы получаем другие недостатки: упоминаться они будут как минимум дважды, а в случае более сложного проекта - трижды: в месте объявления в заголовочном файле, затем в единице трансляции для выделения памяти (.cpp, .ino) и при передаче планировщику. Кроме того, глобальные объекты создаются до того, как будет выполнен вход в функцию main(), а это значит, что это произойдёт до того, как будет проинициализированы аппаратые регистры. Кроме того, порядок создания глобальных объектов не определён. Это грозит тем, что если объекты связаны друг с другом, то нельзя полагаться на эти зависимости. А также то, что в конструкторе нельзя выполнять никаких сложных действий.
Какое решение можно предложить?
В С++ есть понятие видимости переменной, и есть понятие времени жизни.
По области видимости переменные и функции делятся на локальные, локальные статические, глобальные, статические глобальные, а также в классах private, protected и public поля класса, помеченные static или нет. Детально на них останавливаться не буду, но скажу, что нам хорошо подойдут локальные статические переменные: они инициализируются в момент первого вызова функции, в которой располагаются (вернее, при первом входе в область видимости), и живут до конца жизни программы (для встраиваемых систем - в течение всего времени работы).

Рассмотрим код:
template<typename T>
T& CreateInstance()
{
static T obj;
return obj;
}
Здесь всё просто: вызов CreateInstance<MyClass>() в при первом вызове создаст экземпляр типа MyClass и вернёт ссылку на него, а затем будет всегда возвращать ссылку на созданный ранее объект.
Рассмотрим недостатки этого решения и попробуем их побороть. Во-первых, нет возможности явно создать несколько экземпляров (но можно тем не менее создать массив известной длины), а во-вторых, нет возможности передать параметры в конструктор. Третья проблема, мелкая, но всё же, заключается в том, что "за кулисами" происходит ещё кое-что, что нам не надо.

Первая проблема решается достаточно просто:
template<typename T, int N = 0>
T& CreateInstance()
{
static T obj;
return obj;
}
Если надо создать ровно один экземпляр, то ничего не меняется: он создаётся как CreateInstance<MyClass>().
Если надо создать несколько экземпляров, то воспользуемся тем фактом, что каждая специализация шаблона - это разные функции, со своими статическими локальными переменными. То есть CreateInstance<MyClass, 1>() и CreateInstance<MyClass, 2>() создадут свои экземпляры типа MyClass, при этом повторный вызов функции с теми же аргументами - это вызов одной и той же функции, то есть будет возвращаться ссылка на один и тот же объект.
Если надо пробросить параметры в конструктор, то в С++99 сделать ничего не получится. В С++11 это решается с помощью переменных шаблонов (varadic templates):
template<typename T, int N, typename ...Args>
T& CreateInstance(Args... args)
{
static T obj(args...);
return obj;
}
Как включить подджержку стандарта С++11, я писал в одном из первых сообщений темы.
Недостаток этого решения в том, что нельзя задать значение по умолчанию для N, поскольку значение по умолчанию можно задать только для последних аргументов, а пакет параметров может быть только последним. Перегрузку сделать не получится, но можно сделать что-нибудь такое:
template<typename T, int N = 0>
struct Activator
{
template<typename ...Args>
static T& CreateInstance(Args... args)
{
static T obj(args...);
return obj;
}
}

MyClass& m = Activator<MyClass>::CreateInstance(1, "Hello", 3.14f);
Теперь разберём третью проблему. Когда создаётся объект, имеющий нетривиальный конструктор, в качестве статической переменной, "за кулисами" создаётся ещё один объект, который на Arduino имеет размер 8 байт. Моё исследование кода показало, что это вспомогательное поле, которое гарантирует однократность создания объекта и создаёт дополнительную информацию о нём, а также создаёт анонимную функцию, которая вызовет деструктор объекта и регистрирует её дл atexit(). По-моему, совешенно бесполезное занятие :)
Однократность нужна, да, но зачем тратить аж 8 байт, если достаточно 1? А если сильно извратнуться, то и того меньше. Но извращаться не будем, и сделаем просто, но 7 байт сэкономим. Для того, чтобы методика сработала, надо воспользоваться такой штукой, как placement new. Это особый синтаксис оператора new, который позволяет проинициализировать конструктором область памяти, выделенную ранее.
template<typename T, int N = 0>
struct Activator
{
template<typename ...Args>
static T& CreateInstance(Args... args)
{
static byte obj[sizeof(T)];
static byte initialized;
if (initialized == 0)
{
initialized = 1;
return *(new (obj)T(args...));
}
return *(T*)obj;
}
}

MyClass& m = Activator<MyClass>::CreateInstance(1, "Hello", 3.14f);
Выглядит страшновато, да. Сначала создаются переменные obj и initialized. Первая - это массив байтов того же размера, что и тип T, вторая - байт. Обе переменные заполняются нулями, что гарантируется стандартом С++. Дальше магия: если переменные не инициализированы (то есть функция вызвана впервые), то буфер obj интерпретируется как переменная типа T, и вызывается конструктор, который проинициализирует объект. Указатель разыменовывается, возвращается ссылка. При последующих вызовах возвращается просто ссылка на obj, интерпретируемая как ссылка на T.
По-хорошему, надо бы отбросить от типа T ссылку (например, можно передать int& в качестве шаблонного параметра), но об этом в другой раз.

Но это не будет компиллироваться, потому что placement-форма оператора new по умолчанию отсутствует. Чтобы это сделать, надо как-то объявить этот оператор:
inline void* operator new (unsigned size, void* ptr) noexcept
{
return ptr;
}
Это можно сделать либо ранее по тексту программы, либо вынести в отдельный заголовочный файл.
У природы нет плохой погоды, у неё просто на нас аллергия.

Учение без размышления бесполезно, но и размышление без учения опасно /Конфуций/
Слово есть поступок. /Л. Толстой/

Оффлайн xdАвтор темы

  • *****
  • Сообщений: 17 977
  • Благодарностей: 378
    • Skype - deimos.belastro.net
  • Награды Открытие комет, астероидов, сверхновых звезд, научно значимые исследования.
    • Сообщения от xd
    • Белорусская любительская астрономическая сеть
Сейчас воткну себе необходимый софт и начну собирать рабочий проект, думаю где-нибудь на гитхабе. В перспективе хочу сделать из этого отдельную библиотеку для Arduino.
У природы нет плохой погоды, у неё просто на нас аллергия.

Учение без размышления бесполезно, но и размышление без учения опасно /Конфуций/
Слово есть поступок. /Л. Толстой/

Оффлайн xdАвтор темы

  • *****
  • Сообщений: 17 977
  • Благодарностей: 378
    • Skype - deimos.belastro.net
  • Награды Открытие комет, астероидов, сверхновых звезд, научно значимые исследования.
    • Сообщения от xd
    • Белорусская любительская астрономическая сеть
Создал проект для VisualStudio с расширением Visual Micro на Github, начал туда переносить реальный код.
Минимально работающее приложение, которое мигает светодиодом, находится здесь: https://github.com/Alexey-Tkachenko/Multitasking-Ardiuno/tree/2a414c2d7bdff7dc0e8a8cebda33d17571d27853
Репозиторий можно утянуть себе и попробовать. Или просто посмотреть код.
А я пока более брутальные вещи туда переношу :)
У природы нет плохой погоды, у неё просто на нас аллергия.

Учение без размышления бесполезно, но и размышление без учения опасно /Конфуций/
Слово есть поступок. /Л. Толстой/

Оффлайн xdАвтор темы

  • *****
  • Сообщений: 17 977
  • Благодарностей: 378
    • Skype - deimos.belastro.net
  • Награды Открытие комет, астероидов, сверхновых звезд, научно значимые исследования.
    • Сообщения от xd
    • Белорусская любительская астрономическая сеть
А теперь "штатная" версия, с поддержкой освобождения процессора.
https://github.com/Alexey-Tkachenko/Multitasking-Ardiuno/tree/3848545c6b1e96d581ae92f28619d6bef585a5dd
У природы нет плохой погоды, у неё просто на нас аллергия.

Учение без размышления бесполезно, но и размышление без учения опасно /Конфуций/
Слово есть поступок. /Л. Толстой/

Оффлайн xdАвтор темы

  • *****
  • Сообщений: 17 977
  • Благодарностей: 378
    • Skype - deimos.belastro.net
  • Награды Открытие комет, астероидов, сверхновых звезд, научно значимые исследования.
    • Сообщения от xd
    • Белорусская любительская астрономическая сеть
А вот, например, код, который мигает тремя диодами с тремя разными периодами, получается что-то вроде счётчика:

#include "Multitasking/Multitasking.h"

DECLARE_TASK(BlinkerTask) CAN_SLEEP
byte pin;
int delay;
bool state;
public:
BlinkerTask(byte pin, int period) : pin(pin), state(), delay(period/2) {}

BEGIN_TASK
pinMode(pin, OUTPUT);
FOREVER
{
digitalWrite(pin, state = !state);
SLEEP(delay);
}
END_TASK


void setup()
{
(Instance<Scheduler<4>>::CreateNoGuard()
<< Instance<BlinkerTask, 1>::CreateNoGuard(13, 1000)
<< Instance<BlinkerTask, 2>::CreateNoGuard(12, 2000)
<< Instance<BlinkerTask, 3>::CreateNoGuard(11, 4000)
).Run();
}

void loop()
{
}

Согласитесь, код работает прозрачней, чем если писать в "классическом" стиле, и его гораздо проще модифицировать, если потребуется добавить ещё один светодиод или поддержать ещё что-нибудь из периферии.
У природы нет плохой погоды, у неё просто на нас аллергия.

Учение без размышления бесполезно, но и размышление без учения опасно /Конфуций/
Слово есть поступок. /Л. Толстой/

Оффлайн Alexandrid

  • *****
  • Сообщений: 3 531
  • Благодарностей: 348
  • Есть умные, есть начитанные, но двух в одном нет..
    • Сообщения от Alexandrid
Считываю значения из блютус модуля и отправляю их на экран. Когда отправляю команды с телефона в режиме диммера, то это выглядит длинным набором символов.  Если тыкаю в полосу, то приходит координата положения, если провожу пальцем, то приходит много всего. Как разделить эти данные?
#include "U8glib.h"

U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_FAST);
int  my;
String ser;

void setup(void) {
  Serial.begin(9600);

}

void draw(void) {
  u8g.setFont(u8g_font_unifont);
  u8g.setPrintPos(0, 20);
  u8g.print("FFF");
  u8g.print(ser);




}


void loop(void) {


  if (Serial.available())

  {
    ser = Serial.readString();
    my = ser.toInt();

    u8g.firstPage();
    do {
      draw();
    } while ( u8g.nextPage() );

  }


}
« Последнее редактирование: 17 Мая 2017 [00:43:02] от Alexandrid »
Avalon Instruments - Takahashi - QHY+ZWO - Leica - Swarovski Optic.