Jump to content

    

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

Пример простого снипета в 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 и без самопальных кооперативных планировщиков. 
 

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

 

 

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
44 minutes ago, petrov said:

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

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

Share this post


Link to post
Share on other sites
17 minutes ago, alexunder said:

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

2018B

Share this post


Link to post
Share on other sites
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. Графики желательно было подписать. На третьем графике не понятны действующие лица.

Share this post


Link to post
Share on other sites

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

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

 

Share this post


Link to post
Share on other sites
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 тут рядом не лежали. 
Это именно универсальная нотация программирования. 

Share this post


Link to post
Share on other sites
4 минуты назад, AlexandrY сказал:

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

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

 

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

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

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

 

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

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

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

 

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

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

Понятно.

Share this post


Link to post
Share on other sites
15 minutes ago, Arlleex said:

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

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

Share this post


Link to post
Share on other sites
19 минут назад, AlexandrY сказал:

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

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

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

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

Share this post


Link to post
Share on other sites
8 hours ago, AlexandrY said:

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

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

Share this post


Link to post
Share on other sites

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

Edited by twix

Share this post


Link to post
Share on other sites

twix

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

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

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

Share this post


Link to post
Share on other sites

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

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

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

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

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

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now