FT232H eeprom programming 시리얼 넘버 변경 예제

이번 글은 ft232h의 eeprom에 저장된 데이터 중 시리얼 넘버를 수정하는 예제 소스를 싣고 있다.
 

FT232H에는 EEPROM이 들어있고, 이 EEPROM에는 USB 장치의 속성 정보들이 저장되어 있다. 우리가 윈도우에서 장치 관리자를 통해 USB 장치의 속성을 볼 때 나오는 그런 정보들이 들어있으며, EEPROM에 들어있는 정보는 "ftd2xx.h" 헤더 파일에는 아래와 같이 선언되어 있다.
//
// structure to hold program data for FT_EE_Program, FT_EE_ProgramEx, FT_EE_Read 
// and FT_EE_ReadEx functions
//
typedef struct ft_program_data {

DWORD Signature1; // Header - must be 0x00000000 
DWORD Signature2; // Header - must be 0xffffffff
DWORD Version; // Header - FT_PROGRAM_DATA version
// 0 = original
// 1 = FT2232 extensions
// 2 = FT232R extensions
// 3 = FT2232H extensions
// 4 = FT4232H extensions
// 5 = FT232H extensions

WORD VendorId; // 0x0403
WORD ProductId; // 0x6001
char *Manufacturer; // "FTDI"
char *ManufacturerId; // "FT"
char *Description; // "USB HS Serial Converter"
char *SerialNumber; // "FT000001" if fixed, or NULL
WORD MaxPower; // 0 < MaxPower <= 500
WORD PnP; // 0 = disabled, 1 = enabled
WORD SelfPowered; // 0 = bus powered, 1 = self powered
WORD RemoteWakeup; // 0 = not capable, 1 = capable
//
// Rev4 (FT232B) extensions
//
UCHAR Rev4; // non-zero if Rev4 chip, zero otherwise
UCHAR IsoIn; // non-zero if in endpoint is isochronous
UCHAR IsoOut; // non-zero if out endpoint is isochronous
UCHAR PullDownEnable; // non-zero if pull down enabled
UCHAR SerNumEnable; // non-zero if serial number to be used
UCHAR USBVersionEnable; // non-zero if chip uses USBVersion
WORD USBVersion; // BCD (0x0200 => USB2)
//
// Rev 5 (FT2232) extensions
//
UCHAR Rev5; // non-zero if Rev5 chip, zero otherwise
UCHAR IsoInA; // non-zero if in endpoint is isochronous
UCHAR IsoInB; // non-zero if in endpoint is isochronous
UCHAR IsoOutA; // non-zero if out endpoint is isochronous
UCHAR IsoOutB; // non-zero if out endpoint is isochronous
UCHAR PullDownEnable5; // non-zero if pull down enabled
UCHAR SerNumEnable5; // non-zero if serial number to be used
UCHAR USBVersionEnable5; // non-zero if chip uses USBVersion
WORD USBVersion5; // BCD (0x0200 => USB2)
UCHAR AIsHighCurrent; // non-zero if interface is high current
UCHAR BIsHighCurrent; // non-zero if interface is high current
UCHAR IFAIsFifo; // non-zero if interface is 245 FIFO
UCHAR IFAIsFifoTar; // non-zero if interface is 245 FIFO CPU target
UCHAR IFAIsFastSer; // non-zero if interface is Fast serial
UCHAR AIsVCP; // non-zero if interface is to use VCP drivers
UCHAR IFBIsFifo; // non-zero if interface is 245 FIFO
UCHAR IFBIsFifoTar; // non-zero if interface is 245 FIFO CPU target
UCHAR IFBIsFastSer; // non-zero if interface is Fast serial
UCHAR BIsVCP; // non-zero if interface is to use VCP drivers
//
// Rev 6 (FT232R) extensions
//
UCHAR UseExtOsc; // Use External Oscillator
UCHAR HighDriveIOs; // High Drive I/Os
UCHAR EndpointSize; // Endpoint size
UCHAR PullDownEnableR; // non-zero if pull down enabled
UCHAR SerNumEnableR; // non-zero if serial number to be used
UCHAR InvertTXD; // non-zero if invert TXD
UCHAR InvertRXD; // non-zero if invert RXD
UCHAR InvertRTS; // non-zero if invert RTS
UCHAR InvertCTS; // non-zero if invert CTS
UCHAR InvertDTR; // non-zero if invert DTR
UCHAR InvertDSR; // non-zero if invert DSR
UCHAR InvertDCD; // non-zero if invert DCD
UCHAR InvertRI; // non-zero if invert RI
UCHAR Cbus0; // Cbus Mux control
UCHAR Cbus1; // Cbus Mux control
UCHAR Cbus2; // Cbus Mux control
UCHAR Cbus3; // Cbus Mux control
UCHAR Cbus4; // Cbus Mux control
UCHAR RIsD2XX; // non-zero if using D2XX driver
//
// Rev 7 (FT2232H) Extensions
//
UCHAR PullDownEnable7; // non-zero if pull down enabled
UCHAR SerNumEnable7; // non-zero if serial number to be used
UCHAR ALSlowSlew; // non-zero if AL pins have slow slew
UCHAR ALSchmittInput; // non-zero if AL pins are Schmitt input
UCHAR ALDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR AHSlowSlew; // non-zero if AH pins have slow slew
UCHAR AHSchmittInput; // non-zero if AH pins are Schmitt input
UCHAR AHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR BLSlowSlew; // non-zero if BL pins have slow slew
UCHAR BLSchmittInput; // non-zero if BL pins are Schmitt input
UCHAR BLDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR BHSlowSlew; // non-zero if BH pins have slow slew
UCHAR BHSchmittInput; // non-zero if BH pins are Schmitt input
UCHAR BHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR IFAIsFifo7; // non-zero if interface is 245 FIFO
UCHAR IFAIsFifoTar7; // non-zero if interface is 245 FIFO CPU target
UCHAR IFAIsFastSer7; // non-zero if interface is Fast serial
UCHAR AIsVCP7; // non-zero if interface is to use VCP drivers
UCHAR IFBIsFifo7; // non-zero if interface is 245 FIFO
UCHAR IFBIsFifoTar7; // non-zero if interface is 245 FIFO CPU target
UCHAR IFBIsFastSer7; // non-zero if interface is Fast serial
UCHAR BIsVCP7; // non-zero if interface is to use VCP drivers
UCHAR PowerSaveEnable; // non-zero if using BCBUS7 to save power for self-powered designs
//
// Rev 8 (FT4232H) Extensions
//
UCHAR PullDownEnable8; // non-zero if pull down enabled
UCHAR SerNumEnable8; // non-zero if serial number to be used
UCHAR ASlowSlew; // non-zero if A pins have slow slew
UCHAR ASchmittInput; // non-zero if A pins are Schmitt input
UCHAR ADriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR BSlowSlew; // non-zero if B pins have slow slew
UCHAR BSchmittInput; // non-zero if B pins are Schmitt input
UCHAR BDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR CSlowSlew; // non-zero if C pins have slow slew
UCHAR CSchmittInput; // non-zero if C pins are Schmitt input
UCHAR CDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR DSlowSlew; // non-zero if D pins have slow slew
UCHAR DSchmittInput; // non-zero if D pins are Schmitt input
UCHAR DDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR ARIIsTXDEN; // non-zero if port A uses RI as RS485 TXDEN
UCHAR BRIIsTXDEN; // non-zero if port B uses RI as RS485 TXDEN
UCHAR CRIIsTXDEN; // non-zero if port C uses RI as RS485 TXDEN
UCHAR DRIIsTXDEN; // non-zero if port D uses RI as RS485 TXDEN
UCHAR AIsVCP8; // non-zero if interface is to use VCP drivers
UCHAR BIsVCP8; // non-zero if interface is to use VCP drivers
UCHAR CIsVCP8; // non-zero if interface is to use VCP drivers
UCHAR DIsVCP8; // non-zero if interface is to use VCP drivers
//
// Rev 9 (FT232H) Extensions
//
UCHAR PullDownEnableH; // non-zero if pull down enabled
UCHAR SerNumEnableH; // non-zero if serial number to be used
UCHAR ACSlowSlewH; // non-zero if AC pins have slow slew
UCHAR ACSchmittInputH; // non-zero if AC pins are Schmitt input
UCHAR ACDriveCurrentH; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR ADSlowSlewH; // non-zero if AD pins have slow slew
UCHAR ADSchmittInputH; // non-zero if AD pins are Schmitt input
UCHAR ADDriveCurrentH; // valid values are 4mA, 8mA, 12mA, 16mA
UCHAR Cbus0H; // Cbus Mux control
UCHAR Cbus1H; // Cbus Mux control
UCHAR Cbus2H; // Cbus Mux control
UCHAR Cbus3H; // Cbus Mux control
UCHAR Cbus4H; // Cbus Mux control
UCHAR Cbus5H; // Cbus Mux control
UCHAR Cbus6H; // Cbus Mux control
UCHAR Cbus7H; // Cbus Mux control
UCHAR Cbus8H; // Cbus Mux control
UCHAR Cbus9H; // Cbus Mux control
UCHAR IsFifoH; // non-zero if interface is 245 FIFO
UCHAR IsFifoTarH; // non-zero if interface is 245 FIFO CPU target
UCHAR IsFastSerH; // non-zero if interface is Fast serial
UCHAR IsFT1248H; // non-zero if interface is FT1248
UCHAR FT1248CpolH; // FT1248 clock polarity - clock idle high (1) or clock idle low (0)
UCHAR FT1248LsbH; // FT1248 data is LSB (1) or MSB (0)
UCHAR FT1248FlowControlH; // FT1248 flow control enable
UCHAR IsVCPH; // non-zero if interface is to use VCP drivers
UCHAR PowerSaveEnableH; // non-zero if using ACBUS7 to save power for self-powered designs
} FT_PROGRAM_DATA, *PFT_PROGRAM_DATA;



