Обсуждаем контроллеры компании Atmel.
Ответить

Re: Как поместить цикл в case оператора switch

Чт окт 06, 2022 11:35:47

Код:
uint8_t Switch(uint8_t in1, uint8_t in2, uint8_t in3)
{
   static uint8_t state;

   if(in1 && !(in2 || in3))
      state = 1;

   if(in2 && !(in1 || in3))
      state = 2;

   if(in3 && !(in1 || in2))
      state = 3;

   return state;
}

Последний раз редактировалось MLX90640 Чт окт 06, 2022 19:20:53, всего редактировалось 1 раз.

Re: Как поместить цикл в case оператора switch

Чт окт 06, 2022 11:49:56

MLX90640 Можно прокомментировать?

Re: Как поместить цикл в case оператора switch

Чт окт 06, 2022 12:14:43

Эта функция принимает в себя три состояния входов от кнопок (1 - нажато, 0 - не нажато). В условиях if() проверяется, какая одна (только одна) из кнопок была нажата и соответственно присваивает переменной state одно из значений 1, 2, 3. Переменная state сохраняет свое значение при выходе из функции (квалификатор static), таким образом если все кнопки отпущены, остается зафиксированным последнее состояние.
Далее, (в этой же функции ниже или же вне её) оператором switch можно выбрать и выполнить требуемое действие:
Код:
   st = ButtSwitch(Read1(), Read2(), Read3());

   switch(st){
   case 1: LED1() ;break;
   case 2: LED2() ;break;
   case 3: LED3() ;break;
   default:
   }

Функции Read1(), Read2(), Read3() в параметрах ф-ции ButtSwitch() выполняют чтение входа от соотв.кнопки и возвращают 1 или 0, которое передается в ф-цию ButtSwitch.
Функции LED1(), LED2(), LED3() выполняют включение соотв.светодиода и выключение остальных. Вместо этих ф-ций могут быть любые ф-ции, выполняющие назначенные действия.

Re: Как поместить цикл в case оператора switch

Чт окт 06, 2022 12:21:46

Спасибо.

Добавлено after 3 minutes 49 seconds:
Для меня немного сложновато, придется создавать все с самого начала и отказаться от КА

Re: Как поместить цикл в case оператора switch

Чт окт 06, 2022 12:23:43

Можно и по-другому записать, убрав лишние действия:
Код:
 void ButtSwitch(uint8_t in1, uint8_t in2, uint8_t in3)
{
   if(in1 && !(in2 | in3))
      LED1();

   if(in2 && !(in1 | in3))
      LED2();

   if(in3 && !(in1 | in2))
      LED3();
}

Re: Как поместить цикл в case оператора switch

Чт окт 06, 2022 18:28:20

Спасибо, буду пробовать.
А что я с автоматами совсем пошел в другую сторону?

Добавлено after 5 hours 57 minutes 48 seconds:
MLX90640 Я не настолько продвинут, чтобы сразу разобраться в коде.
С простыми "КА" было легче научиться
Исправь, если я ошибаюсь:
Функции LED1() это void LED1(void){PORTB|=(1<<PB0);PORTB&=~(1<<PB1);}
Это для примера функции включения светодиода на PB0 и выключение другого на PB1
uint8_t in1 это PINB0( или 0 или 1) - в качестве примера.Но как это записать в виде функции
и затем соединить я не могу сообразить.

Re: Как поместить цикл в case оператора switch

Чт окт 06, 2022 18:36:57

Не знаю, куда вы там шли, я не смотрел.
Но. Вот просто для понимания. Чтобы понять, как в коде выразить те или иные алгоритмы, весьма полезно нарисовать на бумажке так называемые графы переходов и состояний. Любой КА начинается именно с этого.
Вот например, для одной кнопки, работающей на вкл/выкл при нажатиях будет выглядеть вот так:
Изображение
в ромбиках - ветвление по условию (например, if() ), в прямоугольниках - изменения состояний, действия. Читается сверху вниз, по стрелочкам.
IN - проверка входного состояния кнопки (нажата/не нажата).
P - предыдущее состояние этой кнопки, сохраненное до текущего момента (бала нажата/не была нажата)
Tr - триггер, меняющий свое состояние на противоположное и определяющий действие
STATE - выходное состояние, определяющее дальнейшие действия.

Теперь это надо записать в коде:
Код:
int8_t ButtSwich(uint8_t in)
{
   static uint8_t p, tr;  // переменные сохраняют свое состояние после выхода
   int8_t state = 0;

   /*+++ проверка входного условия IN +++*/
   if(in)
   {
      /*--- проверка предыдущ.состояния P ---*/
      if(p == 0)
      {
         p = 1;
         /*... ветвление триггера Tr ...*/
         if(tr)
         {
            tr = 0;
            state = -1;
         }else
         {
            tr = 1;
            state = 1;
         }/*............................*/
      }/*----------*/
   }else
   {
      p = 0;
   }/*++++++++++++++++++++++++++++++++++++++++*/
   return state;
}

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

Теперь, чтобы сделать то, что вы хотите - три переключаемые кнопки. Для этого нужно составить три таких схемы и добавить ограничивающие условия, как писал ранее.
Код:
int8_t ButtSwich(uint8_t in1, uint8_t in2, uint8_t in3)
{
   static uint8_t p, tr1, tr2, tr3;
   int8_t state = 0;

   /* Блокировка одновременных нажатий кнопок */
   if((in1 && in2)||(in2 && in3)||(in1 && in3)||(in1 && in2 && in3))
   {
      p = 1;
      return state;
   }

   /* Проверка отпускания всех кнопок */
   if(!in1 && !in2 && !in3)
   {
      p = 0;
      return state;
   }

   /* Провека срабатывания входа 1 */
   if(in1)
   {
      if(!p)
      {
         p = 1;
         if(tr1)
         {
            tr1 = 0; tr2 = 0; tr3 = 0;
            state = -1;
         }else
         {
            tr1 = 1; tr2 = 0; tr3 = 0;
            state = 1;
         }
      }
   }

   /* Провека срабатывания входа 2 */
   if(in2)
   {
      if(!p)
      {
         p = 1;
         if(tr2)
         {
            tr2 = 0; tr1 = 0; tr3 = 0;
            state = -2;
         }else
         {
            tr2 = 1; tr1 = 0; tr3 = 0;
            state = 2;
         }
      }
   }

   /* Провека срабатывания входа 3 */
   if(in3)
   {
      if(!p)
      {
         p = 1;
         if(tr3)
         {
            tr3 = 0; tr1 = 0; tr2 = 0;
            state = -3;
         }else
         {
            tr3 = 1; tr1 = 0; tr2 = 0;
            state = 3;
         }
      }
   }

   return state;
}

В условиях использована упрощенная запись.
if(in1) означает "если in1 имеет значение, отличное от 0".
if( !in1) противоположна по смыслу предыдущему, то есть "если in1 равен 0".
Вначале проверяется нажатие более одной кнопки, и если это так, то выход с состоянием 0, а P = 1. Далее проверяется наоборот, отпускание всех кнопок. Если это так, то P = 0 и выход с состоянием 0. Ну и после этого проверяется состояние каждого входа. Состояние P общее для всех. А триггеры раздельные, но сбрасываются во всех остальных входах, кроме активного.

Re: Как поместить цикл в case оператора switch

Чт окт 06, 2022 18:50:41

Спасибо большое за урок .Буду изучать этот код, В принципе ,как работают операторы и циклы я знаю ,но век- живи век -учись. Спасибо.
Если есть время посмотри мой код в файле zip.

Re: Как поместить цикл в case оператора switch

Чт окт 06, 2022 18:54:02

Уровень владения кодописательством надо подтягивать :) Без этого никак.
Лично я придерживаюсь позиции, что следует оформлять показанный фрагмент кода в виде отдельной функции с тремя входными параметрами, а не заталкивать в нее непосредственное чтение физических входов от кнопок. Во-первых, это принцип самого языка Си, когда программа делится на функциональные блоки, оформляемые в виде функций.
Во-вторых, если физически кнопки могут быть реализованы двумя способами - активный (при нажатии) высокий уровень и активный низкий уровень. Да и физические порты кнопок могут изменяться от проекта к проекту. И вам придется исправлять код внутри этого функционального блока под конкретный случай. А если блок оформить так, как показано, то функции чтения кнопок будут отдельные, их можно править как угодно, лишь бы на выходе они давали 0 в неактивном (отпущенном) состоянии и любое число, отличное от 0 (например 1) в состоянии нажатой кнопки.

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

Следующий этап освоения КА - это написание таблиц переходов и состояний. Поскольку при усложнении условий длина текста с if и switсh быстро разрастается на несколько страниц и всё это сложно редактировать и понимать. Таблицы представляют собой индексированные массивы данных, содержащие в себе все возможные состояния и переходы КА.

Re: Как поместить цикл в case оператора switch

Чт окт 06, 2022 19:41:34

Спасибо

Re: Как поместить цикл в case оператора switch

Пт окт 07, 2022 03:43:29

MLX90640, Код жесть. Позже подтянусь. Пока занят. ТС, пока ищи цикл статей Татарчевского. Switch-case технологии.

Re: Как поместить цикл в case оператора switch

Пт окт 07, 2022 06:13:49

Demiurg, в чем тут жесть? Во-первых, надо написать так, чтобы понял автор вопроса, то есть без лишних замудрений. Я то могу написать и с таблицами состояний и переходов. А switch-case - это то же самое, что и if-else, если чо так. только входные условия таковы, что switch-case не будет давать никаких даже визуальных преимуществ - входные параметры раздельные, это общий случай.
Во-вторых, в коде реализована именно так функциональность по переключениям, которую просил автор - три кнопки работают как переключатели, повторное нажатие той же кнопки выключает ее. Если вы беспокоитесь за дребезг кнопок, то его подавление давно уже делается вовне - интервалы опроса кнопок и конденсаторы на кнопках. Но если есть особое желание, можно в показанный блок вставить интегрирующие счетчики.
А если вы хотите встроить в этот код чтение входов кнопок и изменение состояния выходов, то конечно вы можете это сделать, только я выше объяснял, почему это нежелательно.

Повторюсь, switch-case в данном случае ничего особого не дадут - входы разные. Нет, ну можно конечно состояния входов объединить в одну переменную. Но тут в коде показан общий принцип, когда входные параметры отдельные.
Так что никакой жести. Вы просто невнимательно смотрели и не поняли общий принцип.

Re: Как поместить цикл в case оператора switch

Пт окт 07, 2022 06:53:18

Ваши примеры неудобочитаемы, тем более для начинающих. Автомат принимает события к примеру. События отправляет модуль опроса входов. Добавочный параметр код или номер входа. Уже после подавления дребезга.
Пусть у нас событие нажатия. Смотрим, что нажалось. Более подробно позже распишу.
Я слышал и читал про замену switch-case if-ами. Сам пробовал. Код с ифами становится нечитабельным

Re: Как поместить цикл в case оператора switch

Пт окт 07, 2022 07:22:01

Если хотите удобочитаемости, замените switch-case и if-else на таблицы состояний и переходов, и тогда все работает через индексы и извлечения из таблиц.
То, что вы хотите написать, я это знаю. Но вовсе не обязательно именно так делать, потому что, по большому счету, начинается дублирование одного и того же функционала в конкретном примере, что тоже можно назвать жестью. Код события в этом конкретном примере уже и выдается показанной выше функцией.
Вот схема переходов и состояний показанного:
Изображение

Кстати, при всего двух вариантах условия switch-case и if-else равносильны по внешнему виду. Однако, вариант if(!state) или if(state) позволяет компилятору использовать инструкции сравнения с нулем, что работает быстрее, чем сравнение с конкретным числом в switch(state).

Re: Как поместить цикл в case оператора switch

Пт окт 07, 2022 09:24:34

Алгоритм ТС на КА легко обозрим умственно. По вашиму алгоритму слишком громоздко.
Я уже написал. Нажатия, отжатия, удержания, проверка на одновременные нажатия все это в модуле обработки кнопок.
Дальше идёт модуль собственно функционала устройства. Тоже конечный автомат

