Как поместить цикл в case оператора switch
Как поместить цикл в case оператора switch
Необходимо при переключении кнопкой с помощью оператора switch включать мигание светодиода с различной частотой на каком либо выводе.при
использовании конструкции (для примера):
PORTB |= (1<<PB0);
_delay_ms(100);
PORTB&= ~(1<<PB0);
_delay_ms(100);
необходимо зациклить ее в теле case,но невозможно переключить switch в следующий case.Просто невозможно выйти из бесконечного цикла.
Я использовал while(),может быть есть какой либо способ решить эту проблему, я начал изучать программирование МК недавно и до сложных
кодов еще мне очень далеко. Надеюсь на помощь.
Спасибо.
использовании конструкции (для примера):
PORTB |= (1<<PB0);
_delay_ms(100);
PORTB&= ~(1<<PB0);
_delay_ms(100);
необходимо зациклить ее в теле case,но невозможно переключить switch в следующий case.Просто невозможно выйти из бесконечного цикла.
Я использовал while(),может быть есть какой либо способ решить эту проблему, я начал изучать программирование МК недавно и до сложных
кодов еще мне очень далеко. Надеюсь на помощь.
Спасибо.
- Реклама
- Starichok51
- Модератор
- Сообщения: 19055
- Зарегистрирован: Сб авг 14, 2010 15:05:51
- Откуда: г. Озерск, Челябинская обл.
Re: Как поместить цикл в case оператора switch
для реальной помощи положено приводить исходный код и схему.
а также нужно подробно и четко описать, что тебе надо и как это должно работать.
при предоставленном минимуме информации могут помочь только гадалки и экстрасенсы.
а также нужно подробно и четко описать, что тебе надо и как это должно работать.
при предоставленном минимуме информации могут помочь только гадалки и экстрасенсы.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Re: Как поместить цикл в case оператора switch
Ну какой код? Студент не понятно чем занимался, как вдург препод огорошил!
Re: Как поместить цикл в case оператора switch
#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.
Спасибо за Ваши комментарии, надеюсь на Вашу помощь.
#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
У вас переменная i принимает значения 0,1,2, а switch(i) 1,2,3.
- Реклама
Re: Как поместить цикл в case оператора switch
У вас внутри switch одинаковые ветки отличающиеся только задержкой, проще ее хранить в массиве:
и потом просто подставлять:
На AVR умножение наверно лучше делать один раз, после инкремента i, или сразу перемноженные значения хранить...
Код: Выделить всё
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);
Re: Как поместить цикл в case оператора switch
К сожалению не работает, если только я правильно понял идею с массивами:
#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 * time;
PORTD|=(1<<PD0);
pause (delay);
PORTD&=~(1<<PD0);
pause (delay);
}
else
i=0;
}
}
_delay_ms(300);
}
#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 * time;
PORTD|=(1<<PD0);
pause (delay);
PORTD&=~(1<<PD0);
pause (delay);
}
else
i=0;
}
}
_delay_ms(300);
}
Re: Как поместить цикл в case оператора switch
Конечно не работает, потому что это какой-то другой код
Тут, например, будет выход за предела массива:
Да, еще на AVR же вроде int 16-ти битный, значит нужно при умножении приведение к uint32_t использовать... Наверно эффективнее сразу перемноженное хранить:
Опять же, это примерный код, я его не проверял потому что с AVR уже давно дела не имею, мелкие правки нужно самому делать. Например, если массив 32-х битный, то time должен быть задефайнен как 32-х битное значение.
Код: Выделить всё
if(++i >= 3)
{
uint32_t delay = arr[i] * time;
....Код: Выделить всё
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]);
}
Последний раз редактировалось Reflector Вт окт 12, 2021 12:19:07, всего редактировалось 2 раза.
- DX168B
- Друг Кота
- Сообщения: 4468
- Зарегистрирован: Вс янв 24, 2010 19:19:52
- Откуда: Главный Улей России (Moscow)
- Контактная информация:
Re: Как поместить цикл в case оператора switch
Как уже сказали, switch у Вас реагирует только на значения i=1, 2 или 3, а переменная i принимает значения 0..2
Соответсвенно, case 3 у Вас никогда не сработает.
Ну и пока кнопка нажата, у Вас переменная i будет постоянно меняться, пока Вы ее не отпустите.
Как я понял, вы хотите мигать светодиодом с переключаемой с помощью кнопки задержкой.
Вам нужно после фиксации факта нажатия кнопки ожидать ее отпускание и уже после отпускания инкрементировать переменную i.
Вам еще нужно изучить такой эффект как "дребезг контактов" и методы борьбы с ним. Ибо в Протеусе дребезга нет,
а в реальной кнопке он есть.
И чтение лучше явно проверять:
Мало того, пока протекают задержки, весь цикл заморожен и даже кнопка не опрашивается.
Для решения таких проблем используют неблокирующие задержки на "системном таймере",
программу разбивают на модули и реализуют их в виде конечных автоматов, которые меняют свои состояния
в зависимости от различных событий и делают различные действия в зависимости от своего состояния.
Соответсвенно, case 3 у Вас никогда не сработает.
Ну и пока кнопка нажата, у Вас переменная i будет постоянно меняться, пока Вы ее не отпустите.
Как я понял, вы хотите мигать светодиодом с переключаемой с помощью кнопки задержкой.
Вам нужно после фиксации факта нажатия кнопки ожидать ее отпускание и уже после отпускания инкрементировать переменную i.
Вам еще нужно изучить такой эффект как "дребезг контактов" и методы борьбы с ним. Ибо в Протеусе дребезга нет,
а в реальной кнопке он есть.
И чтение лучше явно проверять:
Код: Выделить всё
if( (PINA & (1<<PA0)) == 0) // Бит, соответсвующий выводу PA0 равен нулю (прижали подтянутую ногу к минусу)
{
// Делаем то, что надо
}
Для решения таких проблем используют неблокирующие задержки на "системном таймере",
программу разбивают на модули и реализуют их в виде конечных автоматов, которые меняют свои состояния
в зависимости от различных событий и делают различные действия в зависимости от своего состояния.
Последний раз редактировалось DX168B Вт окт 12, 2021 12:29:28, всего редактировалось 1 раз.
I am DX168B and this is my favourite forum on internet!
Re: Как поместить цикл в case оператора switch
[uquote="DX168B",url="/forum/viewtopic.php?p=4104589#p4104589"]Ну и пока кнопка нажата, у Вас переменная i будет постоянно меняться, пока Вы ее не отпустите.[/uquote]
Там в конце задержка 300ms, если кнопку быстро нажимать, то для проверки сойдет, а дальше если еще и с антидребезгом делать, то это уже продвинутый уровень
Там в конце задержка 300ms, если кнопку быстро нажимать, то для проверки сойдет, а дальше если еще и с антидребезгом делать, то это уже продвинутый уровень
- DX168B
- Друг Кота
- Сообщения: 4468
- Зарегистрирован: Вс янв 24, 2010 19:19:52
- Откуда: Главный Улей России (Moscow)
- Контактная информация:
Re: Как поместить цикл в case оператора switch
К ней еще добавить задержки в switch и там уже гораздо больше, чем 300мс.
I am DX168B and this is my favourite forum on internet!
Re: Как поместить цикл в case оператора switch
Вариантов решения много.
Задержки лучше бы не использовать, или использовать по минимуму.
Для организации задержек идеально подходят таймеры чипа плюс флаги.
1) заводим таймер на срабатывание прерывания с частотой, например в 10 мс.
2) в обработчике прерывания просто инкрементируем переменную-счетчик (не забыть сделать ее при определении как volatile)
3) в начале main обнуляем переменную-счетчик и задаем переменную - максимум (например, через настройку индекса массива на его начало, как вам уже предлагали)
4) главный цикл:
4.1) читаем состояние кнопки (кнопок)
4.2) каскад проверок:
Если переменная - счетчик достигла значения переменной - максимум, обнуляем счетчик и инвертируем выход светодиода. Например, если время свечения / паузы светодиода должно быть полсекунды, переменная - максимум должна быть равна 50.
Если была нажата кнопка, переопределям переменную - максимум (например, через приращение индекса массива, как вам уже предлагали)
Вроде всё. В протеусе должно взлететь, я думаю. В реальной схеме нужно будет добавить обработку дребезга контактов кнопки.
Задержки лучше бы не использовать, или использовать по минимуму.
Для организации задержек идеально подходят таймеры чипа плюс флаги.
1) заводим таймер на срабатывание прерывания с частотой, например в 10 мс.
2) в обработчике прерывания просто инкрементируем переменную-счетчик (не забыть сделать ее при определении как volatile)
3) в начале main обнуляем переменную-счетчик и задаем переменную - максимум (например, через настройку индекса массива на его начало, как вам уже предлагали)
4) главный цикл:
4.1) читаем состояние кнопки (кнопок)
4.2) каскад проверок:
Если переменная - счетчик достигла значения переменной - максимум, обнуляем счетчик и инвертируем выход светодиода. Например, если время свечения / паузы светодиода должно быть полсекунды, переменная - максимум должна быть равна 50.
Если была нажата кнопка, переопределям переменную - максимум (например, через приращение индекса массива, как вам уже предлагали)
Вроде всё. В протеусе должно взлететь, я думаю. В реальной схеме нужно будет добавить обработку дребезга контактов кнопки.
Re: Как поместить цикл в case оператора switch
Cпасибо.Сейчас не могу проверить,
доберусь до компьютера -отпишусь.
доберусь до компьютера -отпишусь.
Re: Как поместить цикл в case оператора switch
Всем спасибо .Все получилось. Для прерываний использовал Т0 в режиме переполнения и срабатывания 8мс.Правда обошелся без массивов.
- DX168B
- Друг Кота
- Сообщения: 4468
- Зарегистрирован: Вс янв 24, 2010 19:19:52
- Откуда: Главный Улей России (Moscow)
- Контактная информация:
Re: Как поместить цикл в case оператора switch
Рады, что смогли чем-то помочь.
Вообще, можете взять за правило.
Пишите один раз код, реализующий задержки на таймере и каждый раз,
когда создаете проект, копируете туда этот код и налаживаете его работу.
Задержки на таймере позволяют организовать асинхронные задержки, а таких задержек можно организовать
великое множество. Саму же программу реализовать можно в виде конечного автомата, который будет состоять
из множества состояний для различных действий. Главный цикл постоянно проходится по автомату и выполняет действия,
согласно текущему состоянию и переключается в другие состояния по каким-то событиям, значениям в переменных или флажках.
Благодаря такому подходу легко можно реализовать нечто, вроде "мигать светодиодом в течении 10 секунд. Если за это время нажали кнопку, зажечь светодиод, а иначе потушить."
Есть по этим вещам множество статей в блоге easyelectronics.
Вот пример того, как выглядит один из конечных автоматов в одном из моих проектов:
Эта функция постоянно вызывается из главного цикла.
Она реализует опрос клавиатуры и антидребезг.
Состояния автомата определяются перечислительным типом enum KbProcState (это такой удобный способ дать числам названия)
и хранятся переменной state
Тут же используются и задержки с таймером.
Все разбито на определенный порядок действий.
Состояния физических кнопок читаются постоянно, функцией CheckPressedKey()
А дальше автомат:
1 состояние (оно же и начальное):
Проверяется, нажато ли вообще что-то.
Если нет, то остаемся в этом же состоянии.
Если нажалось что-то, значит запускаем таймер антидребезга и меняем состояние автомата (переход во второе состояние)
2:
Проверка, не поменялся ли номер нажатой кнопки при очередном опросе.
Если в течении задержки антидребезга номер не менялся, значит кнопка в стабильном состоянии,
происходит переключение в следующее состояние.
Если же номер изменялся (контакт еще нестабилен), то автомат переключается в начальное состояние и все начинается заново.
Последующие состояния уже детектируют короткое или длинное нажатие и выставляют событие (коротко/длинно нажата такая-то кнопка).
Это событие уже используется другими конечными автоматами, реализованными в программе.
Вообще, можете взять за правило.
Пишите один раз код, реализующий задержки на таймере и каждый раз,
когда создаете проект, копируете туда этот код и налаживаете его работу.
Задержки на таймере позволяют организовать асинхронные задержки, а таких задержек можно организовать
великое множество. Саму же программу реализовать можно в виде конечного автомата, который будет состоять
из множества состояний для различных действий. Главный цикл постоянно проходится по автомату и выполняет действия,
согласно текущему состоянию и переключается в другие состояния по каким-то событиям, значениям в переменных или флажках.
Благодаря такому подходу легко можно реализовать нечто, вроде "мигать светодиодом в течении 10 секунд. Если за это время нажали кнопку, зажечь светодиод, а иначе потушить."
Есть по этим вещам множество статей в блоге easyelectronics.
Вот пример того, как выглядит один из конечных автоматов в одном из моих проектов:
Спойлер
Тут язык C++, но это не суть.Эта функция постоянно вызывается из главного цикла.
Она реализует опрос клавиатуры и антидребезг.
Состояния автомата определяются перечислительным типом enum KbProcState (это такой удобный способ дать числам названия)
и хранятся переменной state
Тут же используются и задержки с таймером.
Все разбито на определенный порядок действий.
Состояния физических кнопок читаются постоянно, функцией CheckPressedKey()
А дальше автомат:
1 состояние (оно же и начальное):
Проверяется, нажато ли вообще что-то.
Если нет, то остаемся в этом же состоянии.
Если нажалось что-то, значит запускаем таймер антидребезга и меняем состояние автомата (переход во второе состояние)
2:
Проверка, не поменялся ли номер нажатой кнопки при очередном опросе.
Если в течении задержки антидребезга номер не менялся, значит кнопка в стабильном состоянии,
происходит переключение в следующее состояние.
Если же номер изменялся (контакт еще нестабилен), то автомат переключается в начальное состояние и все начинается заново.
Последующие состояния уже детектируют короткое или длинное нажатие и выставляют событие (коротко/длинно нажата такая-то кнопка).
Это событие уже используется другими конечными автоматами, реализованными в программе.
Код: Выделить всё
///////////////////////////////////////////////////////////////////////
// Keyboard process
void CKeyboard::Process()
{
// Detect pressed key
KbKeys pressedKey = CheckPressedKey();
// Keyboard process state machine
switch (state)
{
// Initial state
case KbProcState::INITIAL:
if(pressedKey != KbKeys::KEY_NONE)
{
prevKey = pressedKey;
kbTim.SetTimer(KEYBOARD_DEBOUNCE_DELAY); // begin debounce timer
state = KbProcState::KEY_PRESS_DEBOUNCE;
}
break;
// Press debounsce
case KbProcState::KEY_PRESS_DEBOUNCE:
if(pressedKey != prevKey)
{
state = KbProcState::INITIAL;
}
else
{
if(kbTim.CheckTimer() == true) // check debounce timer
{
kbTim.SetTimer(KEYBOARD_LONGPRESS_DELAY);
#ifndef KEYBOARD_EVENT_KEY_AND_CLICK_SYNC
currentKey = pressedKey;
#endif
currentClick = ClickType::NO_CLICK;
state = KbProcState::KEY_PRESS;
}
}
break;
// Key pressed state
case KbProcState::KEY_PRESS:
if(pressedKey == KbKeys::KEY_NONE)
{
if(kbTim.CheckTimer() == false)
{
currentClick = ClickType::SHORT_CLICK;
#ifdef KEYBOARD_EVENT_KEY_AND_CLICK_SYNC
currentKey = prevKey;
#endif
}
state = KbProcState::INITIAL;
}
if(kbTim.CheckTimer() == true)
{
currentClick = ClickType::LONG_CLICK;
#ifdef KEYBOARD_EVENT_KEY_AND_CLICK_SYNC
currentKey = pressedKey;
#endif
state = KbProcState::KEY_UNPRESS;
}
break;
// Wait for unpress key
case KbProcState::KEY_UNPRESS:
if(pressedKey == KbKeys::KEY_NONE)
{
#ifndef KEYBOARD_EVENT_KEY_AND_CLICK_SYNC
currentKey = pressedKey;
#endif
state = KbProcState::INITIAL;
}
break;
// Other states
default:
currentKey = KbKeys::KEY_NONE;
currentClick = ClickType::NO_CLICK;
state = KbProcState::INITIAL;
break;
}
}
I am DX168B and this is my favourite forum on internet!
Re: Как поместить цикл в case оператора switch
Подскажите, если не трудно литературу по конечным автоматам .Может быть есть ссылки в интернете по этой теме.
Спасибо.
Спасибо.
Re: Как поместить цикл в case оператора switch
Цифровые устройства Пухальского и Новосельцевой.
Re: Как поместить цикл в case оператора switch
DX168B , metan!
Попробовал эту методику, с прерываниями и конечным автоматом. Все получилось.
Спасибо!
Попробовал эту методику, с прерываниями и конечным автоматом. Все получилось.
Спасибо!
Re: Как поместить цикл в case оператора switch
Возникла проблема. Хочу написать код для переключения кнопками(например три кнопки)
трех выходов. С помощью методики "конечных автоматов" попробовал сделать программу.
Но получился трехканальный переключатель без фиксации ,т.е. включение и выключение
любого канала происходит независимо от состояния других каналов.
Мне нужно превратить этот переключатель в переключатель с фиксацией. Т.е.
при нажатии на любую кнопку остальные выходы обнуляются(неважно что на них было -
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
трех выходов. С помощью методики "конечных автоматов" попробовал сделать программу.
Но получился трехканальный переключатель без фиксации ,т.е. включение и выключение
любого канала происходит независимо от состояния других каналов.
Мне нужно превратить этот переключатель в переключатель с фиксацией. Т.е.
при нажатии на любую кнопку остальные выходы обнуляются(неважно что на них было -
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 КБ) 95 скачиваний


