2018-10-06 07:34 AM
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
2020-07-19 12:01 PM
Hi, did you come right with this?
I'm also having issues with MAX31855
Thanks
2020-07-21 08:00 PM
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
2020-07-21 08:02 PM
Please note that I had switched to SDCC compiler. So, code may require minor changes if you are with a different compiler.
2020-07-22 05:04 AM
Thank you!