Skip to content
Snippets Groups Projects
temperatureRegulation.c 9.72 KiB
Newer Older
/*
 * temperatureRegulation.c
 *
 *  Created on: Apr 16, 2023
 *      Author: Flax
 *
 *      Prefix : REG
 */

// ==============
// Local includes
// ==============
#include "register.h"
Flax's avatar
Flax committed
#include "string.h"
// ==============
// Local typedefs
// ==============
typedef enum {
	REG_MODE_OFF,
	REG_MODE_REGUL,
	REG_MODE_ADC_PRE,
	REG_MODE_ADC_START,
	REG_MODE_ADC_WAIT,
}e_REGMode;

// ===============
// Local constants
// ===============
#define cREGTempSPTMax				(400U)
#define cREGTempSPTMin				(50U)
#define cREGTempSPTDft				(250U)
#define cREGTempSPTStbyDft			(100U)
#define cREGPwmMax					(1023)

#define cREGErrorTempThres			(50)
#define cREGErrorTimeout			(5000)

#define cRegPIDProp					(10)
#define cRegPIDInteg				(5)
#define cRegPIDDeriv				(5)
// Temperature measure transfer function parameters
// OPA gain : 681
// Input divider bridge gain : 100k / (100k + 5k6) = 0.946969
// On legacy Arduino code : gain = 0.39, offset = 23.9, resolution 10 bits
// Translated to 12 bits : 0.0975 ~= 100 / 1024
#define ADC_TO_TEMP_GAIN_NUM		(100U)
#define ADC_TO_TEMP_GAIN_DEN		(1024U)
#define ADC_TO_TEMP_OFFSET			(24U)

#define cREGAdcErrorThres			(4090U)

#define cREGAdcToTempFilter			(50U)
#define cREGAdcAcqCycle				(100U)

#define mREGSetPwm(val)				(mREGPwmSetCompareValue(cREGPwmMax - val))

// ===============
// Local variables
// ===============
static e_REGMode RegModeCurrent_E;

static bool RegOffState_B;
static bool RegStandbyState_B;
static bool RegErrorState_B;
static uint16_t RegTempSPT_U16;
static uint16_t RegTempSPTStby_U16;
static uint16_t RegTempMeas_U16[2U];
static uint16_t RegTempMeasBuffer_U16A[2U];
static int32_t  RegPwmDyc_S32;
static uint32_t RegPwmDyc_U32;
Flax's avatar
Flax committed
static volatile uint16_t RegTempDeg_U16;
static uint16_t RegTempDegFilt_U16;
static uint16_t RegTempFiltCnt_U16;
static uint32_t RegErrorTimeoutCnt_U32;
// ADC
static uint16_t RegAdcCycleCnt_U16;
static bool     RegAdcOngoing_B;

// PID
static int32_t RegEpsilon_S32A[2U];
Flax's avatar
Flax committed
// DEBUG
uint8_t debug_buffer_U8A[3];
uint16_t time_debug_U16 = 0;
// DEBUG
// ==========================
// Local functions prototypes
// ==========================

// ===========================
// Local functions definitions
// ===========================

// ============================
// Shared functions definitions
// ============================

