Recently I've implemented lightweight header library to encapsulate interfacing with FTDI EVE chip, namely, FT800, in templated OO manner. It should compile in modern embedded x86/ARM toolchains, like ones Keil or C++ Builder have.
The idea is as follows - while your core framework provides standard IO bus interface, like mine esfwxe, via EseChannel templated abstract facade, the rest is done in modular manner by EVE library classes.
Short "What is this?" list:
- EsFtEveCore.h - the chip-core template class, which is responsible for instantiation and exposing chip-specific types related to memory region constants, chip control registers, known control commands, and common IO bus functionality, like special nested RAII classes for scoped write and read, as well as simplified wrappers for N-byte read and write, and 'HOST' - flavoured commands (see FT EVE Programming guide).
- EsFtEvePlatform.h - template class standing for Platform (i.e. board) specific implementations, based on Core and Metrics types. While the Core was described above, the Metrics type should define LCD-specific constants, as follows (I used TM035KBH11 LCD panel with resistive touchpad):
Click to expand: EsFtEveMetricsTM035KBH11.h
#ifndef _es_ft_eve_metrics_tm035kbh11_h_ #define _es_ft_eve_metrics_tm035kbh11_h_ struct EsFtEveMetricsTM035KBH11 { static const esU32 DISPLAY_W = 320; static const esU32 DISPLAY_H = 240; static const esU32 PCLK = 3; static const esU32 HCYCLE = 408; static const esU32 HOFFS = 70; static const esU32 HSYNC0 = 0; static const esU32 HSYNC1 = 10; static const esU32 VCYCLE = 263; static const esU32 VOFFS = 13; static const esU32 VSYNC0 = 0; static const esU32 VSYNC1 = 2; static const esU32 SWIZZLE = 2; static const esU32 PCLK_POL = 0; static const esU32 CSPREAD = 1; static const esU32 DITHER = 1; static const esU32 RESISTANCE_THRESHOLD = 1200; private: EsFtEveMetricsTM035KBH11() = delete; EsFtEveMetricsTM035KBH11(const EsFtEveMetricsTM035KBH11&) = delete; EsFtEveMetricsTM035KBH11& operator=(const EsFtEveMetricsTM035KBH11&) = delete; }; #endif // _es_ft_eve_metrics_tm035kbh11_h_
- The FT EVE have multiple co-processor engines on-board, like GPU, touchscreen, Audio, so for each engine, there is separate API template should be provided, if needed. If specific engine is not used in your project, specific dummy EsFtEveNullApi.h is provided with an empty class inside, which may be used as a stub template parameter.
- The final glue template class is defined EsFtEveHardware.h. It provides complete FT EVE hardware, which is built upon specific core, platform, GPU, and other APIs.
/// Complete EVE hardware platform template /// template < typename EveCoreT, typename EvePlatformApiT, typename EveGpuApiT, typename EveAudioApiT, typename EveTouchApiT > class EsFtEveHardware
Following is an example of how to "assemple" complete implementation for FTDI EVE FT800 chip. All FT800 API are used, so no NullApi include is necessary. Distinct FT800 - related definitions and APIs provided in files with FT800 suffixes. The IO bus concept is built using FTDI MPSSE upon EseChannel.h concept from my embedded library core. That's fine for PC-to-embedded tesbed, an may be wrapped around any bare-metal or RTOS-aware SPI driver.
Click to expand snippet:
#include <esfwxe/cpp/concept/EseChannel.h>
#include "EsFtEveCore.h"
//#include "EsFtEveNullApi.h"
#include "EsFtEvePlatform.h"
#include "EsFtEveMetricsTM035KBH11.h"
#include "EsFtEveRegistersFT800.h"
#include "EsFtEveMemoryFT800.h"
#include "EsFtEveCommandsFT800.h"
#include "EsFtEveGpuFT800.h"
#include "EsFtEveAudioFT800.h"
#include "EsFtEveTouchFT800.h"
#include "EsFtEveHardware.h"
//---------------------------------------------------------------------------
/// Implementor of EseChannelIntf abstraction over EsFtdiDeviceMpsseSpi class
class EsFtMpsseSpiChannel : public EseChannel<
EsFtMpsseSpiChannel,
EsFtdiMpsseSpiIntf::Ptr
>
{
public:
EsFtMpsseSpiChannel(const EsFtdiMpsseSpiIntf::Ptr& io) ESE_NOTHROW :
EseChannel(0, 0),
m_io(io)
{}
/// EseChannel interface public services
///
virtual bool isOk() const ESE_NOTHROW { return m_io; }
virtual rtosStatus lock(esU32 tmo = rtosMaxDelay ) ESE_NOTHROW { return rtosOK; }
virtual rtosStatus unlock() ESE_NOTHROW { return rtosOK; }
virtual esU32 dataXferTimeoutEstimateGet(size_t len) const ESE_NOTHROW { return 0; }
virtual int ioCtlSet(esU32 ctl, void* data) ESE_NOTHROW
{
if(ctlRate == ctl)
{
esU32 rate = reinterpret_cast<esU32>(data);
m_io->clockRateSet(rate);
m_io->configApply();
return rtosOK;
}
return EseChannelIntf::ioCtlSet(ctl, data);
}
EsFtdiMpsseSpiIntf::Ptr& doHandleGet()
{
return m_io;
}
const EsFtdiMpsseSpiIntf::Ptr& doHandleGet() const
{
return m_io;
}
protected:
virtual bool doInit() ESE_NOTHROW { return true; }
virtual void doUninit() ESE_NOTHROW {}
virtual bool doCheckConfigured() ESE_NOTHROW
{
m_io->clockRateSet(8000000);
m_io->latencyTimerSet(2);
m_io->modeSet(
EsFtdiMpsseSpiIntf::CPOL_CPHA::MODE0
);
m_io->csLineCtlSet(
EsFtdiMpsseSpiIntf::CS_LINE::ABCD_DBUS3
);
m_io->csLineActiveHighSet( ///< Active CS LOW
false
);
// Set GPIO pins direction:
// AD5 - INT to input
// AD7 - PDN_PIN to output
// the rest is controlled by SPI config
//
m_io->pinsDirInitSet(
0x80
);
m_io->pinsDirFinalSet(
0x80
);
// Set initial power down pin to low (activate PDN)
m_io->pinsStateInitSet(
0
);
m_io->pinsStateFinalSet(
0
);
m_io->configApply();
return true;
}
virtual bool doActivate() ESE_NOTHROW
{
EsFtdiDeviceIntf::Ptr dev = m_io;
if( dev && dev->open() )
{
// Set CS line to inactive state
m_io->csSet(false);
return true;
}
return false;
}
virtual void doDeactivate() ESE_NOTHROW
{
EsFtdiDeviceIntf::Ptr dev = m_io;
dev->close();
}
virtual size_t doReceive(esU8* data, size_t toRead, esU32 ES_UNUSED(tmo)) ESE_NOTHROW {
return m_io->read(
data,
toRead,
EsFtdiMpsseSpiIntf::TRANSFER_OPTIONS_SIZE_IN_BYTES|
EsFtdiMpsseSpiIntf::TRANSFER_OPTIONS_CHIPSELECT_ENABLE
);
}
virtual size_t doSend(const esU8* data, size_t toWrite, esU32 ES_UNUSED(tmo)) ESE_NOTHROW {
return m_io->write(
data,
toWrite,
EsFtdiMpsseSpiIntf::TRANSFER_OPTIONS_SIZE_IN_BYTES|
EsFtdiMpsseSpiIntf::TRANSFER_OPTIONS_CHIPSELECT_ENABLE
);
}
virtual bool doTxBatchBegin() ESE_NOTHROW {
return true;
}
virtual void doTxBatchEnd(bool ok) ESE_NOTHROW {
m_io->csSet(false);
}
virtual bool doRxBatchBegin() ESE_NOTHROW {
return true;
}
virtual void doRxBatchEnd(bool ok) ESE_NOTHROW {
m_io->csSet(false);
}
protected:
EsFtdiMpsseSpiIntf::Ptr m_io;
private:
EsFtMpsseSpiChannel() = delete;
EsFtMpsseSpiChannel(const EsFtMpsseSpiChannel&) = delete;
EsFtMpsseSpiChannel& operator=(const EsFtMpsseSpiChannel&) = delete;
};
typedef EsFtEveCore<
EsFtMpsseSpiChannel,
EsFtEveMemoryFT800,
EsFtEveRegistersFT800,
EsFtEveCommandsFT800
>
EsFtEveCoreFT800;
typedef EsFtEveHardware<
EsFtEveCoreFT800,
EsFtEvePlatform<
EsFtEveCoreFT800,
EsFtEveMetricsTM035KBH11
>,
EsFtEveGpuFT800<
EsFtEveCoreFT800,
EsFtEvePlatform<
EsFtEveCoreFT800,
EsFtEveMetricsTM035KBH11
>
>,
EsFtEveAudioFT800< EsFtEveCoreFT800 >,
EsFtEveTouchFT800< EsFtEveCoreFT800 >
>
EsFtEveHwFT800;
After FTEVE hardware compound type is defined, the rest is as simple as follows:
FTDI EVE header library usage snippet.
EsFtMpsseSpiChannel chnl(spi);
EsFtEveHwFT800 eve(chnl);
chnl.init();
chnl.activate();
eve.apiPlatform.lcdReset(true);
eve.apiPlatform.powerModeSet(
EsFtEvePowerMode::On
);
eve.apiPlatform.bklightLevelSet(0);
eve.apiPlatform.lcdReset(false);
eve.apiAudio.ssVolumeSet(255);
eve.apiPlatform.bklightLevelSet(8);
eve.apiGpu.cmdDlStart();
eve.apiGpu.cmd(CLEAR_COLOR_RGB(255,255,255));
eve.apiGpu.cmd(CLEAR_CST(1,1,1));
eve.apiGpu.cmd(COLOR_RGB(0,0,255));
eve.apiGpu.cmdTextDraw(160,120,28,OPT::CENTERX|OPT::CENTERY,(const esU8*)"Please, tap on a dot");
esU32 result = eve.apiGpu.cmdCalibrate();
eve.apiGpu.cmdLogoPlay();
eve.apiGpu.cmdWaitForCompletion(true); //< Special wait - read writePtr as well
EsThread::sleep(2000);
eve.apiGpu.dl(CLEAR_COLOR_RGB(0,0,128), true);
eve.apiGpu.dl(CLEAR_CST(1,1,1));
eve.apiGpu.dl(COLOR_RGB(255,255,255));
eve.apiGpu.dlPrimitiveBegin(PRIMITIVE::BITMAPS);
eve.apiGpu.dl(VERTEX2II(160, 110, 31, 'F'));
eve.apiGpu.dl(VERTEX2II(184, 110, 31, 'T'));
eve.apiGpu.dl(VERTEX2II(210, 110, 31, 'D'));
eve.apiGpu.dl(VERTEX2II(239, 110, 31, 'I'));
eve.apiGpu.dlPrimitiveEnd();
eve.apiGpu.dl(COLOR_RGB(160, 22, 22)); // change color to red
eve.apiGpu.dl(POINT_SIZE(320)); // set point size to 20 pixels in radius
eve.apiGpu.dlPrimitiveBegin(PRIMITIVE::POINTS); // start drawing points
eve.apiGpu.dl(VERTEX2II(100, 133, 0, 0)); // red point
eve.apiGpu.dlPrimitiveEnd();
eve.apiGpu.dlDisplay();
eve.apiGpu.dlSwap(DLSWAP::FRAME); //< Swap on next frame
The FTDI EVE headers are available for download here: