-
-
Save ericfont/67709eff564948c27aaf2bee0455538f to your computer and use it in GitHub Desktop.
Simple SPI example with libftdi and FTDI UM232H
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Quite minimal example showing how to configure MPSSE for SPI using libftdi | |
// compile like this: g++ minimal_spi.cpp -o minimal_spi -lftdi1 -Wno-deprecated | |
#include <libftdi1/ftdi.h> | |
#include <libftdi1/ftdi.hpp> | |
#include <usb.h> | |
#include <stdio.h> | |
#include <iostream> | |
#include <string.h> | |
#include <time.h> | |
#include <stdio.h> | |
#include <unistd.h> | |
// UM232H development module | |
#define VENDOR 0x0403 | |
#define PRODUCT 0x6014 | |
using namespace Ftdi; | |
namespace Pin { | |
// enumerate the AD bus for conveniance. | |
enum bus_t { | |
SK = 0x01, // ADBUS0, SPI data clock | |
DO = 0x02, // ADBUS1, SPI data out | |
DI = 0x04, // ADBUS2, SPI data in | |
CS = 0x08, // ADBUS3, SPI chip select | |
L0 = 0x10, // ADBUS4, general-ourpose i/o, GPIOL0 | |
L1 = 0x20, // ADBUS5, general-ourpose i/o, GPIOL1 | |
L2 = 0x40, // ADBUS6, general-ourpose i/o, GPIOL2 | |
l3 = 0x80 // ADBUS7, general-ourpose i/o, GPIOL3 | |
}; | |
} | |
// Set these pins high | |
const unsigned char pinInitialState = Pin::CS|Pin::L0|Pin::L1; | |
// Use these pins as outputs | |
const unsigned char pinDirection = Pin::SK|Pin::DO|Pin::CS|Pin::L0|Pin::L1; | |
uint64_t getRealtimeTick() { | |
struct timespec ts; | |
uint64_t theTick = 0U; | |
clock_gettime( CLOCK_REALTIME, &ts ); | |
theTick = ts.tv_nsec; | |
theTick += ts.tv_sec * 1000000000; | |
return theTick; | |
} | |
uint64_t startTick = 0; | |
uint64_t getRealtimeTick_sinceStart() { | |
return getRealtimeTick() - startTick; | |
} | |
int main(void) | |
{ | |
startTick = getRealtimeTick(); | |
printf("Starting nanosecond: %ld\n", startTick); | |
struct ftdi_version_info ftdi_version_info_return = ftdi_get_library_version(); | |
std::cout << "FTDI Version info: " << ftdi_version_info_return.version_str << '\n'; | |
// initialize | |
struct ftdi_context ftdi; | |
int ftdi_status = 0; | |
ftdi_status = ftdi_init(&ftdi); | |
if ( ftdi_status != 0 ) { | |
std::cout << "Failed to initialize device\n"; | |
return 1; | |
} | |
ftdi_status = ftdi_usb_open(&ftdi, VENDOR, PRODUCT); | |
if ( ftdi_status != 0 ) { | |
std::cout << "Can't open device. Got error\n" | |
<< ftdi_get_error_string(&ftdi) << '\n'; | |
return 1; | |
} | |
ftdi_usb_reset(&ftdi); | |
ftdi_set_interface(&ftdi, INTERFACE_ANY); | |
const int nBitsPerFrame = 16; | |
const int nFramesPerTransfer = 20; | |
const int nBitsPerTransfer = nBitsPerFrame * nFramesPerTransfer; | |
const int nBytesPerBuffer = nBitsPerTransfer * 10; | |
int ftdi_set_latency_timer_return = ftdi_set_latency_timer(&ftdi, 1); // read timeout in milliseconds | |
if (ftdi_set_latency_timer_return == -1 ) std::cout << "latency out of range"; | |
if (ftdi_set_latency_timer_return == -2 ) std::cout << "unable to set latency timer"; | |
if (ftdi_set_latency_timer_return == -3 ) std::cout << "USB device unavailable"; | |
// ftdi_write_data_set_chunksize(&ftdi, nBytesPerBuffer*10);//nBitsPerTransfer*10); | |
// ftdi_read_data_set_chunksize(&ftdi, nBytesPerBuffer);//nBitsPerTransfer*10); | |
ftdi_set_bitmode(&ftdi, 0, 0); // reset | |
ftdi_set_bitmode(&ftdi, 0, BITMODE_MPSSE); // enable mpsse on all bits | |
int ftdi_tcioflush_return = ftdi_tcioflush(&ftdi); | |
if (ftdi_tcioflush_return == -1 ) std::cout << "read buffer purge failed"; | |
if (ftdi_tcioflush_return == -2 ) std::cout << "write buffer purge failed"; | |
if (ftdi_tcioflush_return == -3 ) std::cout << "USB device unavailable"; | |
usleep(50000); // sleep 50 ms for setup to complete | |
// Setup MPSSE; Operation code followed by 0 or more arguments. | |
unsigned char writeBuf[nBytesPerBuffer] = {0}; | |
unsigned int icmd = 0; | |
writeBuf[icmd++] = DIS_DIV_5; // opcode: set clk divisor | |
writeBuf[icmd++] = TCK_DIVISOR; // opcode: set clk divisor according to next two bytes, with equation: 30 MHz / (divisor+1) | |
writeBuf[icmd++] = 0x02; // divisor: low bit. | |
writeBuf[icmd++] = 0x00; // divisor: high bit. | |
writeBuf[icmd++] = DIS_ADAPTIVE; // opcode: disable adaptive clocking | |
writeBuf[icmd++] = DIS_3_PHASE; // opcode: disable 3-phase clocking | |
writeBuf[icmd++] = SET_BITS_LOW; // opcode: set low bits (ADBUS[0-7]) | |
writeBuf[icmd++] = pinInitialState; // argument: inital pin states | |
writeBuf[icmd++] = pinDirection; // argument: pin direction | |
// Write the setup to the chip. | |
if ( ftdi_write_data(&ftdi, writeBuf, icmd) != icmd ) { | |
std::cout << "Write failed\n"; | |
} | |
// need to tciflush before reading | |
int ftdi_tciflush_return = ftdi_tciflush(&ftdi); | |
if (ftdi_tciflush_return == -1) std::cout << "read buffer purge failed\n"; | |
if (ftdi_tciflush_return == -2) std::cout << "USB device unavailable\n"; | |
// zero the buffer for good measure | |
memset(writeBuf, 0, sizeof(writeBuf)); | |
icmd = 0; | |
for(int frame_counter = 0; frame_counter < nFramesPerTransfer; frame_counter++ ) { | |
for (int i=0; i<nBitsPerFrame; i++ ) { | |
// Next three commands sets the GPIOL0 pin low. Pulling CS low. | |
writeBuf[icmd++] = SET_BITS_LOW; | |
writeBuf[icmd++] = pinInitialState & ~Pin::CS; | |
writeBuf[icmd++] = pinDirection; | |
// commands to write and read one byte in SPI0 (polarity = phase = 0) mode | |
writeBuf[icmd++] = MPSSE_DO_WRITE | MPSSE_WRITE_NEG | MPSSE_DO_READ; | |
writeBuf[icmd++] = 0x00; // length-1 low byte | |
writeBuf[icmd++] = 0x00; // length-1 high byte | |
writeBuf[icmd++] = i; // byte to send | |
// Next three commands sets the GPIOL0 pin high. Pulling CS high. | |
writeBuf[icmd++] = SET_BITS_LOW; | |
writeBuf[icmd++] = pinInitialState | Pin::CS; | |
writeBuf[icmd++] = pinDirection; | |
} | |
} | |
unsigned char read1_buf[nBytesPerBuffer] = {0}; | |
unsigned char read2_buf[nBytesPerBuffer] = {0}; | |
unsigned char read3_buf[nBytesPerBuffer] = {0}; | |
// submit a new async write | |
struct ftdi_transfer_control *tc_write1 = ftdi_write_data_submit(&ftdi, writeBuf, icmd); | |
// submit a new async write | |
struct ftdi_transfer_control *tc_write2 = ftdi_write_data_submit(&ftdi, writeBuf, icmd); | |
// submit a new async write | |
struct ftdi_transfer_control *tc_write3 = ftdi_write_data_submit(&ftdi, writeBuf, icmd); | |
uint64_t totalBytesRead = 0; | |
while (true) { | |
// wait for write1 to complete | |
/* int write1_transfer_done_return = ftdi_transfer_data_done(tc_write1); | |
if (write1_transfer_done_return < 0 ) { | |
printf("Async Write1 failed with ERROR code: %d\n", write1_transfer_done_return); | |
break; | |
}*/ | |
// submit a new async write1 | |
tc_write1 = ftdi_write_data_submit(&ftdi, writeBuf, icmd); | |
// read the previous write1's data | |
int ftdi_read1_data_return = ftdi_read_data(&ftdi, read1_buf, nBitsPerTransfer); | |
if (ftdi_read1_data_return < 0 ) { | |
printf("Read1 failed with ERROR code: %d\n", ftdi_read1_data_return); | |
break; | |
} | |
totalBytesRead += ftdi_read1_data_return; | |
std::cout << "Read1 " << std::dec << ftdi_read1_data_return << " bytes: 0x" << std::hex << (unsigned int)read1_buf[0] << " 0x" << std::hex << (unsigned int)read1_buf[1] << " ... totalBytesRead: " << std::dec << totalBytesRead << ".\n"; | |
// wait for write2 to complete | |
/* int write2_transfer_done_return = ftdi_transfer_data_done(tc_write2); | |
if (write2_transfer_done_return < 0 ) { | |
printf("Async Write2 failed with ERROR code: %d\n", write2_transfer_done_return); | |
break; | |
}*/ | |
// submit a new async write2 | |
tc_write2 = ftdi_write_data_submit(&ftdi, writeBuf, icmd); | |
// read the previous write2's data | |
int ftdi_read2_data_return = ftdi_read_data(&ftdi, read2_buf, nBitsPerTransfer); | |
if (ftdi_read2_data_return < 0 ) { | |
printf("Read2 failed with ERROR code: %d\n", ftdi_read2_data_return); | |
break; | |
} | |
totalBytesRead += ftdi_read2_data_return; | |
std::cout << "Read2 " << std::dec << ftdi_read2_data_return << " bytes: 0x" << std::hex << (unsigned int)read2_buf[0] << " 0x" << std::hex << (unsigned int)read2_buf[1] << " ... totalBytesRead: " << std::dec << totalBytesRead << ".\n"; | |
// wait for write3 to complete | |
/* int write3_transfer_done_return = ftdi_transfer_data_done(tc_write3); | |
if (write3_transfer_done_return < 0 ) { | |
printf("Async Write3 failed with ERROR code: %d\n", write3_transfer_done_return); | |
break; | |
}*/ | |
// submit a new async write3 | |
tc_write3 = ftdi_write_data_submit(&ftdi, writeBuf, icmd); | |
// read the previous write1's data | |
int ftdi_read3_data_return = ftdi_read_data(&ftdi, read3_buf, nBitsPerTransfer); | |
if (ftdi_read3_data_return < 0 ) { | |
printf("Read3 failed with ERROR code: %d\n", ftdi_read3_data_return); | |
break; | |
} | |
totalBytesRead += ftdi_read3_data_return; | |
std::cout << "Read3 " << std::dec << ftdi_read3_data_return << " bytes: 0x" << std::hex << (unsigned int)read3_buf[0] << " 0x" << std::hex << (unsigned int)read3_buf[1] << " ... totalBytesRead: " << std::dec << totalBytesRead << ".\n"; | |
} | |
// close ftdi | |
ftdi_usb_reset(&ftdi); | |
ftdi_usb_close(&ftdi); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment