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

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

Вт окт 12, 2021 01:09:16

Необходимо при переключении кнопкой с помощью оператора switch включать мигание светодиода с различной частотой на каком либо выводе.при
использовании конструкции (для примера):
PORTB |= (1<<PB0);
_delay_ms(100);
PORTB&= ~(1<<PB0);
_delay_ms(100);
необходимо зациклить ее в теле case,но невозможно переключить switch в следующий case.Просто невозможно выйти из бесконечного цикла.
Я использовал while(),может быть есть какой либо способ решить эту проблему, я начал изучать программирование МК недавно и до сложных
кодов еще мне очень далеко. Надеюсь на помощь.
Спасибо.

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

Вт окт 12, 2021 05:38:47

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

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

Вт окт 12, 2021 10:22:30

Ну какой код? Студент не понятно чем занимался, как вдург препод огорошил!

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

Вт окт 12, 2021 11:01:14

#include <avr/io.h>
#define F_CPU 8000000UL
#include <util/delay.h>
#define time 300
//attiny2313,8Mhz,ДЕЛИТЕЛЬ НА 8 ОТКЛЮЧЕН

void pause (unsigned long a)//Использую вместо стандартной задержки
{ unsigned long counter ;
for(counter=a;counter>0;counter--)
asm("nop");
}
int main(void)
{ uint8_t i=0;
DDRD = 0xFF;
PORTD=0x00;
DDRA = 0x00;
PORTA|=(1<<PA0);
while(1)
{
if (~PINA&(1<<PA0))
{
if (i<3)
{
i++;
}
else
{
i=0;
}
}
switch(i)
{
case 1:
PORTD|=(1<<PD0);
pause (10000L*time);
PORTD&=~(1<<PD0);
pause (10000L*time);
break;

case 2:
PORTD|=(1<<PD0);
pause (5000L*time);
PORTD&=~(1<<PD0);
pause (5000L*time);
break;

case 3:
PORTD|=(1<<PD0);
pause (1000L*time);
PORTD&=~(1<<PD0);
pause (1000L*time);
break;


}
_delay_ms(300);
}
}


