Заголовок сообщения: Програмирование двухрежимной работы микроконтроллера.
Добавлено: Вт июл 12, 2016 17:55:26
Встал на лапы
Зарегистрирован: Вс май 08, 2016 22:45:48 Сообщений: 131
Рейтинг сообщения:0
Здравствуйте. Вопрос: Как реализовать двухрежимную работу микроконтроллера, с возможностью мгновенного переключения. Пояснение: Нужно, чтобы микроконтроллер мог работать в двух режимах. Переключение между режимами осуществляется кнопкой без фиксации (нажал - есть сигнал, отпусти - пропал). Проблема:Задача режима 1 - с задержкой в 1,5 часа, подавать на ножку в течении 3 сек ток. Но проблема, если первый режим включился - 1,5 часа - нельзя будет его переключить. Мне же нужно, чтобы я в любой момент, мог перейти с режима 1 на режим 2. Задача режима 2 - с задержкой в (случайное число в диапазоне между 1,0 и 2,0 часами), подавать на ножку в течении 3 сек ток. Но проблема, такая же, как и в первом случае.
intmain(void) { DDRA |=(1<<3)|(1<<4); /* Устанавливаю Порты PA3 и PA4 - на вывод */ DDRB &=~(1<<4); /* Устанавливаю Порт PB4 на ввод */ PORTB |=(1<<4); /* Включаю внутренний резистор Порта PB4 */ int ONOFF; /* Регулятор двух режимов */ int RTIME = 3600000 + rand() % 7200000; /* Устанавливаю переменную - случайным числом в диапозоне между часом и двумя часами */
while (void) { if (PINB & (1<<4)) /* Если нажали кнопку - включается второй режим, если не нажата, остается изначальный.*/ { int ONOFF = 1; } else { int ONOFF = 0; }
if (PINB & (1<<4) & int ONOFF==1) /* Если нажали кнопку - ,а второй режим уже включен - включается первый режим, если не нажата, остается изначальный. */ { int ONOFF = 0; }
if (ONOFF==1) { PORTA |=(1<<4); /* Подаю ток на ножку PA4 */ _delay_ms(3000); /* время подачи тока на ножку PA4 - 3 сек */ PORTA &=~(1<<4); /* выключаю ножку PA4 */ _delay_ms(5400000); /* полтора часа - режим ожидания */ } else { PORTA |=(1<<4); /* Подаю ток на ножку PA4 */ _delay_ms(3000); /* время подачи тока на ножку PA4 - 3 сек */ PORTA &=~(1<<4); /* выключаю ножку PA4 */ _delay_ms(RTIME); /* Случайное время между часом и двумя часами - режим ожидания */ }
}
}
Который, должен был бы удовлетворять моим нуждам. Но я уверен, что я допустил массу ошибок. Прошу: Помочь разобраться с кодом, возможно есть и более изящное решение.
P.S. при компиляции этого кода выдает такие ошибки. СпойлерSeverity Code Description Project File Line Warning #warning "This file has been moved to <util/delay.h>. Warning #warning "F_CPU not defined for <util/delay.h>" Error recipe for target 'main.o' failed Error expected expression before 'void' Warning unused variable 'ONOFF' Warning unused variable 'ONOFF' Error expected expression before 'int' Warning unused variable 'ONOFF'
Warning #warning "F_CPU not defined for <util/delay.h>"
Макрос F_CPU (тактовая частота контроллера) не указан. Можно добавить #define F_CPU 1000000UL в самом начале основной программы, даже перед #include'ми, но лучше указать в опциях компилятора -DF_CPU=1000000UL. Без этого функция задержек будет врать на порядки. Очень порадовало, что код оформлен почти по-человечески (местами отступы сползли ну и еще кое-что по мелочи). Если я правильно понял, после старта программа должна подождать определенное время, после этого на 3 секунды подать напряжение на ножку. Если кнопка нажата, время выбирается случайным образом, если отпущена - фиксированное, 1,5 часа. От какого момента должен идти отсчет, от последнего включения нагрузки или от нажатия/отпускания кнопки? Если программа досчитала 1.2 часа, но тут нажали кнопку и рандом выдал 1 час что должно произойти? Какая нужна точность? Надо ли переключаться пока нагрузка включена (те самые 3 секунды)? Наиболее правильный способ - использовать таймеры. Они считают независимо от основной программы и не мешают проверять кнопки. Менее точно можно делать в основном цикле фиксированные задержки. Спойлер
Код:
unsignedint sec=0; //счетчик секунд unsignedint max_time=5400; //1.5 часа char mode=0; //0 - фиксированная задержка, 1 - рандомная while(1){ //блок проверки прошло уже заданное время или нет if( sec >= max_time ){ sec = 0; PORT_ON(); //макрос включения нагрузки _delay_ms(3000); //предположим, пока нагрузка включена, кнопка не проверяется PORT_OFF(); }
_delay_ms(1000); //предположим, счет идет квантами по 1 секунде sec++; //в случае использования таймеров переменную sec надо сделать глобальной и менять ее в прерывании. При обнулении вне прерывания надо учитывать атомарность. Без прерываний это неважно.
if( BUTTON_PRESSED() ){ //макрос проверки нажатия на кнопку, вполне можно заменить на (!(PINB & (1<<4))) if(!mode){ //условие будет выполнено если до проверки нажатия кнопки был выставлен 0-й режим, то есть должно произойти переключение режима. mode = 1; max_time = 3600 + random(3600); } }else{ if(mode){ //тоже самое, был включен режим 1, теперь надо переключить на режим 0 mode = 0; max_time = 5400; } } }
В полном объеме реализовывать конечный автомат для такой простой задачи - перебор. А в неполном решение уже есть и для него не обязательно знать что такое конечный автомат.
Вся задача ТС целиком и полностью делается на КА. Можно флагами, по сути тоже автомат, но это мешанина из флагов и условий. В данном случае никакого перебора нет. И применительно к КА нет такого понятия. Есть постановка задачи, анализ ее выполнения и целесообразность применения способов. Если проще сделать на КА, значит делаем на КА. Вот применить недодиспетчеры или RTOS - это уже перебор. Позже выложу пример.
Заголовок сообщения: Re: Програмирование двухрежимной работы микроконтроллера.
Добавлено: Чт июл 14, 2016 22:04:17
Встал на лапы
Зарегистрирован: Вс май 08, 2016 22:45:48 Сообщений: 131
Рейтинг сообщения:0
COKPOWEHEU писал(а):
Странно, у меня все компилируется (даже не ожидал - писал из головы), разве что рандом исправить пришлось. В простейшем случае random()%3600;
Изначально закомпелировал целиком - только ваш код, который вы мне предложили (спасибо). Выдало несколько ошибок. После этого, я немного изменил код, но ошибки остались те же.
DDRA |=(1<<3)|(1<<4); /* Устанавливаю Порты PA3 и PA4 - на вывод */ DDRB &=~(1<<4); /* Устанавливаю Порт PB4 на ввод */ PORTB |=(1<<4); /* Включаю внутренний резистор Порта PB4 */
while(1){ //блок проверки прошло уже заданное время или нет if( sec >= max_time ){ sec = 0; PORTA |=(1<<4); //макрос включения нагрузки _delay_ms(3000); //предположим, пока нагрузка включена, кнопка не проверяется PORTA &=~(1<<4); //макрос выключения нагрузки }
_delay_ms(1000); //предположим, счет идет квантами по 1 секунде sec++; //в случае использования таймеров переменную sec надо сделать глобальной и менять ее в прерывании. При обнулении вне прерывания надо учитывать атомарность. Без прерываний это неважно.
if( PINB & (1<<7) ){ //макрос проверки нажатия на кнопку, вполне можно заменить на (!(PINB & (1<<4))) if(!mode){ //условие будет выполнено если до проверки нажатия кнопки был выставлен 0-й режим, то есть должно произойти переключение режима. mode = 1; max_time = 3600 + random(3600); PORTA |=(1<<3); //макрос включения нагрузки на светодиод. _delay_ms(3000); //предположим, пока нагрузка включена, кнопка не проверяется PORTA &=~(1<<3); //макрос выключения нагрузки } }else{ if(mode){ //тоже самое, был включен режим 1, теперь надо переключить на режим 0 mode = 0; max_time = 5400; for (i = 0; i < 5; i++) //перемигивание светодиода. { } PORTA |=(1<<3); //макрос включения нагрузки на светодиод. _delay_ms(1000); //предположим, пока нагрузка включена, кнопка не проверяется PORTA &=~(1<<3); //макрос выключения нагрузки } } }
Ошибки:Спойлер1) recipe for target 'main.o' failed ссылается на строку @echo Building file: $< 2) expected identifier or '(' before 'volatile' expected ')' before '(' token GccApplication1 ссылаются на строку DDRA |=(1<<3)|(1<<4); 3) expected identifier or '(' before 'volatile' expected ')' before '(' token GccApplication1 ссылаются на строку DDRB &=~(1<<4); 4) expected identifier or '(' before 'volatile' expected ')' before '(' token GccApplication1 ссылаются на строку PORTB |=(1<<4); 5) expected identifier or '(' before 'while' ссылается на строку while(1){
СпойлерSeverity Code Description Project File Line Error recipe for target 'main.o' failed GccApplication1 D:\Dream\GccApplication1\GccApplication1\Debug\Makefile 79 Error expected identifier or '(' before 'volatile' GccApplication1 D:\Dream\GccApplication1\GccApplication1\main.c 12 Error expected ')' before '(' token GccApplication1 D:\Dream\GccApplication1\GccApplication1\main.c 12 Error expected identifier or '(' before 'volatile' GccApplication1 D:\Dream\GccApplication1\GccApplication1\main.c 13 Error expected ')' before '(' token GccApplication1 D:\Dream\GccApplication1\GccApplication1\main.c 13 Error expected identifier or '(' before 'volatile' GccApplication1 D:\Dream\GccApplication1\GccApplication1\main.c 14 Error expected ')' before '(' token GccApplication1 D:\Dream\GccApplication1\GccApplication1\main.c 14 Error expected identifier or '(' before 'while' GccApplication1 D:\Dream\GccApplication1\GccApplication1\main.c 16
PORTB |=(1<<3); //макрос включения нагрузки на светодиод. _delay_ms(3000); //предположим, пока нагрузка включена, кнопка не проверяется PORTB &=~(1<<3); //макрос выключения нагрузки
Нагрузка должна включаться не здесь. Дублировать этот кусок три раза не нужно, даже вредно. Исправление функции random() я уже приводил. Мне казалось, вы знаете хотя бы основы Си... Ну и определитесь с желаемым поведением программы - с какого момента считать и т.д.
Цитата:
вы с ума посходили использовать delay? таймер заюзать религия не позволяет?
Использование таймера даст наибольшую точность, но оно чуть-чуть сложнее. Я привел максимально простой пример в надежде что автор возьмет его за основу и доделает.
автор не может понять как переключиться на другой режим когда процесс на 1 час заперт в делае без возможности выхода..... логические размышления работы алгоритма ему не ведомы проще в цикле инкрементировать две переменных для использования как отсчета реального времени, единственное надо только подбирать опытным путемреальные минуты в wail(1) пишем несколько с трочек...
Код:
i++; //тики в цикле if(i>60000)//типа 60000 это есть минута допустим { j++;// тут считаем минуты i=0; if(j>59) { ch++//тут считаем часы и так далее j=0; } }
далее опрашиваем кнопки и реакция на кнопки мгновенная и минуты тикают как ни в чем не бывало ... или можно добавить обратный отсчет при использовании рандома зная сколько минут натикало в переменной j это значение даже на индикацию можно вывести... а использование delay только там где нет альтернативы
Мой вариант гораздо проще модифицировать, один квант там всегда 1 сек независимо от тактовой частоты, задается _delay_ms(1000), она же является временем реакции на кнопку. Если надо, можно уменьшить до 0.1 сек. Но еще лучше вести отсчет секунд по таймеру, тогда вообще не будет ни от чего зависеть, даже от других прерываний. А высчитывать по вашему методу такты годится для ассемблера, но не для ЯВУ. Возможно, лучше было бы делать таймер не прямого а обратного отсчета, но это зависит от того что хочет автор, а он так и не ответил на мои вопросы.
Просто тарищ понял, что его задача для него не так проста, как хотелось бы.
может быть, я чего-то не понял, но мне показалось, что задача проще пареной репы. даже проще, чем рассказать об этом.
Очень тупо и кратко: 1. кнопку переключения режимов вешаем на сброс 2. сразу после сброса читаем из EEPROM флаг, проверяем, затем инвертируем его и сохраняем в EEPROM 3. по результатам проверки флага переходим к тупому циклу отработки того или иного режима - задержки хоть по году могут быть
что я не понял?
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
Довольно красивое решение, особенно хорошо для контроллеров у которых ресет совмещен с ногой ввода-вывода. Если выводов хватает - лучше все-таки вешать кнопку на прерывание.
Цитата:
2. сразу после сброса читаем из EEPROM флаг, проверяем, затем инвертируем его и сохраняем в EEPROM
Лучше не EEPROM а ОЗУ, оно стирается только при длительной потере питания.
Цитата:
3. по результатам проверки флага переходим к тупому циклу отработки того или иного режима - задержки хоть по году могут быть
Я вас минусовал, за то, что вы, по моему мнению, ерунду написали?
По сбросу решение имеет право на жизнь, но сброс может быть не только по нажатию кнопки. Так что способ от бедности и безнадеги. Потому что пожадничали на нормальный МК и алгоритм. Не вариант.
У EEPROM ограниченный ресурс, а если смена режима будет производиться часто? Опять же этот способ завязан на предыдущий ненадежный способ. Не вариант вдвойне.
В свою очередь, от вас тоже нет конкретики. В общих чертах вы сказали - "фигня вопрос". Ну так на все можно сказать это и типа считать себя опытным зубастым котом. Предложите свой вариант.
Так что: Меленько, подленько, гаденько поступаете.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 9
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения