Вытесняющая многозадачная ОС. Практика AVR

Обсуждаем контроллеры компании Atmel.
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение ARV »

судя по всему, FX-RTOS не для AVR, к тому же платная система - зачем она мне? сайт, как сейчас модно, рассчитан на дебилов с деньгами - картинок много, а ничего конкретного нет. и найти нельзя. зато кнопок "купить" в изобилии. тьфу, кака.

что касается самой темы топика, я из крайности в крайность бросаюсь. сначала был переполнен ликованием от удобства RTOS, сейчас переживаю глубокое разочарование... причем, скорее разочарование в том, что на платформе AVR развернуться в полной мере с RTOS достаточно непросто, а ломка сознания очень большая. просто кардинальная смена парадигмы программирования - это большой шок. привычки из программирования под винду тут никак не подходят.

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

единственное, что я могу уверенно сказать всем, кто захочет освоить RTOS в AVR: никогда не смотрите видеоролики и не читайте статьи о примерах "мигания" светодиодами! ничего общего с реальными задачами такие примеры не имеют абсолютно, и способны только задурить голову. еще раз: демонстрация возможностей RTOS на примере мигания светодиодами - это ОБМАН, жульничество. не ведитесь на это!

еще одно я понял: RTOS на самом деле совсем не сложная штука. главная проблема - сделать переключение контекста, т.е. суметь запихать в один стек все регистры, затем перейти на другой стек и вытащить все регистры из него. все прочее по сути - реализация атомарного доступа к разным глобальным структурам.

и еще одно я понял: не надо бояться обработчиков прерываний! любая RTOS завязана на акивном использовании вызовов функций из обработчиков прерываний, и это никого не смущает. следовательно, не надо этого бояться и в проектах без RTOS. например, все советуют делать обработчик как можно более коротким и быстрым, при этом без обращений к функциям системных библиотек и вообще из других модулей - дескать, ради этого в стек сохраняется много регистров, что замедляет... так вот: насчет системных (библиотечных то есть) функций - требование справедливое, надо быть с ними очень осторожным, т.к. они в большинстве своем не реентерабельны. а вот на счет "своих" функций - фигня: если хотите, то смело их используйте в прерываниях и не бойтесь ничего. конечно, если вы не делаете систему, латентность реакции на запрос прерывания которой должна быть в 50 наносекунд и не более :)
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Demiurg
Это не хвост, это антенна
Сообщения: 1480
Зарегистрирован: Ср июн 25, 2008 15:19:44
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение Demiurg »

[uquote="ARV",url="/forum/viewtopic.php?p=3576237#p3576237"]...[/uquote]
Я ж ведь говорил, что ртос не панацея. Это всего лишь очередь задач, и никак не алгоритм программы. На самом деле, главная проблема в программировании - это взаимодействие программных модулей. Когда я реализовал механизм сообщений, многие мои затруднения закончились.
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение ARV »

Demiurg писал(а):Когда я реализовал механизм сообщений, многие мои затруднения закончились.
видимо, у каждого свои собственные проблемы, и, соответственно, свои собственне пути их разрешения находятся...
мои проблемы с очередью не кончились. очередь без асинхронного обслуживания сообщений в ней, имхо, проблему не решает. ну или я не в том направлении пошел и попал в тупик.

вот смотрите: есть 2 USART, к которым подключены MP3-плейер и GSM-модем. оба обладают особенностью посылать сообщения не тогда, когда ждешь чего-то конкретного от них, но и сами по себе. например, модем может прислать RING, а MP3-модуль может прислать "трек завершен" в совершенно непредсказуемый момент времени. и, если не асинхронного обслуживания этих сообщений, вся логика ломается кардинально. ведь обычно мы работаем с периферией в синхронном режиме "дал задание - дождался завершения", а тут возникает ситуация "дал задание - дождался неизвестно чего - задание завершилось".

в итоге простая задача обработки кнопок, нажимаемых пользователем, выливается в что-то странное:
1. ждем сообщение
2. если это сообщение о нажатии кнопки - обрабатываем
3. если это сообщение не от кнопок - тоже обрабатываем (хотя это не должно быть проблемой обработки кнопок!)
а теперь представьте себе, что обработка всех сообщений вообще ведется в главном цикле, а обработка кнопок должна вестись в функции ввода строки. лично у меня это вылилось в рекурсивное обращение к функции обработки сообщений главного цикла... то есть в главном цикле по определенному сообщению я начинаю ввод строки, изнутри которого вызываю главный цикл, чтобы обработать все отличные от кнопок сообщения...
чтобы это работало, приходится загромождать код почтоянными проверками всяких флагов, чтобы не попасть в бесконечную рекурсию...

кстати, эта же проблема (с порядком обработки сообщений) и испортила мне впечатление от RTOS, разве что немножко иначе...
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Аватара пользователя
GoldenAndy
Поставщик валерьянки для Кота
Сообщения: 1925
Зарегистрирован: Чт июл 28, 2016 07:58:37
Откуда: Kyiv, UA
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение GoldenAndy »

Так выше уже предлагали вариант.
Работа с каждым модулем выносится в отдельный .h+.c файл и делается обработчик, который работает как конечный автомат.
А в основной программе с периодичностью, допустим, 1мс вызываются обработчики.
Общение между модулями организовать через процедуры, вынесенные в заголовочный файл каждого модуля...

