Данные в двух 8-битных сдвиговых регистрах не изменятся

Я пытаюсь вывести 16 бит данных в два 8-битных регистра сдвига (74HC595), которые управляют светодиодной матрицей 8x8. Есть проблема с тем, как код выводит данные в регистры, как будто данные в них даже не сдвигаются. Первые 8 бит обрабатывают строки, а последние 8 бит – столбцы.

Я уже написал этот код на Arduino, но я пытаюсь переписать его на C (мы должны изучать программирование AVR на C для колледжа). Версия Arduino работает нормально, поэтому я знаю, что схема правильная. Я использую Atmel Studio.

Например, я заменил все digitalWrite(clockPin, HIGH) на PORTD = PORTD | (1<<часы). Но я не могу ничего вывести с моим кодом. Для обновления светодиодного дисплея используется переменная refreshFlag. Для обновления состояния игры (которое еще не реализовано) используется переменная updateFlag.

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

// порт 2 (защелка), 4 (часы), 7 (данные). То же, что контакты на плате arduino
#define latchPin 2
#define clockPin 4
#define dataPin 7

//функции
void init_ports(void);
void init_USART(void);
void init_timer0(void);
void sendmsg (char *s);
void drawData(uint8_t myData[]);
void shiftOut(uint8_t myDataOut);

unsigned char qcntr = 0,sndcntr = 0;   /*indexes into the queue*/
unsigned char queue[100];       /*character queue*/

int updateTimeCount;
int updateDelay; 
int updateFlag; // Флаг обновления состояния игры
int refreshFlag;// Флаг обновления отображения


uint8_t data[] = {0b00000001,
                  0b01111110,
                  0b11000110,
                  0b01001010,
                  0b01010010,
                  0b11100010,
                  0b01111110,
                  0b01000010};

int main(void) {  
    //init_USART();// Серийный номер для целей отладки
    init_ports();
    init_timer0();
    clearData();
    newBlock();

    sei();

    PORTB = 0; //PortB для отладки
    updateTimeCount = 0;
    updateDelay = 13; // Игра обновляется каждые 13 * 16 мс = 208 мс.
    updateFlag = 0; 
    refreshFlag = 0; // Отображение обновлений каждые 16 мс

    while (1) {

        if(updateFlag) { //обновляет состояние игры
            PORTB = (1<<5); // светодиод отладки
            updateFlag = 0;     
        }

        if(refreshFlag) { //обновляет дисплей
            drawData(data); //массив выходных данных

            refreshFlag = 0;
        }
    }
    return 0;
}

void drawData(uint8_t myData[]) { // берет данные и выводит их
  for (uint8_t j = 0; j <= 7; j++) {

    // заземлите latchPin и удерживайте низкий уровень, пока вы передаете
    PORTD = PORTD & ~(1<<latchPin); // низкий уровень фиксации штифта

    //сдвигаем данные в regs

    shiftOut(~(1<<j)); //ряд основания. Например, 10111111 означает, что включена 6-я строка.

    shiftOut(myData[j]);  //выводит данные столбца.

    // вернуть защелку в высокий уровень, чтобы сигнализировать чипу, что он
    // больше не нужно прослушивать информацию
    PORTD = PORTD | (1<<latchPin); //фиксация штифта вверху
  }
}

void shiftOut(uint8_t myDataOut) { // сдвигает 8 бит для регистрации

  // Сначала сдвигается 8 бит LSB,
  // по восходящему фронту часов,
  //часы простаивают на низком уровне

//настройка внутренней функции
  uint8_t i=0;
  int pinState;

// подготавливаем сдвиговый регистр для побитового сдвига
  PORTD = PORTD & ~((1<<dataPin) | (1<<clockPin)); //часы и усилитель; контакт данных низкий

  // для каждого бита в myDataOut
  for (i=0; i<=7; i++)  {

    PORTD = PORTD & ~(1<<clockPin); //минимум часов

    //если значение передано в myDataOut и результат битовой маски
    // тогда верно... поэтому, если мы находимся в i=6 и наше значение равно
    // %11010100 код сравнит его с %01000000
    // и переходит к установке pinState в 1.
    if ( myDataOut & (1<<i) ) {
      pinState= 1;
    }
    else {  
      pinState= 0;
    }

    // Устанавливает контакт в HIGH или LOW в зависимости от pinState
    PORTD = PORTD | (pinState<<dataPin);

    // регистрация сдвигов битов при восходящем ходе тактового вывода
    PORTD = PORTD | (1<<clockPin);

    // обнуляем вывод данных после сдвига, чтобы предотвратить просачивание
    PORTD = PORTD & ~(1<<dataPin);
  }

  // остановить смещение
  PORTD = PORTD & ~(1<<clockPin);
}

