• Уважаемые посетители сайта esp8266.ru!
    Мы отказались от размещения рекламы на страницах форума для большего комфорта пользователей.
    Вы можете оказать посильную поддержку администрации форума. Данные средства пойдут на оплату услуг облачных провайдеров для сайта esp8266.ru
  • Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

I2C на RTL...

pvvx

Активный участник сообщества
Попробовал нативно работать с аппартным I2C.
Удобный аппаратный контроллер.
На примере с INA219 получилось работать так:
1) Конфигурируем i2c в нужный режим (их там куча :) )
2) Забиваем в FIFO команды и данные для передачи.
3) Ожидаем готовности или спустя время транзакции, считываем готовые данные от датчика из FIFO.
Никакой тусовки и ногодрыга.
Пример (только кода) прерывания на аппаратном таймере, непрерывно читающем измеренные ток и напряжение на датчике INA219:
Код:
#define i2c_reg(r) *((volatile uint32 *)(pi2c->base_regs + r))
void ina_tick_handler(void *par) {
    PINA219DRV p = &ina219drv;
    i2c_drv_t *pi2c = &p->i2c;
    DiagPrintf("%d,%d\n", p->buf_i2c.us[0], p->buf_i2c.ss[1]);
    switch(p->status) {
    default:
        // Disable controller.
        i2c_reg(REG_DW_I2C_IC_ENABLE) = 0;
        DiagPrintf(".");
        p->status = 1;
        break;
    case 1:
        // Enable controller.
        i2c_reg(REG_DW_I2C_IC_ENABLE) = BIT_IC_ENABLE;
        DiagPrintf("*");
        p->status = 2;
        break;
    case 3:
        if (i2c_reg(REG_DW_I2C_IC_RAW_INTR_STAT) &    BIT_IC_RAW_INTR_STAT_TX_ABRT) {
            uint32 tmp = i2c_reg(REG_DW_I2C_IC_CLR_INTR);
            p->status = 0;
            break;
        } else {
            // Считаем готовые значения тока и напряжения INA219 из FIFO ic I2C
            p->buf_i2c.uc[1] = i2c_reg(REG_DW_I2C_IC_DATA_CMD);
            p->buf_i2c.uc[0] = i2c_reg(REG_DW_I2C_IC_DATA_CMD);
            p->buf_i2c.uc[3] = i2c_reg(REG_DW_I2C_IC_DATA_CMD);
            p->buf_i2c.uc[2] = i2c_reg(REG_DW_I2C_IC_DATA_CMD);
        }
    case 2:
        // Заполним FIFO ic I2C командами (адрес устройства и прочее он сделает сам, автоматически, согласно начальной конфигурации контроллера IC)
        // Задать номер регистра напряжения для чтения в INA219. Write addr reg + stop.
        i2c_reg(REG_DW_I2C_IC_DATA_CMD) = 2 | BIT_IC_DATA_CMD_STOP;
        // Задать транзакцию чтения регистра напряжения из INA219. Read command 2 bytes + stop.
        i2c_reg(REG_DW_I2C_IC_DATA_CMD) = BIT_IC_DATA_CMD_CMD;
        i2c_reg(REG_DW_I2C_IC_DATA_CMD) = BIT_IC_DATA_CMD_CMD | BIT_IC_DATA_CMD_STOP;
        // Задать номер регистра тока для чтения из INA219. Write addr reg + stop.
        i2c_reg(REG_DW_I2C_IC_DATA_CMD) = 1 | BIT_IC_DATA_CMD_STOP;
        // Задать транзакцию чтения регистра тока из INA219. Read command 2 bytes + stop.
        i2c_reg(REG_DW_I2C_IC_DATA_CMD) = BIT_IC_DATA_CMD_CMD;
        i2c_reg(REG_DW_I2C_IC_DATA_CMD) = BIT_IC_DATA_CMD_CMD | BIT_IC_DATA_CMD_STOP;
        p->status = 3;
        break;
    }
}
Буфера накопления данных к передаче для простоты примера убраны и заменены на вывод в LogUART. Льет примерно такой лог:
Снимок1458.gif
Заполненная командами FIFO I2С исполняет такую пачку передач-приема по I2C, каждое прерывание таймера:
Ina219_int.gif
Частоты I2C CLK разгоняются любые, до нескольких МГц. Можно указать и работу в режиме HS, тогда контроллер I2C будет автоматом вставлять команду перехода в SMBUS режим для повышения частоты за МГц-ы... На чтение данных, для заполнения буферов с типом пинг-понг (один заливается, второй обрабатывается и передается на внешний сервер), можно включить и DMA... Но тут пока простой пример.
Скорость измерения у INA219 к 600 us на 12 бит замеры, чтение значений тока и напряжения по I2C при CLK I2C ~360 кГц (такой вышел для теста - плата с датчиком на длинных проводах и соплях - сделана для Ардуиншиков и более не тянет - щупы осциллографа уже вносят завалы фронтов своей емкостью...) занимает порядка 300 us. Это и выйдет максимальной частотой для опроса по таймеру, но в UART это уже в текстовом виде не вывести - не хватит скорости UART :)...

