BitBanging протокол МГц с встроенным ассемблером Micro Python на PyBoard - PullRequest
0 голосов
/ 30 января 2020

Я пытаюсь использовать PyBoard v1.1 для управления протоколом для светодиодов Adafruit NeoPixel SK6812RGBW с использованием встроенного ассемблера Micro python.

Протокол

Как видно из связанной таблицы данных, один светодиод управляется путем сборки 4 8-битных значений rgbw. Каждый старший бит состоит из 0,6 млн. Аналогового максимума, за которым следует 0,6 млн. Цифрового нижнего уровня, а младший бит имеет отношение 0,3 старшего к 0,9 младшему. Это делает каждый бит данных, используемый в 4-байтовом значении цвета светодиода, равным 4 аналоговым битам по 0,3 мкс каждый или всего 128 бит по сравнению с 38,4 мкс. Поток байтов, отправляемый на первый светодиод, также содержит значения для всех последующих светодиодов, он передает все, кроме своего собственного, следующему и т. Д.

Реализация

Протокол может быть реализовано довольно легко с помощью интерфейса SPI Pyboard. Как только поток данных сгенерирован (эффективно 16 байтов на светодиод) и рассчитана скорость передачи (1 с / 0,3 мкс = около 3333333), нужно только создать экземпляр pyb.SPI и вызвать его метод send с байтами в качестве аргумента. .

Задача

Теперь к поставленной задаче: я хочу подключить три разные светодиодные полосы с одной платой PyBoard. Однако есть только 2 шины SPI. Так что после попытки побить протокол с помощью pyb.Pin и циклов я быстро понял, что это не сработает, минимальная скорость переключения составляла 54 мкс, что немного стесняется тех 0,3 мкс, которые мне нужны ...

Реализация V2

После некоторых шагов оптимизации я обратился к встроенному ассемблеру Micro python. Несколько часов спустя мне удалось переключить данный штифт со скоростью 23 нс, измеренной осциллографом. Это было здорово, и все, но мне не нужно было бездумно переключать контакты, мне нужно было переключать контакты в соответствии с потоком битов, следуя точному протоколу. Итак, еще пару часов спустя я закончил следующую реализацию:

@micropython.asm_thumb
def send_bits_on_x9(r0):
    # r0 0th word contains the data array address
    # r0 1st word contains length of data array

    # Store the GPIOB address in r3
    movwt(r3, stm.GPIOB)
    # Store the bit mask for PB6 (the pin X9)
    movw(r4, 1 << 6)

    # Load address into r5
    ldr(r5, [r0, 0])
    # Load array length into r6
    ldr(r6, [r0, 4])

    # Jump to condition evaluation
    b(loop_entry)

    # Main loop
    label(loop)

    # Get current "bit" word
    ldr(r0, [r5, 0])
    # Shift address to next "bit" for next time
    add(r5, r5, 4)

    # Evaluating the bit and toggling accordingly
    cmp(r0, 1)
    ite(eq)
    strh(r4, [r3, stm.GPIO_BSRRL])  # Turn LED on
    strh(r4, [r3, stm.GPIO_BSRRH])  # Turn LED off

    # Delay for a bit
    movwt(r7, 8)  # 20948000 cycles is about 1s
    label(delay)
    sub(r7, r7, 1)
    cmp(r7, 0)
    bgt(delay)

    # Eval loop; using data array length as initial counter value
    sub(r6, r6, 1)
    label(loop_entry)
    cmp(r6, 0)
    bgt(loop)

B6 - это имя процессора для вывода X9, которое я использую в качестве соединения для передачи данных со светодиодами.

Для запуска I встроил его в сценарий демонстрации python:

import array
import uctypes
import micropython
import stm

@micropython.asm_thumb
def send_bits_on_x9(r0):
    ...

send_buffer = array.array("i", [1, 0, 1, 1, 0, 0, 1, 0])
send_bits_on_x9(array.array("i", [uctypes.addressof(send_buffer), len(send_buffer)]))

Проблема

Это работало прекрасно, однако при использовании его вместо стримера SPI, глядя на светодиоды, иногда возникали артефакты видел каждую пару казней. Ниже приведено изображение, когда я смотрел на него с помощью осциллографа: журнал осциллографа с артефактом Как можно видеть, есть место, где по какой-то причине оно перестает переключаться ровно для 2 битов значения: отсутствующие фланги обозначены Это происходит на первый взгляд случайным образом в любой части потока битов, иногда начиная с восходящего фланга, иногда с падающего фланга.

Вопрос

Сейчас очевидно, мой вопрос, почему это произойдет. С SPI этого не происходит, хотя я предполагаю, что реализация C заботится о том, чтобы ничего не прерывало поток. Я пытался отключить сборщик мусора перед вызовом send_bits_on_x9 и повторно включить после, но это не помогло. Я также изменил количество циклов задержки, которые тоже ничего не изменили.

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

Теперь, если кто-нибудь может указать мне ресурс, отличный от официальной документации встроенного ассемблера или даже предоставить некоторую информацию , Я буду благодарен. Ура!

...