#assembly #embedded #led
Вопрос:
TL:DR версия вопроса: мой PRU иногда задерживает время и сбрасывает устройства вниз по течению. Как мне понять, почему?
Я использую процессор AM335x с двумя встроенными процессорами для управления 32 полосками светодиодов RGBW. Программа, работающая на процессоре ARM, подготавливает кадр и загружает его в оперативную память PRU. PRU работает на холостом ходу до тех пор, пока процессор не установит флаг, после чего PRU захватывает данные кадра по 32 байта за раз и разбрасывает их по битам.
Теперь ясно, что мы используем PRU, потому что он может гарантировать время. Я должен переключать высокое/низкое время, которое составляет порядка наносекунд. И это работает, по крайней мере, большую часть времени. Но есть моменты, когда время запаздывает, и это отбрасывает данные, идущие по проводу, и вызывает мерцание светодиодов. Это определенно проблемы со временем — я подключил логический анализатор и могу определить ситуации, когда он тратит 1083 нс на то, что должен был сделать за 600 нс.
Вот основной код в сборке clpru. Существует дополнительный включаемый файл, который просто обрабатывает сопоставление выводов GPIO для регистрации адресов и присваивает нескольким регистрам псевдонимы, он не содержит логики.
Фактическая логика работы начинается здесь, где мы устанавливаем регистр так, чтобы он указывал на начало кадра, а затем считываем счетчик текущего цикла в регистр. (Я использую достаточное количество псевдонимов и макросов, просто чтобы попытаться сделать это читабельным).
Затем мы берем 32 байта и засовываем их в регистры (скретч-это регистр 11, поэтому 32 байта распределяются по r11-r18).
Затем мы вынимаем первый бит из каждого байта и выравниваем его с выходным выводом. Это большой блок операций, выполнение которого занимает до 320 минут (две инструкции в макросе, 32 выполнения указанного макроса).
Как только все будет готово, мы подождем, пока с последнего READ_TIME sleeper
звонка не пройдет 550 минут. WAIT_NS
считывает счетчик циклов в цикле занятости.
Схема отправки для наших светодиодов заключается в том, чтобы отправлять ВЫСОКО. Для нуля мы держим около 250нс (300нс-это спецификация). Для одного мы держим около 600 тысяч. Затем мы держимся до тех пор, пока не пройдет в общей сложности около 1200 минут.
Мы делаем все это по сравнению BASE_WAIT
с предыдущими 550нс. В конце BIT_LOOP
мы сбрасываем спальное место.
Опять же, в 99% случаев (больше!) это работает нормально. Но раз в несколько сотен кадров тайминги сбиваются. Что-то занимает слишком много времени, и я не могу понять почему. И я определенно не понимаю, почему почти все время все в порядке, а потом вдруг начинает дрожать.
Я заметил, что если происходит дрожание, это почти наверняка сбивает время при извлечении следующего байта из оперативной памяти PRU. Согласно документам, доступ к PRU DRAM является детерминированным. В верхней и нижней части каждого кадра я получаю доступ к DDR, просто чтобы проверить флаги, общие с программой ARM.
Поэтому мой вопрос меньше «что не так» и больше «как мне выяснить, что не так» или «что я не понимаю в гарантиях PRU в реальном времени»?
Я пытался подключить prudebug, но не могу уловить моменты, когда он там дает сбои. У меня есть сотни мегабайт CSV-файлов, выводимых моим логическим анализатором, но все, что мне действительно говорит, — это то, что каждые несколько сотен тысяч бит он по какой-то причине отключается. Иногда он остается стабильным в течение миллионов бит. Но затем внезапно время PRU сбивается, но только на несколько сотен наносекунд. Достаточно, чтобы иметь значение, но не настолько, чтобы было очевидно, что происходит не так.
Комментарии:
1. Основываясь на других разговорах, я подозреваю, что основная проблема заключается в том, что доступ к выводам GPIO должен проходить через OCP, а это означает, что задействован процессор ARM.
2. Есть ли у PRU свои собственные GPIO? Ты можешь ими воспользоваться? Может ли второй ПРУ проверить первый (для отладки пупозов)? Передайте изменяющееся значение после каждой строки в вашей программе другому PRU, который проверяет, не замерзло ли значение.
3. У PRU есть свои собственные GPIO, но их недостаточно . Цель состоит в том, чтобы управлять 32 каналами.
4. В PRU есть счетчик команд, который можно активировать. Вы можете использовать один PRU для загрузки из памяти и хранения в GPIO, а другой (только с операциями, не связанными с памятью) для сохранения времени. Они оба могли общаться с xin/xout. Вы не смогли предотвратить задержку, но она не перейдет к более позднему выходу.