Инициализацию i2с контроллера тоже написал свою, она очень простая... Выкинул громоздкий SDK-шный Hal... Она оптимизирована под конкретные задачи и использует совсем мало кода и данных. Может позже скину пример в мою сборку SDK.

PS: 4-ре I2С контроллера в RTL871x по регистрам аналогичны Intel D2000 и расписаны в доках от Intel D2000. Код от SDK к Intel D2000 для I2C, после модификации и вставки пару нюансов тоже работает.
 

Вложения

Последнее редактирование:

pvvx

Активный участник сообщества
Кинул почти полный примерчик, попроще, установок I2C в режим мастер для pool опроса и/или для использования как описано выше постом.
Сам типа драйвер I2C: RTL00_WEB/i2c_drv.c at master · pvvx/RTL00_WEB · GitHub
И пример с logUART: RTL00_WEB/ina219buf.c at master · pvvx/RTL00_WEB · GitHub

Если I2C используется с фиксироваными входами/выходами и не требуется переназначений и смены его конфигурации на ходу, то для инициализации достаточно указать данные в его структуре и вызвать int _i2c_init(i2c_drv_t *pi2c) с указателем на неё... Она вызывает только пару коротких процедур _i2c_ic_on(), i2c_disable() и меняет PwrState для PMU, если используются энерго-экономичные режимы. Всё остальное можно тупо описывать через запись и чтение регистра FIFO аппаратных блоков I2C с паузами для ожидания выполнения и результата (опроса регистра статуса на Break на линии I2C - NACK).
Алго ACK задается в специальном регистре:
// General Call Ack
i2c_reg(REG_DW_I2C_IC_ACK_GENERAL_CALL) = BIT_CTRL_IC_ACK_GENERAL_CALL(1);
Остальные процедуры даны ради примера, чтобы было понятно, как работать с данными встроенными аппаратным I2C контролерами...
 
Последнее редактирование:

pvvx

Активный участник сообщества
Кстати hal'овские i2c_read i2c_write блокирующие или нет?
Которые по DMA - нет.
--------
На основе примера, описанном тут, сделан опрос INA219 c передачей потока в websocket для отображения графика с шагом в 1 ms тока и напряжения. Скрипт на клиента ещё не дописан.
Пример работы websocket-а представлен тут https://esp8266.ru/forum/threads/rtl00-mp3-player.1697/page-11#post-37488 , исходники включены в Web-свалка на RTL871x
 
Последнее редактирование:
  • Like
Реакции: Neov

sharikov