Т.е. main - просто дергает обработчики каждого модуля. А что в модуле творится - уже вопрос модуля.

Если без отдельной очереди сообщений - то это большой и толстый конечный автомат.
Если прикрутить себе какую то очередь - получится уже некое подобие ртоса
ИзображениеИзображение
Изображение
 
Telegram               Лучшая благодарность ->
[+]
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение ARV »

большой и толстый автомат - это, конечно, все в корне меняет :) смысл не в том, чтобы его сделать, а в том, чтобы от него уйти.

например, в винде: если мы ждем ввода с клавиатуры при помощи функции getch, это ведь не означает, что замирает мышка? но в винде мышкой занимается ОС, а в моей программе мышки хоть и нет, но роль её сообщений играют другие сообщения, и обслуживать их некому, кроме той самой функции getch. понимаете, о чем я?

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

Мой уютный бложик... заходите!
OKF
Это не хвост, это антенна
Сообщения: 1385
Зарегистрирован: Вт июн 07, 2011 08:03:18

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение OKF »

Ну так проверили наличие символа и понеслись других обслуживать. Зачем торчать в getch? Да и некрасиво это.)
Demiurg
Это не хвост, это антенна
Сообщения: 1480
Зарегистрирован: Ср июн 25, 2008 15:19:44
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение Demiurg »

Когда запутался, сядь и распиши на бумаге ТЗ. Интерфейс. И так далее, сложные моменты. Когда эта информация будет перед глазами, многое прояснится.
У конечных автоматов в разных состояниях одни и те же кнопки могут выполнять разные задачи. То есть, в каждом состоянии свой набор кнопок и соответствующая реакция. Я могу прислать примеры.

Вариант меню для дисплея на семисегментных индикаторах:
Спойлер

Код: Выделить всё

//==================
#ifndef MENU_H

#define MENU_H

#include "menu.h"

#include "main_def_func.h"
//==================

//==================
// Typedefs:
typedef void (*FuncPtr)(void);
//==================

//==================
typedef struct menu_item
{
   void         *Parent;
   void         *Child;
   void         *Next;
   void         *Prev;
   FuncPtr       PlusFunc;
   FuncPtr       MinusFunc;
   FuncPtr       EnterFunc;
   FuncPtr       MenuInitFunc;
   char __flash *Text;
} menu_item;
//==================

// Externs:
//==================
extern menu_item __flash *CurrMenuItem; // Текущий пункт меню.
extern menu_item __flash *BeginCurrMenuLevel; // Начало массива текущего уровня меню.

extern __flash menu_item Null_Menu;

extern char Menu_Str_Buf []; // Буфер для вывода текста.

extern void (*MenuFuncPtr)(void);
//==================

// Defines and Macros:
//==================
#define NULL_ENTRY Null_Menu
#define NULL_FUNC  (void*)0
#define NULL_TEXT  ""
#define PAGE_MENU  3
//==================

//==================
#define MAKE_MENU(Name, Parent, Child, Next, Prev, PlusFunc, MinusFunc, EnterFunc, MenuInitFunc, Text) \
extern menu_item __flash Parent;                                                                       \
extern menu_item __flash Child;                                                                        \
extern menu_item __flash Next;                                                                         \
extern menu_item __flash Prev;                                                                         \
       menu_item __flash Name =                                                                        \
{                                                                                                      \
      (menu_item*)      &Parent,                                                                       \
      (menu_item*)      &Child,                                                                        \
      (menu_item*)      &Next,                                                                         \
      (menu_item*)      &Prev,                                                                         \
                         PlusFunc,                                                                     \
                         MinusFunc,                                                                    \
                         EnterFunc,                                                                    \
                         MenuInitFunc,                                                                 \
                        {Text}                                                                         \
}
//==================

//==================
#define PARENT         *((menu_item __flash*) (CurrMenuItem->Parent))
#define CHILD          *((menu_item __flash*) (CurrMenuItem->Child))
#define NEXT           *((menu_item __flash*) (CurrMenuItem->Next))
#define PREV           *((menu_item __flash*) (CurrMenuItem->Prev))
#define PLUS_FUNC      *((FuncPtr)   (CurrMenuItem->PlusFunc))
#define MINUS_FUNC     *((FuncPtr)   (CurrMenuItem->MinusFunc))
#define ENTER_FUNC     *((FuncPtr)   (CurrMenuItem->EnterFunc))
#define MENU_INIT_FUNC *((FuncPtr)   (CurrMenuItem->MenuInitFunc))
//==================

//==================
#define SET_MENU_LEVEL(x) \
   Set_Menu_Level(&x)

#define GO_MENU_FUNC(x)  \
   MenuFunc((FuncPtr*)&x)

#define EXTERN_MENU(Name) \
    extern menu_item __flash Name;
//==================

//==================
enum
{
   SET_LEVEL = 0,
   SET_NEXT,
   SET_PREV,
};
//==================

// Prototypes:
//==================
bool Set_Menu_Level (menu_item __flash *NewMenu);
bool MenuFunc(FuncPtr* Function);
//==================

//==================
void Out_Menu_Items_Init (void);
void Out_Menu_Items (void);
//==================

//==================
bool proc_menu_keys (void);
//==================

#endif

//==================
#include "menu.h"
//==================

//==================
static u08 quant_items;
static u08 pos_y_curs;
//==================