FT232H 장치에서 EEPROM 정보를 읽고 쓰는 함수 소스는 아래와 같다. 
드라이버 및 라이브러리 다운로드 및 프로젝트 설정은 https://ryanclaire.blogspot.com/2020/06/ft232h-d2xx-gpio-example.html를 참조하면 된다.

#include "ftd2xx.h"

데이터 구조체 선언

typedef struct _ft232hdev_t
{
long locid;
FT_PROGRAM_DATA EEPData;
char ManufacturerBuffer[32];
char ManufacturerIdBuffer[16];
char DescriptionBuffer[64];
char SerialNumberBuffer[16];
}ft232hdev_t;

typedef struct _ft232h_dev_list_t
{
int counter;
ft232hdev_t *devs;
}_ft232h_dev_list;


PC에 연결된 FT232H 장치 찾는 함수

_ft232h_dev_list *probe_FT232H()
{
int i = 0;
int availdevs = 0;
DWORD numDevs = 0;
FT_STATUS ftStatus = 0;
_ft232h_dev_list *list = NULL;

ftStatus = FT_CreateDeviceInfoList(&numDevs);
if (ftStatus != FT_OK || numDevs == 0)
{
return NULL;
}

list = (_ft232h_dev_list*)malloc(sizeof(_ft232h_dev_list));
if (!list)
{
return NULL;
}

list->counter = numDevs;
list->devs = (ft232hdev_t*)malloc(sizeof(ft232hdev_t)*numDevs);
if (!list->devs)
{
free(list);
return NULL;
}

memset(list->devs, 0, sizeof(ft232hdev_t)*numDevs);

for (i = 0; i < numDevs && i<16; i++)
{
DWORD Flags;
DWORD ID;
DWORD Type;
DWORD LocId;
char sn[16];

ftStatus = FT_GetDeviceInfoDetail(i, &Flags, &Type, &ID, &LocId, sn, NULL, NULL);
if (ftStatus == FT_OK)
{
list->devs[i].locid = LocId;
memcpy(list->devs[i].SerialNumberBuffer, sn, sizeof(char) * 16);
/*
Manufacturer/ ManufacturerId/ Description/ SerialNumber의 버퍼는 따로 지정해야 줘야 한다.
*/
list->devs[i].EEPData.Signature1 = 0x00000000;
list->devs[i].EEPData.Signature2 = 0xffffffff;
list->devs[i].EEPData.Manufacturer = list->devs[i].ManufacturerBuffer;
list->devs[i].EEPData.ManufacturerId = list->devs[i].ManufacturerIdBuffer;
list->devs[i].EEPData.Description = list->devs[i].DescriptionBuffer;
list->devs[i].EEPData.SerialNumber = list->devs[i].SerialNumberBuffer;
}
}

return list;
}


