Skip to main content
Visitor II
October 6, 2018
Question

Problem with SPI communication with MAX31855K from STM8S103F3

  • October 6, 2018
  • 4 replies
  • 2454 views

Hi all,

I am trying to read temperature data from MAX31855K over SPI, using STM8S103F3 on a breakout board.

Development set up is STVD, STVP and Consmic compiler. And using STM8S Standard Peripheral library.

Here is my max31855.h:

#ifndef MAX31855_H
#define MAX31855_H
 
#define MAX31855_SPI_GPIO_PORT GPIOC
#define MAX31855_SPI_SCK_PIN GPIO_PIN_5
#define MAX31855_SPI_MISO_PIN GPIO_PIN_7
#define MAX31855_NCS_GPIO_PORT GPIOA /* Chip Select I/O definition */
#define MAX31855_NCS_PIN GPIO_PIN_3
 
 
#ifdef USE_Delay
#include "main.h"
 
 #define _delay_ Delay /* !< User can provide more timing precise _delay_
 function (with at least 1ms time base), using
 Timer for example */
#else
 #define _delay_ delay /* !< Default _delay_ function with less precise timing */
#endif
 
 
#define MAX31855_CONVERSION_POWER_UP_TIME 200 //in milliseconds
 
#define MAX31855_SELECT() GPIO_WriteLow(MAX31855_NCS_GPIO_PORT, MAX31855_NCS_PIN)
#define MAX31855_DESELECT() GPIO_WriteHigh(MAX31855_NCS_GPIO_PORT, MAX31855_NCS_PIN)
 
void MAX31855_Init(void);
uint8_t MAX31855_ReceiveData(void);
// uint32_t MAX31855_spiread32(void);
void MAX31855_spiread32(struct max_data* data);
int16_t MAX31855_readCelsius(void);
 
static void delay(__IO uint32_t nCount);
 
#endif /* MAX31855_H */

here is the max31855.c

#include "stm8s_gpio.h"
#include "max31855.h"
#include "stdio.h"
 
static void delay(__IO uint32_t nCount);
 
struct max_data {
	uint16_t high;
	uint16_t low;
};
 
#define check_bit(var,pos) ((var) & (1<<(pos)))
 
/**
 * @brief SPI connection with MAX31855
 * @param None
 * @retval None
 */