//================
menu_item __flash *CurrMenuItem; // Текущий пункт меню.

menu_item __flash *BeginCurrMenuLevel; // Начало массива текущего уровня меню.

menu_item __flash *temp_menu;

menu_item __flash  Null_Menu = {(void*)0, (void*)0, (void*)0, (void*)0, NULL_FUNC, NULL_FUNC, NULL_FUNC, NULL_FUNC, {NULL_TEXT}};

void (*MenuFuncPtr)(void);
//================

//======================
// Уровни, пункты, текст - все выводится автоматом.
// Так как все переходы по меню расписаны в структуре, то отпадает надобность в запоминании перемещений по меню.
//======================

//==================
bool Set_Menu_Level (menu_item __flash *NewMenu)
{
   if ((void*)NewMenu == (void*)&NULL_ENTRY)
      return false;
   else
   {
      CurrMenuItem = NewMenu;
      clr_dsp_buf ();
      Print_Buf (1, CurrMenuItem->Text); // вывод названия пункта меню.
      GO_MENU_FUNC (MENU_INIT_FUNC);
      return true;
   }
}
//==================

//==================
bool Set_Menu_Item (menu_item __flash *NewMenu)
{
   if ((void*)NewMenu == (void*)&NULL_ENTRY)
      return false;
   else
   {
      CurrMenuItem = NewMenu;
      clr_dsp_buf ();
      Print_Buf (1, CurrMenuItem->Text); // вывод названия пункта меню.
      GO_MENU_FUNC (MENU_INIT_FUNC);
      return true;
   }
}
//==================

//==================
bool MenuFunc (FuncPtr* Function)
{
   if ((void*) Function == (void*) NULL_FUNC)
      return false;
   else
   {
      ((FuncPtr) Function)();
      return true;
   }
}
//==================

//==================
//--------------------- Навигация по меню. -------------------------------
// Кнопки:
// Esc. Выход из меню. Переход на родительский уровень. Отмена.
// Enter. Вход в меню. Переход на пункт меню. Вход в режим редактирования параметра. Сохранение параметра.
// Plus. Следующий пункт меню. Изменение параметра. Увеличить значение параметра.
// Minus. Предыдущий пункт меню. Изменение параметра. Уменьшить значение параметра.
//------------------------------------------------------------------------
// Esc. Parent Level.
// Enter. Child Level. Func.
// Plus. Next Item. Func.
// Minus. Prev Item. Func.
//------------------------------------------------------------------------
bool proc_menu_keys (void)
{
   if (Get_Event (EV_ID_KEY_PRESSED))
   {
      switch (Get_Key_Code ())
      {
         case KEY_ESC_COD:
            if (SET_MENU_LEVEL (PARENT))
               return true;
            else
               return false;

         case KEY_ENTER_COD:
            if (CurrMenuItem -> EnterFunc != NULL_FUNC)
               GO_MENU_FUNC (ENTER_FUNC);

            if (SET_MENU_LEVEL (CHILD))
               return true;
            else
               return false;

         case KEY_PLUS_COD:
            if (CurrMenuItem -> PlusFunc != NULL_FUNC)
            {
               GO_MENU_FUNC (PLUS_FUNC);
               return false;
            }

            if (SET_MENU_LEVEL (NEXT))
               return true;
            else
               return false;

         case KEY_MINUS_COD:
            if (CurrMenuItem -> MinusFunc != NULL_FUNC)
            {
               GO_MENU_FUNC (MINUS_FUNC);
               return false;
            }

            if (SET_MENU_LEVEL (PREV))
               return true;
            else
               return false;
      }
   }

   else
      return false;
}
//==================

//==================
EXTERN_MENU (L_OUT_MODE);
//==================

//==================
static info_phase_t info_phase;

static u32  i_ph_a;
static u32  i_ph_b;
static u32  i_ph_c;

static u08 menu_flags;

static info_service_t _info_service;
static u08 _info_service_slave;

static soft_timer ST_INFO_SERVICE_OUT_I;

void info_service (void)
{
   switch (_info_service)
   {
      case INFO_SERVICE_INIT:      info_service_init ();     break;
      case INFO_SERVICE_OUT_I_ABC: info_service_out_i ();    break; // Вывод значений тока ABC.
   }
}
//==================

//==================
#ifdef __PROJECT_MODE_WORK__
#pragma inline = forced
#endif
void info_service_init (void)
{
   SET_MENU_LEVEL (L_OUT_MODE);

   _info_service = INFO_SERVICE_OUT_I_ABC;
   _info_service_slave = 0;
}

#ifdef __PROJECT_MODE_WORK__
#pragma inline = forced
#endif
void info_service_out_i (void)
{
   switch (_info_service_slave)
   {
      case 0:
         info_phase = INFO_PHASE_A;
         menu_flags = (1<<DP_FLG);
         set_soft_timer (ST_INFO_SERVICE_OUT_I, 0, 2000);
         _info_service_slave = 1;
         break;

      case 1:
#ifdef __PROJECT_MODE_DEBUG__
         Set_Event (EV_ID_KEY_PRESSED, USER_EVENT); //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
         Set_Kbd_Buf (KEY_PLUS_COD); //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#endif

         if (proc_menu_keys ())
            return;

         if (handle_soft_timer (ST_INFO_SERVICE_OUT_I))
            out_phase_1 ();
         break;
   }
}
//==================