void sendmsg (char *s) { //создать последовательное сообщение
    qcntr = 0;    /*preset indices*/
    sndcntr = 1;  /*set to one because first character already sent*/

    queue[qcntr++] = 0x0d;   /*put CRLF into the queue first*/
    queue[qcntr++] = 0x0a;
    while (*s)
    queue[qcntr++] = *s++;   /*put characters into queue*/

    UDR0 = queue[0];  /*send first character to start process*/
}


/*this interrupt occurs whenever the */
/*USART has completed sending a character*/

ISR(USART_TX_vect) {
    /*send next character and increment index*/
    if (qcntr != sndcntr)
        UDR0 = queue[sndcntr++];
}

ISR(TIMER0_OVF_vect) { // переполнение каждые 16 мс
    TCNT0 = 6; // 256-250 = 6
    ++updateTimeCount;
    if(updateTimeCount >= updateDelay) { // через 208 мс обновить состояние игры
        updateFlag = 1; // Вызов для обновления игры
        updateTimeCount = 0;

    }

    refreshFlag = 1; // Вызов для обновления экрана
}

void init_ports() {
    DDRD = (1<<latchPin) | (1<<clockPin) | (1<<dataPin); //включить контакты
    PORTD = 0; //устанавливаем низкий уровень на всех выводах
    DDRB = (1<<5); //включаем встроенный светодиод для отладки
    PORTB = 0; //устанавливаем низкий уровень на всех выводах
}
void init_USART() {
    UCSR0B = (1<<TXEN0) | (1<<TXCIE0); //включить передатчик и прерывание tx
    UBRR0 = 103; // бод 9600
}
void init_timer0() {
    TCCR0A = 0;
    TIMSK0 = (1<<TOIE0);    // разрешить прерывание
    TCCR0B = (5<<CS00); // предскаляр 1024. 16 МГц/1024 = 15625 Гц = 0,064 мс
    // Для частоты обновления 62,5 Гц 16 мс/0,064 мс = 250
    TCNT0 = 6; // 256-250 = 6
}

Я действительно не могу сказать, в чем проблема, и мне не на что смотреть. Я думаю, что проблема в функциях drawData и shiftOut, потому что именно там все данные смещаются. Я протестировал прерывание от таймера 0, переключив встроенный светодиод, и все работает нормально.
Порты переключаются слишком быстро, чтобы сдвиговые регистры могли получать данные? Где-то смысловая ошибка? Вы видите какие-то проблемы. Спасибо за любую помощь!

, 👍-1

Обсуждение

Насчет «_Я думаю, что проблема в_»: лучше быть уверенным. Разбейте проблему на более мелкие части и проверяйте их одну за другой. Писать полную программу до начала тестирования — плохая идея., @Edgar Bonet

@EdgarBonet Я провел несколько тестов со светодиодами, и флаг обновления не устанавливается на единицу, когда таймер timer0 переполняется. Это означает, что drawData() никогда не вызывается. Но я проверил updateFlag, и он работает. Итак, я попытался вызвать drawData(), когда updateFlag один, но все равно ничего. Так что я не совсем уверен, где проблема., @sparpo

это может помочь ...... https://electronics.stackexchange.com/questions/183299/atmega-16-timer0-with-interrupt ...... http://www.avr-asm-tutorial. сеть/avr_en/micro_beginner/6_Led_Int/6_Led_Int.html, @jsotola

