Например TDA7294

Форум РадиоКот • Просмотр темы - Программатор CH341T как I2C адаптер
Форум РадиоКот
Здесь можно немножко помяукать :)

Текущее время: Ср ноя 19, 2025 00:08:52

Часовой пояс: UTC + 3 часа


ПРЯМО СЕЙЧАС:



Начать новую тему Ответить на тему  [ Сообщений: 23 ]    , 2
Автор Сообщение
Не в сети
 Заголовок сообщения: Re: Программатор CH341T как I2C адаптер
СообщениеДобавлено: Сб фев 08, 2025 22:42:47 
Вымогатель припоя
Аватар пользователя

Карма: 10
Рейтинг сообщений: 171
Зарегистрирован: Ср июн 29, 2022 16:25:45
Сообщений: 523
Рейтинг сообщения: 0
Serjone, поддерживает.
Еще есть 10-битная адресация.

_________________
Белая и Пушистая


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: Программатор CH341T как I2C адаптер
СообщениеДобавлено: Вс фев 09, 2025 01:27:17 
Прорезались зубы
Аватар пользователя

Карма: 5
Рейтинг сообщений: 24
Зарегистрирован: Ср сен 11, 2024 10:18:53
Сообщений: 219
Рейтинг сообщения: 2
Кто имеет интерес - читайте.


Вложения:
i2c_description_rus.pdf [315.96 KiB]
Скачиваний: 111

_________________
Audiatur et altera pars !
Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: Программатор CH341T как I2C адаптер
СообщениеДобавлено: Пн мар 24, 2025 12:40:37 
Родился

Зарегистрирован: Пн мар 24, 2025 11:06:28
Сообщений: 1
Рейтинг сообщения: 0
...может кто-то подружил PC и программатор на CH341T для работы по I2C?

Тоже был озадачен проблемой управления i2c расширителем портов напрямую с компьютера. Моей первой попыткой была платка FT200XD CJMCU-200. Несколько дней проломал над ней голову, ничего не получилось и забил. Спустя какое-то время купил героя этой ветки CH341T и был приятно удивлен как просто ее подключить и как все четко работает. В некоторой степени мне этот форум помог со стартом, вот теперь поделюсь и я своими наработками.

Фото:
СпойлерИзображение


1) Для работы модуля обязательно нужен драйвер. В начале нашел просто файлы драйвера, которые нужно вручную ставить, но не рекомендую так делать. Как оказалось, при ручной установке приложение автоматически не подхватывает необходимую для работы DLL. Нашел корректные файлы драйверов с установщиком в этом репозитории github.

2) Теперь достаточно подключить платку к USB, она определиться как "USB-EPP/I2C... CH341A" в ветке Interface и готова к работе

3) Чтобы протестировать работоспособность существует готовое приложение CH341A-tool в том же репозитории гитхаба. Меня интересовала работа с расширителем портов MCP23017 (зеленая продолговатая платка на 16 логических портов, доступна на Алиэкспресс). Чтобы ей управлять необходимо слать по i2c пары чисел (регистр - значение). Для MCP23017 можно протестировать следующую последовательность на вкладке I2C write/read (изначальный адрес MCP23017 равен 0x20):

0x0A 0x20 - настройка устройства
0x00 0x00 - все пины порта А на output
0x12 0xFF - все пины порта А установить в HIGH
0x12 0x00 - все пины порта А установить в LOW