//==================
bool get_dp_flag (void)
{
   if (menu_flags & (1<<DP_FLG))
      return true;
   else
      return false;
}
//==================

//==================
void out_phase_1 (void)
{
   switch (info_phase)
   {
      case INFO_PHASE_A: set_phase_a_b (); break;
      case INFO_PHASE_B: set_phase_b_c (); break;
      case INFO_PHASE_C: set_phase_c_a (); break;
   }
}

void out_phase_2 (void)
{
   bool a = false;

   switch (info_phase)
   {
      case INFO_PHASE_A:
         set_phase_a_b ();
         a = true;
         break;

      case INFO_PHASE_B:
         set_phase_b_c ();
         a = true;
         break;

      case INFO_PHASE_C:
         set_phase_c_a ();
         a = true;
         break;
   }

   if (a)
      set_soft_timer (ST_INFO_SERVICE_OUT_I, 5000, 2000);
}

void set_phase_a_b (void)
{
   clr_dsp_buf ();
   Print_Char (1, 'A');
   print_i_ph_x (2, i_ph_a);
   info_phase = INFO_PHASE_B;
}

void set_phase_b_c (void)
{
   clr_dsp_buf ();
   Print_Char (1, 'B');
   print_i_ph_x (2, i_ph_a);
   info_phase = INFO_PHASE_C;
}

void set_phase_c_a (void)
{
   clr_dsp_buf ();
   Print_Char (1, 'C');
   print_i_ph_x (2, i_ph_a);
   info_phase = INFO_PHASE_A;
}
//==================

//==================
void enter_1 (void)
{

}

void inc_1 (void)
{

}

void dec_1 (void)
{

}
//==================

//===============================
//         NAME          PARENT      CHILD       NEXT        PREV        PLUS_FUNC    MINUS_FUNC   ENTER_FUNC  MENU_INIT_FUNC TEXT
//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
MAKE_MENU (L_OUT_MODE,   NULL_ENTRY, NULL_ENTRY, NULL_ENTRY, NULL_ENTRY, out_phase_2, out_phase_2, NULL_FUNC, NULL_FUNC,      NULL_TEXT); // NULL_ENTRY, NULL_FUNC, NULL_TEXT
MAKE_MENU (L1_I_1,       L_OUT_MODE, NULL_ENTRY, NULL_ENTRY, NULL_ENTRY, NULL_FUNC,   NULL_FUNC,   NULL_FUNC, NULL_FUNC,      "B");
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение ARV »

OKF писал(а):Зачем торчать в getch?
вот смотрите, зачем.

Код: Выделить всё

if(strcmp(cmd, "RUN") == 0){
   // выполняем действие 
}
надо получить введенную пользователем команду, т.е. строку cmd:

Код: Выделить всё

char cmd[20];

uint8_t i=0;
char c;
while((c=getch()) != '\r'){
   cmd[i++] = c;
   if(i == 20) break;
}
когда написано так - согласитесь, что алгоритм прозрачен и понятен: посимвольно вводим, что желает пользователь и при нажатии ENTER заканчиваем ввод, передава на обработку результат. вопросы контроля размера буфера оставим за скобками.

а теперь напишите вариант ввода по вашему предложению, т.е. "проверили наличие символа и понеслись других обслуживать". просто напишите, и мы посмотрим, насколько:
а) это красиво
б) это понятно
в) это логично

собственно, вопрос к вам риторический, ибо очевидно, что все три аспекта будут на ужасно низком уровне. и код ув. Demiurg подтверждает, что в итоге будет кошмар на улице Вязов...

в конце концов придется признать, что деться некуда от:

Код: Выделить всё

char getch(void){
   while(!usart_rx_ready()){
      default_message_handler(NO_USART_MSG); // обработчик событий по умолчанию, запрещаем ему обработку событий USART
   }
   return usart_resive_char();
}
именно так сейчас и сделано у меня, но это и есть "макаронный код", потому как default_message_handler придется вызывать из множества мест, где логически требуется ожидание чего-то конкретного от одного из источников событий... а поскольку

Код: Выделить всё

int main(void){
   while(1){
      default_message_handler(ALL_MSG_ENABLE);
   }
}
то любая обработка так или иначе возникает именно внутри default_message_handler - рекурсия, что тоже не украшает код...
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Demiurg
Это не хвост, это антенна
Сообщения: 1480
Зарегистрирован: Ср июн 25, 2008 15:19:44
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение Demiurg »

Я привёл пример обработки кнопок для навигации по меню. Именно здесь НЕ УЙТИ от состояний. Если у вас несколько мест, значит делается общая функция. Как у меня proc_menu_keys. А бывает, что в лоб жду сообщения нажатия кнопок, и затем switch-case.

Ввод это уже другая тема. Тем более, обработка буфера по Enter.

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

Из одного проекта:
Спойлер

Код: Выделить всё

//==================
struct msg_cmd __flash table_msg_cmd [] =
{
   {"CH1",     KEY_ESC_COD},
   {"CH2",     KEY_ENTER_COD},
   {"CH3",     TEST_CMD},
   {NULL_TEXT, NULL_CMD},
};

