Alter.Org.UA
 << Back Home EN en   Donate Donate

UART-to-I2C on Arduino

В процес╕ розробки читалки розумних батарейок DJI утворилась така штука як UART-to-I2C адаптер на Arduino. Одразу скажу, що под╕бн╕ реч╕ в природ╕ ╕снують. Дивився ще на cp2112, але там величезна обв'язка з USB HID. ╤ $40. ╤ доставка 2-3 тижн╕ (tm) ╤ ╓ Coptonix #020101 по $25, в нього все в ascii, нав╕ть руками з консол╕ можна щось робити (але не зручно, бо нема local echo), в нього якось мутно описано отримання в╕дпов╕д╕, а статус шини взагал╕ н╕як не переда╓ться. ╤ в╕н Slave

Ма╓мо прошивку, що вм╕╓ в режим╕ I2C Master

  • 3 протоколи на USB UART (115200 8N1)
    • RAW (довжина даних + адреса (1 чи 2 байти) ╕ сам╕ дан╕ у формат╕ шини I2C)
    • Coptonix #020101, т╕льки Master, див. нижче
    • manual: local echo, команди вручну, дан╕ в HEX, ╓ сканер шини
  • 7/10 б╕т адресац╕я на шин╕ I2C
  • пакети до 255 байт
  • python adapter class, що ╕м╕ту╓ i2c_msg з smbus2.SMBus що спрощу╓ ╕нтеграц╕ю з готовими програмами, що працюють з I2C/smbus або портування з Raspbery ui2c.py
  • можлив╕сть надсилати debug messages не порушуючи роботу в режим╕ RAW

RAW mode (default) packet format

