cancel
Showing results for 
Search instead for 
Did you mean: 

Sprintf() float formatting messes with timer CCR register update preventing duty cycle update

SStru.1
Associate II

I am a newcomer to STM32 devices and working with the STM32F103C8T6 with Atollic True Studio IDE, mostly HAL libraries and CUbeMX. I'm trying to implement a proportional integral controller which uses a thermistor for feedback and a PWM signal using Timer 3 to switch a MOSFET driving a 12V 40W 3D printer heater cartridge.

I have set up timer 4 to trigger the ADC conversion every 20ms and I average adc readings over 50 times this (to get the average temperature per second). I then set a flag from the ADC conversion complete callback which lets the PI controller functions be called in the main loop:

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc){
 
	waxThermistor = HAL_ADC_GetValue(&hadc1);
	float temperature = getTemperature((float)waxThermistor);
 
	/*Moving average*/
		if(count_cycles<49){
			runningTotal+=temperature;
			count_cycles++;
		}
 
		if(count_cycles==49){
			startPI=1;
			runningTotal+=temperature;
			float average = (roundf(runningTotal * 100) / 100)/50;
			temperatureInDegrees = average;
			timeElapsed+=1;
			runningTotal = 0;
			count_cycles = 0;
		}
}

In the main loop if startPI is set then two functions are called which calculate control values and then the duty cycle is written to CCR1 on timer 3. The time elapsed in second intervals and the temperature are sent out over UART1.

  while (1)
  {
 
	 checkForCommands();
 
	 if(startPI==1){
		 float pi = PI(setpoint,temperatureInDegrees,200,10); //Set proportional and integral constants
		 int actuate = limitActuation(pi,0,800); //Set maximum duty cycle to 80%
		 htim3.Instance->CCR1 = actuate;
		 startPI=0;
		 int temperature = temperatureInDegrees;
		 int time = timeElapsed;
		 sendTemperature(time,temperature);
	 }

My functions for PI control. I have the timer set up so that a CCR value of 500 will be 50% duty cycle, 200 will be 20% etc:

float PI(float setpoint, float current, int Kp, int Ki){
 
	//if there is something wrong with the ADC and it returns 0 then do nothing.
	if(current==0){
			return 0;
		}
 
	float err = setpoint - current;
	if(err<0){
		err=0;
	}
 
	errorIntegral = errorIntegral + err;
	if(errorIntegral>50){
		errorIntegral=50;
	} else if(errorIntegral <-50){
		errorIntegral=-50;
	}
	float P = Kp*err;
	float E = Ki*errorIntegral;
	return P + E;
}
 
int limitActuation(float input, int min, int max){
	if(input<min) return min;
	if(input>max) return max;
	return input;
}

And I have a function for formatting the string to send:

void sendTemperature(int time, int temperature){
 
	char buffer [2];
 
	itoa(time,buffer,10);
 
	strcat(sendTemp,buffer);
 
	strcat(sendTemp,",");
 
//	char floatBuf[5];
 
//	sprintf(floatBuf,"%.1f",temperature);
 
//	strcat(sendTemp,floatBuf);
 
	itoa(temperature,buffer,10);
 
	strcat(sendTemp,buffer);
 
	strcat(sendTemp,"\n");
 
	usart_send_string(sendTemp);
 
	sendTemp[0] = '\0';
 
//	floatBuf[0] = '\0';
 
}

If I run the version of this function where I send the temperature out as an integer then everything works fine and I am able to achieve quite good setpoint tracking. However if I use sprintf to output the temperature with 2 decimal points, or even 1 then something strange happens:

In the debug I can see that the value for the 'actuate' variable is updating correctly however the value in CCR1 only gets updated once and so the duty cycle just stays fixed.

Could anyone advise what might cause this behaviour?

In short my program works well if send the temperature out over uart as an int - the duty cycle gets lower as the setpoint is approached however if I try to format to have two decimal places the duty cycle (CCR register) doesn't update - I have attached my full main file below.

2 REPLIES 2

> In the debug I can see that the value for the 'actuate' variable is updating correctly however the value in CCR1 only gets updated once

The only thing which "stays between" actuate and CCR1 is the htim3, so if you see actuate changing and TIMx_CCR1 not changing, then look at htim3.

We can then of course discuss merits and drawbacks of using sprintf() and kin in microcontroller environment, default type conversions in C (your float operations are probably all or many of them converting to double and back to float, which is expensive), and usage of Cube/HAL for control operations.

JW

TDK
Guru

Probably a buffer overflow somewhere.

> char buffer [2];

This doesn't seem like enough storage. Are you only writing a single digit?

If you feel a post has answered your question, please click "Accept as Solution".