Добавлено after 45 minutes 40 seconds:
Модуль функционала, как то так:
Код:
Proc_Device
{
   switch (_proc_device)
   {
      case 0:
         bla_bla; // инициализация
         all_outputs_off ();
         _proc_device = 1;
         break;

      case 1:
         if (Get_Events (INPUTS_CHANGE_STATE))
         {
            switch (Get_Inputs_State ())
            {
               case INPUT_1:
                  all_outputs_off ();
//                  out_1_on ();
//                  out_1_switch ();
//                  out_1_blink ();
                  break;

               case INPUT_2:
                  all_outputs_off ();
//                  out_2_on ();
//                  out_2_switch ();
//                  out_2_blink ();
                  break;

               case INPUT_3:
                  all_outputs_off ();
//                  out_3_on ();
//                  out_3_switch ();
//                  out_3_blink ();
                  break;
         }
         break;
   }
}


Модуль обработки кнопок-входов выложу вечером. Обратите внимание, даже если будут одновременно нажатые кнопки, они будут проигнорированы. Автоматом.

Конечные автоматы чем хороши, код читабелен. Легко изменить, добавить, убрать.
Последний раз редактировалось Demiurg Пт окт 07, 2022 11:08:17, всего редактировалось 1 раз.

Re: Как поместить цикл в case оператора switch

Пт окт 07, 2022 09:54:19

Demiurg Спасибо за понимание, жду продолжения

Добавлено after 1 minute 24 seconds:
Demiurg,MLX90640

Позвольте начинающему написать пару строк.
Я начал заниматься ,вернее проявлять интерес, программированием после выхода на пенсию.
Во время учебы в университете у нас был коротенький курс "Автокод Инженер" для "Минск-22",
который читал тот самый Шушкевич (не к ночи упомянут будет).
В дальнейшей своей работе и жизни я (кроме геофизики и прикладной к геофизике электронике)
не встречался с программированием ,тем более с микроконтроллерами.
Я вначале взял платный видеокурс в интернете по основам ассемблера и Си для МК AVR.Но это ,конечно, чистая
теория для понятия основных определений программирования. Для изучения различных приемов и тонкостей
пробовал изучать Си для МК по книгам, но был разочарован способность авторов подавать материал
просто и доходчиво (это мое личное мнение).В интернете нашел видеокурс (тоже после нескольких
попыток поиска подходящих для меня) .Не знаю ,можно упоминать автора здесь или нет, но я прослушал
и законспектировал с последующим изучением более 60 разнообразных уроков.
К сожалению не всегда получалось что изменить ,дополнить для понимания различных приемов программирования.
С простейшими программами (типа кнопок, светодиодов ,ШИМ)больших проблем нет.
Но после упоминания конечных автоматов в этом топике я заинтересовался и попросил автора видеокурса
сделать урок по этой теме. Он сделал два урока и мне понравилась эта идея. Сначала строится простая диаграмма,
потом программа. Хотя и метод как вы пишите if-else почти тоже самое. К сожалению, у меня недостаточно опыта,
чтобы применять метод КА к другим случаем кроме кнопок и светодиодов. Но иногда и это становится проблемой как в этом случае
с тремя кнопками. Пытался сделать АЦП на КА и потерпел фиаско.
Что касается приоритета if-else или switch-case мое мнение, как начинающего и дилетанта в этой области, это два пути решения
одной задачи. Кому что подходит, нравиться и удобно -это и выбирается.
Спасибо за терпение.

Re: Как поместить цикл в case оператора switch

Пт окт 07, 2022 10:38:07

Demiurg, сложная схема? Ха, это вы ещё не видели по-настоящему сложных схем.
Ну это вы написали как раз то, о чем я ранее говорил - вставили в эту функцию как чтение входов, так и выполнение действий с выходами, спрятав в вызова функций остальной функционал текст. Ещё и инициализацию зачем-то запихнули туда. Очень вредный пример, если честно. Инициализация... Чего инициализация? Входов и выходов? Но причём тут это то, оное вообще относится к другому блоку.

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

Re: Как поместить цикл в case оператора switch

Пт окт 07, 2022 11:17:30

MLX90640
1 - Под "сложной" я подразумевал излишне сложно. В куче кони, люди... Разделяй и властвуй. Кнопки, входы отдельно. Функционал отдельно. И почему я вам, с вашим опытом это объясняю?...
2 - Конечный автомат чем хорош. Нулевое состояние можно отвести под инициализацию. Входы, выходы, переменные, периферия и т.д. Многие компиляторы обнуляют переменные, если не указано иное. Бесплатно нулевое состояние под инициализацию, если требуется.
Свой пример поправил. На выбор. ТС-у нужно что то ещё. Пусть озвучивает. Можете свое предложение внести...

Re: Как поместить цикл в case оператора switch

Пт окт 07, 2022 12:24:44

Я бы ещё мог согласиться с остальным. Но вот зачем всюдаже заталкивать инициализацию входов и выходов? Это, извините, и есть жесть, это и есть "люди, кони" в одной куче. А если там не физические кнопки, а например флаги или состояния от контроллера сенсорной клавиатуры или иного устройства? Как ни крути, а инициализации входов и выходов ну никак не подходит к данному блоку.

Вообще, понятие конечного автомата более сложное, чем одни лишь switch-case. Это здесь мы упрощаем до уровня switch-case или if-else.
У КА есть граф его внутренних переходов и состояний, когда в результате его работы меняется не только его выход, но и внутренние его состояния. В частности, переменные p и tr работают на эти состояния. Таким образом, конечному автомвту, чтобы быть конечным автоматом, нужна внутренняя память, а не просто набор ветвлений по Switch-case. Но вот выделять внутреннюю память для состояний "инициализация" и "нормальная работа" - это неправильно, потому что имеет мало смысла и к распознаванию нажатий кнопок отношения не имеет.
Последний раз редактировалось MLX90640 Пт окт 07, 2022 13:44:01, всего редактировалось 1 раз.

Re: Как поместить цикл в case оператора switch

Пт окт 07, 2022 13:43:12

Я показал так называемую switch-case технологию.

Инициализация чего либо в нулевом состоянии конечного автомата допустимо и приветствуется. Даже если это не укладывается в вашей системе понятий.

Это мнения. Ваше. Мое. Не более того. У вас свой опыт, понятия что правильно, что неправильно. У меня свой опыт, свои понятия. Если мне что то непонятно, сомнения, я обращаюсь к более опытным коллегам на профильных форумах либо лично.

В данном случае вы показываете свое предпочтение и свое понимание.
Ответить