電子回路の素人が、USB制御できる0-10V電圧源を「格安で」作る その4 作った制御プログラム
昨日の続き。USB制御できる0-10V電圧源を格安に制作しています。USB-IO2.0(AKI)を使ってUSBからデジタルI/Oを作り、MCP4922で0-5Vの直流電圧を生成し、LM358Nで0-10Vに昇圧することにしました。Microchip 12ビットDAコンバーター SPIインターフェース 【MCP4922-E/P】Kaito7823(5個) STMicroelectronics LM358N オペアンプ(DIP)昨日の日記で考えた仕様をモトに作った、USB-IO2.0を使ってMCP4922をドライブするプログラムです。VisualStudio community 2015で作ってます。ソリューションを新規作成して、formに貼り付ければ動くと思います。少なくとも付属CDのサンプルプログラムが動作していれば。コマンドラインから動かす仕様なので、formには何もコントロールを貼り付ける必要はありません。'MCP4922drive コマンドラインver'LM358をつけて0-10Vで動作するようにする 実行ファイル名をMCP4922drive.exeとすると'' コマンドライン引数は二つ'(例) c:> MCP4922drive A 3.5''DACのAに3.5Vを出力''(例) c:> MCP4922drive B 2.1''DACのBに2.1Vを出力''10以上の引数の場合は10Vに固定'Imports System.Runtime.InteropServicesPublic Class Form1 '****************************************************** '定数や構造体の定義 USB-IO2.0用 (サンプルプログラムそのままコピペ) '****************************************************** Public Const DIGCF_PRESENT = &H2 Public Const DIGCF_DEVICEINTERFACE = &H10 Public Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000 Public Const GENERIC_READ = &H80000000 Public Const GENERIC_WRITE = &H40000000 Public Const FILE_SHARE_READ = &H1 Public Const FILE_SHARE_WRITE = &H2 Public Const OPEN_EXISTING = 3 Public Const INVALID_HANDLE_VALUE = -1 Public Const MyVendorID = &H1352 'Km2Net Public Const MyProductID = &H120 'USB-IO2.0 Public Const MyProductID2 = &H121 'USB-IO2.0(AKI) <StructLayout(LayoutKind.Sequential, Pack:=1, CharSet:=CharSet.Ansi)> Public Structure GUID Dim Data1 As Integer Dim Data2 As Short Dim Data3 As Short <MarshalAs(UnmanagedType.ByValArray, SizeConst:=8)> Dim Data4() As Byte End Structure <StructLayout(LayoutKind.Sequential, Pack:=1)> Public Structure HIDD_ATTRIBUTES Dim Size As Integer Dim VendorID As Short Dim ProductID As Short Dim VersionNumber As Short End Structure <StructLayout(LayoutKind.Sequential, Pack:=1)> Public Structure HIDP_CAPS Dim Usage As Short Dim UsagePage As Short Dim InputReportByteLength As Short Dim OutputReportByteLength As Short Dim FeatureReportByteLength As Short <MarshalAs(UnmanagedType.ByValArray, SizeConst:=16)> Dim Reserved() As Short Dim NumberLinkCollectionNodes As Short Dim NumberInputButtonCaps As Short Dim NumberInputValueCaps As Short Dim NumberInputDataIndices As Short Dim NumberOutputButtonCaps As Short Dim NumberOutputValueCaps As Short Dim NumberOutputDataIndices As Short Dim NumberFeatureButtonCaps As Short Dim NumberFeatureValueCaps As Short Dim NumberFeatureDataIndices As Short End Structure <StructLayout(LayoutKind.Sequential, Pack:=1)> Public Structure SECURITY_ATTRIBUTES Dim nLength As Integer Dim lpSecurityDescriptor As Integer Dim bInheritHandle As Integer End Structure <StructLayout(LayoutKind.Sequential, Pack:=1, CharSet:=CharSet.Ansi)> Public Structure SP_DEVICE_INTERFACE_DATA Dim cbSize As Integer Dim InterfaceClassGuid As GUID Dim Flags As Integer Dim Reserved As Integer End Structure <StructLayout(LayoutKind.Sequential, Pack:=1, CharSet:=CharSet.Ansi)> Public Structure SP_DEVINFO_DATA Dim cbSize As Integer Dim ClassGuid As GUID Dim DevInst As Integer Dim Reserved As Integer End Structure <StructLayout(LayoutKind.Sequential, Pack:=1)> Public Structure SP_DEVICE_INTERFACE_DETAIL_DATA Dim cbSize As Integer Dim DevicePath As Byte End Structure '****************************************************** '関数の定義 USB-IO2.0用 (サンプルプログラムそのままコピペ) '****************************************************** Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Integer) As Integer Public Declare Sub RtlMoveMemory Lib "kernel32" (ByRef Destination As Byte, ByVal Source As IntPtr, ByVal Length As Short) Public Declare Function CreateFile _ Lib "kernel32" _ Alias "CreateFileA" _ (ByVal lpFileName As String, ByVal dwDesiredAccess As Integer, ByVal dwShareMode As Integer, ByRef lpSecurityAttributes As SECURITY_ATTRIBUTES, ByVal dwCreationDisposition As Integer, ByVal dwFlagsAndAttributes As Integer, ByVal hTemplateFile As Integer) _ As Integer Public Declare Function ReadFile _ Lib "kernel32" _ (ByVal hFile As Integer, ByRef lpBuffer As Byte, ByVal nNumberOfBytesToRead As Integer, ByRef lpNumberOfBytesRead As Integer, ByVal lpOverlapped As Integer) _ As Integer Public Declare Function WriteFile _ Lib "kernel32" _ (ByVal hFile As Integer, ByRef lpBuffer As Byte, ByVal nNumberOfBytesToWrite As Integer, ByRef lpNumberOfBytesWritten As Integer, ByVal lpOverlapped As Integer) _ As Integer Public Declare Function HidD_GetAttributes Lib "hid.dll" (ByVal HidDeviceObject As Integer, ByRef Attributes As HIDD_ATTRIBUTES) As Integer Public Declare Function HidD_GetHidGuid Lib "hid.dll" (ByRef HidGuid As GUID) As Integer Public Declare Function HidD_GetPreparsedData Lib "hid.dll" (ByVal HidDeviceObject As Integer, ByRef PreparsedData As Integer) As Integer Public Declare Function HidP_GetCaps Lib "hid.dll" (ByVal PreparsedData As Integer, ByRef Capabilities As HIDP_CAPS) As Integer Public Declare Function SetupDiEnumDeviceInterfaces _ Lib "setupapi.dll" _ (ByVal DeviceInfoSet As Integer, ByVal DeviceInfoData As Integer, ByRef InterfaceClassGuid As GUID, ByVal MemberIndex As Integer, ByRef DeviceInterfaceData As SP_DEVICE_INTERFACE_DATA) _ As Integer Public Declare Function SetupDiGetClassDevs _ Lib "setupapi.dll" _ Alias "SetupDiGetClassDevsA" _ (ByRef ClassGuid As GUID, ByVal Enumerator As String, ByVal hwndParent As Integer, ByVal Flags As Integer) _ As Integer Public Declare Function SetupDiGetDeviceInterfaceDetail _ Lib "setupapi.dll" _ Alias "SetupDiGetDeviceInterfaceDetailA" _ (ByVal DeviceInfoSet As Integer, ByRef DeviceInterfaceData As SP_DEVICE_INTERFACE_DATA, ByVal DeviceInterfaceDetailData As Integer, ByVal DeviceInterfaceDetailDataSize As Integer, ByRef RequiredSize As Integer, ByVal DeviceInfoData As Integer) _ As Integer Public HidDevice As Integer = INVALID_HANDLE_VALUE Public Capabilities As HIDP_CAPS '***************************************************************** ' 関数いろいろ USB-IO2.0用 (サンプルプログラムそのままコピペ) '***************************************************************** '接続する関数 Private Function openDevice() As Boolean Dim HidGuid As GUID Dim DeviceInfoSet As Integer Dim MyDeviceInterfaceData As SP_DEVICE_INTERFACE_DATA Dim MemberIndex As Integer Dim MyDeviceInfoData As SP_DEVINFO_DATA Dim Needed As Integer Dim DetailData As Integer Dim MyDeviceInterfaceDetailData As SP_DEVICE_INTERFACE_DETAIL_DATA Dim DetailDataBuffer() As Byte Dim gch As GCHandle Dim address As Integer Dim DevicePathName As String Dim sa As SECURITY_ATTRIBUTES Dim DeviceAttributes As HIDD_ATTRIBUTES Dim PreparsedData As Long Dim ipt As IntPtr Dim Result As Integer openDevice = False Result = HidD_GetHidGuid(HidGuid) DeviceInfoSet = SetupDiGetClassDevs(HidGuid, vbNullString, 0, (DIGCF_PRESENT Or DIGCF_DEVICEINTERFACE)) MemberIndex = 0 Do MyDeviceInterfaceData.cbSize = Marshal.SizeOf(MyDeviceInterfaceData) Result = SetupDiEnumDeviceInterfaces(DeviceInfoSet, 0, HidGuid, MemberIndex, MyDeviceInterfaceData) If Result <> 0 Then MyDeviceInfoData.cbSize = Marshal.SizeOf(MyDeviceInfoData) Result = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, MyDeviceInterfaceData, 0, 0, Needed, 0) DetailData = Needed MyDeviceInterfaceDetailData.cbSize = Marshal.SizeOf(MyDeviceInterfaceDetailData) ReDim DetailDataBuffer(Needed) ipt = Marshal.AllocHGlobal(Marshal.SizeOf(MyDeviceInterfaceDetailData)) Marshal.StructureToPtr(MyDeviceInterfaceDetailData, ipt, False) Call RtlMoveMemory(DetailDataBuffer(0), ipt, 4) gch = GCHandle.Alloc(DetailDataBuffer, GCHandleType.Pinned) address = gch.AddrOfPinnedObject().ToInt32() Result = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, MyDeviceInterfaceData, address, DetailData, Needed, 0) gch.Free() DevicePathName = System.Text.Encoding.GetEncoding("Shift-JIS").GetString(DetailDataBuffer) DevicePathName = DevicePathName.Substring(4) sa.nLength = 12 sa.lpSecurityDescriptor = 0 sa.bInheritHandle = 0 HidDevice = CreateFile(DevicePathName, GENERIC_READ Or GENERIC_WRITE, (FILE_SHARE_READ Or FILE_SHARE_WRITE), sa, OPEN_EXISTING, 0, 0) If HidDevice <> INVALID_HANDLE_VALUE Then DeviceAttributes.Size = Marshal.SizeOf(DeviceAttributes) Result = HidD_GetAttributes(HidDevice, DeviceAttributes) If (DeviceAttributes.VendorID = MyVendorID) And (DeviceAttributes.ProductID = MyProductID Or DeviceAttributes.ProductID = MyProductID2) Then HidD_GetPreparsedData(HidDevice, PreparsedData) HidP_GetCaps(PreparsedData, Capabilities) openDevice = True Exit Do Else Result = CloseHandle(HidDevice) End If End If Else Exit Do End If MemberIndex = MemberIndex + 1 Loop End Function '閉じる関数 Private Sub closeDevice() If HidDevice <> INVALID_HANDLE_VALUE Then CloseHandle(HidDevice) End If End Sub '送受信関数 Private Sub SendRecv(ByVal sendData() As Byte, ByRef recvData() As Byte) Dim i As Integer Dim s As String Dim NumberOfBytesWritten As Integer Dim NumberOfBytesRead As Integer Dim wrtData(Capabilities.OutputReportByteLength - 1) As Byte Dim readData(Capabilities.InputReportByteLength - 1) As Byte On Error GoTo errJump wrtData(0) = &H0 For i = 0 To 63 wrtData(i + 1) = sendData(i) Next WriteFile(HidDevice, wrtData(0), Capabilities.OutputReportByteLength, NumberOfBytesWritten, 0) Do ReadFile(HidDevice, readData(0), Capabilities.InputReportByteLength, NumberOfBytesRead, 0) If wrtData(64) = readData(64) Then Exit Do End If Loop For i = 0 To 63 recvData(i) = readData(i + 1) Next Exit SuberrJump: MsgBox(Err.Description) End Sub '***************************************************************** ' 自分で書いたMCP4922制御用コードの始まり '***************************************************************** 'ピンの状態を表す配列 'J1のpin7~pin0にセットするビット列を表す0と1の文字列 左端がpin7 送るときはConvert.tobyteによってバイト値に変換する Dim CommandBinStringsArray() As String 'コマンド行インデックスの最大値 (実際のコマンド行数)-1を指定 Dim NofCommmandArray As Integer = 36 '起動時 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load ReDim CommandBinStringsArray(NofCommmandArray) 'コマンドライン引数の取得 Dim cmds As String() = System.Environment.GetCommandLineArgs() Dim selectDAC As Integer If cmds(1) = "A" Then selectDAC = 0 If cmds(1) = "B" Then selectDAC = 1 Dim SetVoltage As Double = CDbl(cmds(2)) If SetVoltage > 10 Then SetVoltage = 10 If SetVoltage < 0 Then SetVoltage = 0 '起動時自動接続 J1を全ピンを出力に変える ConnectUSBIO() '電圧印加 ReadWritePins(SetVoltage, selectDAC) '終了 closeDevice() End End Sub '接続&出力設定 Private Sub ConnectUSBIO() '接続が成功したら If openDevice() = True Then '送受信バッファを用意して Dim sendData(63) As Byte Dim recvData(63) As Byte 'まずは現状の設定を問い合わせる sendData(0) = &HF8 'コマンド :システム設定用フラッシュロム読込み sendData(63) = &H0 'シーケンス SendRecv(sendData, recvData) '送受信 'J1pinの状態(インデックス5)を見る 上位ビットがpin7、下位ビットがpin0になる 'ビットON(=1)になっていれば入力ピン設定されている。全ピンが出力設定(つまりインデックス5の値が0)になっているか確認して、そうなっていなければ0を送る(J1全ピンを出力に設定) If recvData(5) And &H0 = False Then sendData(0) = &HF9 'コマンド :システム設定用フラッシュロム書き込み sendData(1) = 0 '未使用 sendData(2) = recvData(2) 'プルアップを設定(今回使わないけど現状をそのまま返す) sendData(3) = 0 '未使用 sendData(4) = 0 '未使用 sendData(5) = &H0 '入力ピン設定 ビットON(=1)が入力ピン設定なので0を送って全部出力ピンにする sendData(63) = &H0 'シーケンス SendRecv(sendData, recvData) '送信 End If '接続が失敗した場合は何もせずそのまま終了することにする End If End Sub '電圧印加用コマンドを作る Private Sub ReadWritePins(SetVoltage As Double, selectDAC As String) Dim iii As Integer '設定する12bitのバイナリ値(0~4095)を計算 本来、5V=4095であるが、 'LM358オペアンプによって0-10Vに増幅するから、ここでは10Vが指定されたときに4095を出すようにする Dim SetBinValue As Integer = CInt(SetVoltage / 10 * 4095) 'config bits部分をセット 'CS、SCK、SDI、LDACの順でpin7、6、5、4につなぐ 最上位ビット(左端)がpin7 最下位ビット(右端) がpin0 CommandBinStringsArray(0) = "1001" '始状態 CommandBinStringsArray(1) = "00" & selectDAC & "1" 'DAC A/Bをセット CommandBinStringsArray(2) = "01" & selectDAC & "1" CommandBinStringsArray(3) = "0011" 'BUF=1 固定 CommandBinStringsArray(4) = "0111" CommandBinStringsArray(5) = "0011" 'GA=1 固定 ちなみにGA=0(x2倍)にしたからと言って10Vが出るわけではない CommandBinStringsArray(6) = "0111" '確かにVrefの2倍が出るが、上限は5V(VDD)。つまりVrefが2Vのときには4Vが出たりするだけの話 CommandBinStringsArray(7) = "0011" 'SHDN=1 固定 CommandBinStringsArray(8) = "0111" '12 data bits部分をセット 'インデックス9~32はバイナリ値をビット列に置き換えたものとなる 上位ビットから12ビット分送信 Dim tempbit As Integer For iii = 0 To 11 tempbit = Math.Floor(SetBinValue / (2 ^ (11 - iii))) Mod 2 CommandBinStringsArray(9 + iii * 2) = "00" & tempbit & "1" CommandBinStringsArray(10 + iii * 2) = "01" & tempbit & "1" Next iii '残りのビットを用意 CommandBinStringsArray(33) = "0001" 'クロック終了 CommandBinStringsArray(34) = "1001" 'CSを上げる CommandBinStringsArray(35) = "1000" 'LDACを開ける CommandBinStringsArray(36) = "1001" '終状態 'pin3~0の部分を追記 とりあえず今は0埋め 'MCP4922を複数台つなぐのであれば、ここにCSとLDACのデータを書く (SCKとSDI線は複数のMCP4922で共有できる) For iii = 0 To NofCommmandArray CommandBinStringsArray(iii) = CommandBinStringsArray(iii) & "0000" Next iii 'コマンド送信 SendCommand(CommandBinStringsArray, NofCommmandArray) End Sub 'コマンド送信 Private Sub SendCommand(CommandArray() As String, NofIndex As Integer) Dim iii As Integer '入出力バッファを用意して Dim sendData(63) As Byte Dim recvData(63) As Byte sendData(0) = &H20 'コマンド :デジタル入出力 sendData(1) = &H1 '出力1:J1 sendData(63) = &H0 'シーケンス For iii = 0 To NofIndex sendData(2) = Convert.ToByte(CommandArray(iii), 2) '0と1の文字列だったビット列をバイト値に変換してセット SendRecv(sendData, recvData) '送受信 'System.Threading.Thread.Sleep(1) ' Wait とりあえず不要 Next iii End SubEnd Class---------------------------------------------------------------------------------------------------明日に続く。