Temperature monitoring with TC74 on an STM32 microcontroller

The TC74 is a small thermal sensor with a wide temperature range (-40C to +125C) and a digital output (I2C). It comes in a TO-220 package or a small scale SOT-23 package and is powered by 3.3V or 5V. Based on the above it was for our UPSat, as two temperature values (board temperature and battery temperature) need to be measured every minute. The TC74 can withstand the temperature conditions found inside the Cubesat and can provide relatively accurate measurements, with low power consumption.

The majority of thermal sensors have an analog output, the value of which is given as a function of the measured temperature. So, the measurement is susceptible to noise, as the Analog to Digital converter used is found in the micro-controller and each sensor needs an extra pin. On the other hand the TC74 has an embedded ADC and transmits the data digitally, via the I2C Serial Port Interface. The I2C protocol is used for the communication of different ICs on a board with the micro-controller, for example EEPROMs, ADCs or GPIO expanders. It needs two lines, the SDA (data) and the SCL (clock). The lines are pulled high and the ICs pull the lines low. It uses a master/slave approach, where the micro controller is usually the master and therefore is responsible for the SCL line.

Having studied other communication protocols, the I2C is rather peculiar and especially the implementation of the protocol on the TC74. The default address length is 7bits. It can be 10 bits as well. To establish communication, the sender first issues a start condition. It then continues by sending the address of the peripheral that it needs to communicate with and finally a bit showing whether it wants to read data from the device or write data to the device. It then awaits for the an acknowledge (ACK) bit from the device on the next clock sequence and continues the communication accordingly.

The way this protocol is implemented on the TC74 is as follows. The TC74 has two registers, the temperature register, which has the temperature value and the control register (which is used to limit power consumption). So, to read the temperature, the micro-controller first issues a start bit, followed by the 7bit address of the TC74. The TC74 then sends an ACK. The micro-controller then sends the address of the register that it wants to read. In the case of the temperature register, the value is 0x00. The TC74 sends an ACK again. After that, the micro-controller sends a start condition again, the address of the TC74 again followed by an ACK and waits for the data followed by a NACK (not-acknowledge). It finally issues a stop condition.

The code shown bellow implements the communication of a single TC74 with an STM32L152 micro-controller. The pins used are PB6 and PB7 and the I2C1 peripheral was used. The STM32 internal pull-up resistors were used.

The TC74 have 8 different product codes depending on the factory written addresses of the chip. So on a single I2C bus, 8 different TC74 can be used. In this example the TC74A5 is used:

#define TC74_ADDRESS 0x9A

Next, the init function needs to be implemented:

void TC74_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1);
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 10000;
I2C_InitStructure.I2C_Mode = I2C_Mode_SMBusHost;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_Init(I2C1, &I2C_InitStructure);
I2C_Cmd(I2C1, ENABLE);
}

This function initializes the I2C1 peripheral, the ports PB6, PB7 and activates the pull-up resistors.

Next is where the fun stuff begins. The timing sequence is implemented with different functions. The main function to return an integer value of the temperature is:

int TC74_Read_Temperature(uint8_t TC74address)
{
int8_t data1, data2;
I2C_start(I2C1, TC74address, I2C_Direction_Transmitter);
I2C_write(I2C1,0x00);
I2C_stop(I2C1);
I2C_start(I2C1, TC74address, I2C_Direction_Receiver);
data1 = I2C_read_ack(I2C1);
data2 = I2C_read_nack(I2C1);
return data1;
}

Finally each of these functions are described as well:

void I2C_start(I2C_TypeDef* I2Cx, uint8_t address, uint8_t direction)
{
while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));
I2C_GenerateSTART(I2Cx, ENABLE);
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)) ;
I2C_Send7bitAddress(I2Cx, address, direction);
if (direction== I2C_Direction_Transmitter) {
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
}
else if(direction == I2C_Direction_Receiver) {
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
}
}
void I2C_write(I2C_TypeDef* I2Cx, uint8_t data)
{
I2C_SendData(I2Cx, data);
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
}
void I2C_stop(I2C_TypeDef* I2Cx)
{
I2C_GenerateSTOP(I2Cx, ENABLE);
}
int8_t I2C_read_ack(I2C_TypeDef* I2Cx)
{
int8_t data;
I2C_AcknowledgeConfig(I2Cx, ENABLE);
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED));
data=I2C_ReceiveData(I2Cx);
return data;
}
int8_t I2C_read_nack(I2C_TypeDef* I2Cx)
{
uint8_t data;
I2C_AcknowledgeConfig(I2Cx, DISABLE);
I2C_GenerateSTOP(I2Cx, ENABLE);
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED));
data=I2C_ReceiveData(I2Cx);
return data;
}

The full project to read the temperature from the TC74 sensor can be found on my Github page.