Страница 1 из 1
Cvavr вопрос по Си
Добавлено: Вт май 19, 2009 12:23:20
Arik
Здравствуйте столкнулся с проблемой! Пишу программу сканирующую три вывода микроконтроллера тини 2313 Пишу на Си
Код: Выделить всё
while(1)
{
if(PINB.1<1)allarm1();
if(PINB.2<1)allarm2();
if(PINB.3<1)allarm3();
};
если на одном из выводов удерживать низкий уровень, то срабатывает соответствующая подпрограмма allarm(1-3) но мне не нужно чтобы подпрограмма выполнялась постоянно, пока удерживается низкий уровень на выводе. Как сделать чтобы подпрограмма сработала один раз и не срабатывала повторно если низкий уровень всё еще присутствует на выводе, но сканирование оставшихся выводов продолжалось? Спасибо!
Добавлено: Вт май 19, 2009 12:38:41
adrenocrome
заведи лишнюю переменную,в неё пиши флаг отработки подпрограммы. Отработала - пиши в флаг 1 например.
Добавлено: Вт май 19, 2009 17:57:54
Danko
А если за время обработки Alarm3 на 1 входе была "1" и к моменту опроса опять установился "0".
Лучше читать сразу весь порт отбрасывать ненужные биты а из нужных выбирать комбинацию "0" на одном на двух и т.д.
и соответственно вызывать нужную Alarm()
Re: Cvavr вопрос по Си
Добавлено: Вт май 19, 2009 19:38:02
Yellow Tiger
Arik писал(а):Как сделать чтобы подпрограмма сработала один раз и не срабатывала повторно ...
Для этого нужно обнаруживать не уровень, а смену уровня - то есть, не только читать состояние порта с кнопками, но и помнить предыдущее значение и сравнивать с ним побитным XOR'ом - единички в результате побитного XOR'а покажут какие биты изменились, а единички в только что прочитанном покажут, в каких битах переход был с нуля на единичку (ну, а в остальных - ...).
Добавлено: Ср май 20, 2009 00:28:51
xelos
либо прерывания использовать...
1. прерывание по таймеру - анализ входных битов идет периодично и от того, что задетектировали - вызывать соответствующую процедуру. проблема - если уже находимся в процедуре обработки и вызываем новую - кол-во вложенных вызовов ограничено стеком.
2. либо прерывания на изменение порта ввода, если есть такая возможность. проблема та же, что и в первом случае.
все зависит от конкретной задачи. но проблема вложенных вызовов может быть критичной.
Добавлено: Ср май 20, 2009 00:44:44
Vov123
if(PINB.1)alarm1();
else
no_alarm1();
Re: Cvavr вопрос по Си
Добавлено: Ср май 20, 2009 22:57:29
Yellow Tiger
Автор привел такой код:
Arik писал(а):Как сделать чтобы подпрограмма сработала один раз и не срабатывала повторно если низкий уровень всё еще присутствует на выводе...?
Зачем советовать тот же принцип? Ведь и работать будет также:
Единственное отличие в том, что вызов alarm() будет не по нулю, а по единице на входе.
Добавлено: Чт май 21, 2009 12:47:25
Vov123
Да и кодик условный и не для вас.
Можно написать проще-используйте оператор ELSE
До меня дошло-сомневаетесь в правильности.
Так вот,если без ELSE,то принажатии кнопки произойдёт вызов функции,но и при отпускании кнопки alarm будет продалжать "гудеть".
Но при использовании оператора ELSE при отпускании кнопки произойдёт вызов другой функции "alarm-off".
Интересно,кто-же мешает вам это проверить,хотя бы в проте?
Вот поиграйтесь.
Добавлено: Чт май 21, 2009 14:06:27
YKolomiets
Предлагаю так:
unsigned char x1, x2, x3;
x1=x2=x3=1;
while(1)
{
if(PINB.1<1)
{
if(x1)
{
allarm1();
x1=0;
}
}
else
{
x1=1;
}
if(PINB.2<1)allarm2();
тоже самое что и выше но с х2
if(PINB.3<1)allarm3();
тоже самое что и выше но с х3
};
По строки подряд идут не могу отредактировать.
Добавлено: Чт май 21, 2009 18:59:38
Yellow Tiger
Vov123 писал(а):Вот поиграйтесь.
Благодарю, нет нужды. Работать этот код будет именно так,
как не хочет автор вопроса, а именно - пока кнопка нажата, будет, как из пулемета, вызываться функция alarm(), и связано это, как я уже говорил, с тем, что предложенный код устроен так же, как и тот, который
хотел заменить автор вопроса.
Еще раз, чего хотел спрашивавший:
Arik писал(а):срабатывает соответствующая подпрограмма allarm(1-3) но мне не нужно чтобы подпрограмма выполнялась постоянно, пока удерживается низкий уровень на выводе. Как сделать чтобы подпрограмма сработала один раз и не срабатывала повторно если низкий уровень всё еще присутствует на выводе, но сканирование оставшихся выводов продолжалось? Спасибо!
P.S. Не сразу заметил вот это:
Vov123 писал(а):но и при отпускании кнопки alarm будет продалжать "гудеть"
Можно узнать, кто будет вызывать функцию alarm() в этом коде:
когда отпущенная кнопка сделает проверку "
(PINB.1<1)" ложной - Пушкин?
YKolomiets писал(а):Предлагаю так:
Код: Выделить всё
unsigned char x1, x2, x3;
x1=x2=x3=1;
while(1)
{
if(PINB.1<1)
{
if(x1)
{
allarm1();
x1=0;
}
}
else
{
x1=1;
}
...
};
То же самое, что с побитным XOR'ом, только значительно длиннее, медленнее, запутаннее и с массой ненужных переменных.
Добавлено: Чт май 21, 2009 19:55:34
__Alexander
Тут без автора вопроса никак не обойтись, хотя по ходу ему этот вопрос уже не актуален.
Хотел было вопрос задать, а могу только усложнить задачу: А кто сказал, что подпрограмма alarm должна возвращаться в while??? После возврата она начнет заново выполняться. А может автору это не нужно? Вошла в alarm, выполняется там в while(1), но при этом сканирование продолжается, и при нажатии другой клавиши, передаем указатель адреса функции обратно и обрабатываем...? Ну как? Может этого хотел автор?
Добавлено: Пт май 22, 2009 02:55:02
Vov123
Надо было поиграться-поманипулировать оператором ELSE.
Тогда бы не стал вспоминать писателей и задавать глупых вопросов.
Хотя чего я пристал?Обещаю торжественно обходить вас стороной и прошу тех же действий от вас.
Добавлено: Пт май 22, 2009 06:12:14
Arik
Всем спасибо за участие! Раскрою вопрос более конкретно!функция Allarm() содержит АТ команду для GSM модема а сканируемые порты это датчики - движения, подьема гаража и шума. Так вот при срабатывании датчика шума к примеру должна быть отправлена смс с текстом шум.
Моделирую в протеусе и получаю такую картину - если сработал датчик подьема гаража, то смс с текстом подьем отправляется без остановки снова и снова! В реальной жизни это сразу приведет к зависанию модема! Вот мне и нужно чтоб помогли с кодом так, чтобы отправлялось только один раз, даже если датчик остается в активном состоянии.
P.S. Например пришла смс с текстом шум но нет другой смс движение или подьем значит можно не паниковать! Может это дети бомбочками балуются