Active member
Кинул почти полный примерчик, попроще, установок I2C в режим мастер для pool опроса и/или для использования как описано выше постом.
Сам типа драйвер I2C: RTL00_WEB/i2c_drv.c at master · pvvx/RTL00_WEB · GitHub
С обработкой ошибок все плохо. На тесте "отверткой по плате" виснет.
Добавил
Код:
if (_i2c_read(&i2cmaster, MBED_I2C_SLAVE_ADDR0, (char*)&i2cdata_read[0], 3, 1) !=DRV_I2C_OK) {
    rtl_printf("[%s] i2c_read error!\n", __FUNCTION__);
    _i2c_break(&i2cmaster);
    _i2c_ic_off(&i2cmaster);
    _i2c_init(&i2cmaster);
    return I2C_ERROR;
}
перестало виснуть если замыкать sda на gnd но виснет намертво после замыкания scl на 0. Почему переинициализация контроллера не лечит второй случай непонятно.
Еще в драйвере нужна функция развешивания слэйвов типа такой:
(это из AVR)
Код:
void I2C_Reset(void)
{
  for (char i = 0; i < 9; i++)
  {
    I2C_DATA_HI();
    delay(1);
    if ((PINB & SDA_MSK)==0)
    {
      I2C_CLOCK_HI();
      I2C_CLOCK_LO();
      I2C_CLOCK_HI();
    }
  }
  I2C_Stop();
}
Драйвер mbed так же виснет. В нем таймаут в функции чтения не работает.

Итого: для "боевого" кода без допиливания непригодно.
В SDK хоть что-то вообще работает "из коробки" ???

И это я еще не дошел до теста "рашпилем по ЛАТР-у".
 

pvvx

Активный участник сообщества
В SDK хоть что-то вообще работает "из коробки" ???
Современные SDK сделаны как DEMO.
Да и вообще не бывает SDK, в которых всё работает (особенно одновременно :))
Описание контроллеров есть - пишите свои драйвера. Я так и сделал, для примера и вышло меньше объемом и так как мне надо было... Тем более I2С не профессиональная шина, а для игрушек...
 
Последнее редактирование:

Simon

Member
pvvx, подскажите пожалуйста.
Мне нужно выполнить чтение слейва через repeated start. Должно выглядеть вот так(картинка из документации на датчик):
0000.JPG

- Первые два байта - команда для слейва на чтение
- Следующий байт запуск чтения
- 3 байта ответа передает слейв.

i2c_write без стопа в конце проходит нормально.
Но i2c_read перед командой чтения рестартует шину, посылая "стоп-старт". Стоп сбрасывает датчик и тот не отвечает.
Нужно отменить рестарт шины или включить repeated start.
 

Simon

Member
Нашел сам. Помогло:
i2c_restart_enable(i2c_t *obj);
что бы это не значило :)
 

АндрейМ

New member
1) Конфигурируем i2c в нужный режим (их там куча :) )
смотрю на твой драйвер под INA, не хватает понимания, как настраиваются i2c режимы.
Например:

// Master Target Address
i2c_reg(REG_DW_I2C_IC_TAR) = p->addr;
устанавливается адрес слейва, в которого дальше кидаем команды

У BMP180 таких адресов два - выбор регистра и чтение, в оригинальном ногодрыгателе идет посыл 24 бита с отловом ACK:

Код:
bool BMP180_select_reg(uint32_t addr) { // write 24 bits w/ add i2c_start
    int i = 0;
    for (i = 0; i < 24; i++) {
        i2c_step_scl_sda(addr & 0x800000);
        addr <<= 1;
        if ((i & 7) == 7 && i2c_step_scl_sda(1) != I2C_ACKS) { //check ack
            i2c_stop();
            return false;
        }
        if (i == 15) {
            i2c_start();
        }
    }
    return true;
}
Посоветуй, куда копать?
 

pvvx

Активный участник сообщества
Посоветуй, куда копать?
PS: 4-ре I2С контроллера в RTL871x по регистрам аналогичны Intel D2000 и расписаны в доках от Intel D2000. Код от SDK к Intel D2000 для I2C, после модификации и вставки пару нюансов тоже работает.
https://esp8266.ru/forum/attachments/rtl871x_i2c-docx.4665/

Для MLX90614 и прочих SMB это выглядит так:

