Перейти к содержанию

    

Учимся программировать по новому

Пример простого снипета в Stateflow, который принимает состояние кнопки и выдает сообщение когда кнопку нажимают по определенному шаблону.
Скажем у вас в дивайсе есть только одна кнопка и надо с нее выдать несколько команд, тогда нажимаем один раз коротко и один раз долго - это будет одна команда, два раза коротко , один раз долго - другая.  
Снипет ниже это делает

Вход здесь - состояние кнопки btn_pressed
Выход - очередь сообщений Action
image.thumb.png.26c1645bdd548b20ab28c0de6da8d882.png

А вот схема тестовой установки в Simulinkimage.png.11b0e6426cb0159f048dfd15ab715c17.png
 

А вот графики с тестовой установки

image.thumb.png.2a2431b21b6bdf1c56ab7e358becbf11.png

Теперь из этого снипета можно сгенерировать надежный код на C , C++, HDL или для PLC таких как OMRON или SIEMENS
Без RTOS и без самопальных кооперативных планировщиков. 
 

Но мой взгляд очень удобный метод программирования. 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

ИМХО не из всех конструкций в этом Stateflow будут генерироваться правильные автоматы в HDL.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
44 minutes ago, petrov said:

ИМХО не из всех конструкций в этом Stateflow будут генерироваться правильные автоматы в HDL.

Кто же спорит. Да, нужна сноровка.
Было бы это так же просто как ардуино,  на этом месте было бы уже вытоптанное поле, а StateFlow выпускался бы в эксклюзивной бесплатной версии под Synergy.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Интересные примеры. Какая версия Matlab использовалась?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
17 minutes ago, alexunder said:

Интересные примеры. Какая версия Matlab использовалась?

2018B

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
6 часов назад, AlexandrY сказал:

Без RTOS и без самопальных кооперативных планировщиков.

Идея не нова сама по себе - первое, что попалось на глаза:

https://www.natural-sciences.ru/ru/article/view?id=30104

https://scratch.mit.edu/about

 

Хоть второй и, по сути, игрушка, но полноценно раскрывает идею визуального программирования.

 

А теперь по существу.

AlexandrY, а реальный выхлоп в виде Си-кода, и результирующего ассемблерного кода на реальной платформе (что у Вас там, Renesas Cortex-M4?) привести можете? А то Вам сначала пикосекундные задержки в Zynq дорогу перешли, то жесткий пресвятой реалтайм жизненно необходим; а тут (вдруг ?) окажется, что кнопку опросить килобайт 10 кода уйдет:wink:

 

P.S. Графики желательно было подписать. На третьем графике не понятны действующие лица.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

это какой-то вырожденный вариант цепей/сетей Петри (Petri net) ? вроде как и обычное описание FSM (state-condition) тоже вырожденный вариант

если можно больше одного "шарика" по диаграмме гонять, то невырожденный вроде бы... вообще Petri net штука занятная, но в общем виде не особо для программирования удобная

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
1 hour ago, Arlleex said:

 а тут (вдруг ?) окажется, что кнопку опросить килобайт 10 кода уйдет:wink:

Код с этой модели  вот такой генерируется:

Spoiler

/*
 * File: ButtonService.c
 *
 * Code generated for Simulink model 'ButtonService'.
 *
 * Model version                  : 1.7
 * Simulink Coder version         : 9.0 (R2018b) 24-May-2018
 * C/C++ source code generated on : Wed Feb 27 17:13:16 2019
 *
 * Target selection: ert.tlc
 * Embedded hardware selection: ARM Compatible->ARM Cortex
 * Code generation objectives: Unspecified
 * Validation result: Not run
 */

#include "ButtonService.h"
#include "ButtonService_private.h"

/* Named constants for Chart: '<Root>/ButtonService' */
#define ButtonServic_IN_NO_ACTIVE_CHILD ((uint8_T)0U)
#define ButtonService_IN_PRESSED       ((uint8_T)1U)
#define ButtonService_IN_RELEASED      ((uint8_T)2U)

/* Exported block parameters */
int32_T ACT1 = 1;                      /* Variable: ACT1
                                        * Referenced by: '<Root>/ButtonService'
                                        */
int32_T ACT2 = 2;                      /* Variable: ACT2
                                        * Referenced by: '<Root>/ButtonService'
                                        */
int32_T LONG_TIMEOUT = 5;              /* Variable: LONG_TIMEOUT
                                        * Referenced by: '<Root>/ButtonService'
                                        */
