Я застрял с Python и светодиодной лентой. Светодиодная лента с чипом WS2801 и адресацией через SPI организована в виде такой матрицы:
---- ---- ---- ----
140 | | | | | | | | 15
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
155 | | | | | | | | 0
---- ---- ----
Каждая черточка представляет собой один светодиод (один пиксель). В столбце 16 пикселей, что составляет 8 строк.
Нумерация начинается снизу справа. Таким образом, самый правый столбец начинается с индекса 0 и заканчивается индексом 15. Четыре пикселя между строками не будут освещены. Таким образом, второй самый правый столбец идет сверху вниз с индексом 20 вниз с индексом 35. Самый левый столбец находится в диапазоне от 140 вверху до 155 внизу.
Что я хочу сделать, так это визуализировать спектр песни во время ее исполнения. Низкие частоты должны отображаться в левых столбцах, более высокие частоты в правых столбцах.
Мой код основан на PixelPi (https://github.com/scottjgibson/PixelPi). Я опускаю часть с БПФ, потому что проблема не в ней.
# Each pixel consumes 3 bytes
PIXEL_SIZE = 3
# Lots of colors
BLACK = bytearray(b'\x00\x00\x00')
AQUA = bytearray(b'\x00\xff\xff')
AQUAMARINE = bytearray(b'\x7f\xff\xd4')
def filter_pixel(input_pixel, brightness):
output_pixel = bytearray(PIXEL_SIZE)
input_pixel[0] = int(brightness * input_pixel[0])
input_pixel[1] = int(brightness * input_pixel[1])
input_pixel[2] = int(brightness * input_pixel[2])
output_pixel[0] = gamma[input_pixel[0]]
output_pixel[1] = gamma[input_pixel[1]]
output_pixel[2] = gamma[input_pixel[2]]
return output_pixel
# Initialize LED strip matrix
height = 16
base = [155, 120, 115, 80, 75, 40, 35, 0]
# Do the indexes of this column go from bottom to top?
up = [False, True, False, True, False, True, False, True]
color = [NAVY, RED, MAROON, DARKBLUE, DARKCYAN, PALEGREEN, YELLOWGREEN, YELLOW]
# Output matrix, filled with black pixels
empty_output = bytearray(args.num_leds * PIXEL_SIZE + 3)
for led in range(args.num_leds):
empty_output[led * PIXEL_SIZE:] = filter_pixel(BLACK, 1)
current_color = bytearray(PIXEL_SIZE)
corrected_color = bytearray(PIXEL_SIZE)
while True: # (Actually while song is playing)
# Returns an array of length 8 with values between 0 and 4095
matrix = calculate_levels(matrix, weighting, data, CHUNK_SIZE, sample_rate)
# Copy the matrix with only black pixels. Copying seems to be faster than resetting all not needed pixels to black
pixel_output[:] = empty_output
for col in range(len(base)):
current_color[:] = color[col][:]
# Do some gamma correction
corrected_color[:] = filter_pixel(current_color[:], 1)
# Each column is 16 pixels high. The maximum value of the FFT to be returned for each column is 4095. 4096 / 256 = 16
lighted_height = round(matrix[col]/float(1 << 8), 2)
for row in range(max(16, int(lighted_height) + 1)):
pixel_index = base[col] + row if up[col] == True else base[col] - row
pixel_index = pixel_index * PIXEL_SIZE
if (row < int(lighted_height)):
# Pixel's brightness in 100%
pixel_output[pixel_index:] = corrected_color[:]
elif (row <= int(lighted_height) and row + 1 > int(lighted_height)):
# Pixel's brightness is between 0 and 1
pixel_output[pixel_index:] = filter_pixel(current_color[:], lighted_height - int(lighted_height))
#print "[col:", col, ", row:", row, "] : ", pixel_index, "lighted_height:", lighted_height, "int(lighted_height)", int(lighted_height), "lighted:", lighted
# As I uncomment these two lines, at least all pixels on the other columns are displayed.
#spidev.write(pixel_output)
#spidev.flush()
spidev.write(pixel_output)
spidev.flush()
Проблема в том, что этот код освещает только самый правый столбец (от 0 до 15). Все остальные столбцы кажутся черными.
Когда я помещаю spidev.write(pixel_output)
и spidev.flush()
в цикл col таким образом, что pixel_output записывается для каждого столбца, по крайней мере некоторые индикаторы в других столбцах загораются. Однако они каким-то образом появляются довольно случайным образом, и звук больше не является гладким.
Кстати, светодиодная лента отлично справляется с такими примерами PixelPi, как замирание и погоня. Может ли это быть связано с какими-то свойствами микросхемы WS2801, о которых я не знаю? Или я не правильно рассчитал матрицу pixel_output?
Обновление: еще одна странность:
i = 0
x = 0
while x < 160:
if i != 0 and i % 16 == 0:
x = x + 4
pixel_index = x * PIXEL_SIZE
pixel_output[pixel_index:] = filter_pixel(WHITE, 1)
i = i + 1
x = x + 1
print "i, x", i, x
time.sleep(0.1)
spidev.write(pixel_output)
spidev.flush()
Это должно на самом деле подсветить пиксель 0 до последнего и оставить 4 пикселя после 16 выполнений цикла. Однако он не пропускает ни одного пикселя и, таким образом, останавливается до того, как будет достигнут последний пиксель.