Страница 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 писал(а):

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

      if(PINB.1<1)allarm1();
Как сделать чтобы подпрограмма сработала один раз и не срабатывала повторно если низкий уровень всё еще присутствует на выводе...?
Зачем советовать тот же принцип? Ведь и работать будет также:
Vov123 писал(а):

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

if(PINB.1)
    alarm1();
else
    no_alarm1();
Единственное отличие в том, что вызов 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() в этом коде:

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

if(PINB.1<1)allarm1();
когда отпущенная кнопка сделает проверку "(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
}
А в обработчике прерывания от таймера уменьшать счётчик времени:

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

if(alarm1_time)
    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