FT232H 장치의 EEPROM 정보를 FT_PROGRAM_DATA 구조체 데이터에 읽어오는 함수

FT_STATUS GetEEPROMInfo(ft232hdev_t *dev)
{
FT_HANDLE ftdiHandle = NULL;
FT_STATUS ftStatus = FT_INVALID_HANDLE;

if (!dev) return FT_INVALID_ARGS;

if (dev->locid)
{
ftStatus = FT_OpenEx((PVOID)dev->locid, FT_OPEN_BY_LOCATION, &ftdiHandle);
}
else if (dev->SerialNumberBuffer[0])
{
ftStatus = FT_OpenEx(dev->SerialNumberBuffer, FT_OPEN_BY_SERIAL_NUMBER, &ftdiHandle);
}

if (ftStatus != FT_OK)
{
if (ftdiHandle)
{
FT_Close(ftdiHandle);
ftdiHandle = NULL;
}
return ftStatus;
}

/**/
ftStatus = FT_EE_Read(ftdiHandle, &dev->EEPData);
if (ftStatus == FT_OK)
{
printf("VendorId : 0x%x \r\n", dev->EEPData.VendorId);
printf("ProductId : 0x%x \r\n", dev->EEPData.ProductId);
printf("SerialNumber : %s \r\n", dev->EEPData.SerialNumber);
printf("Description : %s \r\n", dev->EEPData.Description);
}
else
{
printf("EEPROM Read Fail \r\n");
}

FT_Close(ftdiHandle);

return ftStatus;
}

