4-битный счетчик вверх и вниз

нас просят создать код Arduino, который будет запускать 4-битный двоичный счетчик ВВЕРХ при нажатии кнопочного переключателя, а затем 4 - битный двоичный счетчик вниз при втором щелчке. Тогда наоборот. Это должно быть сделано с помощью только одного переключателя. Я вычислил часть обратного отсчета, но не знаю, как добавить код обратного отсчета для одного и того же переключателя. Может ли кто-нибудь помочь мне с этим. Спасибо. пожалуйста, смотрите ниже мою программу

int buttonState = 0;
int timer = 300;
int pin8 = 8;
int pin9 = 9;
int pin10 = 10;
int pin11 = 11;

void setup() 
{ 
pinMode(7,INPUT); 
pinMode(8,OUTPUT); 
pinMode(9,OUTPUT); 
pinMode(10,OUTPUT); 
pinMode(11,OUTPUT); 

}

void loop() 
{ 
digitalWrite(pin8, LOW); 
digitalWrite(pin9, LOW); 
digitalWrite(pin10, LOW); 
digitalWrite(pin11, LOW);
buttonState = digitalRead(7);
  
if(buttonState == HIGH) 
{
digitalWrite(pin8, HIGH);
delay(timer);
digitalWrite(pin8, LOW);
delay(timer); //bit 1
digitalWrite(pin9, HIGH);
delay(timer);
digitalWrite(pin9, LOW);
delay(timer); //bit 2
digitalWrite(pin8, HIGH);
digitalWrite(pin9, HIGH);
delay(timer);
digitalWrite(pin8, LOW);
digitalWrite(pin9, LOW);
delay(timer); //bit 3
digitalWrite(pin10, HIGH);
delay(timer);
digitalWrite(pin10, LOW);
delay(timer); //bit 4
digitalWrite(pin8, HIGH);
digitalWrite(pin10, HIGH);
delay(timer);
digitalWrite(pin8, LOW);
digitalWrite(pin10, LOW);
delay(timer); //bit 5
digitalWrite(pin9, HIGH);
digitalWrite(pin10, HIGH);
delay(timer);
digitalWrite(pin9, LOW);
digitalWrite(pin10, LOW);
delay(timer); //bit 6
digitalWrite(pin8, HIGH);
digitalWrite(pin9, HIGH);
digitalWrite(pin10, HIGH);
delay(timer);
digitalWrite(pin8, LOW);
digitalWrite(pin9, LOW);
digitalWrite(pin10, LOW);
delay(timer); //bit 7
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin11, LOW);
delay(timer); //bit 8
digitalWrite(pin8, HIGH);
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin8, LOW);
digitalWrite(pin11, LOW);
delay(timer); //bit 9
digitalWrite(pin9, HIGH);
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin9, LOW);
digitalWrite(pin11, LOW);
delay(timer); //bit 10
digitalWrite(pin8, HIGH);
digitalWrite(pin9, HIGH);
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin8, LOW);
digitalWrite(pin9, LOW);
digitalWrite(pin11, LOW);
delay(timer); //bit 11
digitalWrite(pin10, HIGH);
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin10, LOW);
digitalWrite(pin11, LOW);
delay(timer); //bit 12
digitalWrite(pin8, HIGH);
digitalWrite(pin10, HIGH);
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin8, LOW);
digitalWrite(pin10, LOW);
digitalWrite(pin11, LOW);
delay(timer); //bit 13
digitalWrite(pin9, HIGH);
digitalWrite(pin10, HIGH);
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin9, LOW);
digitalWrite(pin10, LOW);
digitalWrite(pin11, LOW);
delay(timer); //bit 14
digitalWrite(pin8, HIGH);
digitalWrite(pin9, HIGH);
digitalWrite(pin10, HIGH);
digitalWrite(pin11, HIGH);
delay(timer); 
digitalWrite(pin9, LOW);
digitalWrite(pin9, LOW);
digitalWrite(pin10, LOW);
digitalWrite(pin11, LOW);
delay(timer); //bit 15
}
}

, 👍2

Обсуждение

почему бы вам просто не посчитать переменную от 0 до 0x0F и не определить четыре бита на светодиодных выходах? ... код будет простым и не повторяющимся, @jsotola

