Вот шаблон, который я пытался создать для матричного дисплея 8x8, чтобы каждый горящий светодиод представлял собой определенный счетчик:
00000001
00000010
00000100
00001000
00010000
00100000
01000000
10000000
10000001
10000010
10000100
10001000
10010000
10100000
11000000
11000001
...
и так далее..... пока все биты не станут 1.
Мне удалось сдвинуть биты справа налево, так что 0x01
превратилось в 0x80
, но как мне удержать старший бит на своем месте и начать сдвигать новый бит из младшего бита (с правой стороны), как описано выше?
void MAX7219_SendData(uint8_t address, uint8_t data) {
uint8_t txData[2] = {address, data};
MAX7219_CS_Low();
HAL_SPI_Transmit(&MAX7219_SPI_INSTANCE, txData, 2, HAL_MAX_DELAY);
MAX7219_CS_High();
}
Я использую вышеуказанную функцию для связи с микросхемой MAX7219 через SPI.
Я использую матричный дисплей MAX7219 для отображения шаблона.
MAX7219 содержит 8 регистров, которые управляют восемью строками дисплея (дисплей 8x8).
Например, если я отправлю 00000001
вместе с адресом регистра в виде 16-битного слова, загорится верхний правый светодиод. Итак, если я отправлю двоичную последовательность, как показано выше, будет выглядеть так, как будто свет проходит через все светодиоды. Как только верхняя строка заполнена, то же самое должно начаться и в следующей строке и так далее…
По сути, матрица покажет все, что находится в ее регистрах (каждый из 8 бит). Частота обновления составляет около 500 Гц, как указано в техническом описании, если это может быть полезно.
С какими данными вы имеете дело? У вас есть счетчик и вам нужно сгенерировать соответствующий шаблон? У вас есть текущий шаблон и вам нужно создать шаблон-преемник? Пожалуйста, отредактируйте свой вопрос, чтобы показать MCVE
Ни один SPI не встроенный вообще не имеет никакого отношения к вашему вопросу. Это чисто вопрос алгоритма битового шаблона.
Пожалуйста, отредактируйте и покажите свой нерабочий код и удалите функцию MAX7219_SendData
, которая совершенно не связана с вашим вопросом.
«Мне удалось сдвинуть биты справа налево, так что 0x01 становится 0x80, но как мне удержать старший бит на своем месте и начать сдвигать новый бит от младшего бита (с правой стороны), как описано выше?»: возможно использовать две переменные? Я предлагаю вам начать разработку алгоритма с листа бумаги и карандаша.
Существует 37 возможных шаблонов. Просто жестко закодируйте таблицу поиска.
@n.m.couldbeanAI в соответствии с моим кодом ниже (см. демо-версию в реальном времени). Я считаю, что это всего лишь 36 шаблонов (потому что OP не нуждается в нулях.)
Я отредактировал вопрос, чтобы получить лучшую картину..
Вы можете использовать другую переменную (val_base
в приведенном ниже коде) для хранения базового шаблона, к которому вы будете применять сдвиги текущего бита.
В приведенном ниже коде я использовал два вложенных цикла: один для перебора групп шаблонов (в каждой группе определенные биты слева равны 1), и один для сдвига текущего бита.
Обратите внимание, что количество итераций во внутреннем цикле зависит от внешнего цикла (поскольку каждый раз, когда мы переходим к следующей группе, должно применяться меньше сдвигов).
#include <stdio.h>
#include <stdint.h>
int main() {
uint8_t val_base = 0;
for (int iGroup = 0; iGroup < 8; ++iGroup) { // iterate over the 8 groups of patterns
for (int iShift = 0; iShift < (8-iGroup); ++iShift) { // iterate on the patterns in each group
uint8_t val = (1 << iShift) + val_base;
printf("%08B\n", val);
}
val_base |= (1 << (8 - iGroup - 1)); // for next iteration
}
}
Выход:
00000001
00000010
00000100
00001000
00010000
...
11111010
11111100
11111101
11111110
11111111
Примечание:
В рабочем коде самое эффективное, что вы можете сделать, — это, вероятно, создать жестко запрограммированную таблицу поиска со всеми шаблонами.
Вы все равно можете использовать приведенный выше код, чтобы легко создать эту таблицу поиска.
Совет от профессионала: в более новом стандарте C или в более новой версии glibc вы можете использовать printf("%08B\n", val)
вместо print_uint8_as_binary
.
@KamilCuk, здорово! обновил мой ответ.
Я решил найти подвижный бит - установить бит, слева от которого есть неустановленный бит, и, если его нет, обработать крайние случаи (установить младший бит или перевернуть).
Вот реализация и тест:
char bitpattern_increment(char bits) {
char retval_new_pattern = 0;
int movable_bit_pos = 0;
if (bits == 0) {
retval_new_pattern = 0x01;
goto end;
}
for (; movable_bit_pos < 7; movable_bit_pos++) {
if ((bits >> movable_bit_pos) & 0x01) {
if (((bits >> (movable_bit_pos + 1)) & 0x01) == 0x00) {
retval_new_pattern = bits & ~(0x01 << movable_bit_pos);
retval_new_pattern |= (0x01 << (movable_bit_pos + 1));
goto end;
}
}
}
if (bits == (char)0xFF) {
retval_new_pattern = 0x00;
goto end;
}
retval_new_pattern = bits | 0x01;
end: return retval_new_pattern;
}
Протестировал это следующим образом (начиная с 0xFF, чтобы легко проверять крайние случаи):
char bitpattern = 0xFF;
for (int i = 0; i < 40; i++) {
bitpattern = bitpattern_increment(bitpattern);
std::bitset<8> bitpattern_set(bitpattern);
std::cout << bitpattern_set << std::endl;
}
Тестовый вывод:
00000000
00000001
00000010
00000100
00001000
00010000
00100000
01000000
10000000
10000001
10000010
10000100
10001000
10010000
10100000
11000000
11000001
11000010
11000100
11001000
11010000
11100000
11100001
11100010
11100100
11101000
11110000
11110001
11110010
11110100
11111000
11111001
11111010
11111100
11111101
11111110
11111111
00000000
00000001
00000010
вопрос помечен как C, а не C++ (std::bitset
, std::cout
). Также использование goto
— это запах кода.
1) Неважно, C это или C++, тот код, который интересует автора, является валидным C-кодом. Для тестирования я использовал C++. Он будет работать с C. 2) Утверждение «Использование goto
— это запах кода» само по себе является нонсенсом. Код с goto может читаться быстрее и проще, если он реализован правильно, а также в некоторых случаях упрощает управление памятью. Как и у любого инструмента, у него есть правильные и неправильные варианты использования. 3) Автор волен взять мой код и адаптировать его по своему вкусу.
«запах кода» не означает, что он всегда плох. Это просто означает, что обычно есть лучшие решения. Я думаю, это относится и к goto
в целом. Я с трудом могу припомнить случаи, когда его использование улучшало код. Но, конечно, вы можете использовать его по своему усмотрению.
Я предложу другой зацикленный ответ, основанный на способе перемещения растровых спрайтов старого стиля. Я думал, что это было довольно здорово, пока не увидел невероятный ответ Эрика. Таблица поиска IRL самая быстрая.
#include <stdio.h>
void shiftbits()
{
int bits, k;
k = bits = 0;
printf("%2i : %08B \n", ++k, bits);
for (int i = 8; i; i--)
{
int mask = 3;
bits |= 1;
printf("%2i : %08B \n", ++k, bits);
for (int j = i-1 ; j; j--)
{
bits ^= mask;
mask = mask + mask;
printf("%2i : %08B \n", ++k, bits);
}
}
}
int main()
{
shiftbits();
return 0;
}
Его неэлегантность заключается в том, что printf встречается один раз в каждом из вложенных циклов.
Я смутно припоминаю счетчики событий на основе газоразрядных ламп, которые использовали подобные правила еще в далеком прошлом (конец 60-х?). Толстые вакуумные трубки, имевшие кольцо из круглых стержневых электродов, смотрящих по краям.
Есть и другой способ написать это, который для меня скорее механический, чем мистический. (Мой мозг с трудом отслеживает, какие переменные увеличиваются, какие уменьшаются и как их значения связаны друг с другом.)
Следующее кажется более прямым выражением желаемой цели. Вкусы различаются.
#include <stdio.h>
#include <stdint.h>
int main( void ) {
uint8_t pwr = 0x80, val = 0, trv = 0;
do {
do {
printf( "%08B\n", val | trv );
trv = trv ? trv << 1 : 1; // shift left OR seed first '1'
} while( trv < pwr ); // until shifted maximally
val |= trv, trv = 0; // burn it in and reset to zero
} while( ( pwr >>= 1 ) != 0 ); // shifting left boundary to the right
printf( "%08B\n", val ); // spill the final version
return 0;
}
Это можно увидеть бегущим здесь
Код для генерации битового шаблона будет гораздо более уместен для вашего вопроса, чем код для его отправки через SPI. что ты уже испробовал?