int32_T PTTRN_1 = 5;                   /* Variable: PTTRN_1
                                        * Referenced by: '<Root>/ButtonService'
                                        */
int32_T PTTRN_2 = 9;                   /* Variable: PTTRN_2
                                        * Referenced by: '<Root>/ButtonService'
                                        */

/* Block states (default storage) */
DW_ButtonService_T ButtonService_DW;

/* External inputs (root inport signals with default storage) */
ExtU_ButtonService_T ButtonService_U;

/* External outputs (root outports fed by signals with default storage) */
ExtY_ButtonService_T ButtonService_Y;

/* Real-time model */
RT_MODEL_ButtonService_T ButtonService_M_;
RT_MODEL_ButtonService_T *const ButtonService_M = &ButtonService_M_;

/* Model step function */
void ButtonService_step(void)
{
  boolean_T condIsTrue;
  int32_T q0;

  /* Chart: '<Root>/ButtonService' incorporates:
   *  Inport: '<Root>/btn_pressed'
   *  Outport: '<Root>/pttrn'
   */
  ButtonService_DW.chartAbsoluteTimeCounter++;
  condIsTrue = (ButtonService_DW.is_c3_ButtonService == ButtonService_IN_PRESSED);
  if ((!condIsTrue) || (!ButtonService_DW.condWasTrueAtLastTimeStep_1)) {
    ButtonService_DW.durationLastReferenceTick_1 =
      ButtonService_DW.chartAbsoluteTimeCounter;
  }

  ButtonService_DW.condWasTrueAtLastTimeStep_1 = condIsTrue;
  condIsTrue = (ButtonService_DW.is_c3_ButtonService ==
                ButtonService_IN_RELEASED);
  if ((!condIsTrue) || (!ButtonService_DW.condWasTrueAtLastTimeStep_1_b)) {
    ButtonService_DW.durationLastReferenceTick_1_o =
      ButtonService_DW.chartAbsoluteTimeCounter;
  }

  ButtonService_DW.condWasTrueAtLastTimeStep_1_b = condIsTrue;
  if (ButtonService_DW.is_active_c3_ButtonService == 0U) {
    ButtonService_DW.chartAbsoluteTimeCounter = 0;
    ButtonService_DW.is_active_c3_ButtonService = 1U;

    /* Outport: '<Root>/pttrn' */
    ButtonService_Y.pttrn = 1;

    /* Outport: '<Root>/action' */
    ButtonService_Y.action = 0;
    if (ButtonService_U.btn_pressed) {
      ButtonService_DW.durationLastReferenceTick_1 =
        ButtonService_DW.chartAbsoluteTimeCounter;
      ButtonService_DW.is_c3_ButtonService = ButtonService_IN_PRESSED;
      ButtonService_DW.condWasTrueAtLastTimeStep_1 =
        (ButtonService_DW.is_c3_ButtonService == ButtonService_IN_PRESSED);
    } else {
      ButtonService_DW.durationLastReferenceTick_1_o =
        ButtonService_DW.chartAbsoluteTimeCounter;
      ButtonService_DW.is_c3_ButtonService = ButtonService_IN_RELEASED;
      ButtonService_DW.condWasTrueAtLastTimeStep_1_b =
        (ButtonService_DW.is_c3_ButtonService == ButtonService_IN_RELEASED);
    }
  } else if (ButtonService_DW.is_c3_ButtonService == ButtonService_IN_PRESSED) {
    if (!ButtonService_U.btn_pressed) {
      condIsTrue = (ButtonService_DW.is_c3_ButtonService ==
                    ButtonService_IN_PRESSED);
      if ((!condIsTrue) || (!ButtonService_DW.condWasTrueAtLastTimeStep_1)) {
        ButtonService_DW.durationLastReferenceTick_1 =
          ButtonService_DW.chartAbsoluteTimeCounter;
      }

      ButtonService_DW.condWasTrueAtLastTimeStep_1 = condIsTrue;
      if (ButtonService_DW.chartAbsoluteTimeCounter -
          ButtonService_DW.durationLastReferenceTick_1 >= (LONG_TIMEOUT << 1)) {
        if (ButtonService_Y.pttrn > 1073741823) {
          q0 = MAX_int32_T;
        } else if (ButtonService_Y.pttrn <= -1073741824) {
          q0 = MIN_int32_T;
        } else {
          q0 = ButtonService_Y.pttrn << 1;
        }

        if (q0 > 2147483646) {
          ButtonService_Y.pttrn = MAX_int32_T;
        } else {
          ButtonService_Y.pttrn = q0 + 1;
        }
      } else if (ButtonService_Y.pttrn > 1073741823) {
        ButtonService_Y.pttrn = MAX_int32_T;
      } else if (ButtonService_Y.pttrn <= -1073741824) {
        ButtonService_Y.pttrn = MIN_int32_T;
      } else {
        ButtonService_Y.pttrn <<= 1;
      }

      if (ButtonService_Y.pttrn == PTTRN_1) {
        /* Outport: '<Root>/action' */
        ButtonService_Y.action = ACT1;
        ButtonService_Y.pttrn = 1;
      } else {
        if (ButtonService_Y.pttrn == PTTRN_2) {
          /* Outport: '<Root>/action' */
          ButtonService_Y.action = ACT2;
          ButtonService_Y.pttrn = 1;
        }
      }

      ButtonService_DW.durationLastReferenceTick_1_o =
        ButtonService_DW.chartAbsoluteTimeCounter;
      ButtonService_DW.is_c3_ButtonService = ButtonService_IN_RELEASED;
      ButtonService_DW.condWasTrueAtLastTimeStep_1_b =
        (ButtonService_DW.is_c3_ButtonService == ButtonService_IN_RELEASED);
    }
  } else {
    condIsTrue = (ButtonService_DW.is_c3_ButtonService ==
                  ButtonService_IN_RELEASED);
    if ((!condIsTrue) || (!ButtonService_DW.condWasTrueAtLastTimeStep_1_b)) {
      ButtonService_DW.durationLastReferenceTick_1_o =
        ButtonService_DW.chartAbsoluteTimeCounter;
    }

    ButtonService_DW.condWasTrueAtLastTimeStep_1_b = condIsTrue;
    if (ButtonService_DW.chartAbsoluteTimeCounter -
        ButtonService_DW.durationLastReferenceTick_1_o > 10) {
      /* Outport: '<Root>/pttrn' */
      ButtonService_Y.pttrn = 1;
      ButtonService_DW.durationLastReferenceTick_1_o =
        ButtonService_DW.chartAbsoluteTimeCounter;
      ButtonService_DW.is_c3_ButtonService = ButtonService_IN_RELEASED;
      ButtonService_DW.condWasTrueAtLastTimeStep_1_b =
        (ButtonService_DW.is_c3_ButtonService == ButtonService_IN_RELEASED);
    } else if (ButtonService_U.btn_pressed) {
      ButtonService_DW.durationLastReferenceTick_1 =
        ButtonService_DW.chartAbsoluteTimeCounter;
      ButtonService_DW.is_c3_ButtonService = ButtonService_IN_PRESSED;
      ButtonService_DW.condWasTrueAtLastTimeStep_1 =
        (ButtonService_DW.is_c3_ButtonService == ButtonService_IN_PRESSED);
    } else {
      /* Outport: '<Root>/action' */
      ButtonService_Y.action = 0;
    }
  }

  /* End of Chart: '<Root>/ButtonService' */
}

