Програмирование двухрежимной работы микроконтроллера.

Обсуждаем контроллеры компании Atmel.
Ответить
Встал на лапы
Сообщения: 131
Зарегистрирован: Вс май 08, 2016 22:45:48

Сообщение sTRog »

Здравствуйте.
Вопрос: Как реализовать двухрежимную работу микроконтроллера, с возможностью мгновенного переключения.
Пояснение: Нужно, чтобы микроконтроллер мог работать в двух режимах. Переключение между режимами осуществляется кнопкой без фиксации (нажал - есть сигнал, отпусти - пропал).
Проблема: Задача режима 1 - с задержкой в 1,5 часа, подавать на ножку в течении 3 сек ток. Но проблема, если первый режим включился - 1,5 часа - нельзя будет его переключить. Мне же нужно, чтобы я в любой момент, мог перейти с режима 1 на режим 2.
Задача режима 2 - с задержкой в (случайное число в диапазоне между 1,0 и 2,0 часами), подавать на ножку в течении 3 сек ток. Но проблема, такая же, как и в первом случае.

Я попробовал написать код
Спойлер

Код: Выделить всё

 
#include <avr/io.h>
#include <avr/delay.h>
#include <stdlib.h>

int main(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'
Реклама
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

_delay_ms(5400000);
Не ругается на такую большую задержку?
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 секунды)?
Наиболее правильный способ - использовать таймеры. Они считают независимо от основной программы и не мешают проверять кнопки. Менее точно можно делать в основном цикле фиксированные задержки.
Спойлер

Код: Выделить всё

unsigned int sec=0; //счетчик секунд
unsigned int 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;
    }
  }
}
Реклама
Встал на лапы
Сообщения: 131
Зарегистрирован: Вс май 08, 2016 22:45:48

Сообщение sTRog »

Спасибо за ответ. При компиляции вашего кода выдет такие ошибки
СпойлерSeverity Code Description Project File Line
Error recipe for target 'main.o' failed GccApplication1 D:\СОН\GccApplication1\GccApplication1\Debug\Makefile 79
Error expected '=', ',', ';', 'asm' or '__attribute__' before '|=' token GccApplication1 D:\СОН\GccApplication1\GccApplication1\main.c 7
Error expected '=', ',', ';', 'asm' or '__attribute__' before '&=' token GccApplication1 D:\СОН\GccApplication1\GccApplication1\main.c 8
Error expected '=', ',', ';', 'asm' or '__attribute__' before '|=' token GccApplication1 D:\СОН\GccApplication1\GccApplication1\main.c 9
Error expected identifier or '(' before 'while' GccApplication1 D:\СОН\GccApplication1\GccApplication1\main.c 11
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

Странно, у меня все компилируется (даже не ожидал - писал из головы), разве что рандом исправить пришлось. В простейшем случае random()%3600;
Спойлер

Код: Выделить всё

$ make
avr-gcc  -mmcu=atmega8 -Wall -gdwarf-2 -std=gnu99    -DF_CPU=12000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT main.o -MF dep/main.o.d  -c  ../main.c
avr-gcc -mmcu=atmega8 -Wl,-Map=firmware.map main.o    -o firmware.elf
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature  firmware.elf firmware.hex
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O ihex firmware.elf firmware.eep || exit 0
avr-objdump -h -S firmware.elf > firmware.lss

AVR Memory Usage
----------------
Device: atmega8

Program:     550 bytes (6.7% Full)
(.text + .data + .bootloader)

Data:          4 bytes (0.4% Full)
(.data + .bss + .noinit)
Покажите весь код который пытаетесь собрать.
Реклама
Эиком - электронные компоненты и радиодетали
Это не хвост, это антенна
Сообщения: 1492
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

Конечный автомат. Автоматное программирование. Гугл.
Контактная информация:
Реклама
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

В полном объеме реализовывать конечный автомат для такой простой задачи - перебор. А в неполном решение уже есть и для него не обязательно знать что такое конечный автомат.
Реклама
Это не хвост, это антенна
Сообщения: 1492
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

Вся задача ТС целиком и полностью делается на КА. Можно флагами, по сути тоже автомат, но это мешанина из флагов и условий.
В данном случае никакого перебора нет. И применительно к КА нет такого понятия. Есть постановка задачи, анализ ее выполнения и целесообразность применения способов. Если проще сделать на КА, значит делаем на КА. Вот применить недодиспетчеры или RTOS - это уже перебор.
Позже выложу пример.
Контактная информация:
Встал на лапы
Сообщения: 131
Зарегистрирован: Вс май 08, 2016 22:45:48

Сообщение sTRog »

COKPOWEHEU писал(а):Странно, у меня все компилируется (даже не ожидал - писал из головы), разве что рандом исправить пришлось. В простейшем случае random()%3600;
Изначально закомпелировал целиком - только ваш код, который вы мне предложили (спасибо). Выдало несколько ошибок. После этого, я немного изменил код, но ошибки остались те же.

Вот код
Спойлер

Код: Выделить всё

#define F_CPU 1000000UL
#include <avr/io.h>
#include <avr/delay.h>
#include <stdlib.h>
#include <avr/interrupt.h>

unsigned int sec=0; //счетчик секунд
unsigned int max_time=5400; //1.5 часа
char mode=0; //0 - фиксированная задержка, 1 - рандомная

 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
