使用FT232H实现MCP4725 DAC的I2C输出电压控制
1、下载库libMPSSE.zip
2、安装VS,安装C++编译库
3、修改
为:
/*! * \file sample-dynamic.c * * \author FTDI * \date 20110512 * * Copyright © 2000-2014 Future Technology Devices International Limited * * THIS SOFTWARE IS PROVIDED BY FUTURE TECHNOLOGY DEVICES INTERNATIONAL LIMITED ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FUTURE TECHNOLOGY DEVICES INTERNATIONAL LIMITED * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * Project: libMPSSE * Module: I2C Sample Application - Interfacing MCP4725 DAC */ /******************************************************************************/ /* Include files */ /******************************************************************************/ /* Standard C libraries */ #include<stdio.h> #include<stdlib.h> #include<string.h> /* OS specific libraries */ #ifdef _WIN32 #include<windows.h> #else // _WIN32 #include<dlfcn.h> #include <unistd.h> // For Sleep() on Linux/macOS #define Sleep(ms) usleep(ms * 1000) #endif // _WIN32 /* Include D2XX header*/ #include "ftd2xx.h" /* Include libMPSSE header */ #include "libMPSSE_i2c.h" /******************************************************************************/ /* Macro and type defines */ /******************************************************************************/ /* Helper macros */ #ifdef _WIN32 #define GET_FUN_POINTER GetProcAddress #define CHECK_ERROR(exp) {if(exp==NULL){printf("%s:%d:%s(): NULL expression\ encountered \n",__FILE__, __LINE__, __FUNCTION__);exit(1);}else{;}}; #else // _WIN32 #define GET_FUN_POINTER dlsym #define CHECK_ERROR(exp) {if(dlerror() != NULL){printf("line %d: ERROR \ dlsym\n",__LINE__);}} #endif // _WIN32 #define APP_CHECK_STATUS(exp) {if(exp!=FT_OK){printf("%s:%d:%s(): status(0x%x) \ != FT_OK\n",__FILE__, __LINE__, __FUNCTION__,exp);exit(1);}else{;}}; #define APP_CHECK_STATUS_NOEXIT(exp) {if(exp!=FT_OK){printf("%s:%d:%s(): status(0x%x) \ != FT_OK\n",__FILE__, __LINE__, __FUNCTION__,exp);}else{;}}; #define CHECK_NULL(exp){if(exp==NULL){printf("%s:%d:%s(): NULL expression \ encountered \n",__FILE__, __LINE__, __FUNCTION__);exit(1);}else{;}}; /* Application specific macro definations */ #define I2C_DEVICE_BUFFER_SIZE 256 #define CHANNEL_TO_OPEN 0 #define I2C_DEVICE_ADDRESS_MCP4725 0x60 #define VDD_VOLTAGE 3.3f // !!重要!! 请根据您的实际电路修改此值 /* Application configuration/debugging */ #define TEST_EEPROM 0 // 禁用原始的EEPROM测试 #define FAST_TRANSFER 0 /* Declaration of function pointers */ typedef FT_STATUS(*pfunc_I2C_GetNumChannels)(uint32 *numChannels); typedef FT_STATUS(*pfunc_I2C_GetChannelInfo)(uint32 index, FT_DEVICE_LIST_INFO_NODE *chanInfo); typedef FT_STATUS(*pfunc_I2C_OpenChannel)(uint32 index, FT_HANDLE *handle); typedef FT_STATUS(*pfunc_I2C_CloseChannel)(FT_HANDLE handle); typedef FT_STATUS(*pfunc_I2C_InitChannel)(FT_HANDLE handle, ChannelConfig *config); typedef FT_STATUS(*pfunc_I2C_DeviceRead)(FT_HANDLE handle, uint32 deviceAddress, uint32 sizeToTransfer, uint8 *buffer, uint32 *sizeTransfered, uint32 options); typedef FT_STATUS(*pfunc_I2C_DeviceWrite)(FT_HANDLE handle, uint32 deviceAddress, uint32 sizeToTransfer, uint8 *buffer, uint32 *sizeTransfered, uint32 options); static pfunc_I2C_GetNumChannels p_I2C_GetNumChannels = NULL; static pfunc_I2C_GetChannelInfo p_I2C_GetChannelInfo = NULL; static pfunc_I2C_OpenChannel p_I2C_OpenChannel = NULL; static pfunc_I2C_CloseChannel p_I2C_CloseChannel = NULL; static pfunc_I2C_InitChannel p_I2C_InitChannel = NULL; static pfunc_I2C_DeviceRead p_I2C_DeviceRead = NULL; static pfunc_I2C_DeviceWrite p_I2C_DeviceWrite = NULL; /* 新增函数原型声明 */ void TestDeviceMCP4725(); static FT_STATUS read_mcp4725_status(); /******************************************************************************/ /* Global variables */ /******************************************************************************/ static FT_HANDLE ftHandle; static uint8 buffer[I2C_DEVICE_BUFFER_SIZE] = {0}; /******************************************************************************/ /* Public function definitions */ /******************************************************************************/ static uint8 initialize_library() { #ifdef _WIN32 HMODULE h_libMPSSE = LoadLibraryA("libMPSSE.dll"); if (!h_libMPSSE) { printf("Failed loading libMPSSE.dll. Please check if the file exists in the working directory\n"); return 0; } #else void *h_libMPSSE = dlopen("libMPSSE.so", RTLD_LAZY); if(!h_libMPSSE) { printf("Failed loading libMPSSE.so. Please check if the file exists in the shared library folder(/usr/lib or /usr/lib64)\n"); exit(1); } #endif p_I2C_GetNumChannels = (pfunc_I2C_GetNumChannels)GET_FUN_POINTER(h_libMPSSE, "I2C_GetNumChannels"); p_I2C_GetChannelInfo = (pfunc_I2C_GetChannelInfo)GET_FUN_POINTER(h_libMPSSE, "I2C_GetChannelInfo"); p_I2C_OpenChannel = (pfunc_I2C_OpenChannel)GET_FUN_POINTER(h_libMPSSE, "I2C_OpenChannel"); p_I2C_CloseChannel = (pfunc_I2C_CloseChannel)GET_FUN_POINTER(h_libMPSSE, "I2C_CloseChannel"); p_I2C_InitChannel = (pfunc_I2C_InitChannel)GET_FUN_POINTER(h_libMPSSE, "I2C_InitChannel"); p_I2C_DeviceRead = (pfunc_I2C_DeviceRead)GET_FUN_POINTER(h_libMPSSE, "I2C_DeviceRead"); p_I2C_DeviceWrite = (pfunc_I2C_DeviceWrite)GET_FUN_POINTER(h_libMPSSE, "I2C_DeviceWrite"); CHECK_ERROR(p_I2C_GetNumChannels); CHECK_ERROR(p_I2C_GetChannelInfo); CHECK_ERROR(p_I2C_OpenChannel); CHECK_ERROR(p_I2C_CloseChannel); CHECK_ERROR(p_I2C_InitChannel); CHECK_ERROR(p_I2C_DeviceRead); CHECK_ERROR(p_I2C_DeviceWrite); return 1; } static void cleanup_library() { } /******************************************************************************/ /* 新增的MCP4725相关函数 */ /******************************************************************************/ /*! * \brief 从MCP4725读取当前状态和数据 * \return 返回 FT_STATUS 状态码 */ static FT_STATUS read_mcp4725_status() { FT_STATUS status; uint8 readBuffer[5]; // MCP4725读取操作返回5个字节 uint32 bytesRead = 0; printf("\n--- Reading MCP4725 Status ---\n"); // 执行I2C读操作。对于MCP4725,读取时不需要先写入寄存器地址。 // Master必须在读取最后一个字节后发送NACK信号。 status = p_I2C_DeviceRead(ftHandle, I2C_DEVICE_ADDRESS_MCP4725, 5, readBuffer, &bytesRead, I2C_TRANSFER_OPTIONS_START_BIT | I2C_TRANSFER_OPTIONS_STOP_BIT | I2C_TRANSFER_OPTIONS_NACK_LAST_BYTE); if (status != FT_OK) { printf("!! I2C read failed with status: %d\n", status); return status; } if (bytesRead != 5) { printf("!! I2C read failed: expected 5 bytes but received %d\n", bytesRead); return FT_IO_ERROR; } // 解析并打印读取到的数据 // 字节 1: 状态字节 uint8 isReady = (readBuffer[0] & 0x80) >> 7; uint8 powerDownMode = (readBuffer[0] & 0x06) >> 1; // 字节 2 & 3: 当前DAC寄存器值 uint16 dacValue = ((readBuffer[1] & 0x0F) << 8) | readBuffer[2]; float currentVoltage = (float)dacValue / 4095.0 * VDD_VOLTAGE; // 字节 4 & 5: EEPROM中存储的值 uint8 eepromPowerDown = (readBuffer[3] & 0x60) >> 5; uint16 eepromValue = ((readBuffer[3] & 0x0F) << 8) | readBuffer[4]; float eepromVoltage = (float)eepromValue / 4095.0 * VDD_VOLTAGE; printf(" EEPROM Write Status : %s\n", isReady ? "Ready (Completed)" : "Busy"); printf(" Current DAC Settings:\n"); printf(" - Power Mode : "); switch(powerDownMode) { case 0: printf("Normal\n"); break; case 1: printf("Power-Down (1kOhm to GND)\n"); break; case 2: printf("Power-Down (100kOhm to GND)\n"); break; case 3: printf("Power-Down (500kOhm to GND)\n"); break; } printf(" - Register Value : %u (0x%03X)\n", dacValue, dacValue); printf(" - Output Voltage : %.3f V\n", currentVoltage); printf(" Stored EEPROM Settings:\n"); printf(" - Power Mode : "); switch(eepromPowerDown) { case 0: printf("Normal\n"); break; case 1: printf("Power-Down (1kOhm to GND)\n"); break; case 2: printf("Power-Down (100kOhm to GND)\n"); break; case 3: printf("Power-Down (500kOhm to GND)\n"); break; } printf(" - Stored Value : %u (0x%03X)\n", eepromValue, eepromValue); printf(" - Power-up Voltage : %.3f V\n", eepromVoltage); printf("--------------------------------\n"); return FT_OK; } /*! * \brief 设置MCP4725的输出电压 * \param[in] voltage 想要设置的目标电压值 (浮点数) * \return 返回 FT_STATUS 状态码 */ static FT_STATUS set_mcp4725_voltage(float voltage) { FT_STATUS status; uint32 bytesTransfered = 0; uint16 digitalValue; uint8 dataBuffer[2]; if (voltage < 0.0) voltage = 0.0; if (voltage > VDD_VOLTAGE) voltage = VDD_VOLTAGE; digitalValue = (uint16)((voltage / VDD_VOLTAGE) * 4095.0); // 准备I2C快速写入模式的数据包 (2字节) dataBuffer[0] = (digitalValue >> 8) & 0x0F; dataBuffer[1] = digitalValue & 0xFF; printf("-> Setting voltage to %.3fV (Digital: %u). Sending bytes [0x%02X, 0x%02X]...\n", voltage, digitalValue, dataBuffer[0], dataBuffer[1]); status = p_I2C_DeviceWrite(ftHandle, I2C_DEVICE_ADDRESS_MCP4725, 2, dataBuffer, &bytesTransfered, I2C_TRANSFER_OPTIONS_START_BIT | I2C_TRANSFER_OPTIONS_STOP_BIT); if (status != FT_OK) { printf("!! I2C write failed with status: %d\n", status); } else if (bytesTransfered != 2) { printf("!! I2C write failed: incorrect number of bytes transferred (%d)\n", bytesTransfered); status = FT_IO_ERROR; } else { printf(" OK\n"); } return status; } /*! * \brief MCP4725 DAC 交互式测试函数 */ void TestDeviceMCP4725() { char inputBuffer[32]; float targetVoltage; while(1) { printf("\nEnter a voltage (0.0 to %.1f) or 'q' to quit: ", VDD_VOLTAGE); // 使用fgets安全地读取用户输入 if (fgets(inputBuffer, sizeof(inputBuffer), stdin) == NULL) { break; // 遇到文件结尾或错误 } // 检查退出条件 if (inputBuffer[0] == 'q' || inputBuffer[0] == 'Q') { printf("Exiting...\n"); break; } // 尝试将输入字符串转换为浮点数 if (sscanf(inputBuffer, "%f", &targetVoltage) != 1) { printf("Invalid input. Please enter a number or 'q'.\n"); continue; } // 检查电压范围 if (targetVoltage < 0.0 || targetVoltage > VDD_VOLTAGE) { printf("Voltage out of range. Please enter a value between 0.0 and %.1f.\n", VDD_VOLTAGE); continue; } // 设置电压 set_mcp4725_voltage(targetVoltage); } } /*! * \brief Main function / Entry point of the sample application */ int main() { FT_STATUS status = FT_OK; FT_DEVICE_LIST_INFO_NODE devList = {0}; ChannelConfig channelConf; uint32 channels = 0; uint32 i = 0; if (!initialize_library()) { printf("initialize_library failed!\n"); return 0; } memset(&channelConf, 0, sizeof(channelConf)); channelConf.ClockRate = I2C_CLOCK_FAST_MODE; // 400 kbps channelConf.LatencyTimer = 255; status = p_I2C_GetNumChannels(&channels); APP_CHECK_STATUS(status); printf("Number of available I2C channels = %d\n",(int)channels); if(channels>0) { for(i=0;i<channels;i++) { status = p_I2C_GetChannelInfo(i,&devList); APP_CHECK_STATUS(status); printf("Information on channel number %u:\n",(unsigned int)i); printf(" Flags=0x%x\n",devList.Flags); printf(" Type=0x%x\n",devList.Type); printf(" ID=0x%x\n",devList.ID); printf(" LocId=0x%x\n",devList.LocId); printf(" SerialNumber=%s\n",devList.SerialNumber); printf(" Description=%s\n",devList.Description); printf(" ftHandle=0x%p\n",devList.ftHandle); } status = p_I2C_OpenChannel(CHANNEL_TO_OPEN,&ftHandle); APP_CHECK_STATUS(status); printf("\nhandle=0x%p status=%d\n",ftHandle,(unsigned int)status); status = p_I2C_InitChannel(ftHandle,&channelConf); APP_CHECK_STATUS(status); // **新增功能**: 在开始交互前,先读取并显示芯片当前状态 status = read_mcp4725_status(); if (status != FT_OK) { printf("Could not communicate with MCP4725. Please check wiring and address.\n"); p_I2C_CloseChannel(ftHandle); cleanup_library(); #ifdef _WIN32 system("pause"); #endif return 1; } // 调用交互式测试函数 TestDeviceMCP4725(); status = p_I2C_CloseChannel(ftHandle); } cleanup_library(); #ifdef _WIN32 system("pause"); #endif return 0; }
4、打开工程
5、编译运行
运行截图:
拔掉I2C设备:
测试通过。
附上可直接运行的成品:Debug.zip