При симуляции в Proteus зацикливается в первом case , т.е. не переключается в следующий .На макетной плате после первого нажатия кнопки светодиод горит постоянно примерно 5 сек(pause (10000L*time)
и потом гаснет ,дальнейшие манипуляции кнопкой просто повторяют эту картину(нет мигания светодиода).
Что касается студента - я вышел на пенсию и для поддержания здоровья занялся программированием МК, база у меня есть(физфак БГУ) и пока голова работает хочу нагружать ее насколько это возможно.
В марте мне будет 71.
Спасибо за Ваши комментарии, надеюсь на Вашу помощь.

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

Вт окт 12, 2021 11:18:31

У вас переменная i принимает значения 0,1,2, а switch(i) 1,2,3.

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

Вт окт 12, 2021 11:20:32

У вас внутри switch одинаковые ветки отличающиеся только задержкой, проще ее хранить в массиве:
Код:
uint16_t arr[] = { 10000, 5000, 1000 };

и потом просто подставлять:
Код:
if (~PINA & (1<<PA0))
{
    if(++i >= 3) i = 0;
}

uint32_t delay = arr[i] * time;
PORTD|=(1<<PD0);     
pause (delay);
PORTD&=~(1<<PD0);   
pause (delay);

На AVR умножение наверно лучше делать один раз, после инкремента i, или сразу перемноженные значения хранить...

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

Вт окт 12, 2021 11:48:04

К сожалению не работает, если только я правильно понял идею с массивами:


#include <avr/io.h>
#define F_CPU 8000000UL
#include <util/delay.h>
#define time 300
//attiny2313,8Mhz,ДЕЛИТЕЛЬ НА 8 ОТКЛЮЧЕН

void pause (unsigned long a)
{ unsigned long counter ;
for(counter=a;counter>0;counter--)
asm("nop");
}
int main(void)
{
uint16_t arr[] = { 10000, 5000, 1000 };


uint8_t i=0;
DDRD = 0xFF;
PORTD=0x00;
DDRA = 0x00;
PORTA|=(1<<PA0);
while(1)
{

if (~PINA & (1<<PA0))
{
if(++i >= 3)
{

uint32_t delay = arr[i] * time;
PORTD|=(1<<PD0);
pause (delay);
PORTD&=~(1<<PD0);
pause (delay);
}
else
i=0;
}
}
_delay_ms(300);

}

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

Вт окт 12, 2021 11:56:17

Конечно не работает, потому что это какой-то другой код :) Тут, например, будет выход за предела массива:
Код:
if(++i >= 3)
{
    uint32_t delay = arr[i] * time;
....

Да, еще на AVR же вроде int 16-ти битный, значит нужно при умножении приведение к uint32_t использовать... Наверно эффективнее сразу перемноженное хранить:
Код:
uint32_t arr[] = { 10000 * time, 5000 * time, 1000 * time };

while(1)
{
    if (~PINA & (1<<PA0))
    {
       if(++i >= 3) i = 0;
    }
 
    PORTD|=(1<<PD0);     
    pause (arr[i]);
    PORTD&=~(1<<PD0);   
    pause (arr[i]);

}

Опять же, это примерный код, я его не проверял потому что с AVR уже давно дела не имею, мелкие правки нужно самому делать. Например, если массив 32-х битный, то time должен быть задефайнен как 32-х битное значение.
Последний раз редактировалось Reflector Вт окт 12, 2021 12:19:07, всего редактировалось 2 раз(а).

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

Вт окт 12, 2021 12:19:02

Как уже сказали, switch у Вас реагирует только на значения i=1, 2 или 3, а переменная i принимает значения 0..2
Соответсвенно, case 3 у Вас никогда не сработает.
Ну и пока кнопка нажата, у Вас переменная i будет постоянно меняться, пока Вы ее не отпустите.
Как я понял, вы хотите мигать светодиодом с переключаемой с помощью кнопки задержкой.
Вам нужно после фиксации факта нажатия кнопки ожидать ее отпускание и уже после отпускания инкрементировать переменную i.
Вам еще нужно изучить такой эффект как "дребезг контактов" и методы борьбы с ним. Ибо в Протеусе дребезга нет,
а в реальной кнопке он есть.

И чтение лучше явно проверять:
Код:
if( (PINA & (1<<PA0)) == 0// Бит, соответсвующий выводу PA0 равен нулю (прижали подтянутую ногу к минусу)
{
   
// Делаем то, что надо
}
 


Мало того, пока протекают задержки, весь цикл заморожен и даже кнопка не опрашивается.
Для решения таких проблем используют неблокирующие задержки на "системном таймере",
программу разбивают на модули и реализуют их в виде конечных автоматов, которые меняют свои состояния
в зависимости от различных событий и делают различные действия в зависимости от своего состояния.
Последний раз редактировалось DX168B Вт окт 12, 2021 12:29:28, всего редактировалось 1 раз.

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

Вт окт 12, 2021 12:26:23

Ну и пока кнопка нажата, у Вас переменная i будет постоянно меняться, пока Вы ее не отпустите.

Там в конце задержка 300ms, если кнопку быстро нажимать, то для проверки сойдет, а дальше если еще и с антидребезгом делать, то это уже продвинутый уровень :)

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

Вт окт 12, 2021 12:30:58

К ней еще добавить задержки в switch и там уже гораздо больше, чем 300мс.

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

Вт окт 12, 2021 13:03:55

Вариантов решения много.
Задержки лучше бы не использовать, или использовать по минимуму.
Для организации задержек идеально подходят таймеры чипа плюс флаги.
1) заводим таймер на срабатывание прерывания с частотой, например в 10 мс.
2) в обработчике прерывания просто инкрементируем переменную-счетчик (не забыть сделать ее при определении как volatile)
3) в начале main обнуляем переменную-счетчик и задаем переменную - максимум (например, через настройку индекса массива на его начало, как вам уже предлагали)
4) главный цикл:
4.1) читаем состояние кнопки (кнопок)
4.2) каскад проверок:
Если переменная - счетчик достигла значения переменной - максимум, обнуляем счетчик и инвертируем выход светодиода. Например, если время свечения / паузы светодиода должно быть полсекунды, переменная - максимум должна быть равна 50.
Если была нажата кнопка, переопределям переменную - максимум (например, через приращение индекса массива, как вам уже предлагали)

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

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

