Чтение PIN-кода ATmega328p

int main(void)
{

DDRD = 0x00 ;
DDRB = DDRB | 0b00100000 ;
while (1)
{
     while(!(PIND & (1<<6))){
       PORTB |= 1<<PORTB5 ;  
    }
    while((PIND & (1<<6)))  {
        PORTB &= ~(1<<PORTB5) ;
    }

}
return 0;
}

Я пытаюсь создать базовую систему безопасности с помощью переключателя, который при поломке подхватывается PIND7, а затем устанавливает PB5. И наоборот, когда переключатель установлен, низкий сигнал улавливается PIND7, а затем очищает бит на PB5. У микроконтроллера нет проблем, когда переключатель установлен, он немедленно сбрасывает бит в PB5, но когда переключатель ломается, для установки бита в PB5 требуется около 30 секунд. Спасибо за любую помощь, которую вы можете оказать, я извиняюсь, если мой код неаккуратный, я всего лишь новичок.

у вас есть подтягивающий резистор на контакте D7?

Ответы (1)

Обычно очень помогает простая таблица истинности.

Вот моя попытка расшифровать это из вашего текста:

PIND6 | PORTB5
  0   |   1
  1   |   0

Эта таблица истинности просто говорит, что если PIND6 равен 0, то PORTB5 должен быть равен 1, и «наоборот». Собственно говоря, PORTB5 является логическим НЕ для PIND6.

Это математическое уравнение для того, что нам нужно:
PORTB5 = !PIND6, что совпадает с ПОРТБ 5 "=" ПИН 6 ¯ .


Я не думаю, что вы хотите иметь два цикла while в своем основном цикле. Это не лучший способ проверки состояния, так как вы можете застрять в них. Как я полагаю, вы получаете.

Вместо этого вам следует либо использовать оператор if, либо просто назначить PORTD, либо использовать любой из них вместе с отклоненным вводом. Я покажу все три из них с некоторым интервалом между ними. Вы не должны держать все три, вы должны выбрать один из них.

  • Однострочник затрудняет понимание.
  • Оператор if гораздо легче понять.
  • Дебаунсер обнаруживает, когда переключатель меняет вход, вход может быстро меняться на логическую 1, логический 0, сотни раз в течение пары микросекунд при переходе, и вы не хотите, чтобы ваш выход также менялся таким образом. . Поэтому вместо этого мы обнаруживаем изменение, а затем смотрим через некоторое время, когда коммутационный шум уляжется.

Вот непроверенный код:

int main(void)
{
    DDRD = 0x00 ;
    DDRB = DDRB | 0b00100000 ;
    unsigned int debounce = 0; //remove this line if you don't care about debouncing
    char old_PIND = PIND;//remove this line if you don't care about debouncing
    while (1)
    {
        ////////////////////////////////////////////////
        //You can do either this:
        ////////////////////////////////////////////////
        PORTB = (PORTB&(~(1<<PORTB5)))|(~((PIND&(1<<6))>>1));
        //(PORTB&(~(1<<PORTB5))) => PORTB but with a 0 on bit 5
        //(~((PIND&(1<<6))>>1)) => PIND, but only 6th bit, inverted and shifted 1 step ->
        //And then just OR them together
        //You could probably remove some parenthesis, but I prefer having more than fewer
        ////////////////////////////////////////////////
        //Or you can do this, it will give you the same result
        ////////////////////////////////////////////////
        if(PIND&(1<<6)){//If it's a 1
            PORTB &= ~(1<<PORTB5);//then set PORTB5 to 0
        }else{
            PORTB |= (1<<PORTB5); //if PIND6 is a 0, set PORTB5 to 1
        }

        ////////////////////////////////////////////////
        //Or you can do this if you want debouncing
        ////////////////////////////////////////////////
        if(debounce==0){
            if(old_PIND!=PIND){
                //if we're in here, it means that the input has changed
                //and that the input may bounce a lot for the next couple of microseconds. 

                //we want to wait some time and then use the value we get later
                //because that value will (hopefully) be stable, without any burst noise. 

                debounce = 10000;//This sets the debounce-timer
                //if you want to do this "properly", then you should use one of 
                //the built in timers that Atmega328P supports,
                //instead of using a variable like I do. But this will work.               
            }else{
                //if we're in here, it means that the input has stayed the same 
                //for one debounce-timer, so now it's stable.

                //same code as before: 
                if(PIND&(1<<6)){//If it's a 1
                    PORTB &= ~(1<<PORTB5);//then set PORTB5 to 0
                }else{
                    PORTB |= (1<<PORTB5); //if PIND6 is a 0, set PORTB5 to 1
                }
            }
            old_PIND = PIND;//update old_PIND so we can detect changes
        }else{
            debounce-=1;
        }


    }
    return 0;
}

Итак, технически, просто замените операторы while на операторы if и выучите слово «логическая инверсия», и вы будете на хорошей стороне.

Если вы хотите быть педантичным или «правильным», вам следует использовать прерывания вместе с внутренними таймерами, потому что они делают код более эффективным. Но этот код решит проблему, как вы ее представили.

Я подозреваю, что для этого также потребуется некоторая логика устранения дребезга.
@RJR Хм, это зависит от того, где эта вещь будет использоваться. Конечно, он может переключаться туда и обратно несколько раз... но с данной информацией я не вижу в этом проблемы. Ах, возможно, это то, что пытался сделать цикл while. Прямо сейчас я ничего не изменю, потому что OP должен раскрыть больше информации. ... или хммм.. Я могу просто добавить это для полноты картины.