Данные в двух 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, переключив встроенный светодиод, и все работает нормально.
Порты переключаются слишком быстро, чтобы сдвиговые регистры могли получать данные? Где-то смысловая ошибка? Вы видите какие-то проблемы. Спасибо за любую помощь!
@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
- Не удается получить OC1B (контакт 10) для вывода
- Регистры ввода-вывода SAM3X8E (Arduino Due)
- 4-битный счетчик вверх и вниз
- 74HC595 регистр сдвига не работает
- Почему некоторые контакты Arduino Nano (D3, D4, A3, A4, A6, A7) не могут быть установлены на высокий уровень?
- Светодиод Arduino PWM с замиранием в сборке
- Как погасить светодиод за определенное время с помощью FastLED
- Быстрая загрузка (WS2812B) и неисправимая проблема
Насчет «_Я думаю, что проблема в_»: лучше быть уверенным. Разбейте проблему на более мелкие части и проверяйте их одну за другой. Писать полную программу до начала тестирования — плохая идея., @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