void REGInit (void)
{
	RegOffState_B = true;
	RegStandbyState_B = false;
	RegModeCurrent_E = REG_MODE_OFF;

	// TODO : load NVM values
	RegTempSPT_U16 = cREGTempSPTDft;
	RegTempSPTStby_U16 = cREGTempSPTStbyDft;
	RegErrorTimeoutCnt_U32 = cREGErrorTimeout;
	LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1);
	LL_TIM_EnableCounter(TIM2);

	RegModeCurrent_E = REG_MODE_ADC_PRE;
	mREGAdcStopConversion();
	switch (RegModeCurrent_E)
	case REG_MODE_OFF:
		break;
	case REG_MODE_REGUL:
		if (RegAdcCycleCnt_U16 < cREGAdcAcqCycle)
			RegAdcCycleCnt_U16++;
			if (mREGAdcIsReady())
			{
				RegAdcOngoing_B = true;
				RegModeCurrent_E = REG_MODE_ADC_PRE;
			}
			else
			{
				// ADC not ready
				RegTempMeasBuffer_U16A[1] = 0xFFFF;
				RegAdcCycleCnt_U16 = 0;
			}
		break;
	case REG_MODE_ADC_PRE:
		RegModeCurrent_E = REG_MODE_ADC_START;
		break;
	case REG_MODE_ADC_START:
		mREGAdcStartConversion();
		RegModeCurrent_E = REG_MODE_ADC_WAIT;
		break;
	case REG_MODE_ADC_WAIT:
		if (mREGAdcEndOfConversion() == true)
			// Read ADC input
			RegTempMeasBuffer_U16A[1] = mREGAdcReadValue();

			// Calculate average
			RegTempMeasBuffer_U16A[0] += RegTempMeasBuffer_U16A[1];
			RegTempMeasBuffer_U16A[0] = RegTempMeasBuffer_U16A[0] >> 1U;
			RegTempMeas_U16[1] = RegTempMeas_U16[0]; // Memorise previous value
			RegTempMeas_U16[0] = RegTempMeasBuffer_U16A[0];

			if (RegTempMeasBuffer_U16A[1] >= cREGAdcErrorThres)
			{
				// Error
				RegErrorState_B = true;
			}
			RegAdcCycleCnt_U16 = 0;
			RegAdcOngoing_B = false;
			RegModeCurrent_E = REG_MODE_REGUL;
	// Convert to degrees
	RegTempDeg_U16 = (uint16_t)(((((uint32_t)ADC_TO_TEMP_GAIN_NUM * (uint32_t)(RegTempMeas_U16[0])) / (uint32_t)ADC_TO_TEMP_GAIN_DEN) + (uint32_t)ADC_TO_TEMP_OFFSET) & (uint32_t)0x0000FFFF);

Flax's avatar
Flax committed
	/**********************************************************************************/
	/* PID */
	/**********************************************************************************/

	// Regulation is done in degrees
	RegEpsilon_S32A[1] = RegEpsilon_S32A[0]; // Memorise previous value
	RegEpsilon_S32A[0] = RegTempSPT_U16 - RegTempDeg_U16;
	// Check error - Overheat
	if (RegEpsilon_S32A[0] > cREGErrorTempThres)
	{
		if (RegErrorTimeoutCnt_U32 > 0)
		{
			RegErrorTimeoutCnt_U32--;
		}
		else
		{
			// Error - Too much temperature error for too long
			RegErrorState_B = true;
		}
	}
	else
	{
		RegErrorTimeoutCnt_U32 = cREGErrorTimeout;
	}

	// Process PID output
	RegPwmDyc_S32 = (RegEpsilon_S32A[0] * cRegPIDProp)
					+ ((RegEpsilon_S32A[0] - RegEpsilon_S32A[1]) * cRegPIDDeriv)
					+ ((RegEpsilon_S32A[0] + RegEpsilon_S32A[1]) * cRegPIDInteg);
	// Check regulation state before processing the output PWM
	// if ((RegErrorState_B == false) && (RegOffState_B == false))
	if ((RegOffState_B == false))
		if (RegPwmDyc_S32 > (int32_t)cREGPwmMax)
		{
			RegPwmDyc_U32 = (uint32_t)cREGPwmMax;
		}
		else if (RegPwmDyc_S32 > 0)
		{// Only keep positive values
			RegPwmDyc_U32 = RegPwmDyc_S32;
		}
		else
		{
			RegPwmDyc_U32 = 0U;
		}
	{ // Shut the PWM down
		RegPwmDyc_U32 = 0U;
	}
