Hello Element14 community.
I welcome you to my blog with number 10. In previous nine blogs I described my path to completing my first gesture controlled game as part of Experimenting with Gesture Sensor Design Challenge. My final Gesture controlled Tetris implementation I have shown in previous ninth blog. In blog number one I described my plans as part of competition. In second and third blogs I shown MAX25405 Gesture Sensor Evaluation Kit from Hardware and Software point of view. In blogs four, five and six I shown my steps to implementing gesture-controlled game and finally, in blogs seven and eight I shown and described issues which I faced when playing with gesture sensor.
As I mentioned at the end of previous blog post I had to redesign my idea for second project. I originally want to develop second gesture-controlled game using Maxim Firmware Framework which I later realized as very hard. Instead, I decided to implement my own library. It is similar amount of work required. I decided to split low-level library and gesture detecting algorithm. In this blog I will show my low-level library which can control Maxim Integrated (ADI) MAX25405 Gesture Sensor by reading and writing its registers.
Library Design
I designed library very similarly to my MAX40080 Current Sense Amplifier library which I created as part previous competition. Library is designed as multiplatform, and platform specific stuff is separated to the files files with _PlatformSpecific suffix. Currently only support for MAX78000 MCU is provided. It is different MCU than MAX32620 which is bundled with the Kit. This time driver is more complicated because MAX25405 support two communication interfaces. MAX40080 supports only I2C but this time for MAX25405 I had to implement SPI also. There is also significant improvement in comparison to my previous MAX40080 Library. It allows you to instantiate multiple devices at the same time. Even more you can combine SPI and I2C devices. I don’t think it is very useful, but it is possible. For both SPI and I2C communication you can instantiate more devices. In case of I2C you specify I2C address of the instantiated device to the MAX25405_InitI2C function. It take two parameters. First specify pointer to MAX25405_Device which you should allocate on stack or as a global varibale. Second parameter is I2C address. For passing you can use constants MAX25405_I2CAddress_CS_Low_4F and MAX25405_I2CAddress_CS_High_50. Other address does not make very sense but of course you can pass and try them.
MAX25405_Device dev; MAX25405_Status status = MAX25405_InitI2C(&dev, MAX25405_I2CAddress_CS_High_50);
In case of SPI, initialization is similar but instead of address you pass number of Chip Select pin. Pin number is platform dependent and can be between 0 to 127 (not 255). Number meaning is platform specific. In case of my MAX78000FHTR implementation number 0 means Chip Select at GPIO P0.9 and 1 means Chip Select at GPIO 0.8. This choice is fixed and matches pinout of MAX25405EVKIT Shield Board connected to the MAX78000FTHR board. Evaluation Kit Shield Board allows connecting two sensor and my library supports it. Sensor numbered 1 on the shield is accessible by initializing with 0 and second sensor is accessible by initializing with value 1.
After initializing you can use all functions prefixed with MAX25405_. You always need to pass pointer to device structure for target device. All functions return status code. 0 means success. Other values can be used for determining error cause and possibly implementing advanced error correction logic like retry logic for certain errors. Note that when using SPI there are very few errors possible because SPI has no mechanism for detecting errors. Following list (from MAX25405.h file) contains all functions with their parameters.
MAX25405_Status MAX25405_InitI2C(MAX25405_Device* dev, MAX25405_I2CAddress address); MAX25405_Status MAX25405_InitSPI(MAX25405_Device* dev, uint8_t chipSelectPortNumber); MAX25405_Status MAX25405_Deinit(MAX25405_Device* dev); MAX25405_Status MAX25405_Reset(MAX25405_Device* dev); MAX25405_Status MAX25405_ForceSync(MAX25405_Device* dev); MAX25405_Status MAX25405_TriggerOneShoot(MAX25405_Device* dev); MAX25405_Status MAX25405_GetPendingInterrupts(MAX25405_Device* dev, MAX25405_Interrupt* pendingInterrupts); MAX25405_Status MAX25405_GetDefaultConfiguration(MAX25405_Configuration* config); MAX25405_Status MAX25405_GetConfiguration(MAX25405_Device* dev, MAX25405_Configuration* config); MAX25405_Status MAX25405_SetConfiguration(MAX25405_Device* dev, MAX25405_Configuration* config); MAX25405_Status MAX25405_GetSinglePixelData(MAX25405_Device* dev, int16_t* value, int column, int row); MAX25405_Status MAX25405_GetPixelsData(MAX25405_Device* dev, int16_t* values, int column, int row, int pixelsCount); MAX25405_Status MAX25405_GetAllPixelData(MAX25405_Device* dev, int16_t* values);
Github
Library is available online at Github: https://github.com/misaz/MAX25405-Library
Example
Recommended flow when using is Initialize, configure, wait for interrupt or poll and read pixel data. Following code example show the recommended way. For simplification it lacks error checks.
MAX25405_Device max25405dev; MAX25405_Configuration config; // driver initialization MAX25405_InitSPI(&max25405dev, 0); // sensor configuration MAX25405_GetDefaultConfiguration(&config); config.ledConfig.enableDrivePwmOutput = 1; MAX25405_SetConfiguration(&max25405dev, &config); // getting data while (1) { MAX25405_Interrupt interrupt = 0; MAX25405_GetPendingInterrupts(&max25405dev, &interrupt); if (interrupt & MAX25405_Interrupt_EndOfConversion) { int16_t data[60]; MAX25405_GetAllPixelData(&max25405dev, data); // process data here } }
First Design
For testing I connected sensor using the setup which I will use for my project. As I mentioned I will use MAX78000 MCU on MAX78000FTHR board. This board mates with Shield board of the MAX25405. Connection is very easy. I also connected Display to the design now. I connected display to the Bridgetek ME817 board and connected SPI Interface of the BT817 to the pin headers of MAX78000FTHR board. Note that the SPI interface is shared between both devices and even more it is also shared with some onboard chips (SPI SRAM and possibly SD card slot). Due to this overloading I was unable to use HW accelerated CS pins of the MCU because there are only three HW accelerated pins on SPI peripheral and they are routed to the different pins than I need. For this reason, I used software-controlled CS pins instead.
First program
For my first program I created just heatmap. I gathered data from the sensor and rendered them at display in matrix. You can see my first outputs from this design at following video:
Conclusion
In this blog post I shown my new library for configuring and interfacing with MAX25405 Gesture Sensor. I shown my setup which I will use in next project including connection. Except MAX25405 I also setup display and created first application utilizing my new library and display. Then I show RAW data from the sensor. It is first step in my journey to complete second project. In meantime I plan to make some experiments with multiple sensors, so most probably this will be topic of some blog post in near future, and I also plan blog about secret options of MAX25405 which are not mentioned in datasheet, but I found them elsewhere. Note that my library can configure them.
Thank you for reading this blog post and stay tuned for next parts.
Next blog: Blog #11: Undocumented features of MAX25405