Добавлено: Пт май 22, 2009 12:20:23
Yellow Tiger
Vov123 писал(а):Тогда бы не стал ... задавать глупых вопросов.
Ты не понял - это был не вопрос, это был намёк! Подсказка.
Ладно, мастер "операторов"
Else, дам еще один шанс...
1. Пишем простенькую программку, чтобы проверить будет ли вызываться что-либо в теле оператора
if, если его условие не выполняется, а "оператора"
ELSE не предусмотрено:
Код: Выделить всё
//=== Global definitions
void InitDevices( void ); //Init controller perifery
void main(void)
{
InitDevices();
while (1)
{
if (!PINB.0) // if PB.0's pressed
PINB.1 = 1;
};
}
2. Подключаем к PB.0 кнопку, а к PB.1 - осцилл
3. Запускаем - экран заполнится уровнем "1", нажимаем кнопку - на экране появляется меандр, отпускаем кнопку - меандр исчезает, снова нажимаем - меандр появляется (см. кнопку 1 на Control Panel):

4. Сравниваем поведение программы в контроллере с этими словами:
Yellow Tiger писал(а):пока кнопка нажата, будет, как из пулемета, вызываться функция alarm()
, а затем с этими:
Vov123 писал(а):если без ELSE,то ... и при отпускании кнопки alarm будет продалжать "гудеть"
и делаем выводы.
P.S. Если добавить "оператор"
else, то в поведении тиньки относительно нажатий кнопки
нифига не изменится...:
Код: Выделить всё
while (1)
{
if (!PINB.0) // if PB.0's pressed
PINB.1 = 1;
else
#asm("nop");
};
Добавлено: Пт май 22, 2009 13:57:40
ARV
что-то мне ход дискуссии стал напоминать соседнюю тему известного автора
a...mm-а : едва ли не в первом ответе был дан верный совет, однако на протяжении многих последующих постов все идет и идет обсасывание неверных вариантов...
решение "в лоб":
Код: Выделить всё
#define f_1 0b00000001
#define f_2 0b00000010
#define f_3 0b00000100
unsigned char flags = 0;
while(1){
// проверка первого варианта f_1
if(!(PINB & f_1)){
// если в пине 0, то проверим, не было ли уже такого ранее?
if(!(flag & f_1)){
// если флажок не установлен, то вызываем свою функцию
alarm1();
// и устанавливаем флаг обработки события
flag |= f_1;
}
} else flag &= ~f_1; // если в пине 1, то сбросим флажок этого события
// далее аналогично для обработки состояний f_2 и f_3
}
логичнее и оптимальнее было бы функцию выдачи сообщения сделать одну, а не три, передавать в нее "сработавший" пин, а внутри нее анализировать состояние флажков и устанавливать их при необходимости, а так же выводить сообщение, соответствующее переданному пину...
что-то типа такого:
Код: Выделить всё
void alarm(unsigned char pin){
if(flag & pin) return; // если флаг уже стоит - сразу выход
flag |= pin;
switch(pin){
case f_1: SMS_SEND("караул!!!");
case f_2: SMS_SEND("грабят!!!");
case f_3: SMS_SEND("насилуют");
default: SMS_SEND("ошибка!");
}
}
разумеется, и это не самое элегантное решение, но не буду же я
все сам за вас делать?