void MAX31855_Init(void)
{
 /* Enable SPI clock */
 CLK_PeripheralClockConfig(CLK_PERIPHERAL_SPI, ENABLE);
 
 /* Configure SPI pins: SCK and MISO */
 GPIO_Init(MAX31855_SPI_GPIO_PORT, (GPIO_Pin_TypeDef)(MAX31855_SPI_SCK_PIN | MAX31855_SPI_MISO_PIN), GPIO_MODE_OUT_PP_LOW_FAST);
 
 /* Configure MAX31855 ChipSelect pin (NCS) in Output push-pull mode */
 GPIO_Init(MAX31855_NCS_GPIO_PORT, MAX31855_NCS_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
 
 /* Initialize SPI */
 /* TODO: Not sure about SPI_NSS_SOFT and 0x07 */
 SPI_Init(SPI_FIRSTBIT_MSB, SPI_BAUDRATEPRESCALER_64, SPI_MODE_MASTER,
 SPI_CLOCKPOLARITY_LOW, SPI_CLOCKPHASE_1EDGE, SPI_DATADIRECTION_1LINE_RX,
 SPI_NSS_SOFT, 0x07);
 SPI_Cmd(ENABLE);
 
 
 
 /* Required to ensure proper MAX31855 display when the board is powered-on ... */
 _delay_(0x4000 * MAX31855_CONVERSION_POWER_UP_TIME); /* 0x4000 = 1ms _delay_ using Fcpu = 16Mhz*/
}
 
uint8_t MAX31855_ReceiveData(void) {
 while (SPI_GetFlagStatus(SPI_FLAG_RXNE) == RESET); // wait for a byte to come in; checks if the Receive register is not empty
 return SPI_ReceiveData();
}
 
void MAX31855_spiread32(struct max_data* data) {
 // uint32_t d = 0;
 data->high = 0;
 data->low = 0;
 
 MAX31855_SELECT();
 _delay_(0x4000); // 1ms
 
 //SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
 // SPI_Cmd(ENABLE);
 
 data->high = MAX31855_ReceiveData();
 data->high <<= 8;
 data->high |= MAX31855_ReceiveData();
 // d <<= 8;
 data->low |= MAX31855_ReceiveData();
 data->low <<= 8;
 data->low |= MAX31855_ReceiveData();
 
 // SPI.endTransaction();
 // SPI_Cmd(DISABLE);
 
 MAX31855_DESELECT();
 
 // return d;
}
 
int16_t MAX31855_readCelsius(void) {
 struct max_data data;
 // float centigrade = v;
 int16_t internal;
 
 MAX31855_spiread32(&data);
 printf("high: %d low: %d\n\r", data.high, data.low);
 
 // Bit 3 of low is always supposed to be 0.
	if (check_bit( data.low , 3)) {
		printf("Bit 3 is set. WRONG!\r\n");
	}
 
	// Bit 1 (17th bit of SPI data) of high is always supposed to be 0.
	if (check_bit(data.high, 1)) {
		printf("Bit 17 is set. WRONG!\r\n");
 return 0;
	}
 
	if (check_bit(data.low, 0)) {
		printf("Thermocouple is open. No connection.\r\n");
 return 0;
	}
	if (check_bit(data.low, 1)) {
		printf("Thermocouple is short circuited to ground.\r\n");
 return 0;
	}
	if (check_bit(data.low, 2)) {
		printf("Thermocouple is short circuited to VCC.\r\n");
 return 0;
	}
 
 internal = (data.low >> 4) / 16;
 printf("Internal: %d\n\r", internal);
 
 return (data.high >> 2) / 4;
}
 
#ifndef USE_Delay
/**
 * @brief Inserts a delay time.
 * The delay function implemented in this driver is not a precise one,
 * however it allows the insertion of 1ms delay when Fcpu is 16Mhz if
 * the passed parameter is 0x4000.
 * Any change in system clock frequency will impact this delay duration.
 *
 * User is given the possibility to develop a customized and accurate
 * delay function by the mean of timers for example.
 * Uncommenting " #define USE_Delay" line in the stm8s_eval_lcd.h file
 * will allow the consideration of the new function by this driver.
 *
 * @param nCount: specifies the _delay_ time length.
 * @retval None
 */
static void delay(__IO uint32_t nCount)
{
 /* Decrement nCount value */
 while (nCount != 0)
 {
 nCount--;
 }
}
#endif /* USE_Delay*/

And here is the main function:

It already contains several printf() calls as debugging purpose.

I mostly suspect MAX31855_Init() function which initialize the SPI. I coded it for receive-only mode, however not sure if used correct values for the parameters I used for SPI_Init() function call.

Somewhere I read calling SPI_ReceiveData() is not enough and we have to check status of SPI_FLAG_RXNE flag. So created MAX31855_ReceiveData().

Here is the connection from STM8S103 => MAX31855

PC7 (SPI_MISO) => DO

PA3 (SPI_NSS) => CS

PC5 (SPI_SCK) => CLK

I also set AFR1 bit of option byte OPT2 to 1 as per datasheet. Hope i did it correct.

Overall it outputs non consistent garbage values. Thus SPI communication is not happening right.

Could somebody help me to fix the issue over SPI communication with MAX31855K?

I would be happy to hear if somebody already have code for working with MAX31855.

Thanks in advance!

Junaid

    This topic has been closed for replies.

    4 replies

    Visitor II
    July 19, 2020

    Hi, did you come right with this?

    I'm also having issues with MAX31855

    Thanks

    Junaid PVAuthor
    Visitor II
    July 22, 2020

    Yes, I had got that working.

    These are my update max31855.h and max31855.c:

    max31855.h:

    // @author: Junaid PV (http://junix.in)
     
    #ifndef MAX31855_H
    #define MAX31855_H
     
    #define check_bit(var,pos) ((var) & (1<<(pos)))
     
    #define MAX31855_SPI_GPIO_PORT GPIOC
    #define MAX31855_SPI_SCK_PIN GPIO_PIN_5
    #define MAX31855_SPI_MISO_PIN GPIO_PIN_7
    #define MAX31855_NCS_GPIO_PORT GPIOA /* Chip Select I/O definition */
    #define MAX31855_NCS_PIN GPIO_PIN_3
     
    struct max_data {
    	uint16_t high;
    	uint16_t low;
    };
     
    #define MAX31855_CONVERSION_POWER_UP_TIME 200 //in milliseconds
     
    #define MAX31855_SELECT() GPIO_WriteLow(MAX31855_NCS_GPIO_PORT, MAX31855_NCS_PIN)
    #define MAX31855_DESELECT() GPIO_WriteHigh(MAX31855_NCS_GPIO_PORT, MAX31855_NCS_PIN)
     
    void MAX31855_Init(void);
    uint8_t MAX31855_ReceiveData(void);
    // uint32_t MAX31855_spiread32(void);
    void MAX31855_spiread32(struct max_data* data);
    uint8_t max31855_print_error(struct max_data* data);
    int16_t MAX31855_get_internal_temperature(struct max_data* data);
    int16_t MAX31855_get_junction_temperature(struct max_data* data);
     
    #endif /* MAX31855_H */

    max31855.c:

    // @author: Junaid PV (http://junix.in)
     
    #include "stm8s_gpio.h"
    #include "max31855.h"
    #include "stdio.h"
    #include "delay.h"
     
    /**
     * @brief SPI connection with MAX31855
     * @param None
     * @retval None
     */
    void MAX31855_Init(void)
    {
     /* Enable SPI clock */
     CLK_PeripheralClockConfig(CLK_PERIPHERAL_SPI, ENABLE);
     
     /* Configure MAX31855 ChipSelect pin (NCS) in Output push-pull mode */
     GPIO_Init(MAX31855_NCS_GPIO_PORT, MAX31855_NCS_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
     
     /* Initialize SPI */
     /* TODO: Not sure about SPI_NSS_SOFT and 0x07 */
     SPI_Init(SPI_FIRSTBIT_MSB, SPI_BAUDRATEPRESCALER_8, SPI_MODE_MASTER,
     SPI_CLOCKPOLARITY_LOW, SPI_CLOCKPHASE_1EDGE, SPI_DATADIRECTION_2LINES_FULLDUPLEX,
     SPI_NSS_SOFT, 0x07);
     SPI_Cmd(ENABLE);
     
    	// Ensure MAX31855 is not selected by default.
    	MAX31855_DESELECT();
    }
     
    uint8_t MAX31855_ReceiveData(void) {
    	// Need to send dummy byte to get data in read buffer.
    	// In fact we send nothing since MAX31855 is read only device,
    	// but this line is required otherwise we will wait indefinitely for the SPI_FLAG_RXNE flag.
    	SPI_SendData(0x00);
     while (SPI_GetFlagStatus(SPI_FLAG_RXNE) == RESET); // wait for a byte to come in; checks if the Receive register is not empty
     return SPI_ReceiveData();
    }
     
    void MAX31855_spiread32(struct max_data* data) {
     data->high = 0;
     data->low = 0;
     
     MAX31855_SELECT();
    	// Wait for 1 millisecond to allow MAX31855 to prepare the data.
     _delay_ms(1);
     
    	// Read MSBytes
     data->high = MAX31855_ReceiveData();
     data->high <<= 8;
     data->high |= MAX31855_ReceiveData();
    	// Read LSBytes
     data->low |= MAX31855_ReceiveData();
     data->low <<= 8;
     data->low |= MAX31855_ReceiveData();
     
     MAX31855_DESELECT();
    }
     
    uint8_t max31855_print_error(struct max_data* data) {
     // Bits 0, 1, 2 and 3 should not be 1s
    	// Bit 3 of low is always supposed to be 0.
     // Bit 1 of high (17th bit of SPI data) of high is always supposed to be 0.
    	return (data->low & 0b0000000000001111) || (data->high & 0b0000000000000001);
    }
     
    int16_t MAX31855_get_internal_temperature(struct max_data* data) {
    	int16_t temp = data->low;
     
    	// ignore bottom 4 bits - they're just thermocouple data
    	temp >>= 4;
     
    	// check sign bit!
    	if (temp & 0x800) {
    		// Convert to negative value by extending sign and casting to signed type.
    		temp = 0xF800 | (temp & 0x7FF);
    	}
    	else {
    		// pull the bottom 11 bits off
    		temp = temp & 0x7FF;
    	}
     
    	return temp / 16;
    }
     
    int16_t MAX31855_get_junction_temperature(struct max_data* data) {
    	int16_t temp = data->high;
     
    	if (check_bit(temp, 15)) {
    		// Negative value, drop the lower 18 bits and explicitly extend sign bits.
    		temp = 0xFFFFC000 | ((temp >> 2) & 0x00003FFFF);
    	}
    	else {
    		// Positive value, just drop the lower 2 bits.
    		temp >>= 2;
    	}
     
     return temp / 4;
    }

    As far as I remember, the important change was to send a byte to MAX31855 before reading data from it, see function MAX31855_ReceiveData().

    Then, in my main function has something like this:

    int16_t temp;
    struct max_data data;
    char szTemp[8];
     
    MAX31855_Init();
     
    while (1) {
     MAX31855_spiread32(&data);
     if (!max31855_print_error(&data)) {
     temp = MAX31855_get_junction_temperature(&data);
     sprintf(szTemp, "%04d", temp);
     }
    }

    Hope it helps.

    Regards,

    Junaid

    Junaid PVAuthor
    Visitor II
    July 22, 2020

    Please note that I had switched to SDCC compiler. So, code may require minor changes if you are with a different compiler.

    Visitor II
    July 22, 2020

    Thank you!