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

I2C

I2C (Inter-Integrated Circuit) — синхронная последовательная шина для связи микроконтроллера с датчиками, памятью и другими микросхемами. Она использует две линии с открытым стоком: данные SDA и тактовый сигнал SCL. Обе линии должны быть подтянуты резисторами к напряжению питания интерфейса.

Для каждой доступной шины модуль определяет числовую константу, например i2c.I2C0. Набор шин зависит от устройства; обращение к несуществующей константе возвращает nil.

Модуль предоставляет константы i2c.MASTER и i2c.SLAVE, но описанные ниже транзакции поддерживаются только в режиме i2c.MASTER. API передаёт 7-битные адреса от 0x00 до 0x7f; 10-битная адресация не поддерживается.

Распространённые частоты шины — 100 и 400 кГц. Допустимая частота, номиналы подтягивающих резисторов и длина соединений зависят от всех устройств и общей ёмкости шины.

i2c.pins([as_table])

Показать текущие назначения SDA и SCL для доступных шин.

Если as_table равен true, функция возвращает массив записей с полями id, sda и scl. В остальных случаях она выводит назначения в консоль и ничего не возвращает.

local buses = i2c.pins(true)
for _, bus in ipairs(buses) do
    print(bus.id, bus.sda, bus.scl)
end

i2c.setpins(id, sda, scl)

Изменить назначение выводов шины до её первого подключения.

Аргументы:

  • id: идентификатор шины, например i2c.I2C0;
  • sda: GPIO с поддержкой ввода и вывода;
  • scl: GPIO с поддержкой вывода.

Значение -1 сохраняет текущее назначение соответствующего сигнала. SDA и SCL должны использовать разные GPIO. Функция ничего не возвращает или вызывает исключение.

i2c.setpins(i2c.I2C0, pio.GPIO25, pio.GPIO21)

bus = i2c.attach(id, mode [, speed])

Подключить шину I2C.

Аргументы:

  • id: идентификатор шины, например i2c.I2C0;
  • mode: i2c.MASTER; константа i2c.SLAVE зарезервирована для ведомого режима, для которого модуль не предоставляет операции обмена;
  • speed (необязательно): частота шины в герцах, по умолчанию 400000.

Возвращает экземпляр шины или вызывает исключение.

local bus = i2c.attach(i2c.I2C0, i2c.MASTER, 400000)

bus:detach()

Отключить экземпляр и освободить занятые ресурсы. После этого остальные методы экземпляра вызывают исключение. Повторный вызов detach() безопасен.

bus:setspeed(speed)

Изменить частоту этого экземпляра шины. Новое значение применяется сразу и сохраняется для следующих транзакций. Если к одной аппаратной шине подключены экземпляры с разными частотами, bus:start() применяет частоту выбранного экземпляра.

bus:start()

Добавить условие START в текущую транзакцию. Повторный вызов до stop() добавляет repeated START.

bus:address(address, read)

Добавить в транзакцию 7-битный адрес устройства и направление обмена.

Аргументы:

  • address: целое число от 0x00 до 0x7f;
  • read: true для чтения, false для записи.

Перед вызовом нужно добавить START функцией bus:start().

byte1, ... = bus:read([length [, as_string]])

Прочитать от 1 до 128 байтов. Перед чтением нужно отправить адрес с аргументом read = true.

Аргументы:

  • length (необязательно): число байтов, по умолчанию 1. Значения меньше 1 заменяются на 1, значения больше 128 — на 128;
  • as_string (необязательно): если true, функция возвращает одну бинарную строку. Иначе каждый байт возвращается отдельным числом.

Чтение выполняет накопленную часть транзакции немедленно. Завершите обмен вызовом bus:stop().

-- Выберите нужную форму результата:
local high, low = bus:read(2)
-- local block = bus:read(16, true)

bus:write(data1 [, data2, ...])

Добавить в транзакцию один или несколько целых байтов и бинарных строк. У целого аргумента передаётся младший байт. Перед записью нужно отправить адрес с аргументом read = false.

bus:write(0x10, 0x20, "data")

bus:stop()

Добавить условие STOP и выполнить накопленную транзакцию. Функция ничего не возвращает или вызывает исключение, в том числе если устройство не подтвердило адрес или истёк тайм-аут.

Пример работы с EEPROM 24LC256

Пример записывает байты в EEPROM, считывает их обратно и проверяет результат. Выводы A2–A0 микросхемы в этом примере задают адрес 0x51.

local eeprom = i2c.attach(i2c.I2C0, i2c.MASTER, 400000)
local device_address = 0x51

local function write_address(address)
    eeprom:write((address >> 8) & 0xff, address & 0xff)
end

local function wait_until_ready()
    for _ = 1, 100 do
        local ready = pcall(function()
            eeprom:start()
            eeprom:address(device_address, false)
            eeprom:stop()
        end)

        if ready then
            return
        end

        thread.sleepms(1)
    end

    error("EEPROM write timeout")
end

local function write_byte(address, value)
    eeprom:start()
    eeprom:address(device_address, false)
    write_address(address)
    eeprom:write(value)
    eeprom:stop()
    wait_until_ready()
end

local function read_byte(address)
    eeprom:start()
    eeprom:address(device_address, false)
    write_address(address)

    eeprom:start() -- repeated START
    eeprom:address(device_address, true)
    local value = eeprom:read()
    eeprom:stop()
    return value
end

for address = 0, 100 do
    write_byte(address, address)
end

for address = 0, 100 do
    local value = read_byte(address)
    assert(value == address, string.format(
        "address %d: read %d, expected %d",
        address,
        value,
        address
    ))
end