Электрический кот
Аватара пользователя
Сообщения: 1031
Зарегистрирован: Чт июн 20, 2013 00:00:58
Откуда: москва, м.Сходненская

Сообщение Mishany »

вы с ума посходили использовать delay?
таймер заюзать религия не позволяет?
и на кой там библиотека прерываний?
переменную i объявлять типо не надо?
Последний раз редактировалось Mishany Чт июл 14, 2016 22:23:57, всего редактировалось 1 раз.
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

Где функция main()?
Где объявление переменной i?
PORTB |=(1<<3); //макрос включения нагрузки на светодиод.
_delay_ms(3000); //предположим, пока нагрузка включена, кнопка не проверяется
PORTB &=~(1<<3); //макрос выключения нагрузки
Нагрузка должна включаться не здесь. Дублировать этот кусок три раза не нужно, даже вредно.
Исправление функции random() я уже приводил.
Мне казалось, вы знаете хотя бы основы Си...
Ну и определитесь с желаемым поведением программы - с какого момента считать и т.д.
вы с ума посходили использовать delay?
таймер заюзать религия не позволяет?
Использование таймера даст наибольшую точность, но оно чуть-чуть сложнее. Я привел максимально простой пример в надежде что автор возьмет его за основу и доделает.
Электрический кот
Аватара пользователя
Сообщения: 1031
Зарегистрирован: Чт июн 20, 2013 00:00:58
Откуда: москва, м.Сходненская

Сообщение Mishany »

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

Код: Выделить всё

i++; //тики в цикле
if(i>60000)//типа 60000 это есть минута допустим
{
j++;// тут считаем минуты
i=0;
if(j>59)
{
ch++//тут считаем часы и так далее
j=0;
}
}
далее опрашиваем кнопки и реакция на кнопки мгновенная и минуты тикают как ни в чем не бывало ...
или можно добавить обратный отсчет при использовании рандома зная сколько минут натикало в переменной j это значение даже на индикацию можно вывести... а использование delay только там где нет альтернативы
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

Мой вариант гораздо проще модифицировать, один квант там всегда 1 сек независимо от тактовой частоты, задается _delay_ms(1000), она же является временем реакции на кнопку. Если надо, можно уменьшить до 0.1 сек. Но еще лучше вести отсчет секунд по таймеру, тогда вообще не будет ни от чего зависеть, даже от других прерываний. А высчитывать по вашему методу такты годится для ассемблера, но не для ЯВУ.
Возможно, лучше было бы делать таймер не прямого а обратного отсчета, но это зависит от того что хочет автор, а он так и не ответил на мои вопросы.
Это не хвост, это антенна
Сообщения: 1492
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

Просто тарищ понял, что его задача для него не так проста, как хотелось бы.
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18783
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Demiurg писал(а):Просто тарищ понял, что его задача для него не так проста, как хотелось бы.
может быть, я чего-то не понял, но мне показалось, что задача проще пареной репы. даже проще, чем рассказать об этом.

Очень тупо и кратко:
1. кнопку переключения режимов вешаем на сброс
2. сразу после сброса читаем из EEPROM флаг, проверяем, затем инвертируем его и сохраняем в EEPROM
3. по результатам проверки флага переходим к тупому циклу отработки того или иного режима - задержки хоть по году могут быть

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

Мой уютный бложик... заходите!
Контактная информация:
Это не хвост, это антенна
Сообщения: 1492
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

ARV писал(а):что я не понял?
Вам бы я не доверил даже проект для мигания светодиодов писать. Все равно через (_,_) напишете.
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18783
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Demiurg писал(а):Все равно через (_,_) напишете
неконструктивно. пожалуйста, конкретизируйте.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Это не хвост, это антенна
Сообщения: 1492
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

ARV писал(а):...
Конкретика по всем трем пунктам.
Контактная информация:
Говорящий с текстолитом
Аватара пользователя
Сообщения: 1525
Зарегистрирован: Чт июн 10, 2010 20:11:19

Сообщение COKPOWEHEU »

1. кнопку переключения режимов вешаем на сброс
Довольно красивое решение, особенно хорошо для контроллеров у которых ресет совмещен с ногой ввода-вывода. Если выводов хватает - лучше все-таки вешать кнопку на прерывание.
2. сразу после сброса читаем из EEPROM флаг, проверяем, затем инвертируем его и сохраняем в EEPROM
Лучше не EEPROM а ОЗУ, оно стирается только при длительной потере питания.
3. по результатам проверки флага переходим к тупому циклу отработки того или иного режима - задержки хоть по году могут быть
Тут и правда проще не придумаешь.
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18783
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

Demiurg писал(а):Конкретика по всем трем пунктам.
минусанул оба сообщения. я не уважаю такие наезды и таких наездников.
извините за прямоту
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Это не хвост, это антенна
Сообщения: 1492
Зарегистрирован: Ср июн 25, 2008 15:19:44

Сообщение Demiurg »

Я вас минусовал, за то, что вы, по моему мнению, ерунду написали?

По сбросу решение имеет право на жизнь, но сброс может быть не только по нажатию кнопки. Так что способ от бедности и безнадеги. Потому что пожадничали на нормальный МК и алгоритм. Не вариант.

У EEPROM ограниченный ресурс, а если смена режима будет производиться часто? Опять же этот способ завязан на предыдущий ненадежный способ. Не вариант вдвойне.

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

Так что: Меленько, подленько, гаденько поступаете.
Контактная информация:
Ответить

Вернуться в «AVR»