u08 select_cmd (void)
{
   struct msg_cmd __flash *ptr = table_msg_cmd;

   while (ptr -> msg)
   {
      char __flash *ptr_2 = ptr -> msg;

      for (u08 i = 0; i < mirf_PAYLOAD; i++)
      {
         if (!ptr_2 [i]) return ptr -> cmd;

         if (ptr_2 [i] != mirf_buf [i]) break;
      }

      ptr++;
   }

   return false;
}

bool select_msg (u08 a)
{
   struct msg_cmd __flash *ptr = table_msg_cmd;

   for (; ptr -> msg; ptr++)
   {
      if (a == ptr -> cmd)
      {
         print_buf (mirf_buf, 1, ptr -> msg);
         return true;
      }
   }

   return false;
}
//==================

//==================
static u08 _proc_nrf24l01;
static u08 attempt_cnt;

void proc_nrf24l01 (void)
{
   switch (_proc_nrf24l01)
   {
      u08 status;

      case PROC_NRF24L01_INIT_1:
         mirf_init(); // Инициализация портов МК. SPI в том числе.
         set_timer (ST_PROC_NRF24L01, 100); // Между включением питания и работой трансивера должна быть задержка 100 мс.
         _proc_nrf24l01 = PROC_NRF24L01_INIT_2;
         break;

      case PROC_NRF24L01_INIT_2:
         if (wait (ST_PROC_NRF24L01))
         {
            mirf_config(); // Настройка модуля на прием.
            set_timer (ST_PROC_NRF24L01, 2); // Задержка 2 мс.
            _proc_nrf24l01 = PROC_NRF24L01_INIT_3;
         }
         break;

      case PROC_NRF24L01_INIT_3:
         if (wait (ST_PROC_NRF24L01))
            _proc_nrf24l01 = PROC_NRF24L01_INIT_4;
         break;

      case PROC_NRF24L01_INIT_4:
         set_receive_data (); // Настройка модуля на прием.
         _proc_nrf24l01 = PROC_NRF24L01_WORK;
         break;

      case PROC_NRF24L01_WORK:
         if (status = receive_transf_data ())
         {
            switch (status)
            {
               case RECEIVED_DATA:
                  switch (select_cmd ())
                  {
                     case KEY_ESC_COD:                    // Номер канала.
                        set_led_1_blink_on ();
                        break;

                     case KEY_ENTER_COD:                    // Номер канала.
                        set_led_2_blink_on ();
                        break;

                     case KEY_PREV_COD:                    // Тестовое сообщение.
                        set_led_3_blink_on ();
                        set_timer (ST_SEND_TEST, SEND_TEST_DELAY); // Таймер отправки тестового сообщения.
                        break;
                  }
                  break;

               case DATA_TRANSFERRED:
                  _proc_nrf24l01 = PROC_NRF24L01_INIT_4;
                  break;

               case ERROR_TRANSF_DATA:
                  _proc_nrf24l01 = PROC_NRF24L01_INIT_4;
                  break;
            }
         }

         if (Get_Event (EV_ID_KEY_PRESSED))
         {
            Clr_Event (EV_ID_KEY_PRESSED);

            u08 cmd = GetKeyCode ();

            select_msg (cmd);

            if (cmd == TEST_CMD)
            {
               set_leds_blink_off ();

               attempt_cnt = 0;
               set_timer (ST_TIMEOUT_RECEIVE_TEST, TEST_TIMEOUT); // Таймаут приема тестового сообщения.
            }

            if (get_show_id_errors_state () > 0)
               set_show_id_errors_off ();

            set_transf_data ();
         }

         if (wait (ST_SEND_TEST))
         {
            print_buf (mirf_buf, 1, "CH3");

            attempt_cnt = 0;
            set_timer (ST_TIMEOUT_RECEIVE_TEST, TEST_TIMEOUT); // Таймаут приема тестового сообщения.

            set_transf_data ();
         }

         if (wait (ST_TIMEOUT_RECEIVE_TEST))
         {
            attempt_cnt++;
            if (attempt_cnt >= MAX_ATTEMPT)
            {
               set_error_id (ERR_ID_2); // Таймаут приема тестового сообщения.
            }
            else
            {
               select_msg (TEST_CMD);
               set_timer (ST_TIMEOUT_RECEIVE_TEST, TEST_TIMEOUT); // Таймаут приема тестового сообщения.

               set_transf_data ();
            }
         }

         leds_blink ();
         show_id_errors ();
         break;

      case PROC_NRF24L01_ERROR:
         if (Get_Event (EV_ID_KEY_PRESSED))
         {
            Clr_Event (EV_ID_KEY_PRESSED);

            set_show_id_errors_off ();
            _proc_nrf24l01 = PROC_NRF24L01_INIT_4;
            return;
         }

         show_id_errors ();
         break;

      default:
         break;
OKF
Это не хвост, это антенна
Сообщения: 1385
Зарегистрирован: Вт июн 07, 2011 08:03:18

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение OKF »

[uquote="ARV",url="/forum/viewtopic.php?p=3577093#p3577093"]

Код: Выделить всё

char cmd[20];

uint8_t i=0;
char c;
while((c=getch()) != '\r'){
   cmd[i++] = c;
   if(i == 20) break;
}
когда написано так - согласитесь, что алгоритм прозрачен и понятен: посимвольно вводим, что желает пользователь и при нажатии ENTER заканчиваем ввод, передава на обработку результат. вопросы контроля размера буфера оставим за скобками.

а теперь напишите вариант ввода по вашему предложению, т.е. "проверили наличие символа и понеслись других обслуживать". просто напишите[/uquote]
Да так же само, только без while:

Код: Выделить всё

char cmd[20];
uint8_t i=0;
char c;
if (con_available()) {
  if ((c=getch()) != '\r') {
    cmd[i++] = c;
    if(i == 20) con_println("error");
  }
}
Не думаю, что здесь недостаточно прозрачно. Хотя это простой случай.
Мне бы была понятна полезность ОС если Вы используете не свой код, в котором может быть что хочешь. И который сложно/утомительно переделывать под свои нужды. А так, если изначально пишете для себя... Конечно, если несколько процессов, наглядность может страдать. Ну так можно и как то оформить покрасивше.) Не думаю что вызовы ОС добавят красоты и прозрачности.
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение ARV »

друзья мои, вы не пытаетесь взглянуть на проблему с той стороны, которая важна для меня. вы пытаетесь свести мою задачу к тому, что вы уже делали. увы, это не подходит, особенно с учетом того, что с RTOS вы ничего не делали...
OKF писал(а):Да так же само, только без while
нет, не то же самое. у вас отсутствует именно цикл, в котором происходит ожидание завершения пользовательского ввода. у вас линейный кусочек, который еще надо вплести в общее целое.
Demiurg писал(а):Ввод это уже другая тема
так и моя задача - другая. меню, даже многоуровневое и с разными наворотами я делал не меньше раз, чем вы. и даже без цикла, в котором происходит "хождение" по меню, и даже с циклом.

к сожалению, проблема не в ОС... а в том, что не получается сделать красиво и логично.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Demiurg
Это не хвост, это антенна
Сообщения: 1480
Зарегистрирован: Ср июн 25, 2008 15:19:44
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение Demiurg »

Даю подсказку ещё раз. Даже ещё одну. Есть такая штука, называется интеллект карта. Гугл в помощь. Не поленитесь, сядьте и распишите ваш проект. Ваши затруднения. Составьте интеллект карту. Ячейка интеллект карты может быть как примитивом, так и "макросом". Когда вы нарисуете карту, весь проект будет перед глазами. Вы сейчас, судя по всему крутите проект в голове. Из за этого не видите деталей и общей картины. Отступите от проблемы. Начните перенос на бумагу. Этим вы отвлечетесь, выйдете из проблемы. А когда нарисуете, встанете уровнем выше. И решение придёт. И такое, что сами охренеете.
Кстати, составление взаимосвязей конечных автоматов подобие интеллект карт.
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение ARV »

Не хочу показаться неблагодарной скотиной...
Но какое может быть еще решение, кроме уже многократно огрворенного? То есть кроме путаного автомата состояний или обработки сообщений из очереди в разных местах?
Не поручусь наверняка, но все-таки думаю, что вариантов больше и нет. Если, конечно, отказаться от готовой RTOS, что я был вынужден сделать по причине того, что никто толком не смог мне в этом помочь, а сам я себя загнал в тупик?
Что касается бумаги и т.п. планирования - это у меня в избытке сделано и продолжает делаться. Понимание общих принципов не снимает проблемы деталей, а в них кроется дьявол. Решение есть, но оно не вызывает моего полного удовлетворения.

Давайте, если не возражаете, перейдем от конкретных примеров из ваших практик к решению тех задач, которые, вероятно, вам и не попадались. И не нужно примеров готового кода, достаточно (и даже предпочтительнее) алгоритм решения, принцип.

Вот простейшая задача: СМС GSM-модуль может принять в виде PDU-сообщения, в котором каждый символ кодируется 2 байтами, т.е. размер сообщения может достигать 320 байт, плюс сведения о номере отправителя и т.п. служебная информация - итого 360 байт. То есть буфер меньшего размера в принципе не позволит принять и обработать СМС. Требуется быть готовым в любой момент принять и обработать СМС, причем в качестве обработки один из вариантов - отправка ответного СМС которое - см. ранее, - так же потребует буфера). Следует учитывать, что отправка СМС может потребовать до 60 секунд (!), в течение которых неизвестен результат попытки отправить, но зато в это время может прийти ЕЩЕ одно СМС, в ответ на которое надо отправить СМС, так же может поступить входящий звонок, который следует отработать по-своему. Кстати, анализ входящего звонка - это тоже анализ принятой по USART строки (там номер звонящего), то есть опять требуется какой-то буфер...