Регистры MCP23017:
Спойлер
Код:
#define MCPR_IODIR_A   0x00   // Для блока A | Настраивает работу портов на (1 вход, 0 выход) (соотношение бита к порту 0b00000000 - pa7 pa6 pa5 pa4 pa3 pa2 pa1 pa0)
#define MCPR_IODIR_B   0x01   // Для блока B |
#define MCPR_IPOL_A      0x02   // Для блока A | Задает для инпутных портов инверсию получаемого значения
#define MCPR_IPOL_B      0x03   // Для блока B |
#define MCPR_GPINTEN_A   0x04   // Для блока A | Опредетяет разрешена ли работа портов в качестве источника прерывания
#define MCPR_GPINTEN_B   0x05   // Для блока B |
#define MCPR_DEFVAL_A   0x06   // Для блока A | Хранит бит для каждого порта и если значение на порту не равно этому биту, то генерит прерывание (если разрешено регистром GPINTEN)
#define MCPR_DEFVAL_B   0x07   // Для блока B |
#define MCPR_INTCON_A   0x08   // Для блока A | Если 1чка для порта - то прерывания будут при любой смене состояния порта
#define MCPR_INTCON_B   0x09   // Для блока B |
#define MCPR_IOCON      0x0A   // Управление всем устройством, каждый бит настройка (слева направо идут 0b00100000):
                           // BANK =  0   : если 0, то регистры идут как тут поочередно. Если 1, то сначала все регистры для порта A, после все для B
                           // MIRROR =  0 : если 0, то порт А и В генерит прерывания на свои ножки, если 1, то сразу на ножки прерываний обеих портов
                           // SEQOP =   1 : если 1, то адрес инкрементируется (можно слать последовательно данные и следующая порция попадет в следующий регистр)
                           // DISSLW =  0 : можно включать(0) и отключать (1) ножку SDA в i2c выходе
                           // HAEN =  0   : пины адреса всегда включены в микросхеме MCP23017 (этот бит не влияет)
                           // ODR =   0   : делает выходы открытый коллектор (1) или обычные цифровые (0)
                           // INTPOL =  0 : значение выхода на пинах прерывания: 1 - при активном hight, 0 - при активном будет low
                           // последний бит зарезервирован
#define MCPR_GPPU_A      0x0C   // Для блока A | Определяет будет ли подтяжка input портов (если 1, то подтяжка 100кОм на +)
#define MCPR_GPPU_B      0x0D   // Для блока B |
#define MCPR_INTF_A      0x0E   // Для блока A | Только для чтения. Определяет на какой из ножек было прерывание (там будет 1)
#define MCPR_INTF_B      0x0F   // Для блока B |
#define MCPR_INTCAP_A   0x10   // Для блока A | Напряжение на всех портах в момент события прерывания
#define MCPR_INTCAP_B   0x11   // Для блока B |
#define MCPR_GPIO_A      0x12   // Для блока A | Текущие значения портов input (чтение считывает порт, запись модифицирует OLAT регистр)
#define MCPR_GPIO_B      0x13   // Для блока B |
#define MCPR_OLAT_A      0x14   // Для блока A | Доопределяет output ножки (чтение его читает буфер, а не порт, запись модифицирует выходной буфер, который изменит выходные порты)
#define MCPR_OLAT_B      0x15   // Для блока B |


4) Программа для теста это хорошо, но в реальных условиях нужна своя программа. Написал на c++ минимальный простой код, который реализует пример, аналогичный blink на ардуино. Мигает всеми пинами порта A (порт A - это пины 0-7, порт B - пины 8-15).

Код:
Спойлер
Код:
#include <windows.h>
#include <iostream>

// Загрузка функций из CH341DLL.dll
typedef BOOL(__stdcall* CH341OpenDevice_t)(ULONG iIndex);
typedef BOOL(__stdcall* CH341CloseDevice_t)(ULONG iIndex);
typedef BOOL(__stdcall* CH341StreamI2C_t)(ULONG iIndex, ULONG iWriteLength, PUCHAR iWriteBuffer, ULONG iReadLength, PUCHAR oReadBuffer);

