CH341DLL.DLL + I2C не работает должным образом с VB.NET

Я пишу класс VB.NET для реализации функциональности CH341DLL.DLL. Метод CH341StreamI2C() используется для потоковой записи и чтения в устройство. Таким образом, я импортировал метод CH341StreamI2C() из DLL:

<DllImport("CH341DLL.DLL", SetLastError:=True, CallingConvention:=CallingConvention.StdCall)>
Private Shared Function CH341StreamI2C(ByVal iIndex As Integer, ByVal iWriteLength As Integer, ByRef iWriteBuffer As IntPtr, ByVal iReadLength As Integer, ByRef oReadBuffer As IntPtr) As Boolean
End Function

Для проверки работы этого метода я использую датчик влажности и температуры I2C HTU21D. Его IIC-адрес — 40h, а регистр, в котором происходит получение температуры, — E3h. Поэтому я вызываю метод CH341StreamI2C() следующим образом:

Dim writeBuffer as Byte() = {&H40, &hE3} 'Address+Command
Dim s As String = Encoding.Unicode.GetString(writeBuffer)
Dim writeBufPtr As IntPtr = Marshal.StringToHGlobalAuto(s) 'Get pointer for write buffer
Dim wLen As Integer = writeBuffer.Length
Dim readBufPtr As IntPtr = IntPtr.Zero 'Init read pointer
Dim rLen as Integer = 3 'Sensor must return 3 bytes
Dim res As Boolean = CH341StreamI2C(0, wLen, writeBufPtr, rLen, readBufPtr)

Я использую логический анализатор, чтобы посмотреть, что находится на линиях SDA и SCL. И результат непредсказуем. Например, если вызвать предыдущий код 4 раза, это результат:

Линии SDA и SCL при вызове CH341StreamI2C()

Видно, что физически устройство CH341 записывает в строку непредсказуемые значения. Это не ошибка DLL, потому что другие приложения используют этот метод и результат правильный. Для справки, другие методы, например. CH341ReadI2C() и CH341WriteI2C(), которые считывают/записывают только один байт за раз, работают правильно в моем коде.

Какова вероятная причина такого поведения? Может быть, я неправильно рассортировал буфер записи? Как правильно это сделать?


person Aave    schedule 09.09.2018    source источник
comment
Можете ли вы показать нам объявление функции DLL? Я не уверен, что вы должны преобразовывать его в строку.   -  person Visual Vincent    schedule 09.09.2018
comment
Удалите ByRef из аргументов буфера, они передаются ByVal. Лучше объявить их как Byte().   -  person Hans Passant    schedule 09.09.2018
comment
@HansPassant спасибо, это правда.   -  person Aave    schedule 12.09.2018


Ответы (1)


Если вы используете это, исходное объявление является:

BOOL WINAPI CH341StreamI2C(ULONG iIndex, ULONG iWriteLength, PVOID iWriteBuffer, ULONG iReadLength, PVOID oReadBuffer);

Поскольку параметры буфера равны PVOIDs, вы должны иметь возможность просто маршалировать их непосредственно в байтовые массивы:

<DllImport("CH341DLL.DLL", SetLastError:=True, CallingConvention:=CallingConvention.StdCall)>
Private Shared Function CH341StreamI2C(ByVal iIndex As Integer, ByVal iWriteLength As Integer, ByVal iWriteBuffer As Byte(), ByVal iReadLength As Integer, ByVal oReadBuffer As Byte()) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function

Массивы являются ссылочными типами (классами), что означает, что вы всегда обращаетесь к ним через их указатель памяти. Таким образом, когда вы передаете их функции (P/Invoked или нет), вы фактически передаете указатель массива, а не сам массив. Это действительно полезно при P/Invoking, поскольку часто позволяет передавать массивы как есть.

person Visual Vincent    schedule 09.09.2018
comment
@Aave: Рад, что смог помочь! Удачи тебе с твоим проектом! - person Visual Vincent; 09.09.2018