Давайте попробуем мозговым штурмом решить эту простенькую задачку, причем не абы как, а с ориентиром на минимизацию расхода ОЗУ, как наиболее дефицитного ресурса AVR. Для простоты считаем, что кроме сообщений GSM-модема (входящих и исходящих), требуется обрабатывать только сообщения от таймера каждую секунду и матричной клавиатуры 3х4 кнопки. Причем, как вы понимаете, и кнопки, и таймер могут послать свои сообщения как во время ожидания ответа от GSM-модуля, так и в процессе работы с модулем, а реакция на эти сообщения может потребовать взаимодействия с GSM-модулем (например, потребовать той самой злосчастной отправки СМС).

Чтобы вы не думали, будто я на вашей шее хочу в рай въехать, скажу, что я решил эту проблему уже дважды: при помощи RTOS и без оной. Оба варианта работали, но не так элегантно были реализованы, как мне того хотелось бы.
Вариант с RTOS использовал мною собственноручно написанный менеджер "кучи" (на самом деле распределения между задачами общего массива в 640 байт) и несколько задач с собственными очередями сообщений. Принимаемые от GSM-модуля строки перемещались в динамически выделенные области и указатели на эти области помещались в очередь сообщений для асинхронной обработки задачами. Этот вариант не устроил меня тем, что по мере увеичения числа асинхронных "девайсов" требовалось все больше и больше новых задач, больше ОЗУ под кучу и стек каждой задачи, к тому же все то было крайне некрасиво, ибо нарушало основной принцип работы с динамической памятью - освобождать память там же, где и выделял. К сожалению, это было в принципе невозможно, и порождало ситуации блокировок мьютексами или семафорами процессов, которым памяти не хватало. Такие блокировки сводили RTOS к обычной кооперативной ОС со всеми ее недостатками.
Вариант без RTOS я делаю сейчас по принципу, что обрисовал ранее: рекурсивный вызов обработчика очереди сообщений везде, где требуется ожидание чего-либо (с контролем за тем, чтобы рекурсия не была слишком глубоко рекурсивной). Этот вариант так же вполне успешно работает, но, как и ранее, не обладает красотой и стройностью.