int main() {
   // Загрузка DLL
   HMODULE hDLL = LoadLibrary("CH341DLL.dll");
   if (!hDLL) {
      std::cerr << "Failed to load CH341DLL.dll. Install the driver for CH341T" << std::endl;
      std::cin.get();
      return -1;
   }

   // Получение указателей на функции
   CH341OpenDevice_t CH341OpenDevice = (CH341OpenDevice_t)GetProcAddress(hDLL, "CH341OpenDevice");
   CH341CloseDevice_t CH341CloseDevice = (CH341CloseDevice_t)GetProcAddress(hDLL, "CH341CloseDevice");
   CH341StreamI2C_t CH341StreamI2C = (CH341StreamI2C_t)GetProcAddress(hDLL, "CH341StreamI2C");

   if (!CH341OpenDevice || !CH341CloseDevice || !CH341StreamI2C) {
      std::cerr << "Failed to get function pointers from CH341DLL.dll" << std::endl;
      FreeLibrary(hDLL);
      std::cin.get();
      return -1;
   }

   // Открытие устройства CH341A (индекс 0)
   ULONG deviceIndex = 0;
   if (!CH341OpenDevice(deviceIndex)) {
      std::cerr << "Failed to open CH341A device" << std::endl;
      FreeLibrary(hDLL);
      std::cin.get();
      return -1;
   }

   // Адрес I2C устройства
   const UCHAR i2cAddress = 0x20;

   // Последовательность команд
   UCHAR commands[4][2] = {
      { 0x0A, 0x20 },
      { 0x00, 0x00 },
      { 0x12, 0x00 },
      { 0x12, 0xFF }
   };

   // Буфер для данных
   UCHAR writeBuffer[3]; // 1 байт для адреса + 2 байта данных
   UCHAR readBuffer[1];  // Не используется в данном случае

   for (int i = 0; i < 2; ++i) {
      // Подготовка буфера для записи
      writeBuffer[0] = i2cAddress << 1; // Адрес устройства (сдвинутый влево для I2C)
      writeBuffer[1] = commands[i][0];
      writeBuffer[2] = commands[i][1];

      // Отправка команды через I2C
      if (!CH341StreamI2C(deviceIndex, 3, writeBuffer, 0, readBuffer)) {
         std::cerr << "Failed to send I2C command " << i + 1 << std::endl;
         CH341CloseDevice(deviceIndex);
         FreeLibrary(hDLL);
         std::cin.get();
         return -1;
      }
   }
   
   // Мигалка
   int i = 2;
   while (true) {
      writeBuffer[0] = i2cAddress << 1;
      writeBuffer[1] = commands[i][0];
      writeBuffer[2] = commands[i][1];

      if (!CH341StreamI2C(deviceIndex, 3, writeBuffer, 0, readBuffer)) {
         std::cerr << "Failed to send I2C command " << i + 1 << std::endl;
         CH341CloseDevice(deviceIndex);
         FreeLibrary(hDLL);
         std::cin.get();
         return -1;
      }

      Sleep(500);
      
      i++;
      if (i > 3) i = 2;
   }

   std::cout << "Commands sent successfully!" << std::endl;

   // Закрытие устройства
   CH341CloseDevice(deviceIndex);

   // Освобождение DLL
   FreeLibrary(hDLL);

   std::cin.get();
   return 0;
}


Все реализуется через библиотеку CH341DLL.dll из драйвера (если бы его ставили вручную, тогда требовалось ее таскать с приложением, а так она подхватывается из System32). Основная функция взаимодействия это CH341StreamI2C, которая принимает 4 аргумента: индекс устройства (если подключена только 1 плата CH341T, ее индекс 0), количество байт для отправки, буфер для отправки, количество байт для приема, буфер приема. Чтобы найти какие именно коды управления нужно слать конкретно для вашего i2c устройства, можно штудировать документацию. Но как по мне гораздо проще изучить библиотеку для этого устройства под Ардуино. Как правило, ввиду дефицита памяти, они написаны минималистично и можно легко отследить какая информация отсылается/принимается при инициализации и работе конкретного устройства.

5) На платке конвертора есть перемычка логики 3.3в или 5в. Пока тестировал заметил особенность. Если логика выставлена в 5в и пину расширителя портов задать HIGH состояние. То если выключить компьютер и через некоторое время его включить - это значение сохранится при старте. Дело в том, что конденсаторы в БП компьютера до некоторого значения разряжаются, а после продолжительное время хранят минимальный заряд, которого хватает чтобы не сбросить логику MCP23017. Это может вызвать путаницу и непредсказуемое поведение. Но если перемычку выставить в 3.3в, то стабилизатор на платке решает эту проблему и при включении компьютера все пины выключены.

Самый простой способ - взять ардуино-нанку и работать через нее с чем душа пожелает.
:wink:

Просто, но в некоторых случаях недостаточно надежно. У меня есть опыт в использовании Ардуино в режиме 24/7/365 и все они рано или почти рано зависают. Да, для перезагрузки есть Watchdog timer, и он прям спасает. Но вот, к примеру, UNO нормально перезагружается, а STM32 среди успешных ребутов иногда зависает так, что Watchdog не может ее вернуть. Это редкий случай, наверно, раз в месяц, но все же оно есть. Еще после ребута есть риск, что расширитель портов себя может некорректно повести, например, моргнет портами. Лучше этого избежать заранее.


Вернуться наверх
 
Показать сообщения за:  Сортировать по:  Вернуться наверх
Начать новую тему Ответить на тему  [ Сообщений: 23 ]    , 2

Часовой пояс: UTC + 3 часа


Кто сейчас на форуме

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 18


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Русская поддержка phpBB
Extended by Karma MOD © 2007—2012 m157y
Extended by Topic Tags MOD © 2012 m157y