В console при использовании i2c_drv.c:
project.mk:
ADD_SRC_C += project/src/driver/i2c_drv.c
CFLAGS += -DUSE_I2C_CONSOLE=1
Код:
>ati2c ?
I2C Init:
        ati2c i [sda_pin [scl_pin [mode [speed]]]]
I2C Deinit:
        ati2c d
I2C Write:
        ati2c W address data1 [data2 ... [data8]...]
I2C write + stop:
        ati2c w address data1 [data2 ... [data8]...]
I2C Read:
        ati2c R address count
I2C read + stop:
        ati2c r address count
I2C get:
        ati2c g address wrcount wrdata1 [..wrdata6] rdcount
>ati2c i // инициализируем I2С  с параметрами по умолчанию
I2C1 Init: 24 25 02 0000c350
I2C1 Status = 1
>ati2c g 5a 1 6 3 // По адресу устройства 0x5a  запрашиваем smb чтение регистра 6 и читаем 3 байта
I2C1 get[3]: 5a 5d 3a 53
I2C1 Status = 1
>ati2c g 5a 1 7 3 // По адресу устройства 0x5a  запрашиваем smb чтение регистра 7 и читаем 3 байта
I2C1 get[3]: 5a 4a 3a 79
I2C1 Status = 1
>ati2c d
I2C1 DeInit
>
Из PDF для MLX90614
Снимок1610.gif
На ногах RTL:
Снимок1611.gif
 
Последнее редактирование:

pvvx

Активный участник сообщества
В web-свалку добавлен пример работы по I2C с MLX90614 (Infra Red Thermometer)
Снимок1615.jpg

Регистрация остывания ложки (websocket, 100 точек Ta и To в сек), вынутой из кружки чая:
Снимок1614.gif
Ta - Ambient temperature
To - Object temperature


Криво, ступенями, т.к. высыхает чай на ложке :)
Температура самого датчика (Ta) падает - до замера нагрел рукой...

Опрашивать быстрее 10 ms MLX90614 нет смысла...
Снимок1616.gif
При опросе To и Ta в 1 ms он вообще гонит халтуру :)
 
Последнее редактирование:
  • Like
Реакции: A_D

pvvx

Активный участник сообщества
С обработкой ошибок все плохо. На тесте "отверткой по плате" виснет.
...
И это я еще не дошел до теста "рашпилем по ЛАТР-у".
Не виснет с webcsocket c данными MLX90614 в последней версии - как угодно замыкайте SCL, SDA, GND и всё равно заработает снова...
 

pvvx

Активный участник сообщества
>ati2c g 76 1 d0 1 // адрес 0x76, передать 1 байт 0xd0 (задать номер регистра), прочитать 1 байт.
I2C1 get[1]: 76 58 // 0x58 - ID BMP280
I2C1 Status = 1 // =1 ok - шина I2c свободна, =2 stop ещё не передан, ...
-------
ati2c g 68 1 75 10 // адрес 0x68, передать 1 байт 0x75 (задать номер регистра), прочитать 16 байт.
I2C1 get[16]: 68 71 00 1a b4 00 ed 44 00 24 18 00 b8 c0 dd f7 a8 // 0x71 - ID MPU9250
I2C1 Status = 1
 
Последнее редактирование:

АндрейМ

New member
ADD_SRC_C += project/src/driver/i2c_drv.c
Потребовалось на одной шине два устройства. Принцип - отдельная инициализация i2c, драйверам периферии передается готовая структура.
Но из-за невозможности менять IC_TAR при IC_ENABLE==1 неправильно срабатывают _i2c_write() для второго и далее устройств.
Предлагаю текущий адрес слейва держать в структуре i2c_drv_t и в процедурах _i2c_read и write добавить проверку и при необходимости
i2c_reg(REG_DW_I2C_IC_ENABLE) = 0;
перед
i2c_reg(REG_DW_I2C_IC_TAR) = p->addr;

Короче фьюче реквест :). В остальном драйвер отличный.
 

