Skip to main content
Lead
August 7, 2021
Solved

Can I combine Encoder with Input Capture at DMA?

  • August 7, 2021
  • 8 replies
  • 3996 views

Hi!

I have a STM32F373 processor and I have 2 Encoders and 3 Input Captures.

I have enabled DMA for input capture.

Timer 4:

0693W00000D1n2KQAR.png0693W00000D1n2PQAR.pngTimer 19:

0693W00000D1n2jQAB.png0693W00000D1n2tQAB.png 

And I have this code.

My questions are:

  1. Can I use Encoder together with Input Capture if Input Capture has DMA?
  2. What will DMA store inside the arrays input_capture[2]? Is it the counter value? I'm assuming it will be some kind of a counted value, right?
  3. Will this work?
static TIM_HandleTypeDef* tim4_handle;
static TIM_HandleTypeDef* tim19_handle;
volatile static uint32_t input_capture0[2] = {0};
volatile static uint32_t input_capture1[2] = {0};
volatile static uint32_t input_capture2[2] = {0};
static uint32_t pulse_count[4] = {0};
 
void STM32_PLC_Start_Counters(TIM_HandleTypeDef* htim4, TIM_HandleTypeDef* htim19) {
	/* Encoder - Prescaler does not effect encoder */
	if(HAL_TIM_Encoder_Start(htim4, TIM_CHANNEL_ALL) != HAL_OK)
		Error_Handler();
	if(HAL_TIM_Encoder_Start(htim19, TIM_CHANNEL_ALL) != HAL_OK)
		Error_Handler();
 
	/*
	 * Input capture for measuring frequency
	 * For TIM4 and TIM19
	 * Timer clock: 48 Mhz
	 * Prescaler: 4799
	 * Counter: 65535 (0xffff)
	 * Update frequency: 6.5535 Hz
	 * Example: For every second, it will count 10000
	 * Lowest frequency measurement: 1/(0xFFFF*0.0001) = 0.1526 Hz
	 * Highest frequency measurement: 1/(1*0.0001) = 10000 Hz
	 */
	if(HAL_TIM_IC_Start_DMA(htim4, TIM_CHANNEL_3, (uint32_t*)input_capture1, 2) != HAL_OK)
		Error_Handler();
	if(HAL_TIM_IC_Start_DMA(htim4, TIM_CHANNEL_4, (uint32_t*)input_capture0, 2) != HAL_OK)
		Error_Handler();
	if(HAL_TIM_IC_Start_DMA(htim19, TIM_CHANNEL_3, (uint32_t*)input_capture2, 2) != HAL_OK)
		Error_Handler();
 
	/* Save */
	tim4_handle = htim4;
	tim19_handle = htim19;
}
 
int16_t STM32_PLC_Encoder_Get(uint8_t i) {
	if(i == 0)
		return tim4_handle->Instance->CNT;
	else
		return tim19_handle->Instance->CNT;
}
 
static float compute_frequency(uint16_t input_capture[]) {
	/*
	 * Typical worst case scenarios:
	 * T1: 0xFFFF - T0: 0x0
	 * T1: 0x0 - T0: 0xFFFF
	 * T1: 0x7FFF - T0: 0x7FFF
	 * T1: 0x0	 -	T0: 0x0
	 */
	if(input_capture[1] > input_capture[0]) {
		return (float) 1/((input_capture[1] - input_capture[0])*0.0001f);
	} else if(input_capture[1] < input_capture[0]) {
		return (float) 1/((input_capture[1] + 0xFFFF - input_capture[0])*0.0001f);
	} else if(input_capture[1] == 0x7FFF && input_capture[0] == 0x7FFF){
		return (float) 1/(0xFFFF*0.0001f);
	} else {
		return 0;
	}
}
 
float STM32_PLC_Input_Capture_Get(uint8_t i) {
	if(i == 0)
		return compute_frequency((uint16_t*)input_capture0);
	else if(i == 1)
		return compute_frequency((uint16_t*)input_capture1);
	else
		return compute_frequency((uint16_t*)input_capture2);
}
 
void STM32_PLC_Input_Capture_Reset() {
	for(uint8_t i = 0; i < 2; i++){
		input_capture0[i] = 0;
		input_capture1[i] = 0;
		input_capture2[i] = 0;
	}
}
 
void STM32_PLC_Pulse_Count_Set(uint8_t i){
	pulse_count[i]++;
}
 
uint32_t STM32_PLC_Pulse_Count_Get(uint8_t i){
	return pulse_count[i];
}
 
void STM32_PLC_Pulse_Count_Reset(){
	memset(pulse_count, 0, sizeof(pulse_count));
}

This topic has been closed for replies.
Best answer by DMårt

Is it totaly normal that I getting a number 65535 if I connect 3.3V to encoder plus? And I get only value 1 if I connect it to encoder minus.

If I not connect anything, I get the value 0. Why? I thougth encoder till remember the value.

Edit:

Now it's working!

static TIM_HandleTypeDef* tim4_handle;
static TIM_HandleTypeDef* tim19_handle;
static uint16_t encoder_pulses_per_revolution[2] = {0};
static int16_t encoder[2] = {0};
 
void STM32_PLC_Start_Encoder(TIM_HandleTypeDef* htim4, TIM_HandleTypeDef* htim19) {
	/* Encoder - Prescaler does not effect encoder */
	if(HAL_TIM_Encoder_Start(htim4, TIM_CHANNEL_ALL) != HAL_OK)
		Error_Handler();
	if(HAL_TIM_Encoder_Start(htim19, TIM_CHANNEL_ALL) != HAL_OK)
		Error_Handler();
 
	/* Save */
	tim4_handle = htim4;
	tim19_handle = htim19;
}
 
float STM32_PLC_Encoder_Get(uint8_t i) {
	/* Safety */
	if(encoder_pulses_per_revolution[i] == 0)
		encoder_pulses_per_revolution[i] = 1;
	float pulses = (float)encoder_pulses_per_revolution[i];
	return ((float)STM32_PLC_Encoder_Get_Raw(i))/pulses;
}
 
int16_t STM32_PLC_Encoder_Get_Raw(uint8_t i) {
	if(i == 0)
		encoder[i] += __HAL_TIM_GET_COUNTER(tim4_handle);
	else
		encoder[i] += __HAL_TIM_GET_COUNTER(tim19_handle);
	return encoder[i];
}
 
void STM32_PLC_Encoder_Set_Pulses_Per_Revolutions(uint8_t i, uint16_t pulses_per_revolution) {
	encoder_pulses_per_revolution[i] = pulses_per_revolution;
}

8 replies

TDK
August 7, 2021

Yes.

IC will capture the CNT value at the time of the event, which is then transferred by DMA.

"If you feel a post has answered your question, please click ""Accept as Solution""."
DMårtAuthor
Lead
August 7, 2021

So in this case, combining Encoder with IC....bad idea?

Do you have another solution?

I think I need to have DMA because if I using interrupts, then the interrupts will...well...interrupt other parts of the code.

STM32MP151AAC3 custom board with STM32-OS as operating system: https://github.com/DanielMartensson/STM32-ComputerSTM32MP257FAK3 custom board with STM64-OS as operating system: https://github.com/DanielMartensson/STM64-Computer
TDK
August 7, 2021
It doesn't seem very useful to me.
Solution for what? I don't see you explaining what you want anywhere. There are plenty of other timers on the chip.
"If you feel a post has answered your question, please click ""Accept as Solution""."