使用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


