How to correctly read data returned by MFRC522 via SPI using STM8S103F3?

1.4k views Asked by At

I had tested reading Mifare RFID 1K cards with an RFID-RC522 module via UART using a Pyhton script on my PC. Now I am using STM8S103F3 to interface with the same RFID-RC522 (MFRC522) via SPI, but I have the problem of not getting the correct values/addresses returned by MFRC522 when sending the same commands as I did with the Python script/Terminal. It's the 1st time I use SPI so I suspect myself of not having properly configured SPI or doing something wrong with the write/read sequence, but I could not manage to troubleshoot it myself.

Here is my main() function:

int main ( void ) {

  initClockHSI();
  initGPIO();
  initUART1();
  initMasterSPI();

  initMFRC522();

  return 0;
}

The init functions are as follows:

void initClockHSI ( void ) {
  CLK_DeInit();                                          // Deinitializes the CLK peripheral registers to their default reset
  CLK_SYSCLKConfig ( CLK_PRESCALER_CPUDIV1 );            // (uint8_t)0x80 CPU clock division factors 1                   
  CLK_SYSCLKConfig ( CLK_PRESCALER_HSIDIV1 );            // (uint8_t)0x00 High speed internal clock prescaler: 1
  CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO,             // (uint8_t)0x01 Enable the automatic clock switching mode
                        CLK_SOURCE_HSI,                  // (uint8_t)0xE1 Clock Source HSI
                        DISABLE,                         // DISABLE = 0
                        CLK_CURRENTCLOCKSTATE_DISABLE);  // CLK_CURRENTCLOCKSTATE_DISABLE = (uint8_t)0x00 Current clock disable
}


void initGPIO ( void ) {
  // UART1:
  GPIO_DeInit(GPIOD);
  GPIO_Init(GPIOD, GPIO_PIN_5, GPIO_MODE_OUT_PP_HIGH_SLOW); // (uint8_t)0xD0 Output push-pull, high level, 2MHz
  GPIO_Init(GPIOD, GPIO_PIN_6, GPIO_MODE_IN_PU_NO_IT);      // UART1 Rx (uint8_t)0x40 Input pull-up, no external interrupt

  // MFRC522:
  GPIO_DeInit(GPIOC);
  // MOSI:
  GPIO_Init (GPIOC, GPIO_PIN_6, GPIO_MODE_OUT_PP_HIGH_SLOW);// (uint8_t)0xD0 Output push-pull, high level, 2MHz
  // SCK:
  GPIO_Init (GPIOC, GPIO_PIN_5, GPIO_MODE_OUT_PP_LOW_SLOW); // (uint8_t)0xC0 Output push-pull, low level, 2MHz
  // MISO (Input):
  GPIO_Init (GPIOC, GPIO_PIN_7, GPIO_MODE_IN_PU_NO_IT);     // (uint8_t)0x40 Input pull-up, no external interrupt

  GPIO_DeInit(GPIOB);
  // NRSTPD:
  GPIO_Init(GPIOB, GPIO_PIN_4, GPIO_MODE_OUT_PP_HIGH_SLOW); // (uint8_t)0xD0 Output push-pull, high level, 2MHz
  // NSS (pulled-up externally via 10k R):
  GPIO_Init(GPIOB, GPIO_PIN_5, GPIO_MODE_OUT_PP_HIGH_SLOW); // (uint8_t)0xD0 Output push-pull, high level, 2MHz
}


void initMasterSPI ( void ) {
 SPI_DeInit();                      // Deinitializes the SPI peripheral registers to their default reset values

 SPI_Init(SPI_FIRSTBIT_MSB,         // (uint8_t)0x00 MSB bit will be transmitted first
          SPI_BAUDRATEPRESCALER_16, // (uint8_t)0x18 SPI frequency = frequency(CPU)/16 == 1 MHz baud
          SPI_MODE_MASTER,          // (uint8_t)0x04 SPI Master configuration
          SPI_CLOCKPOLARITY_LOW,    // (uint8_t)0x00 Clock to 0 when idle
          SPI_CLOCKPHASE_1EDGE,     // (uint8_t)0x00 The first clock transition is the first data capture edge
          SPI_DATADIRECTION_2LINES_FULLDUPLEX,  // (uint8_t)0x00 2-line uni-directional data mode enable
          SPI_NSS_SOFT,             // (uint8_t)0x02 Software slave management disabled ?! isn't it enabled, when it's called SOFT ?!
          0x00);                    // CRCPolynomial

 SPI_Cmd(ENABLE);                   // Enables or disables the SPI peripheral; parameter can be: ENABLE or DISABLE
}


void initMFRC522 ( void ) {
  char result;

  // 1. hard reset MFRC522:
  result = resetHardMFRC522 ();

  // 3. set timer to start automatically at the end of the TRANSMISSION:
  result = writeMFRC522 ( TModeReg, 0x80 ); // TModeReg = (0x2A == 42):defines settings for the internal timer; 0x80 == 1000 0000 --> MSB = 1 => timer starts automatically at the end of the TRANSMISSION in all communication modes at all speeds
}