Flax's avatar
Flax committed
	// DEBUG
	// https://github.com/nathandunk/BetterSerialPlotter#example-arduino-code
	time_debug_U16++;
	if (time_debug_U16 == 1000)
	{
		time_debug_U16 = 0;
	}
	DISPValueToDigits(time_debug_U16, debug_buffer_U8A);
	LL_USART_TransmitData8(USART2, 0x74); // "t"
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	LL_USART_TransmitData8(USART2, 0x30 + debug_buffer_U8A[0]);
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	LL_USART_TransmitData8(USART2, 0x30 + debug_buffer_U8A[1]);
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	LL_USART_TransmitData8(USART2, 0x30 + debug_buffer_U8A[2]);
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	LL_USART_TransmitData8(USART2, 0x09); // "/t"
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	DISPValueToDigits(RegTempDeg_U16 / 10, debug_buffer_U8A);
	LL_USART_TransmitData8(USART2, 0x30); // "0"
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	LL_USART_TransmitData8(USART2, 0x2E); // "."
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	LL_USART_TransmitData8(USART2, 0x30 + debug_buffer_U8A[0]);
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	LL_USART_TransmitData8(USART2, 0x30 + debug_buffer_U8A[1]);
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	LL_USART_TransmitData8(USART2, 0x30 + debug_buffer_U8A[2]);
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	LL_USART_TransmitData8(USART2, 0x09); // "/t"
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	DISPValueToDigits(RegPwmDyc_U32 / 10, debug_buffer_U8A);
	LL_USART_TransmitData8(USART2, 0x30); // "0"
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	LL_USART_TransmitData8(USART2, 0x2E); // "."
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	LL_USART_TransmitData8(USART2, 0x30 + debug_buffer_U8A[0]);
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	LL_USART_TransmitData8(USART2, 0x30 + debug_buffer_U8A[1]);
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	LL_USART_TransmitData8(USART2, 0x30 + debug_buffer_U8A[2]);
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	LL_USART_TransmitData8(USART2, 0x0A); // "/n"
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	LL_USART_TransmitData8(USART2, 0x0D); // "/r"
	while (LL_USART_IsActiveFlag_TXE(USART2) == 0) {}
	// DEBUG

	/**********************************************************************************/

	// Set PWM output duty cycle
	if (RegAdcOngoing_B == false)
	{
		// mREGSetPwm(cREGPwmMax);
		mREGSetPwm(RegPwmDyc_U32);
		//mREGPwmSetCompareValue(cREGPwmMax / 32);
	}
	else
	{
		// No switching during ADC acquisition
		mREGSetPwm(0U);
	}
	// Filter physical temperature - for cleaner display
	if (RegTempFiltCnt_U16 < cREGAdcToTempFilter)
	{
		RegTempFiltCnt_U16++;
	}
	else
	{ // Sub-sampling for cleaner display
		RegTempFiltCnt_U16 = 0U;
		RegTempDegFilt_U16 = RegTempDeg_U16;
	}

bool REGIsError (void)
{
	return RegErrorState_B;
}

bool REGIsStandby(void)
{
	return RegStandbyState_B;
}

bool REGIsOff(void)
{
	return RegOffState_B;
}

void REGErrorReset (void)
{
	RegOffState_B = false;
}

void REGStandbySet(void)
{
	RegStandbyState_B = true;
}

void REGStandbyReset(void)
{
	RegStandbyState_B = false;
}

void REGOffSet(void)
{
	RegOffState_B = true;
}

void REGOffReset(void)
{
	RegOffState_B = false;
}

bool REGSPTSet (uint16_t temp_spt_u16)
{
	bool ret_B = false;
	if ((temp_spt_u16 <= (uint16_t)cREGTempSPTMax) && (temp_spt_u16 >= (uint16_t)cREGTempSPTMin))
	{
		RegTempSPT_U16 = temp_spt_u16;
		ret_B = true;
	}
	return (ret_B);
}

uint16_t REGSPTGet (void)
{
	return RegTempSPT_U16;
}

uint16_t REGTempGet (void)
{
	return RegTempDegFilt_U16;

bool REGSPTStbySet (uint16_t temp_spt_u16)
{
	bool ret_B = false;
	if ((temp_spt_u16 <= (uint16_t)cREGTempSPTMax) && (temp_spt_u16 >= (uint16_t)cREGTempSPTMin))
	{
		RegTempSPTStby_U16 = temp_spt_u16;
		ret_B = true;
	}
	return (ret_B);
}

uint16_t REGSPTStbyGet (void)
{
	return RegTempSPTStby_U16;
}

uint16_t REGTempPhysDeg (void)
{
	return (RegTempDeg_U16);
}