Добавлено: Пт май 22, 2009 15:11:27
Барсик
А флаг, как мне кажется, лучше сбрасывать по прошествию некоторого времени. А то если будет "трах-бах-бах-бубух", то придётся ещё и с дребезгом датчика бороться.
Сделать из одного таймера системные часы, чтобы возникало прерывание, например, раз в миллисекунду.
В программе написать:
Код: Выделить всё
#define MAX_ALARM1_TIME 5000 // 5 секунд
unsigned int alarm1_time = 0; // счётчик времени
В теле функции alarm написать:
Код: Выделить всё
if(!alarm1_time)
{
sendSms(?????);
alarm1_time = MAX_ALARM1_TIME
}
А в обработчике прерывания от таймера уменьшать счётчик времени:
Добавлено: Пт май 22, 2009 23:17:02
__Alexander
ARV писал(а):
что-то типа такого:
Код: Выделить всё
void alarm(unsigned char pin){
if(flag & pin) return; // если флаг уже стоит - сразу выход
flag |= pin;
switch(pin){
case f_1: SMS_SEND("караул!!!");
case f_2: SMS_SEND("грабят!!!");
case f_3: SMS_SEND("насилуют");
default: SMS_SEND("ошибка!");
}
}
разумеется, и это не самое элегантное решение, но не буду же я
все сам за вас делать?

И отличается этот код от приведенного автором только тем, что при установленном флаге выходим сразу. А автор хочет продолжать сканирование остальных кнопок.
Я бы сделал так:
char pins, pinslast;
pins = ((PINB >> 1) & $0x07;
....
pinlast = pins;
И собственно все. Смотрим pins и сравниваем с его предыдущим состоянием pinslast. Допустим приняли 4 (типа PIN.3 нажат), смотрим, что pinlast равно 1, значит что PIN.1 был уже нажат и предпринимаем соответсвующие действия.
Если приняли 7, значит нажали все три клавиши, и тоже думаем что лучше сделать... может послать одну СМС с тремя словами сразу?
А вобще решений немеряно. Каждое имеет место. И мы не совсем дискутируем, а предлагаем как бы поступил каждый из нас. И на выяснение чей же код чучше, может уйти тысячи лет.

Добавлено: Сб май 23, 2009 07:56:44
ARV
__Alexander писал(а):И отличается этот код от приведенного автором только тем, что при установленном флаге выходим сразу. А автор хочет продолжать сканирование остальных кнопок.
уважаемый, вы бы хоть подумали 5 минут, прежде чем это писать... я предлагаю вариант замены функциё
alarm1()...alarm3(), введенных автором топика, а анализ пинов осуществляется "снаружи" примерно по тому алгоритму, что я и предложил. в этом случае единожды сработавший пин обрабатывается так же один раз, а остальные продолжают сканироваться.
Добавлено: Сб май 23, 2009 21:39:56
__Alexander
ARV писал(а):уважаемый, вы бы хоть подумали 5 минут, прежде чем это писать...
Забей.
Я многое чего не понимаю, допустим даже вот такое:
Почему функция strlen, которая возвращает длину строки, при указании жесткого размера строки выдает на три байта больше?
Типа такого:
chat Str[] = "YO";
chat Str1[2] = "YO";
так вот на strlen(Str1) выдает что байт аж 5!
Я тоже прежполагаю, что служебные символы компилятор может добавлять сам, но почему три? Че-то тут не то.
Когда нить разберусь, а может быть это и RTFM.
PS. Компилятор IAR