Страница 4 из 11
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Сб фев 23, 2019 09:26:22
ARV
судя по всему, FX-RTOS не для AVR, к тому же платная система - зачем она мне? сайт, как сейчас модно, рассчитан на дебилов с деньгами - картинок много, а ничего конкретного нет. и найти нельзя. зато кнопок "купить" в изобилии. тьфу, кака.
что касается самой темы топика, я из крайности в крайность бросаюсь. сначала был переполнен ликованием от удобства RTOS, сейчас переживаю глубокое разочарование... причем, скорее разочарование в том, что на платформе AVR развернуться в полной мере с RTOS достаточно непросто, а ломка сознания очень большая. просто кардинальная смена парадигмы программирования - это большой шок. привычки из программирования под винду тут никак не подходят.
в общем, я сейчас весьма в растрепанных чувствах. занимаюсь своим проектом уже вне парадигмы RTOS, хотя под её влиянием получается что-то вроде собственной кооперативки.
единственное, что я могу уверенно сказать всем, кто захочет освоить RTOS в AVR: никогда не смотрите видеоролики и не читайте статьи о примерах "мигания" светодиодами! ничего общего с реальными задачами такие примеры не имеют абсолютно, и способны только задурить голову. еще раз:
демонстрация возможностей RTOS на примере мигания светодиодами - это ОБМАН, жульничество. не ведитесь на это!
еще одно я понял: RTOS на самом деле совсем не сложная штука. главная проблема - сделать переключение контекста, т.е. суметь запихать в один стек все регистры, затем перейти на другой стек и вытащить все регистры из него. все прочее по сути - реализация атомарного доступа к разным глобальным структурам.
и еще одно я понял: не надо бояться обработчиков прерываний! любая RTOS завязана на акивном использовании вызовов функций из обработчиков прерываний, и это никого не смущает. следовательно, не надо этого бояться и в проектах без RTOS. например, все советуют делать обработчик как можно более коротким и быстрым, при этом без обращений к функциям системных библиотек и вообще из других модулей - дескать, ради этого в стек сохраняется много регистров, что замедляет... так вот: насчет системных (библиотечных то есть) функций - требование справедливое, надо быть с ними очень осторожным, т.к. они в большинстве своем не реентерабельны. а вот на счет "своих" функций - фигня: если хотите, то смело их используйте в прерываниях и не бойтесь ничего. конечно, если вы не делаете систему, латентность реакции на запрос прерывания которой должна быть в 50 наносекунд и не более

Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Вс фев 24, 2019 04:57:05
Demiurg
[uquote="ARV",url="/forum/viewtopic.php?p=3576237#p3576237"]...[/uquote]
Я ж ведь говорил, что ртос не панацея. Это всего лишь очередь задач, и никак не алгоритм программы. На самом деле, главная проблема в программировании - это взаимодействие программных модулей. Когда я реализовал механизм сообщений, многие мои затруднения закончились.
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Вс фев 24, 2019 08:19:27
ARV
Demiurg писал(а):Когда я реализовал механизм сообщений, многие мои затруднения закончились.
видимо, у каждого свои собственные проблемы, и, соответственно, свои собственне пути их разрешения находятся...
мои проблемы с очередью не кончились. очередь без асинхронного обслуживания сообщений в ней, имхо, проблему не решает. ну или я не в том направлении пошел и попал в тупик.
вот смотрите: есть 2 USART, к которым подключены MP3-плейер и GSM-модем. оба обладают особенностью посылать сообщения не тогда, когда ждешь чего-то конкретного от них, но и сами по себе. например, модем может прислать RING, а MP3-модуль может прислать "трек завершен" в совершенно непредсказуемый момент времени. и, если не асинхронного обслуживания этих сообщений, вся логика ломается кардинально. ведь обычно мы работаем с периферией в синхронном режиме "дал задание - дождался завершения", а тут возникает ситуация "дал задание - дождался неизвестно чего - задание завершилось".
в итоге простая задача обработки кнопок, нажимаемых пользователем, выливается в что-то странное:
1. ждем сообщение
2. если это сообщение о нажатии кнопки - обрабатываем
3. если это сообщение не от кнопок - тоже обрабатываем (хотя это не должно быть проблемой обработки кнопок!)
а теперь представьте себе, что обработка всех сообщений вообще ведется в главном цикле, а обработка кнопок должна вестись в функции ввода строки. лично у меня это вылилось в рекурсивное обращение к функции обработки сообщений главного цикла... то есть в главном цикле по определенному сообщению я начинаю ввод строки, изнутри которого вызываю главный цикл, чтобы обработать все отличные от кнопок сообщения...
чтобы это работало, приходится загромождать код почтоянными проверками всяких флагов, чтобы не попасть в бесконечную рекурсию...
кстати, эта же проблема (с порядком обработки сообщений) и испортила мне впечатление от RTOS, разве что немножко иначе...
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Вс фев 24, 2019 10:50:52
GoldenAndy
Так выше уже предлагали вариант.
Работа с каждым модулем выносится в отдельный .h+.c файл и делается обработчик, который работает как конечный автомат.
А в основной программе с периодичностью, допустим, 1мс вызываются обработчики.
Общение между модулями организовать через процедуры, вынесенные в заголовочный файл каждого модуля...
Т.е. main - просто дергает обработчики каждого модуля. А что в модуле творится - уже вопрос модуля.
Если без отдельной очереди сообщений - то это большой и толстый конечный автомат.
Если прикрутить себе какую то очередь - получится уже некое подобие ртоса
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Вс фев 24, 2019 13:32:59
ARV
большой и толстый автомат - это, конечно, все в корне меняет

