Перейти к содержанию
Материалы раздела

Оборудование: модуль 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.p1hw.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.p1hw.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 информационных бит;
  • parityhw.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_*() больше не обслуживают консольный поток.

Выводы и константы

Логические выводы разъёма

uLuaSTM32
hw.p10PA14
hw.p1PA4
hw.p2PA2
hw.p3PA0
hw.p4PA7
hw.p5PA6
hw.p6PA3
hw.p7PA1
hw.p8PA13
hw.p9PB1

Те же выводы доступны по физическим именам: 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 присутствуют в прошивке, хотя не относятся к p1p10. Используйте их только если документация конкретного устройства подтверждает, что соответствующая линия выведена и свободна.

Краткая таблица возможностей

ВозможностьВыводы
ШИМp2, p3, p6, p7
АЦПp1p7
Измерение частотыp4, p5, p9
UART2p2 — TX, p6 — RX
WS2812p4
DHT22Любой свободный вывод без конфликта линии прерывания

Описание синтаксиса, типов и строковых буферов находится в разделе «Язык uLua».