char resetHardMFRC522 () {
  unsigned int i;

  // 1. Pull reset line LOW:
  GPIO_WriteLow(NRSTPD_PORT, NRSTPD_PIN);

  // 2. keep reset line LOW for some time; reset timing requirements: min 100ns (page 34)
  for ( i = 0; i < 1000; i++)   // @ 16 Mhz: 1 clock cycle == 62,5 ns;
    nop();
    //// 1000  == 340 - 370 us, rectangular

  // 3. Pull reset line HIGH:
  GPIO_WriteHigh(NRSTPD_PORT, NRSTPD_PIN);

  // 4. wait for a stable oscillator; oscillator start-up time is the start up time of the crystal + 37,74µs;
  for ( i = 0; i < 60000; i++)  // @ 16 Mhz: 1 clock cycle == 62,5 ns; 60000 == 22,4 ms
    nop();  // ;

  // 5. check for wake-up procedure end (hard Power-Down mode): bit 'PowerDown' 1 --> 0:
  if ( ( readMFRC522( CommandReg ) & 0x10 ) )       // if 1 returned    ; 0x10 == 0001 0000 (BIt 4: PowerDown)
    return ERROR;   // 1

  return OK;        // 0    => Bit 4 (PoweDown) of CommandReg is cleared => chip should be ready to work
}


char readMFRC522 ( unsigned char address ) {    // 0x01
  char dataMFRC522;

  // 2. Before sending data, the user must pull low an SS signal to let the slave device know it is the recipient of the message:
  GPIO_WriteLow(NSS_PORT, NSS_PIN);         // pull NSS line LOW before start of SPI communication

  // 3. wait while SPI is busy communicating, exit when any ongoing SPI transfer has finished
  while(SPI_GetFlagStatus(SPI_FLAG_BSY))        // RESET(0) or SET(1)
    ;

  // 4. send serially the address to MFRC522 with MSB set (R mode) & LSB cleared:
  SPI_SendData( ( ( address << 1 ) & 0x7E ) | 0x80 );   // highest MFRC522 address is 0x3f == 0011 1111;    0x82 == 1000 0010

  // 5. check that the address is indeed sent:
  while( ! SPI_GetFlagStatus(SPI_FLAG_TXE) )
    ;

  // 6. check that data has been received:
  while( ! SPI_GetFlagStatus(SPI_FLAG_RXNE) )       // data received ?
    ;

  // 7. read the value returned serially:
  dataMFRC522 = SPI_ReceiveData();          // Returns the most recent received data by the SPI peripheral; resets SPI_FLAG_RXNE

  // 8. wait while SPI is busy communicating, exit when SPI transfer has finished
  while(SPI_GetFlagStatus(SPI_FLAG_BSY))        // RESET(0) or SET(1)
    ;

  // 9. raise NSS line HIGH afer end of SPI communication:
  GPIO_WriteHigh(NSS_PORT, NSS_PIN);

  return dataMFRC522;
}


char writeMFRC522 ( unsigned char address, unsigned char value ) {
  char addressMFRC522;

  // 2. Before sending data, the user must pull low an SS signal to let the slave device know it is the recipient of the message:
  GPIO_WriteLow(NSS_PORT, NSS_PIN);         // pull NSS line LOW before start of SPI communication

  // 3. wait while SPI is busy communicating, exit when any ongoing SPI transfer has finished
  while(SPI_GetFlagStatus(SPI_FLAG_BSY))        // RESET(0) or SET(1)
    ;

  // 4. send serially the address to MFRC522 with MSB cleared (W mode) & LSB cleared:
  SPI_SendData( ( address << 1 ) & 0x7E );      // highest MFRC522 address is 0x3f == 0011 1111;

  // 5. check that the address is indeed sent:
  while( ! SPI_GetFlagStatus(SPI_FLAG_TXE) )
    ;

  // 6. send serially the data to MFRC522:
  SPI_SendData( value );

  // 7. check that the data is indeed sent:
  while(!SPI_GetFlagStatus(SPI_FLAG_TXE))
    ;

  // 8. check that data has been received as reposnse to sent address:
  while( ! SPI_GetFlagStatus(SPI_FLAG_RXNE) )       // data received ?
    ;

  // 9. read the address returned serially:
  addressMFRC522 = SPI_ReceiveData();           // Returns the most recent received data by the SPI peripheral; resets SPI_FLAG_RXNE

  // 10. wait while SPI is busy communicating, exit when SPI transfer has finished:
  while(SPI_GetFlagStatus(SPI_FLAG_BSY))        // RESET(0) or SET(1)
    ;

  // 11. raise NSS line HIGH afer end of SPI communication
  GPIO_WriteHigh(NSS_PORT, NSS_PIN);

  if ( addressMFRC522 == address )
    return OK;      // 0
  else
    return ERROR;   // 1
}

My problem arises within the call of the last function initMFRC522() within my main() function:

  • when I only execute the first part, resetHardMFRC522 (); , then I get the expected reset value 0x20 for the CommandRegister of the MFRC522 after the hard reset;
  • however, when I execute also the second part, the function result = writeMFRC522 ( TModeReg, 0x80 );, then I don't get the expected reset value 0x20 for the CommandRegister of the MFRC522 after the hard reset, plus I don't receive the same address returned as the one I write to (as per MFRC522 protocol). I cannot figure out where my problem is ? I checked the SPI settings, but the setting of the clock polarity and phase seem to be the correct ones. The pin input/output directions also seem to be correct. I guess I am misusing / overusing / not using correctly the SPI flags or I don't have the correct command sequence while reading/writing, but I could not figure out where my mistake is from reading the datasheet and looking at other people's code on the net ?
1

There are 1 answers

1
koko On

the issue was that, when trying to read a register value, I need to send a 2nd dummy byte via MOSI after the register address itself because the 1st byte returned is a non-sense byte and the real register content is returned as a 2nd byte which requires a CLK signal (which itself is generated by the 2nd dummy byte sent). That was different in comparison to using UART but I got some help interpreting the datasheet :-)