Возможно, вы дадите мне новые идеи, как можно решить мою проблему третьим способом, одновременно эффективным и красивым?
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Demiurg
Это не хвост, это антенна
Сообщения: 1480
Зарегистрирован: Ср июн 25, 2008 15:19:44
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение Demiurg »

Без лишних слов. Пришлите мне ТЗ. Что за устройство, что делает, составные модули. Интерфейс управления. Можете в личку, если это интеллектуальная собственность. Если есть аналоги, пришлите ссылки.
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение ARV »

Demiurg писал(а):Без лишних слов
боюсь, если я вас загружу всем запрошенным, это отнимет у вас слишком много времени, т.к. только краткое описание параметров файла конфигурации для моего проекта состоит из 11-страничного файла :)

но в общем и целом это не секрет, и я могу рассказать о своей задумке, хотя это и не может считаться ТЗ.
я пытаюсь сделать многофункциональное устройство контроля и управления, совмещающее в себе охранно-пожарную сигнализацию, массив дискретных регуляторов температуры и несколько дополнительных возможностей по анализу входных сигналов и формированию соответствующих выходных. в качестве интерфейса управления мною выбрана система голосового меню, работающего как при нажатии кнопок на клавиатуре устройства, так и при дистанционном дозвоне. для воспроизведения голосовых сообщений применяется отдельный модуль MP3-плейера с отдельной SD-картой со звуковыми файлами. кроме прочего предусматривается возможность управления и контроля при помощи СМС. в случае возникновения одной или нескольких заранее определенных в настройках "тревожных" ситуаций предусматривается на выбор несколько вариантов оповещения: от включения сирены до обзвона по списку заданных заранее номеров с голосовым оповещением и/или рассылки соответствующих СМС так же по списку. в качестве интерфейса конфигурирования применяется SD-карта, на которую помещается текстовый файл с нужными параметрами (формат по типу конфигов линуха), и на эту же карту пишется лог всех важных событий в системе.

это если вкратце. аналогов в разной степени похожести полно как среди коммерческих изделий (китайских в основном), так и среди любительских. но в том виде, как я решил все сделать, полных аналогов нет.

не знаю, чем это может помочь вам помочь мне :)))
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Demiurg
Это не хвост, это антенна
Сообщения: 1480
Зарегистрирован: Ср июн 25, 2008 15:19:44
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение Demiurg »

Без лишних слов, это типа не благодарный и прочая муть. Давайте ваш талмуд. К примеру, мне нужно понять, как задуман кнопочный интерфейс. Мне нужна вся навигация по управлению и вводу. В общем от вас тз
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение ARV »

все-таки я предлагаю обсуждать все постепенно. мне не надо, чтобы вы за меня сделали свой вариант.

я вам расскажу про кнопочный интерфейс прямо здесь, ок?

итак.
кнопки в виде матрицы, совпадающей с телефонной клавой. при нажатии на любую кнопку формируется сообщение MSG_KEY. в параметре которого передается символ нажатой кнопки, т.е. '0','*' или '5'. сообщение помещается в "главную" очередь (кроме главной есть еще очередь MP3-плейера). если был дозвон с разрешенного номера, то активируется то же самое меню с перенаправлением звука в GSM-модем, и сообщения MSG_KEY формируются по сигналам DTMF, которые принимаются от GSM-модема.

прием от модема реаизован следующим образом. в кольцевой буфер помещаются по прерываниям все приходящие символы, и одновременно среди их потока ищется символ '\r', являющийся признаком конца строки (симвл '\n' игнорируется и из потока исключается). каждая полученная таким образом строка посылается в главную очередь сообщением MSG_GSM. при разборе очереди это сообщение обрабатывается путем анализа принятой строки, выделения из нее типа сообщения (например, для DTMF это будет совпадение начала строки с подстрокой "+DTMF"), и вызывается соответствующий обработчик. т.к. принимаемых от модема сообщений много, то и обработчиков много разных, но конкретно для системы меню используется только обработчик DTMF, который формирует MSG_KEY, попадающее в главную очередь.

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

