Attaching a MAX6675 Thermocouple-to-Digital Converter with SPI

As my first Longan Nano programming exercise I’m trying to connect a MAX6675 chip over SPI while also using the LCD screen.

As the LCD screen is using SPI0 and as the SPI2 pins are not all accessible over the normal pins on the breadboard, I’m trying to use SPI1 for this. This should work, as long as no card is inserted into the TF card slot, right?

Based on the existing example programs in the SDK and after reading the SPI chapter in the GD32VF103 User Manual I came up with the following first attempt to read data from the MAX6675 chip in a PlatformIO project:

Of course it doesn’t work… :grinning:

So if someone having experience with SPI and the Longan Nano could take a look at the code and maybe can point out obvious errors, I would be very grateful. I will publish any improvements in the GitHub repository.

I updated the MAX6675 test program after studying the SPI documentation in the GD32VF103 User Manual back and forth, and finally I can read values from the MAX6675 via SPI1, but only after switching to SPI software mode and by explicitly controlling the CS/NSS pin.

I couldn’t get it to work in “hardware mode”, so I would be interested if anybody could tell what was wrong with my attempts to configure SPI1 in hardware mode.

This is the setup, the Longan Nano being the master and the MA6675 being the slave (screen shot from the GD32VF103 User Manual):

MRU

I want to use the “MRU” hardware mode according to table “Table 18-2. SPI operating modes” in section “18.5.3. SPI operating modes” in the the GD32VF103 User Manual:

  • Register configuration: MSTMOD = 1, RO = 1, BDEN = 0, BDOEN: Don’t care
  • MOSI: Not used, MISO: Reception

Under “18.5.2. NSS function” it says:

Master mode

If the application wants to use NSS line to control the SPI slave, NSS should be configured to hardware output mode (SWNSSEN=0, NSSDRV=1). NSS stays high after SPI is enabled and goes low when transmission or reception process begins. When SPI is disabled, the NSS goes high.

This is the configuration of SPI1 in my code, that should accomplish the configuration with SWNSSEN=0, NSSDRV=1:

    spi_init_struct.trans_mode           = SPI_TRANSMODE_RECEIVEONLY;
    spi_init_struct.device_mode          = SPI_MASTER;
    spi_init_struct.frame_size           = SPI_FRAMESIZE_16BIT;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_2EDGE;
    spi_init_struct.nss                  = SPI_NSS_HARD;
    spi_init_struct.prescale             = SPI_PSC_256;
    spi_init_struct.endian               = SPI_ENDIAN_MSB;
    spi_init(SPI1, &spi_init_struct);

    spi_nss_output_enable(SPI1);

Inside a loop the program then does the following to receive the data from the MAX6675:

    spi_i2s_data_receive(SPI1);
    spi_enable(SPI1);
    while (RESET == spi_i2s_flag_get(SPI1, SPI_FLAG_RBNE));
    uint16_t const data = spi_i2s_data_receive(SPI1);
    spi_disable(SPI1);

First I retrieve the data from SPI1 to empty the buffer. Then SPI1 is enabled. Then the loop waits until there’s data available in the receive buffer. Then it reads the data and disables SPI1.

According to the documentation I would expect that NSS goes from high to low when spi_enable(SPI1) is called, but this is not happening. When I watch the signals with a logic analyzer, I can see a clock signal for exactly 16 cycles on the SCK pin, but the NSS pin stays high. Note that I’m setting the NSS pin to high explicitly in the GPIO configuration code, otherwise it would stay low forever.

This is the GPIO configuration for SPI1 that I used for hardware mode:

    /* SPI1_SCK(PB13), SPI1_MISO(PB14) GPIO pin configuration */
    gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13);
    gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_14);
    /* SPI1_CS(PB12) GPIO pin configuration */
    gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);

As the CS/NSS pin should be automatically controlled by the hardware, I configured GPIOB pin 12 as GPIO_MODE_AF_PP. Is this correct?

I’m also experiencing this issue, has anyone managed to get hardware NSS management to work?

Here is my spi init code:

spi_parameter_struct spi_struct;

spi_struct.device_mode = SPI_MASTER;
spi_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_struct.frame_size = SPI_FRAMESIZE_16BIT;
spi_struct.nss = SPI_NSS_HARD;
spi_struct.endian = SPI_ENDIAN_MSB;
spi_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_2EDGE;
spi_struct.prescale = SPI_PSC_8;

rcu_periph_clock_enable(RCU_AF);
rcu_periph_clock_enable(RCU_SPI1);
rcu_periph_clock_enable(RCU_GPIOB);

gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12 );
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15 );

spi_i2s_deinit( SPI1 );
spi_nss_output_enable( SPI1 );
spi_init( SPI1, &spi_struct );

and my spi transfer function:

uint16_t spi_transact( uint16_t mosi_dat )
{
  while( RESET == spi_i2s_flag_get( SPI1, SPI_FLAG_TBE ) );
  spi_i2s_data_transmit( SPI1, mosi_dat );

  while( RESET == spi_i2s_flag_get( SPI1, SPI_FLAG_RBNE ) );
  uint16_t ret = spi_i2s_data_receive( SPI1 );
  return ret;
}

Looking at the output with an oscope, the transfer starts correctly when the SPI_DATA register is loaded, but the NSS pin is always pulled high. I’m beginning to think this may be a hardware bug.

I also noted that the SPI_INIT_MASK #define in gd32vf103_spi.h does not mask the SWNSSEN bit. So if it’s ever configured to SPI_NSS_SOFT, the spi_init function will not reset it. I bodged in the mask to fix this issue and saw no change. Given that I’m only configuring the peripheral once, and the reset value is 0x0000, having no change makes sense, but it’s probably still a bug.