connecting CR95HF to the esp32 using SPI

I’ve been working on a freestyle libre glucose monitor reader. the sensor tag uses ISO/IEC 15693 to communicate with the NFC reader. following the work of @JoernL I’ve purchased a CR95HF based reader and connected it to my esp32.  the original design used arduino pro-mini, but i’ve opted to use adafruit huzzah feather esp32 as it includes all the peripherals needed: batter charger, bluetooth, and WiFi support so I can upload data to mqtt.

I’ve assumed the migration, or at least the SPI connection would be transparent, but boy was I wrong.

it took me several tries to figure out the pin assignment. as it turns out not all pins on the esp32 are equal. GPIO-12 is used for the boot process and should be pulled down. don’t use it. GPIO-34 to GPIO-39 are input only, and can’t be used to control other devices.

I’ve ended up with the following wiring:

Signal esp32 RFID Click
SCK SCK/5 SCK
MOSI MOSI/18 SDI
MISO MISO/19 SDO
SS A5/4 CS
IRQ 27/A10 INTI
SSI0 VCC SSI0
SSI1 GND SSI1
Power 3V 3.3V
Gnd GND GND

Note that SSI0 needs to be connected to “1” and SSI1 needs to be “0” at power up so the CR95HF will work in SPI mode. on some boards it is already hardwired, on mine it wasn’t.

after figuring that out I’ve started implementing the protocol. there’s a lot of samples out there. it almost worked ok.

the data I got when reading from the device was shifted one bit to the left. for example if I was expecting a value of 0x4E (0100 1110 binary), what I was reading was 0x9C (1001 1100 binary) as if the SPI was missing the Most Significant Bit.

checking the signals with a osciloscope showed that the signal looked exactly as expected on the CR95HF datasheet.

at first I’ve suspected the clock speed. but testing all clock divisors did not fix anything. then I wondered if the SPI Mode was wrong, but both were using mode 0 (CPOL=0, CPHA=0)

so I kept digging in the ESP32 documentation. till I found the following statment at the SPI device driver documentation:

Due to the input delay of MISO pin, ESP32 SPI master cannot read data at very high speed. The frequency allowed is rather low when the GPIO matrix is used. Currently only frequency not greater than 8.8MHz is fully supported. When the frequency is higher, you have to use the native pins or the dummy bit workaround.

Dummy bit workaround: We can insert dummy clocks (during which the host does not read data) before the read phase actually begins. The slave still sees the dummy clocks and gives out data, but the host does not read until the read phase. This compensates the lack of setup time of MISO required by the host, allowing the host reading at higher frequency.

I wasn’t sure it’s exactly the same case, as I already fiddled the clock speed. but the symptoms sounded like it’s related, so I went back to the refernce manual. and table 27 said

Table 27: Clock Polarity and Phase, and Corresponding SPI Register Values for SPI Master

Registers mode0 mode1 mode2 mode3
SPI_CK_IDLE_EDGE 0 0 1 1
SPI_CK_OUT_EDGE 0 1 1 0
SPI_MISO_DELAY_MODE 2(0) 1(0) 1(0) 2(0)

section 7.4.2 “GP-SPI Timing” has some details on why and how the data is delayed by the GPIO Matrix.

I’ve headed to the esp32-hal-spi.c file and found the spiStopBus() initialize thr ctrl2 register where SPI_MISO_DELAY_MODE register reside. I’ve added the line

spi->dev->ctrl2.val = 0;
spi->dev->ctrl2.miso_delay_mode = 2;

and it worked ! rejoice !

knowing that someday I’ll update the SDK and I’ll forget to patch it, I wanted to move the code into my sketch.

so on my setup() I’ve added
SPI.begin();
spi_t * _spi;
_spi= SPI.bus();
_spi->dev->ctrl2.miso_delay_mode = 2;

SPI.setDataMode(SPI_MODE0);
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV128);

and got a nasty message :

error: invalid use of incomplete type 'spi_t {aka struct spi_struct_t}'

luckly the nice people at esp32 github provided the answer.  spressif style is to obfuscate HAL structures by declaring the struc in the .h file and fleshing it out only inside the .c file.

so I had to redecalre spi_t :
#include "soc/spi_struct.h"
struct spi_struct_t {
spi_dev_t * dev;
#if !CONFIG_DISABLE_HAL_LOCKS
xSemaphoreHandle lock;
#endif
uint8_t num;
};

success.

8 thoughts on “connecting CR95HF to the esp32 using SPI

  1. This is a great post! Do you have the full code Repo available? I have the rfid click and have been trying to get it to works with my esp32

  2. I really enjoyed this post! What would I have to do to use this with a different card format i.e. ISO/IEC 14443A?

  3. hello
    what a nice post, thanks
    could you help me??. i got an adafruit fona and the click rfid but the wiring and code coluldn’t find an example. i want only to read data from the rfid reader.

  4. Thank you very much for this post. It solved an issue I spent a lot of time with. Honestly I would probably never solve :). Again thanks.

  5. Can you show us a picture of the final result please? What size was it?
    Have you made any further improvements?
    Congratulations and thanks for sharing

Comments are closed.