в общем, как видите, основное уродство в том, что при обработке меню необходимо обращаться к обработчику сообщений по умолчанию. а ведь само начало системы меню тоже возникает при обрабоке сообщений... та самая рекурсия, о которой я говорил.

Добавлено after 14 minutes:
то есть потоки управляющих воздействий на меню могут быть такими:
1. по кнопкам с пульта BUTTON --> MSG_KEY(сh) --> MENU(ch)
2. по сигналам из модема DTMF --> MSG_GSM(str) --> HANDLER(MSG_GSM) --> MSG_KEY(ch) --> MENU(ch)
где ch - символ кнопки
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
viiv
Грызет канифоль
Сообщения: 287
Зарегистрирован: Чт ноя 06, 2014 13:09:06

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение viiv »

[uquote="ARV",url="/forum/viewtopic.php?p=3576237#p3576237"]что касается самой темы топика, я из крайности в крайность бросаюсь. сначала был переполнен ликованием от удобства RTOS, сейчас переживаю глубокое разочарование... причем, скорее разочарование в том, что на платформе AVR развернуться в полной мере с RTOS достаточно непросто, а ломка сознания очень большая. просто кардинальная смена парадигмы программирования - это большой шок. привычки из программирования под винду тут никак не подходят.[/uquote]

ARV, в этом и проблема: Вы пытаетесь сравнить два подхода, но сравниваете неправильно :-)
Пример: у Вас есть GSM модуль на UART-e. "Читатель" этого UART-а - это отдельная задача (процесс, ... или как хотите назовите) Со своим контекстом, стеком и прочими аттрибутами - грубо говоря, для AVR "вынь да полож" 128 байт под это дело. Плюсы же очевиднеы: все состояния, связанные с разбором потока данных от GSM модема можно "скрыть"/"изолировать" в этой задаче.
Вы смотрите, блин, 128 байт задаче. А если у меня 8 задач - этож целый килобайт им надо отдать за просто так, а майн-циклу нужен всего один стек и все можно разрулить. Но, блин, "макаронный код" получается.

*) Да, каждая задача - это накладные расходы, на ее стек, контекст; от этого никуда не деться. Если есть непреодалимое желание запихнуть, например в мегу 8-ую, то скорей всего Вас ждет разочарование: задача, работающая по таймеру, задача опроса клавиатуры, задача-"читатель" UART-а и уже почти половины SRAM-а нету

У RTOS-ников подход другой: сейчас я пишу задачу "читатель" UART-а, подо мной API UART-а (грубо говоря uart_get_char (uart_t* u)), Надо мной API более высокого уровня, распознал принятие SMS - надо "дернуть" задачу "читателя" SMS, распознал входящий вызов - "дернуть" задачу ожидающую входящие вызовы и.т.д... В этот момент меня не интересует, влезу ли я в мегу 8-ю или нет. Да, я вообщето и не знаю, буду ли я выполнятся на AVR - может на STM32 :-)

Это потом, когда я буду собирать проект из кубиков, станет ясно какую мегу надо ставить (а может и не мегу).

===
Вы уже видите плюсы данного подхода, но AVR-ская "жадность" к ресурсам микроконтроллера не отпускает Вас :-) На старших мегах (например, 128-ой) уже можно "жить" с RTOS :-)


PS. Упростите свою задачу (что бы осталась только "соль"), Вы напишите как Вы сейчас делаете. Я постараюсь написать, как это будет выглядеть на uOS-embedded, другие участники может напишут свое решение, может кто-то напишет на freeRTOS. Сравним. Думаю полезно будет. И это, я не в коем случае не агитирую, за uOS. :-) Просто я ее немного знаю, и было пару проектов на ней.
Demiurg
Это не хвост, это антенна
Сообщения: 1480
Зарегистрирован: Ср июн 25, 2008 15:19:44
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение Demiurg »

Инкапсуляцию можно спокойно сделать в программном модуле.
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Вытесняющая многозадачная ОС. Практика AVR

Сообщение ARV »

я уже довольно много написал о том, как сделано сейчас.
и именно так, как вы говорите, я и делал - слушатель usart, читатель СМС и т.п.
проблема макаронного кода начиналась в момент, когда логически надо отправить СМС и ждать ответа (до 60 секунд), и это не может быть причиной для притормаживания других процессов. а как же слушатель USART? jy же в любой момент может услышать то, что снова потребует отправки СМС и ожидания, а это означает повторный вход в функцию отправки/ожидания... и так далее. в итоге приходится каждый чих окружать всякими мьютексами, семаформаи и т.п. "блокировками", и в итоге получается, что вместо ПАРАЛЛЕЛЬНОГО исполнения код исполняется ПОСЛЕДОВАТЕЛЬНО, т.е. КООПЕРАТИВНО.
и именно это меня сильно разочаровало... кооперативно я и так могу сделать, и сделал. где же плюсы RTOS?!
и на счет ОЗУ вы не совсем правильно меня поняли: я не жалел ОЗУ, а не понимал (и сейчас не понимаю), как им правильно распорядиться в RTOS. уже писал о том, как через сообщения обменивался строками... мне рекомендовали использоать статические буферы с семаформаи - см. ранее, в чем прелесть тогда RTOS?!

Добавлено after 49 seconds:
viiv писал(а):На старших мегах (например, 128-ой) уже можно "жить" с RTOS
именно на такой платформе и пытался...
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Ответить

Вернуться в «AVR»