Write UART -> I2C, RD=0

 WRITE RD=0

 * 8bit data length (doesn't include address)

 *  7bit address: adr7+R/W
 *     xxxxxxx r/w 
 * 10bit address: 11110 adr2+R/W Ack adr8
 *     11110xx r/w xxxxxxxx

 * data bytes

Request Read I2C -> UART, RD=1

READ  RD=1

 * 8bit data length (doesn't include address), always 1 since data contains requested length

 *  7bit address: adr7+RD
 *     xxxxxxx r/w 
 * 10bit address: 11110 adr2+RD Ack adr8
 *     11110xx r/w xxxxxxxx

 * requested length (1 byte)

I2C Bus management

для керування використову╓ться нед╕йсна адреса 0xff address зя якою передаються послыдовнысть внутрышных комеад

Mode - 0xff
0x01 0xff 0xff mode
  mode: 0 - RAW
        1 - Coptonix
        2 - Manual

Transaction - 0xfe
0x01 0xff 0xfe on/off
        0 - end transaction
        1 - begon transaction

Log level - 0xfd
0x01 0xff 0xfd level
        0 - disabled
        1 - enabled (2 and higher reserved)

Конвертор очику╓ 1й байт з довжиною пакету data length. Зчиту╓ перший байт адреси ╕, якщо треба, 2й. Дал╕ йде очикування даних. П╕сля отримання повного пакету дан╕ передаються на вказаний I2C пристр╕й. Якщо протягом CMD_TIMEOUT (1сек) не поступа╓ наступний байт, вважа╓ться, що траписась помилка ╕ буфер прийому очищуються.

╤сну╓ можлив╕сть перевести контролер в ╕нший режим вручну. Для цього в момент настання таймауту вх╕дний буфер перев╕ря╓ться на наявн╕сть наступних команд. В решт╕ випаду╕в отриман╕ дан╕ ╕гноруються.

mode=coptonix
mode=manual
version?

UI2C Reply format

 * 8bit data length for packets of 1-0xef bytes
 *     or
 * 0xff length for packets of 0xf0-0xff bytes
 *     or
 * 0xff error code (see below)

 * data bytes (for READ requests only)

UI2C Erros

1:  data too long  (should not happen)
2:  NACK addr      (no such device)
3:  NACK data      (data transmission aborted)
4:  unknown
5:  timeout

I2C Bus addressed read

Часто використову╓ться режим, у якому до I2C пристрою в╕дправля╓ться команда (чи адреса), п╕сля чого очику╓ться в╕дпов╕дь. При цьому Master device не зв╕льня╓ шину м╕ж WRITE та READ. Оск╕льки за замовчанням шина зв╕льня╓ться, використовуються додаткова посл╕довн╕сть begin/end transaction

> 0x01 0xff 0xfe 0x01     begin transaction
< no reply

> 0x01 0x16 0x3f                        send 1 byte (0x3f) to device 0x0b (=0x16/2)
< 0x01                                  1 byte sent

> 0x01 0x17 0x20                        request read up to 0x20 bytes from device 0x0b 
                                                           (=0x17/2, lower bit means READ)
< 0x20 0xXX 0xXX .... 0xXX              0x20 bytes received + data itself

> 0x01 0xff 0xfe 0x00     end transaction
< no reply

Error reporting

> 0x01 0xff 0xfe 0x01     begin transaction
< no reply

> 0x02 0x16 0x22 0x3e                   send 2 byte (0x22 0x3e) to device 0x0b (=0x16/2)
< 0xff 0x03                             Error (0xff) NACK data (0x03) - device rejected request

> 0x01 0xff 0xfe 0x00     end transaction
< no reply

Command set: Manual, Coptonix

Режим Manual в╕др╕зня╓ться розширеним набором команд ╕ local echo

requestactionCoptonix replyManual reply
a<CR>get remote I2C device addressaXX<CR>Dst Address: XXX<CR>
cXXX<CR>set remote I2C device addressc<CR>OK<CR>
s<CR>save remote I2C device address in EEPROM and make it defaults<CR>OK<CR>
wXXX...XXXX<CR>write date to remote devicew<CR>Status string<CR>
xXXX...XXXX<CR>write date to remote device and wait for reply x<CR>[iXXX...XXX]<CR>Formatted hex dump
Manual / Non-standard extension
xNN,XXX...XXXX<CR>write date to remote device and wait for reply, limit to NN bytes
x<CR>[iXXX...XXX]<CR>
Formatted hex dump
r<CR>switch to RAW modeUI2C vX.X RAW mode<CR>
m<CR>switch to Manual modeUI2C vX.X Manual mode<CR>
lN<CR>set log level to NLogging ON<CR>
or nothing
?<CR>scan I2C busformatted list of devices
v<CR>get FW revisionUI2C vX.X<CR>

Manual Example

version?                request version and wait ~1 sec
UI2C v1.0
mode=manual             switch to manual mode
UI2C v1.0 Manual mode
a                       get selected device
Dst Address: fe
w223e                   send bytes 0x22 0x3e
Status: 2 NACK addr
?                       scan bus
Scanning...
I2C device found at address 0x0B!
done
c0b                     select device 0x0b
OK
w223e                   send bytes 0x22 0x3e
Status: 3 NACK data
x10,3f                  send byte 0x2f and wait for reply up to 0x10 bytes
Status: 0 OK                                                                    
<
58 0F 3F 3F 3F 3F 3F 3F
3F 3F 3F 3F 3F 3F 3F 3F

Coptonix Example

a                       get selected device
aFE
c0B                     select device 0x0b
c
w223E                   send bytes 0x22 3e
w3F                     send byte 0x3f and wait for reply
i5B0F3F3F3F3F3F3F....

Python smbus2/i2c compatibility

Модуль ui2c ╓ м╕н╕мальною функц╕ональною зам╕ною модулю smbus2 ╕ повторю╓ його ╕нтерфейс, що спрощу╓ адаптау╕ю ╕снуючих модул╕в. Також, для ╕дентиф╕кац╕╖ наявност╕ UI2C адаптера ╕сну╓ метод probe_ui2c_device(dev_name, speed=115200)

ui2c.py

  • class i2c_msg - keep prepared read/write requests and related data buffers
    • read(address, length)
    • write(address, buf)
  • class UartI2C - handle I/O with I2C over UI2C
    • __init__(self, dev_name=None, speed=115200)
    • open(self, dev_name)
    • close(self)
    • i2c_rdwr(self, *i2c_msgs) - execute prepared requests inside single transaction
  • method probe_ui2c_device(dev_name, speed=115200)
  • property verbose - log level, 0-3
  • property ui2c_logging - enable UI2C internal logging, must be adjusted before open()/__init__()

Python Example

    # replaced imports
    #import smbus2
    #from smbus2 import i2c_msg

    import ui2c
    from ui2c import i2c_msg

    # check UI2C presence
    if(not ui2c.probe_ui2c_device(dev_name, speed):
        raise IOError("UI2C not detected")

    # open port
    bus = ui2c.UartI2C(dev_name, speed)

    # prepare write packet with command
    part_write = i2c_msg.write(dev_addr, [cmd.value])
    
    # prepare reply request (read) packet
    part_read = i2c_msg.read(dev_addr, 2 + (1 if bus.pec else 0))

    # write command and wait for reply in single transaction
    bus.i2c_rdwr(part_write, part_read)
    received_data = bytes(part_read)

    print("Received data:", received_data)

Спочатку ви в╕дкрива╓те порт адаптера UART-to-I2C за допомогою ui2c.UartI2C(dev_name, speed), де dev_name це назва пристрою або ╕дентиф╕катор порту, а speed - швидк╕сть передач╕ даних (бод).

Дал╕ ви визнача╓те адресу пристрою I2C (dev_addr) ╕ команду, яку потр╕бно в╕дправити (cmd).

Пот╕м ви п╕дготовля╓те пакет для запису команди за допомогою i2c_msg.write(). Пакет запису м╕стить адресу пристрою та байт команди.

Аналог╕чно, ви п╕дготовля╓те пакет для читання за допомогою i2c_msg.read(). Пакет читання вказу╓ адресу пристрою та к╕льк╕сть байт╕в для читання.

Для виконання транзакц╕╖ I2C ви виклика╓те uart_i2c.i2c_rdwr(part_write, part_read), яке спочатку в╕дправля╓ пакет запису, а пот╕м викону╓ операц╕ю читання. Отриман╕ дан╕ збер╕гаються в об'╓кт╕ part_read.

Нарешт╕, ви можете отримати отриман╕ дан╕ з part_read за допомогою bytes(part_read) ╕ обробити або в╕добразити ╖х, як потр╕бно.

Будь ласка, зам╕н╕ть dev_name на фактичне ╕м'я пристрою або ╕дентиф╕катор порту вашого адаптера UART-to-I2C ╕ адаптуйте зм╕нн╕ dev_addr та cmd в╕дпов╕дно до ваших конкретних вимог.

Зауважте, що цей приклад передбача╓, що б╕бл╕отека ui2c встановлена локально ╕ доступна.

C/C++ LIB

Структура i2c_msg

Представля╓ пов╕домлення I2C для операц╕й читання або запису.

struct i2c_msg {
    uint16_t addr;   /**< Адреса пристрою I2C. */
    uint16_t flags;  /**< Флаги I2C пакета. */
    uint16_t len;    /**< Довжина буфера даних. */
    uint8_t* buf;    /**< Вказ╕вник на буфер даних. */
};

i2c_msg_read

Форму╓ запит на зчитування даних з пристрою I2C у вказану структуру i2c_msg.

Поверта╓: 1, якщо операц╕я усп╕шна, 0 - у раз╕ помилки.

void i2c_msg_read(struct i2c_msg* msg, int address, int length);

i2c_msg_write

Форму╓ запит на запис даних до пристрою I2C за буфера структури i2c_msg.

Поверта╓: 1, якщо операц╕я усп╕шна, 0 - у раз╕ помилки.

void i2c_msg_write(struct i2c_msg* msg, int address, char* data, int length);

i2c_msg_free

Зв╕льня╓ ресурси, пов'язан╕ з╕ структурою i2c_msg.

void i2c_msg_free(struct i2c_msg* msg);

ui2c_open

В╕дкрива╓ пристр╕й адаптера UART-to-I2C для зд╕йснення комун╕кац╕╖.

Параметри:

  • dev_name - Назва пристрою (посл╕довного порту) для в╕дкриття. Приклади: "COM1" (Windows), "/dev/ttyUSB0" (Linux).
  • speed - Швидк╕сть зв'язку UART, 115200 за замовчанням.
F_HANDLE ui2c_open(const char *dev_name, int speed);

ui2c_close

Закрива╓ дескриптор посл╕довного порта.

void ui2c_close(F_HANDLE fd);

ui2c_probe

Перев╕ря╓ наявн╕сть адаптера UART-to-I2C на дескриптор╕ посл╕довного порта.

Параметри:

  • fd - Дескриптор посл╕довного порту.

Поверта╓: 1, якщо адаптер UART-to-I2C присутн╕й, 0 - в ╕ншому випадку.

int ui2c_probe(F_HANDLE fd);

probe_ui2c_device

Перев╕ря╓ наявн╕сть адаптера UART-to-I2C за ╕менем посл╕довного порта.

Параметри:

  • dev_name - Назва пристрою (посл╕довного порту) для перев╕рки. Приклади: "COM1" (Windows), "/dev/ttyUSB0" (Linux).
  • speed - Швидк╕сть зв'язку UART.

Поверта╓: 1, якщо пристр╕й UART-to-I2C присутн╕й, 0 - в ╕ншому випадку.

int probe_ui2c_device(const char *dev_name, int speed);

ui2c_enable_logging

Ув╕мкнути режим журналювання на адаптер╕ UART-to-I2C.

Параметри:

  • fd - Дескриптор посл╕довного порту.
  • uLevel - Р╕вень журналювання.
void ui2c_enable_logging(F_HANDLE fd, unsigned char uLevel);

ui2c_rdwr

Викону╓ операц╕╖ читання та запису I2C пристрою через адаптер UART-to-I2C в╕дпов╕дно до вказаних структур i2c_msg не зв╕льняючи шину I2C м╕ж операц╕ями

Параметри:

  • fd - Дескриптор посл╕довного порта.
  • msgs - Масив структур i2c_msg, що описують операц╕╖ читання та запису.
  • num_msgs - К╕льк╕сть структур i2c_msg в масив╕.

Поверта╓: 0, якщо операц╕я усп╕шна, або код помилки.

  • UI2C_2W_STATUS_OK 0x00 // OK
  • UI2C_2W_ERR_TOO_LONG 0x01 // data too logs
  • UI2C_2W_ERR_ADDR_NACK 0x02 // addr NACK
  • UI2C_2W_ERR_DATA_NACK 0x03 // data NACK
  • UI2C_2W_ERR_UNKNOWN 0x04 // general error
  • UI2C_2W_ERR_TIMEOUT 0x05 // timeout

int ui2c_rdwr(F_HANDLE fd, struct i2c_msg **msgs, int num_msgs);

i2c_probe_dev

Перев╕ря╓ наявнысть на шин╕ I2C пристрою з зазначеною адресою.

Поверта╓: 1, якщо операц╕я усп╕шна, 0 - у раз╕ в╕дсутност╕ або помилки.

i2c_probe_dev(F_HANDLE fd, int dev_addr);

C Example

#include 
#include 
#include 


int main(int argc, char** argv) { // Define the i2c_msgs struct i2c_msg msg1, msg2; int addr = 0x0b; // default DJI battery address char data[] = { 0x3f }; i2c_msg_write(&msg1, addr, data, 1); // request hardware ID 0x3f command i2c_msg_read(&msg2, addr, 2); // read hardware ID, 2 bytes

// Initialize the UartI2C int uart_i2c; if(argc<2) { printf("Serial port with UI2C adapter not specified\n"); return -1; } printf("Try open %s\n", argv[1]); uart_i2c = ui2c_open(argv[1], 115200); if(uart_i2c <= 0) return -1;

printf("Probe UI2C adapter...\n"); if(!ui2c_probe(uart_i2c)) { printf("Probe UI2C failed\n"); return -1; }

printf("Probe device...\n"); if(!i2c_probe_dev(uart_i2c, addr)) { printf("Probe I2C device @0x%x failed\n", addr); return -1; }

// Perform the i2c_rdwr operation printf("Send/receive...\n"); struct i2c_msg* msgs[2] = { &msg1, &msg2 }; ui2c_rdwr(uart_i2c, &msgs[0], 2);

// Print the received data printf("Received ID: %4.4x\n", *((uint16_t*)(msg2.buf)));

// Cleanup i2c_msg_free(&msg1); i2c_msg_free(&msg2); ui2c_close(uart_i2c);

return 0; }

Download

GitHub: https://github.com/Alter-1/ui2c
Firmware hex: ui2c-v1-hex.tar.gz
Sources: ui2c-v1.04.tar.gz Adruino code, Python API, C/C++ API

2023.06.28


См. также


FB or mail alterX@alter.org.ua (remove X)   Share
Автор: Alter (Александр А. Телятников) Сервер: Apache+PHP под FBSD © 2002-2024