смысл не в том, чтобы его сделать, а в том, чтобы от него уйти.
например, в винде: если мы ждем ввода с клавиатуры при помощи функции getch, это ведь не означает, что замирает мышка? но в винде мышкой занимается ОС, а в моей программе мышки хоть и нет, но роль её сообщений играют другие сообщения, и обслуживать их некому, кроме той самой функции getch. понимаете, о чем я?
вместо логически изолированных функций получается каша, макаронный код.
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Вс фев 24, 2019 14:16:30
OKF
Ну так проверили наличие символа и понеслись других обслуживать. Зачем торчать в getch? Да и некрасиво это.)
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Вс фев 24, 2019 14:29:21
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");
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Вс фев 24, 2019 15:37:50
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 - рекурсия, что тоже не украшает код...
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Вс фев 24, 2019 16:52:06
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;
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Вс фев 24, 2019 19:35:07
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");
}
}
Не думаю, что здесь недостаточно прозрачно. Хотя это простой случай.
Мне бы была понятна полезность ОС если Вы используете не свой код, в котором может быть что хочешь. И который сложно/утомительно переделывать под свои нужды. А так, если изначально пишете для себя... Конечно, если несколько процессов, наглядность может страдать. Ну так можно и как то оформить покрасивше.) Не думаю что вызовы ОС добавят красоты и прозрачности.
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Вс фев 24, 2019 20:28:14
ARV
друзья мои, вы не пытаетесь взглянуть на проблему с той стороны, которая важна для меня. вы пытаетесь свести мою задачу к тому, что вы уже делали. увы, это не подходит, особенно с учетом того, что с RTOS вы ничего не делали...
OKF писал(а):Да так же само, только без while
нет, не то же самое. у вас отсутствует именно цикл, в котором происходит ожидание завершения пользовательского ввода. у вас линейный кусочек, который еще надо вплести в общее целое.
Demiurg писал(а):Ввод это уже другая тема
так и моя задача - другая. меню, даже многоуровневое и с разными наворотами я делал не меньше раз, чем вы. и даже без цикла, в котором происходит "хождение" по меню, и даже с циклом.
к сожалению, проблема не в ОС... а в том, что не получается сделать красиво и логично.
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Вс фев 24, 2019 20:40:34
Demiurg
Даю подсказку ещё раз. Даже ещё одну. Есть такая штука, называется интеллект карта. Гугл в помощь. Не поленитесь, сядьте и распишите ваш проект. Ваши затруднения. Составьте интеллект карту. Ячейка интеллект карты может быть как примитивом, так и "макросом". Когда вы нарисуете карту, весь проект будет перед глазами. Вы сейчас, судя по всему крутите проект в голове. Из за этого не видите деталей и общей картины. Отступите от проблемы. Начните перенос на бумагу. Этим вы отвлечетесь, выйдете из проблемы. А когда нарисуете, встанете уровнем выше. И решение придёт. И такое, что сами охренеете.
Кстати, составление взаимосвязей конечных автоматов подобие интеллект карт.
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Вс фев 24, 2019 22:23:38
ARV
Не хочу показаться неблагодарной скотиной...
Но какое может быть еще решение, кроме уже многократно огрворенного? То есть кроме путаного автомата состояний или обработки сообщений из очереди в разных местах?
Не поручусь наверняка, но все-таки думаю, что вариантов больше и нет. Если, конечно, отказаться от готовой RTOS, что я был вынужден сделать по причине того, что никто толком не смог мне в этом помочь, а сам я себя загнал в тупик?
Что касается бумаги и т.п. планирования - это у меня в избытке сделано и продолжает делаться. Понимание общих принципов не снимает проблемы деталей, а в них кроется дьявол. Решение есть, но оно не вызывает моего полного удовлетворения.
Давайте, если не возражаете, перейдем от конкретных примеров из ваших практик к решению тех задач, которые, вероятно, вам и не попадались. И не нужно примеров готового кода, достаточно (и даже предпочтительнее) алгоритм решения, принцип.
Вот простейшая задача: СМС GSM-модуль может принять в виде PDU-сообщения, в котором каждый символ кодируется 2 байтами, т.е. размер сообщения может достигать 320 байт, плюс сведения о номере отправителя и т.п. служебная информация - итого 360 байт. То есть буфер меньшего размера в принципе не позволит принять и обработать СМС. Требуется быть готовым в любой момент принять и обработать СМС, причем в качестве обработки один из вариантов - отправка ответного СМС которое - см. ранее, - так же потребует буфера). Следует учитывать, что отправка СМС может потребовать до 60 секунд (!), в течение которых неизвестен результат попытки отправить, но зато в это время может прийти ЕЩЕ одно СМС, в ответ на которое надо отправить СМС, так же может поступить входящий звонок, который следует отработать по-своему. Кстати, анализ входящего звонка - это тоже анализ принятой по USART строки (там номер звонящего), то есть опять требуется какой-то буфер...
Давайте попробуем мозговым штурмом решить эту простенькую задачку, причем не абы как, а с ориентиром на минимизацию расхода ОЗУ, как наиболее дефицитного ресурса AVR. Для простоты считаем, что кроме сообщений GSM-модема (входящих и исходящих), требуется обрабатывать только сообщения от таймера каждую секунду и матричной клавиатуры 3х4 кнопки. Причем, как вы понимаете, и кнопки, и таймер могут послать свои сообщения как во время ожидания ответа от GSM-модуля, так и в процессе работы с модулем, а реакция на эти сообщения может потребовать взаимодействия с GSM-модулем (например, потребовать той самой злосчастной отправки СМС).
Чтобы вы не думали, будто я на вашей шее хочу в рай въехать, скажу, что я решил эту проблему уже дважды: при помощи RTOS и без оной. Оба варианта работали, но не так элегантно были реализованы, как мне того хотелось бы.
Вариант с RTOS использовал мною собственноручно написанный менеджер "кучи" (на самом деле распределения между задачами общего массива в 640 байт) и несколько задач с собственными очередями сообщений. Принимаемые от GSM-модуля строки перемещались в динамически выделенные области и указатели на эти области помещались в очередь сообщений для асинхронной обработки задачами. Этот вариант не устроил меня тем, что по мере увеичения числа асинхронных "девайсов" требовалось все больше и больше новых задач, больше ОЗУ под кучу и стек каждой задачи, к тому же все то было крайне некрасиво, ибо нарушало основной принцип работы с динамической памятью - освобождать память там же, где и выделял. К сожалению, это было в принципе невозможно, и порождало ситуации блокировок мьютексами или семафорами процессов, которым памяти не хватало. Такие блокировки сводили RTOS к обычной кооперативной ОС со всеми ее недостатками.
Вариант без RTOS я делаю сейчас по принципу, что обрисовал ранее: рекурсивный вызов обработчика очереди сообщений везде, где требуется ожидание чего-либо (с контролем за тем, чтобы рекурсия не была слишком глубоко рекурсивной). Этот вариант так же вполне успешно работает, но, как и ранее, не обладает красотой и стройностью.
Возможно, вы дадите мне новые идеи, как можно решить мою проблему третьим способом, одновременно эффективным и красивым?
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Вс фев 24, 2019 22:38:31
Demiurg
Без лишних слов. Пришлите мне ТЗ. Что за устройство, что делает, составные модули. Интерфейс управления. Можете в личку, если это интеллектуальная собственность. Если есть аналоги, пришлите ссылки.
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Пн фев 25, 2019 06:58:57
ARV
Demiurg писал(а):Без лишних слов
боюсь, если я вас загружу всем запрошенным, это отнимет у вас слишком много времени, т.к. только краткое описание параметров файла конфигурации для моего проекта состоит из 11-страничного файла
но в общем и целом это не секрет, и я могу рассказать о своей задумке, хотя это и не может считаться ТЗ.
я пытаюсь сделать многофункциональное устройство контроля и управления, совмещающее в себе охранно-пожарную сигнализацию, массив дискретных регуляторов температуры и несколько дополнительных возможностей по анализу входных сигналов и формированию соответствующих выходных. в качестве интерфейса управления мною выбрана система голосового меню, работающего как при нажатии кнопок на клавиатуре устройства, так и при дистанционном дозвоне. для воспроизведения голосовых сообщений применяется отдельный модуль MP3-плейера с отдельной SD-картой со звуковыми файлами. кроме прочего предусматривается возможность управления и контроля при помощи СМС. в случае возникновения одной или нескольких заранее определенных в настройках "тревожных" ситуаций предусматривается на выбор несколько вариантов оповещения: от включения сирены до обзвона по списку заданных заранее номеров с голосовым оповещением и/или рассылки соответствующих СМС так же по списку. в качестве интерфейса конфигурирования применяется SD-карта, на которую помещается текстовый файл с нужными параметрами (формат по типу конфигов линуха), и на эту же карту пишется лог всех важных событий в системе.
это если вкратце. аналогов в разной степени похожести полно как среди коммерческих изделий (китайских в основном), так и среди любительских. но в том виде, как я решил все сделать, полных аналогов нет.
не знаю, чем это может помочь вам помочь мне

Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Пн фев 25, 2019 07:34:44
Demiurg
Без лишних слов, это типа не благодарный и прочая муть. Давайте ваш талмуд. К примеру, мне нужно понять, как задуман кнопочный интерфейс. Мне нужна вся навигация по управлению и вводу. В общем от вас тз
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Пн фев 25, 2019 08:14:20
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 - символ кнопки
Re: Вытесняющая многозадачная ОС. Практика AVR
Добавлено: Пн фев 25, 2019 11:39:42
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.

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