Как сказал Эдгар Боне: начните с максимально простого кода для тестирования/отладки базовой функциональности. Сначала заставьте это работать, прежде чем переходить к более сложному коду. Посмотрите, можно ли воспроизвести вашу проблему с (гораздо) более простым кодом., @StarCat

почему вы используете прямой доступ к регистру, а не цифровую запись и функцию Arduino shiftOut?, @Juraj

@juraj Я использую код C, а не Arduino, @sparpo

Возможно, вы захотите добавить некоторые задержки в функцию переключения. Вы устанавливаете вывод данных на правильное значение, устанавливаете вывод часов на высокий уровень, а затем устанавливаете вывод данных обратно на НИЗКИЙ. Все сразу друг за другом. Я бы начал добавлять несколько светодиодов к выводам данных и синхронизации, просто чтобы увидеть, передаются ли какие-либо данные. С этими высокоскоростными сигналами я подключаю 5 В, чтобы увидеть, является ли сигнал когда-либо НИЗКИМ, или подключаю наоборот к GND, чтобы увидеть, является ли сигнал когда-либо ВЫСОКИМ. С текущим светодиодом высокой яркости даже очень короткий сигнал все еще виден, хотя может проявляться только как свечение прицела., @Gerben

Начните с модификатора volatile для переменных, доступных как в ISR, так и в основной программе., @KIIV

@KIIV Да, спасибо! Это решило проблему с refreshFlag. Я предполагаю, что компилятор интерпретировал это как if(0) . Однако на дисплее все еще нет вывода, поэтому я все еще пытаюсь это отладить., @sparpo

@Gerben Итак, на контактах данных и тактовых импульсах есть небольшое свечение без какой-либо дополнительной задержки, поэтому данные определенно передаются. Однако, когда я пишу это, я думаю, что нашел проблему., @sparpo

@KIIV @Gerben @Edgar_Bonet @StarCat @jsotola @Juraj Данные не были отправлены, поскольку перед циклом while была вызвана функция clearData(). Я чувствую себя таким глупым! Однако возникла проблема с refreshFlag, которую заметил @KIIV! Спасибо за вашу помощь!, @sparpo


1 ответ


Лучший ответ:

1

Спасибо всем, кто прокомментировал мой вопрос, с вашей помощью мне удалось решить проблему!

Первая проблема заключалась в том, что компилятор интерпретировал if(refreshFlag) {...} как if(0) {...}, поэтому данные никогда не выводились на дисплей.

Это происходит из-за того, что компилятор не видит, что refreshFlag может быть установлен в 1 в процедуре обслуживания прерывания timer0. С его точки зрения я написал оператор if, который никогда не запускается.

Как указал @KIIV, если ключевое слово volatile используется при объявлении refreshFlag, это сообщает компилятору, что эта переменная может измениться в любое время.

Вторая и более досадная проблема заключалась в том, что я забыл удалить вызов функции clearData(), которая очищает массив данных. Так что я просто ничего не выводил в регистры!

Вот как должны выглядеть фиксированные переменные и main:

int updateTimeCount;
int updateDelay; 
volatile int updateFlag; // Флаг обновления состояния игры
volatile int refreshFlag;// Флаг обновления отображения


uint8_t data[] = {0b00000001,
                  0b01111110,
                  0b11000110,
                  0b01001010,
                  0b01010010,
                  0b11100010,
                  0b01111110,
                  0b01000010};

int main(void) {  
    //init_USART();// Серийный номер для целей отладки
    init_ports();
    init_timer0();

    sei();

    PORTB = 0; //PortB для отладки
    updateTimeCount = 0;
    updateDelay = 13; // Игра обновляется каждые 13 * 16 мс = 208 мс.
    updateFlag = 0; 
    refreshFlag = 0; // Отображение обновлений каждые 16 мс

    while (1) {

        if(updateFlag) { //обновляет состояние игры
            PORTB = (1<<5); // светодиод отладки
            updateFlag = 0;     
        }

        if(refreshFlag) { //обновляет дисплей
            drawData(data); //массив выходных данных

            refreshFlag = 0;
        }
    }
    return 0;
}
,

Можно принять свой собственный ответ как решение проблемы. Вам придется подождать 48 часов, прежде чем вы сможете его принять., @VE7JRO