/* Model initialize function */
void ButtonService_initialize(void)
{
  /* Registration code */

  /* initialize error status */
  rtmSetErrorStatus(ButtonService_M, (NULL));

  /* states (dwork) */
  (void) memset((void *)&ButtonService_DW, 0,
                sizeof(DW_ButtonService_T));

  /* external inputs */
  ButtonService_U.btn_pressed = false;

  /* external outputs */
  (void) memset((void *)&ButtonService_Y, 0,
                sizeof(ExtY_ButtonService_T));

  /* SystemInitialize for Chart: '<Root>/ButtonService' */
  ButtonService_DW.is_active_c3_ButtonService = 0U;
  ButtonService_DW.is_c3_ButtonService = ButtonServic_IN_NO_ACTIVE_CHILD;
  ButtonService_DW.chartAbsoluteTimeCounter = 0;
}

/* Model terminate function */
void ButtonService_terminate(void)
{
  /* (no terminate code required) */
}

/*
 * File trailer for generated code.
 *
 * [EOF]
 */

 

Тут надо понимать, что код сгенерирован может быть очень по разному.
В Matlab есть куча настроек как в генерируемом коде давать имена, как объявлять аргументы, как сепарировать функции, какие типы использовать и т.д. и т.п.
Код приведенный здесь сгенерирован по встроенному критерию матлаба лучшей  удобочитаемости (как ни странно), а есть еще критерии оптимальности, экономии RAM, экономии Flash и т.д.