Вт окт 12, 2021 13:53:17

Cпасибо.Сейчас не могу проверить,
доберусь до компьютера -отпишусь.

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

Ср окт 13, 2021 22:56:42

Всем спасибо .Все получилось. Для прерываний использовал Т0 в режиме переполнения и срабатывания 8мс.Правда обошелся без массивов.

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

Пт окт 15, 2021 16:19:30

Рады, что смогли чем-то помочь. :beer:

Вообще, можете взять за правило.
Пишите один раз код, реализующий задержки на таймере и каждый раз,
когда создаете проект, копируете туда этот код и налаживаете его работу.
Задержки на таймере позволяют организовать асинхронные задержки, а таких задержек можно организовать
великое множество. Саму же программу реализовать можно в виде конечного автомата, который будет состоять
из множества состояний для различных действий. Главный цикл постоянно проходится по автомату и выполняет действия,
согласно текущему состоянию и переключается в другие состояния по каким-то событиям, значениям в переменных или флажках.
Благодаря такому подходу легко можно реализовать нечто, вроде "мигать светодиодом в течении 10 секунд. Если за это время нажали кнопку, зажечь светодиод, а иначе потушить."
Есть по этим вещам множество статей в блоге easyelectronics.

Вот пример того, как выглядит один из конечных автоматов в одном из моих проектов:

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

Сб окт 16, 2021 23:29:02

Подскажите, если не трудно литературу по конечным автоматам .Может быть есть ссылки в интернете по этой теме.
Спасибо.

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

Сб окт 16, 2021 23:42:53

Цифровые устройства Пухальского и Новосельцевой.

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

Пн окт 18, 2021 02:24:41

Цикл статей Татарчевского. Здесь тоже статья, в ней ссылка на архив.
Моя реализация программных таймеров.

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

Чт фев 03, 2022 14:30:05

DX168B , metan!
Попробовал эту методику, с прерываниями и конечным автоматом. Все получилось.
Спасибо!

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

Чт окт 06, 2022 11:03:05

Возникла проблема. Хочу написать код для переключения кнопками(например три кнопки)
трех выходов. С помощью методики "конечных автоматов" попробовал сделать программу.
Но получился трехканальный переключатель без фиксации ,т.е. включение и выключение
любого канала происходит независимо от состояния других каналов.
Мне нужно превратить этот переключатель в переключатель с фиксацией. Т.е.
при нажатии на любую кнопку остальные выходы обнуляются(неважно что на них было -
0v,+5v или blink для светодиода) и на выходе канала с этой включенной кнопкой появляется
нужный сигнал +5v или blink.Причем выключить этот канал можно включением другого("переключение
с фиксацией") или просто повторным нажатием на кнопку включенного канала.
Проблема в том, что я не смог добиться отключения активного канала включением любого другого.
Перепробовал различные приемы выключения других выходов при включении нужного мне канала.
Помещал код типа PORTB&=~(1<<PB3); в switch для blink_click,пробовал в button_ KA после строки
поднятия флага fclk: if(fclk =1){PORTB&=~(1<<PB3)}.Номера портов здесь условные.
Я понимаю, что мне не хватает опыта для использования таких тонкостей.
Поэтому и прошу помощи.
Спасибо.
В архиве код и протеус
https://drive.google.com/file/d/1kOjc23 ... sp=sharing
Вложения
temp2.rar
(103.71 KiB) Скачиваний: 49
Ответить