FT232H 장치의 EEPROM에 데이터를 저장하는 함수

FT_STATUS ProgramEEPROM(ft232hdev_t *dev)
{
FT_HANDLE ftdiHandle = NULL;
FT_STATUS ftStatus = FT_INVALID_HANDLE;


if (!dev) return FT_INVALID_ARGS;

if (dev->locid)
{
ftStatus = FT_OpenEx((PVOID)dev->locid, FT_OPEN_BY_LOCATION, &ftdiHandle);
}
else if (dev->SerialNumberBuffer[0])
{
ftStatus = FT_OpenEx(dev->SerialNumberBuffer, FT_OPEN_BY_SERIAL_NUMBER, &ftdiHandle);
}

if (ftStatus != FT_OK)
{
if (ftdiHandle)
{
FT_Close(ftdiHandle);
ftdiHandle = NULL;
}
return ftStatus;
}


ftStatus = FT_EE_Program(ftdiHandle, &dev->EEPData);
if (ftStatus == FT_OK)
{
printf("Program EEPROM SUCCESS !! \r\n");
}
else
{
printf("Program EEPROM fail !! \r\n");
}


FT_Close(ftdiHandle);

return ftStatus;
}


FT232H 시리얼 넘버 변경 예제 소스

void reprogram_ft232h_eeprom()
{
_ft232h_dev_list *list = probe_FT232H();

if (list)
{
for (int i = 0; i < list->counter; i++)
{
printf("GetEEPROMInfo locid %d\r\n", list->devs[i].locid);
GetEEPROMInfo(&list->devs[i]);
}

// 
sprintf_s(list->devs[0].ManufacturerBuffer, "FTDI");
sprintf_s(list->devs[0].ManufacturerIdBuffer, "FT");
sprintf_s(list->devs[0].DescriptionBuffer, "USB HS Serial Converter");
sprintf_s(list->devs[0].SerialNumberBuffer, "RYANCLAIRE");

printf("ProgramEEPROM locid %d\r\n", list->devs[0].locid);
ProgramEEPROM(&list->devs[0]);

Sleep(100);
printf("GetEEPROMInfo locid %d\r\n", list->devs[0].locid);
GetEEPROMInfo(&list->devs[0]);
}

safe_free(list);
}

위 예제 소스의 실행 결과는 아래와 같다. 


장치 관리자에서도 데이터의 Serial Number가 바뀐 것을 알 수 있다.
 


관련 글 

댓글

이 블로그의 인기 게시물

간단한 cfar 알고리즘에 대해

windows에서 간단하게 크롬캐스트(Chromecast)를 통해 윈도우 화면 미러링 방법

간단한 칼만 필터(Kalman Filter) 소스 코드와 사용 예제

안드로이드(android) 전체 화면 시계 앱(clock app) 예제 코드

mkfs.fat Device or resource busy 에러 해결법