Даю гарантию вручную вы напишите больше кода, особенно завязните в таймаутах и ожиданиях ивентов (или как в матлабе их называют - темпоральных функциях - after(time), before(time), duration(state) ... )  
Скорее всего начнете сразу писать доморощенный шедулер.
Обратите внимание на то что модель не зависает ни на такт внутри себя, она на каждом такте возвращает управление внешнему процессу.  

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

1 hour ago, yes said:

это какой-то вырожденный вариант цепей/сетей Петри (Petri net) ? вроде как и обычное описание FSM (state-condition) тоже вырожденный вариант

Мда, шарики навели на ложную ассоциацию с сетями Petri.  Но сети Petri тут рядом не лежали. 
Это именно универсальная нотация программирования. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
4 минуты назад, AlexandrY сказал:

Тут надо понимать, что код сгенерирован может быть очень по разному.

ИМХО, приведенный фрагмент кода, несмотря на то, что он сгенерирован по критерию лучшей удобочитаемости, слабо читаем. За лесом переменных сути не особо-то видно.

 

7 минут назад, AlexandrY сказал:

Даю гарантию вручную вы напишите больше кода, особенно завязните в таймаутах и ожиданиях ивентов (или как в матлабе их называют - темпоральных функциях - after(time), before(time), duration(state) ... )  
Скорее всего начнете сразу писать доморощенный шедулер.

Даю гарантию, что напишу меньше кода, не завязну в таймаутах и ожиданиях. Обойдусь без доморщенного шедулера и код будет куда более читаемым. Но я материально не заинтересован доказывать Вам это:wink:

 

13 минут назад, AlexandrY сказал:

Обратите внимание на то что модель не зависает ни на такт внутри себя, она на каждом такте возвращает управление внешнему процессу.

Правильно построенная задача в кооперативной/вытесняющей RTOS тоже лишние такты в тепло не превращает.

 

14 минут назад, AlexandrY сказал:

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

Понятно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
15 minutes ago, Arlleex said:

Даю гарантию, что напишу меньше кода, не завязну в таймаутах и ожиданиях. Обойдусь без доморщенного шедулера и код будет куда более читаемым. Но я материально не заинтересован доказывать Вам это:wink:

Я тоже материально не заинтересован, однако легко разработал эту модель и еще имею время с вами ее обсуждать. В этом и есть суть удобства. 
Код из матлаба читать не надо.  
Раньше тоже возмущались почему из C-и генерируется такой слабочитаемый ассемблер. Однако привыкли. 
  

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
19 минут назад, AlexandrY сказал:

Я тоже материально не заинтересован, однако легко разработал эту модель и еще имею время с вами ее обсуждать. В этом и есть суть удобства. 
Код из матлаба читать не надо.  
Раньше тоже возмущались почему из C-и генерируется такой слабочитаемый ассемблер. Однако привыкли.

Тогда последние вопросы - допустим, есть эта модель.

1. Как встроить ее в проект, который крутится, например, под RTOS с LwIP и прочим middleware? Как подружить ее с уже написанным и отлаженным софтом (например, после определения команды с кнопки дать семафор планировщику, т.е. банально вызвать OS_SemGive())?

2. Если выхлоп из StateFlow получается модуль из двух файлов .h и .c, то, подключая их к своему проекту на злом уровне оптимизации, каков шанс, что оптимизатор не выкинет половину сгенерированного кода? А если выкинет, то как разобраться, почему он это выкинул, если код из матлаба читать не возможно (и, якобы, не надо)?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
8 hours ago, AlexandrY said:

Кто же спорит. Да, нужна сноровка.
Было бы это так же просто как ардуино,  на этом месте было бы уже вытоптанное поле, а StateFlow выпускался бы в эксклюзивной бесплатной версии под Synergy.

Да в принципе не сложно там всё, наверное как раз дороговизна и мешает заменить древний дрянной HDL.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Скажу так, идея фуфло в принципе.

Изменено пользователем twix

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

twix

Визуализация это обработка графической информации. Так вот затраты мозга на работу с графикой по сравнению

с текстом ну как у обычного компа, примерно в 100... 1000 раз.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Процесс разрабоки программы необходимо разделять на четыре и более, сколько необходимо, этапов. Приоритет ВСЕГДА должен принадлежать заказчику - клиент всегда прав. На первом этапе заказчик должен возможно точнее описать в ТЗ все свои требования, а исполнитель подтвердить или опровергнуть возможность разработки программы в соответствии с ТЗ. 

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

ТС для примера показал алгоритм ввода необходимого клиенту значения посредством ШИМ двух кнопок. Этот метод давно разработан и широко используется.

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

https://zenner.nt-rt.ru/

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти