Оборудование: модуль hw
Модуль hw связывает программу на uLua с оборудованием устройства. Через него
можно читать входы, управлять выходами и светодиодами, измерять аналоговый
сигнал и частоту, обмениваться данными по UART или USB CDC и запускать
обработчики по времени.
Набор выводов зависит от устройства и его прошивки. Сначала сверяйтесь со схемой разъёма в документации своего устройства, а затем используйте эту страницу как справочник API.
Большинство команд ничего не возвращает. Недопустимый вывод, занятый аппаратный ресурс или неверный аргумент завершают программу ошибкой. Обработчики таймеров и входов должны быть именованными функциями верхнего уровня.
GPIO
hw.gpio_init(pin, mode)
Назначает выводу одну из ролей:
hw.inp— цифровой вход с подтяжкой вверх;hw.out— цифровой выход;hw.pwm— ШИМ;hw.adc— аналоговый вход;hw.capture— измерение частоты и коэффициента заполнения;hw.uart_tx,hw.uart_rx— UART;hw.ws2812— лента адресных светодиодов;hw.dht22— датчик DHT22.
hw.gpio_init(hw.p1, hw.out)
Перевод в hw.inp освобождает связанные с выводом ресурсы: измерение частоты,
наблюдение за уровнем, фоновый АЦП или DHT22. Это безопасный промежуточный шаг
перед сменой назначения:
hw.gpio_init(hw.p3, hw.inp)
hw.gpio_init(hw.p3, hw.pwm)
Прямые вызовы hw.pwm_init(), hw.uart_setup() и hw.ws2812_write() не всегда
могут освободить прежнюю роль. Если вывод занят, они завершаются ошибкой
pin busy.
hw.gpio_set(pin, value)
Устанавливает на цифровом выходе низкий уровень для 0 и высокий для любого
ненулевого значения.
hw.gpio_set(hw.p1, 1)
hw.gpio_get(pin) -> int
Возвращает текущий цифровой уровень: 0 или 1.
local level = hw.gpio_get(hw.p1)
hw.pin(index) -> pin
Преобразует числовой идентификатор в значение типа pin. Это требуется, если
номер получен из команды или цикла:
for i = 1, 5 do
hw.gpio_init(hw.pin(i), hw.out)
end
hw.pin(1)…hw.pin(9) соответствуют hw.p1…hw.p9, а hw.pin(0) —
hw.p10. В прошивке существуют и идентификаторы 10..13 для физических
выводов PA5, PA8, PA9 и PA10. Обычное число нельзя передать вместо
значения типа pin.
Реакция на изменение входа
hw.gpio_watch(pin, level, debounce_ms, func[, arg1[, arg2]]) -> int
Регистрирует функцию, которая будет вызвана, когда на цифровом входе устойчиво
появится уровень 0 или 1. Возвращённый идентификатор нужен для отмены.
hw.gpio_init(hw.p1, hw.inp)
function on_high(pin, level)
hw.print("pin=")
hw.print(pin)
hw.print(" level=" .. level .. "\n")
end
local watch_id = hw.gpio_watch(hw.p1, 1, 20, on_high)
while true do
hw.delay_ms(100)
end
Обработчик получает pin, level, а затем до двух пользовательских аргументов,
если они указаны. Ограничения:
- вывод должен находиться в режиме
hw.inp; - одновременно действует не более четырёх наблюдений;
- задержка подавления дребезга измеряется с шагом 1 мс;
- на одном выводе можно зарегистрировать только одно наблюдение;
- DHT22 и наблюдения используют линии внешних прерываний. Два физических
вывода с одинаковым номером линии, например
PA1иPB1, нельзя использовать для них одновременно, даже если это разные выводы.
hw.gpio_unwatch(watch_id)
Отменяет наблюдение с указанным идентификатором.
ШИМ
hw.pwm_init(pin, frequency_hz)
Запускает ШИМ с частотой 1..20000 Гц. Поддерживаются hw.p3, hw.p7,
hw.p2 и hw.p6.
Все четыре канала используют таймер TIM2. Если повторно вызвать pwm_init() с
другой частотой, она изменится у всех активных каналов; в момент перенастройки
возможен короткий выброс на выходе.
hw.pwm_set(pin, duty_percent)
Задаёт коэффициент заполнения от 0 до 100 процентов.
hw.gpio_init(hw.p3, hw.pwm)
hw.pwm_init(hw.p3, 1000)
hw.pwm_set(hw.p3, 50)
Аналого-цифровой преобразователь
hw.adc_start(pin[, hz])
Запускает фоновое измерение с частотой 1..1000 Гц. Если частота не указана,
используется 10 Гц. Поддерживаются hw.p1…hw.p7; разрешение — 10 бит,
результат лежит в диапазоне 0..1023.
Одновременно измеряется только один аналоговый вход. Новый вызов переносит АЦП
на другой вывод, а прежний переводит в hw.inp.
hw.adc_start(hw.p5, 20)
hw.adc_read(pin) -> int
Возвращает последний результат фонового измерения. Сначала вызовите
adc_start(). Если первое измерение ещё не завершилось, функция выдаст ошибку
adc rdy.
hw.adc_start(hw.p5)
hw.delay_ms(150)
local value = hw.adc_read(hw.p5)
Измерение частоты и коэффициента заполнения
Сначала назначьте выводу режим hw.capture:
hw.gpio_init(hw.p5, hw.capture)
hw.delay_ms(200) -- пауза должна быть длиннее ожидаемого периода сигнала
local frequency = hw.capture_freq(hw.p5)
local duty = hw.capture_duty(hw.p5)
hw.capture_freq(pin) -> int
Возвращает частоту входного сигнала в герцах.
hw.capture_duty(pin) -> int
Возвращает коэффициент заполнения в процентах.
Измерение поддерживают hw.p5, hw.p4 и hw.p9. Доступно два аппаратных
слота. p4 и p5 используют одни и те же каналы TIM3, поэтому одновременно
включить их нельзя; p9 можно сочетать с любым из них. Пока не получены полный
период и импульс, чтение завершается ошибкой cap empty.
UART
hw.uart_setup(baud, bits, parity, stop)
Настраивает UART2 и автоматически назначает hw.p2 передатчиком, а hw.p6
приёмником. Поэтому отдельные gpio_init() обычно не нужны.
hw.uart_setup(115200, 8, hw.parity_none, 1)
Параметры:
bits— 7 или 8 информационных бит;parity—hw.parity_none,hw.parity_evenилиhw.parity_odd;stop— 1 или 2 стоп-бита;- при частоте контроллера 48 МГц допустима скорость примерно
733..921600бод.
Девятибитные данные не поддерживаются. В режиме 7 бит передатчик отбрасывает
старший бит, а приёмник возвращает значения 0..127.
Приёмный и передающий кольцевые буферы вмещают по 128 байт.
Повторный uart_setup() очищает оба. Если программа не успевает читать вход,
лишние байты теряются; это видно по счётчикам uart_ovr и uart_rx_drops в
пакете диагностики.
hw.uart_write(str)
Передаёт строку. Байты уходят из кольцевого буфера по прерыванию, но функция возвращается только после завершения всей передачи.
hw.uart_available() -> int
Возвращает число принятых и ещё не прочитанных байтов.
hw.uart_read() -> int
Возвращает следующий байт или -1, если входной буфер пуст.
Для протокола из строк можно использовать более высокоуровневый
модуль cmd.
WS2812
hw.ws2812_write(pin, data)
Управляет лентой WS2812 на hw.p4. Каждый байт строки — индекс в прошитой
палитре из 256 цветов и задаёт цвет одного светодиода. За вызов можно передать
не более 64 индексов.
hw.gpio_init(hw.p4, hw.ws2812)
hw.ws2812_write(hw.p4, "\xC4\x2E\x15")
Этот пример включает красный, зелёный и синий светодиоды. Это не строка из RGB-троек: одному светодиоду соответствует ровно один байт.
Палитра устроена так:
0выключает светодиод,1..15задают основные цвета;16..231образуют куб RGB с шестью уровнями каждого компонента:index = 16 + 36 × r + 6 × g + b, гдеr,gиbлежат в0..5;232..255— шкала серого от тёмного к светлому.
DHT22
hw.dht_temp(pin) -> int
Возвращает температуру в десятых долях градуса Цельсия. Например, 235
означает 23,5 °C.
hw.dht_hum(pin) -> int
Возвращает относительную влажность в десятых долях процента.
hw.dht_ts(pin) -> int
Возвращает время последнего успешного измерения в миллисекундах или 0, если
данных ещё нет.
hw.gpio_init(hw.p6, hw.dht22)
local last_ts = 0
while true do
local ts = hw.dht_ts(hw.p6)
if ts ~= 0 and ts ~= last_ts then
last_ts = ts
local temperature = hw.dht_temp(hw.p6)
local humidity = hw.dht_hum(hw.p6)
hw.print(temperature .. " " .. humidity .. "\n")
end
hw.delay_ms(100)
end
Датчик опрашивается в фоне, поэтому функции возвращают последнее сохранённое
значение. Явный gpio_init() необязателен: первый вызов hw.dht_*() сам
назначит вывод. Одновременно поддерживается один DHT22; обращение к другому
выводу переносит конфигурацию и очищает прежние данные.
DHT22 можно подключить к любому допустимому выводу, если его линия внешнего
прерывания не занята функцией gpio_watch().
USB CDC
Эти функции работают, пока USB CDC находится в консольном режиме.
Входной консольный буфер вмещает 128 байт; непрочитанный избыток учитывается в
счётчике cdc_rx_drops.
hw.cdc_write(str)
Передаёт строку в USB-консоль.
hw.cdc_available() -> int
Возвращает число доступных входных байтов.
hw.cdc_read() -> int
Возвращает следующий байт или -1, если данных нет.
hw.cdc_write("ready> ")
while hw.cdc_available() == 0 do
hw.delay_ms(1)
end
return hw.cdc_read()
Время
hw.delay_ms(ms)
Приостанавливает программу на uLua на указанное число миллисекунд. Прошивка при этом не выполняет пустой цикл ожидания.
hw.uptime() -> int
Возвращает число секунд с запуска. Счётчик всегда неотрицателен и обнуляется примерно раз в 4,25 года.
hw.now() -> int
Возвращает миллисекундный счётчик, усечённый до 28 бит. Он проходит полный цикл
примерно за 3,1 суток и становится отрицательным во второй половине цикла.
Поэтому значения now() нельзя напрямую сравнивать как обычные числа.
hw.now_delta(t0) -> int
Вычисляет прошедшее время с учётом переполнения hw.now().
local started = hw.now()
-- работа
local elapsed = hw.now_delta(started)
Однозначно измеряется интервал меньше 2²⁷ мс — около 37 часов. Для большего
или ошибочного интервала функция возвращает 0x07FFFFFF.
Таймеры
hw.timer_after(ms, func[, arg1[, arg2]]) -> int
hw.timer_every(ms, func[, arg1[, arg2]]) -> int
hw.timer_cancel(timer_id)
timer_after() вызывает функцию один раз, timer_every() — периодически,
timer_cancel() отменяет таймер. Одновременно работают два таймера; задержка
не превышает 65 535 мс. Обработчик должен быть именованной функцией верхнего
уровня и может получить до двух аргументов.
Таймеры существуют только пока выполняется текущая программа на uLua. Если основная функция завершится, ожидающие вызовы будут отменены.
Подробности и примеры: «Таймеры».
Встроенные светодиоды
hw.rled(true) -- включить красный
hw.gled(false) -- выключить зелёный
Физическая логика светодиода скрыта внутри прошивки: true всегда означает
«включить».
Вывод и диагностика
hw.print(value)
Печатает число, логическое значение, nil или строку в USB-консоль. Перевод
строки автоматически не добавляется.
hw.print("value=" .. 42 .. "\n")
hw.stats() -> strconst
Возвращает служебную строку в нижнем регистре с шестнадцатеричной записью пакета STATS v4. Она предназначена для машинной диагностики, а не для показа пользователю. В пакете находятся версия формата, состояние виртуальной машины, время работы, UID контроллера, использование памяти сохранённой программы, счётчики потерь CDC/UART, версия ABI и версия прошивки.
Ненулевые счётчики uart_ovr и uart_rx_drops означают потерю входных байтов
UART. cdc_tx_drops и cdc_rx_drops показывают потерю сообщений или байтов
USB CDC из-за заполненного буфера либо отключения компьютера.
hw.reset()
Перезагружает микроконтроллер программным сбросом.
Управление режимом USB
hw.ctrl_b(enable)
Разрешает или запрещает переход из консоли в загрузчик байт-кода по
последовательности Ctrl-B U L (02 55 4C). Одиночный Ctrl-B, за которым
нет U L, остаётся обычным входным байтом.
hw.ctrl_c(enable)
Разрешает или запрещает остановку программы двумя нажатиями Ctrl-C в течение
500 мс. Одиночный Ctrl-C остаётся входным байтом.
hw.console(enable)
hw.console(true)включает консольный режим USB CDC;hw.console(false)включает двоичный режим загрузки и управления.
После перехода в двоичный режим функции hw.cdc_*() больше не обслуживают
консольный поток.
Выводы и константы
Логические выводы разъёма
| uLua | STM32 |
|---|---|
hw.p10 | PA14 |
hw.p1 | PA4 |
hw.p2 | PA2 |
hw.p3 | PA0 |
hw.p4 | PA7 |
hw.p5 | PA6 |
hw.p6 | PA3 |
hw.p7 | PA1 |
hw.p8 | PA13 |
hw.p9 | PB1 |
Те же выводы доступны по физическим именам: hw.pa14, hw.pa4, hw.pa2,
hw.pa0, hw.pa7, hw.pa6, hw.pa3, hw.pa1, hw.pa13 и hw.pb1.
Дополнительные физические имена hw.pa5, hw.pa8, hw.pa9 и hw.pa10
присутствуют в прошивке, хотя не относятся к p1…p10. Используйте их только
если документация конкретного устройства подтверждает, что соответствующая
линия выведена и свободна.
Краткая таблица возможностей
| Возможность | Выводы |
|---|---|
| ШИМ | p2, p3, p6, p7 |
| АЦП | p1…p7 |
| Измерение частоты | p4, p5, p9 |
| UART2 | p2 — TX, p6 — RX |
| WS2812 | p4 |
| DHT22 | Любой свободный вывод без конфликта линии прерывания |
Описание синтаксиса, типов и строковых буферов находится в разделе «Язык uLua».