`if(buttonState == HIGH) "означает"до тех пор, пока кнопка нажата". Вероятно, вы хотите использовать флаг, который меняется при нажатии кнопки., @PMF


1 ответ


2

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

Сначала вам нужно понять, что такое счетчик на самом деле: по сути, это просто переменная, содержащая число. Где именно эта переменная сохраняется, не важно. В вашем случае вы бы использовали переменную в своем коде. Аппаратное обеспечение счетчика/таймера в Arduino использует для него фиксированный SFR (специальный регистр функций).

То, что счетчик равен 4 битам, означает, что его значение удерживается только 4 битами. Это дает вам диапазон значений от 0 до 15 (2^4 значения). Таким образом, 4-битная переменная счетчика будет отсчитываться от 0 до 15. У вас может быть просто переменная счетчика, которую вы можете увеличивать или уменьшать (в зависимости от направления, которое вы хотите посчитать).

// Объявить переменную counter и инициализировать нулем
int counter_variable = 0;
// Увеличить
counter_variable++;
// или Уменьшить
counter_variable--;

Для "двоичной" части: здесь важно понимать, что десятичное число от 0 до 15 уже сохраняется в переменной как двоичное (поскольку микроконтроллеры/компьютеры знают только двоичное, ничего больше). Таким образом, вы можете записать состояние 4 наименее значимых байтов переменной счетчика:

digitalWrite(pin8  ,counter_variable & 0b0001);
digitalWrite(pin9  ,counter_variable & 0b0010);
digitalWrite(pin10 ,counter_variable & 0b0100);
digitalWrite(pin11 ,counter_variable & 0b1000);

& - это побитовое И. В результате операции биты будут установлены только тогда, когда они установлены с обеих сторон. Итак, здесь мы изолируем соответствующие биты. digitalWrite() будет интерпретировать все, что угодно, кроме нуля как ВЫСОКОЕ. Таким образом, вывод устанавливается ВЫСОКИМ, если соответствующий бит установлен в counter_variable. Но вы видите, что мы здесь повторяем себя. Мы могли бы использовать цикл for и определить наши контакты в массиве:

// at global scope
int led_pins[4] = {8, 9, 10, 11};

// в операторе button if
for(int i=0; i<4; i++){
    digitalWrite(led_pins[i], counter_variable & (1 << i));
}

Мы организовали контакты светодиодов в массив, чтобы их можно было легко индексировать. Затем мы перебираем массив пинов и устанавливаем пины аналогично последней версии. Но теперь мы используем (1 << i). Оператор << сдвигает биты числа перед ним на количество цифр, которые идут после него. Десятичная дробь 1-это двоичная дробь 0b0001. Итак, когда i равно 2 (например), это сдвинет биты на 2 цифры влево, давая вам 0b0100. Мы просто генерируем те же битовые паттерны, что и в последней версии.

Но мы можем даже оптимизировать дальше с помощью прямого манипулирования портом. Внутри микроконтроллера выходные контакты управляются с помощью простых специальных функциональных регистров. Они работают в некотором роде как переменная. Все выходные контакты организованы в так называемые "Порты" (группы до 8 контактов). Каждый порт имеет один регистр PORTx (где x обозначает символ, связанный с этим портом), который задает состояние выводов, если они находятся в режиме вывода. Установка соответствующего бита в правильном регистре PORTx приведет к тому, что контакт станет ВЫСОКИМ. Очистка бита (установка на ноль) приведет к тому, что вывод станет НИЗКИМ.

Вы используете контакты Arduino с 8 по 11. Если вы посмотрите на схему распиновки Arduino Uno, то увидите, что эти контакты также помечены как PB0-PB3, что означает "Порт B, контакты от 0 до 3 (внутри этого порта)". Соответствующий регистр PORTx - PORTB. Таким образом, мы можем установить все контакты порта сразу, присвоив этому регистру новое значение. Следующий код установит чередующийся шаблон включения/выключения для всех 8 контактов PORTB:

PORTB = 0b10101010;

Теперь мы хотим использовать только самые младшие 4 бита и установить их на значения, которые имеет наша переменная счетчика. Таким образом, мы маскируем 4 старших бита побитовым оператором И, а затем присваиваем это значение регистру:

PORTB = counter_variable & 0b00001111;

Итак, теперь оператор if в void loop() может выглядеть следующим образом (используя цикл for):

if(buttonState == HIGH){
    // переменная счетчика подсчета
    for(int counter_variable=0; counter_variable < 16; counter_variable++){
        // запись на выводы
        PORTB = counter_variable & 0b00001111;
        // задержите немного, чтобы показать значение
        delay(timer);
    }
}

И теперь мы хотим, чтобы каждая последовательная кнопка считалась в другом направлении, чем раньше. Таким образом, мы можем объявить другую переменную как направление, а затем использовать другой оператор if для переключения между двумя циклами for:

// On global scope
int direction = 1;

// в пустом цикле()

if(buttonState == HIGH){
    if(direction == 1){
        // переменная счетчика подсчета
        for(int counter_variable=0; counter_variable < 16; counter_variable++){
            // запись на выводы
            PORTB = counter_variable & 0b00001111;
            // задержите немного, чтобы показать значение
            delay(timer);
        }
        // изменить направление
        direction = -1;
    } else {
        // переменная счетчика подсчета
        for(int counter_variable=15; counter_variable >= 0; counter_variable--){
            // запись на выводы
            PORTB = counter_variable & 0b00001111;
            // задержите немного, чтобы показать значение
            delay(timer);
        }
        // изменить направление
        direction = 1;
    }
}

Примечание:

  • Это, наверное, не самый эффективный способ, но я думаю, что он вполне понятен (что тоже важно).
  • Это все еще использует много вызовов delay (), как и ваш код. Это сделает вашу кнопку невосприимчивой. Счетчик должен сначала закончить работу, прежде чем кнопка снова отреагирует. Это может быть нормально для тебя. Если нет, вам нужно реструктурировать программу для использования неблокирующего стиля кодирования, как в примере BlinkWithoutDelay из Arduino IDE (с использованием millis()). В Интернете есть много учебных пособий по этому поводу.
  • При использовании неблокирующего стиля кодирования вам также необходимо проверить кнопку на переход в HIGH, а не на состояние high. В противном случае код будет выполняться только при нажатии кнопки.
  • Кнопки подпрыгивают при нажатии. Таким образом, при нажатии кнопки происходит много переходов между максимумом и МИНИМУМОМ в течение нескольких мс. В приведенном выше коде это не важно, потому что начатому счетчику требуется так много времени, что каждый отскок завершается после завершения счетчика. С кодом, который требует меньше нескольких мс, у вас могут возникнуть проблемы. Для этого я бы предложил библиотеку Bounce2. Он обрабатывает обнаружение отказов за вас.
,