pvvx

Активный участник сообщества
Потребовалось на одной шине два устройства. Принцип - отдельная инициализация i2c, драйверам периферии передается готовая структура.
Но из-за невозможности менять IC_TAR при IC_ENABLE==1 неправильно срабатывают _i2c_write() для второго и далее устройств.
Предлагаю текущий адрес слейва держать в структуре i2c_drv_t и в процедурах _i2c_read и write добавить проверку и при необходимости
i2c_reg(REG_DW_I2C_IC_ENABLE) = 0;
перед
i2c_reg(REG_DW_I2C_IC_TAR) = p->addr;

Короче фьюче реквест :). В остальном драйвер отличный.
Это не "драйвер", а упрошенный пример использования оборудования без громоздкого оф. HAL и рассчитывался на одно устройство. Там ещё много "но" ради упрощения.
А цель достигнута - вы как-то смогли разобраться и можете теперь сами модифицировать и строить варианты с I2C под свои нужды...
 

АндрейМ

New member
А цель достигнута - вы как-то смогли разобраться и можете теперь сами модифицировать и строить варианты с I2C под свои нужды...
Не нужно скромничать - для примера Ваш драйвер слишком работоспособный и отлично вписывается в иерархию вместо куска HAL. Моя идеология позволяет переписывать уёжища, но тут форкать рабочую систему для пары бантиков считаю неправильным. Но Вам, конечно, виднее.

ЗЫ: предлагаю перейти на "ТЫ" )))
 

pvvx

Активный участник сообщества
Ну с BMPx80 уже не I2C, а SMBus.
Контроллер тоже работает по сигналам в "стандарте" SMBus -> http://smbus.org/specs/SMBus_3_0_20141220.pdf
Еще в драйвере нужна функция развешивания слэйвов типа такой:
(это из AVR)
Код:
void I2C_Reset(void)
{
  for (char i = 0; i < 9; i++)
  {
    I2C_DATA_HI();
    delay(1);
    if ((PINB & SDA_MSK)==0)
    {
      I2C_CLOCK_HI();
      I2C_CLOCK_LO();
      I2C_CLOCK_HI();
    }
  }
  I2C_Stop();
}
Последний раз не удалось заставить его гнать 11 бит "1" подряд. Он всё стремится передать команду перехода на другую скорость и прочие SMBus-ные коды переключения и, обнаружив что устройств на шине нет, не хочет на шину выводить произвольные байты...
Может есть какое решение, а то ногодрыг GPIO не катит - слишком много переключений - надо включить "Открытый коллектор" на GPIO SDA и CLKи т.д...
Тем более такое "развешивание" не подходит для SMBus устройств.
Совместимость SMBus с устройством I2C » Все о РадиоЭлектроТехнике
 
Последнее редактирование:

АндрейМ

New member
Немного сумбурно получается - разобрался, почему не проходили изменения TAR. Контроллер не позволяет менять его, если очередь не пуста:
Код:
    if (pbmp->addr != i2c_reg(REG_DW_I2C_IC_TAR)) {
        int cnt = 0;
        while (!(i2c_reg(REG_DW_I2C_IC_STATUS) & BIT_IC_STATUS_TFE)) {
            rtw_udelay_os(100);
            if (cnt++ > 100) {
                return 0;
            }
        }
    }
после выхода из цикла чтение/запись нормально проходят на новый адрес.
 
Последнее редактирование:

Ксения

New member
Всем здравствуйте!
Подскажите, а каким образом конфигурируются пины GPIO (PC_4 и PC_5)? Из кода этого непонятно...
Имеем плату RAK473. Пытаемся прочитать несколько байт от устройства по I2C: функции _i2c_setup, _i2c_init, _i2c_set_speed проходят без ошибок, но _i2c_read возвращает либо I2C abort либо I2C Timeout. Посмотрели осциллографом, сигналов на выводах нет вообще... Единственная версия - что они неправильно сконфигурированы.

Заранее спасибо за ответ!
 
Сверху Снизу