Hur får man en korrekt och proper Input Capture funktion i STM32?

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
DanielM
Inlägg: 2166
Blev medlem: 5 september 2019, 14:19:58

Hur får man en korrekt och proper Input Capture funktion i STM32?

Inlägg av DanielM »

Jag håller på lite med Input Capture i STM32 och det finns flera vägar att utföra sådant. Ena vägen är att använda interrupts, vilket jag tycker är inte korrekt väg att gå om man har en frekvens på 10 kHz. Risken är att interrupts tar upp hela processorns uppdrag då.

Den andra vägen är att använda DMA, vilket jag använder. Jag har valt TIM16 och TIM17 för att använda med Input Capture.
Skärmklipp.PNG
Skärmklipp.PNG
TIM16 och TIM17 ingår i bussen APB2.
Skärmklipp.PNG
Som ni kan se så har jag valt prescaler till 4799 och det har med att APB2 är inställd på 48 MHz.
Skärmklipp.PNG
Om vi gör lite matematik så säger vi:

\(f = \frac{APB2 Hz}{prescaler + 1}= \frac{48*10^6}{4799+1} = 10000 Hz\)

Alltså blir den nya räknaren för TIM17 och TIM16 10000 Hz.
Perioden väljer jag till 65535 och det betyder att kommer ta

\(\frac{65535}{10000} = 6.5535 s\)

6.5535 sekunder för TIM16 och TIM17 att kunna räkna upp till 65535.

Det betyder att minimala & maximala frekvensen som min input capture kan mäta är:
\(f_{min} = \frac{1}{65535*0.0001} = 0.1526 Hz\)
\(f_{max} = \frac{1}{1*0.0001} = 10000 Hz\)

Här använder jag konstanten 0.0001 för att räkna om 65535 och 1 till sekunder.

DMA:

Jag har valt att min DMA kommer se ut så här. Som ni ser så använder jag en cirkulär buffer av datatypen uint16_t half-word.
Skärmklipp.PNG
Så när jag startar mina input captures så ser koden ut så här:

Kod: Markera allt

#define LENGTH_ARRAY 100
static uint16_t input_capture0[LENGTH_ARRAY] = {0};
static uint16_t input_capture1[LENGTH_ARRAY] = {0};
static TIM_HandleTypeDef* handler_tim17;
static TIM_HandleTypeDef* handler_tim16;

void Input_Capture_Init(TIM_HandleTypeDef* htim17, TIM_HandleTypeDef* htim16) {
	/*
	 * Input capture for measuring frequency
	 * For TIM17 and TIM16
	 * Timer clock: 48 Mhz
	 * Prescaler: 4799
	 * Counter: 65535 (0xffff)
	 * Update frequency: 0.1526 Hz (1/0.1526 = 6.5535 seconds)
	 * 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(htim16, TIM_CHANNEL_1, (uint32_t*)input_capture1, LENGTH_ARRAY) != HAL_OK)
		Error_Handler();
	if(HAL_TIM_IC_Start_DMA(htim17, TIM_CHANNEL_1, (uint32_t*)input_capture0, LENGTH_ARRAY) != HAL_OK)
		Error_Handler();

	/* Save */
	handler_tim17 = htim17;
	handler_tim16 = htim16;

}
När jag vill ha en period från enskild input capture så anropar jag följande:

Kod: Markera allt

static uint16_t compute_period(uint16_t input_capture[], uint8_t left) {
	/* Find the index - i0 and i1 can never be larger than LENGTH_ARRAY and lower than 0 */
	uint8_t a = 2*LENGTH_ARRAY - left;
	uint8_t i0 = (a - 2) % LENGTH_ARRAY;
	uint8_t i1 = (a - 1) % LENGTH_ARRAY;

	/* Check absolute value */
	if(input_capture[i1] > input_capture[i0])
		return input_capture[i1] - input_capture[i0];
	else if(input_capture[i1] < input_capture[i0])
		return input_capture[i0] - input_capture[i1];
	else
		return 0;
}


uint16_t Input_Capture_Get_Raw(uint8_t i){
	if(i == 0)
		return compute_period(input_capture0, handler_tim17->hdma[TIM_DMA_ID_CC1]->Instance->CNDTR);
	else
		return compute_period(input_capture1, handler_tim16->hdma[TIM_DMA_ID_CC1]->Instance->CNDTR);
}
Där i är en variabel från 0 till 1 beroende på om jag vill använda TIM17 eller TIM16.


Här kommer det magiska

Kod: Markera allt

handler_tim17->hdma[TIM_DMA_ID_CC1]->Instance->CNDTR
CNDTR betyder hur många element som är kvar att skriva i buffern input_captureX.
Skärmklipp.PNG
Skärmklipp.PNG
Skärmklipp.PNG
Detta betyder att från början så är CNDTR == LENGTH_ARRAY och när jag har satt in ett värde i input_captureX så minskar CNDTR ett steg. När CNDTR == 0 så betyder det att alla element i input_captureX har fått ett uppdaterat värde och därmed så blir CNDTR == LENGTH_ARRAY igen och den cirkulära buffern börjar om på index 0 och repeterar processen.

Som ni ser ovan så använder jag ALLTID dom två sista elementen innan nuvarande index hos buffern.
Till exempelvis om DMA tänker skriva på index 0, så använder jag dom två sista indexerna, vilket är sista och näst sista elementet i arrayen.
Se kod här hur jag räknar ut skillnaderna i min buffer. Simulera koden här.

Kod: Markera allt

#include <stdio.h>
#include <stdint.h>

#define LEN 10

uint16_t buffer[LEN] = {0x0,0xFF,0xFFFF,0xF,0xFF,0xF,0xFFFF,0xFFF,0xFF,0xFF};

int main()
{

    for(int i = LEN; i >= 0; i--){
        uint8_t a = 2*LEN - i;
        uint8_t i0 = (a - 2) % LEN;
        uint8_t i1 = (a - 1) % LEN;
        
        /* Check absolute value */
        uint16_t absolute;
        if(buffer[i1] > buffer[i0])
            absolute = buffer[i1] - buffer[i0]; 
        else if(buffer[i1] < buffer[i0])
            absolute = buffer[i0] - buffer[i1]; 
        else
            absolute = 0;
        

        printf("i0 = %i, i1 = %i, i = %i Abs = 0x%X\n", 
                i0, i1, i, absolute);
    }

    return 0;
}
Resultatet. Notera att inget index är negativt eller över LEN.

Kod: Markera allt

i0 = 8, i1 = 9, i = 10 Abs = 0x0
i0 = 9, i1 = 0, i = 9 Abs = 0xFF
i0 = 0, i1 = 1, i = 8 Abs = 0xFF
i0 = 1, i1 = 2, i = 7 Abs = 0xFF00
i0 = 2, i1 = 3, i = 6 Abs = 0xFFF0
i0 = 3, i1 = 4, i = 5 Abs = 0xF0
i0 = 4, i1 = 5, i = 4 Abs = 0xF0
i0 = 5, i1 = 6, i = 3 Abs = 0xFFF0
i0 = 6, i1 = 7, i = 2 Abs = 0xF000
i0 = 7, i1 = 8, i = 1 Abs = 0xF00
i0 = 8, i1 = 9, i = 0 Abs = 0x0
Problem:

När jag mäter differensen mellan varje period hos min input capture så får jag detta. Ni ser vad hackigt det är? Varför blir det så?
Dessa visar en skillnad mellan två element hos input_capture0 buffer. Men ibland får ett värde jätte högt och ibland jätte lågt.
Hur kan jag propert och korrekt konstruera en Input Capture i STM32?
Skärmklipp.PNG
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
hummel
Inlägg: 2259
Blev medlem: 28 november 2009, 10:40:52
Ort: Stockholm

Re: Hur får man en korrekt och proper Input Capture funktion i STM32?

Inlägg av hummel »

Det bör fungera bra med interrupt om din ISR är snabb. Hur många klockcykler tar din ISR?
Användarvisningsbild
Swech
EF Sponsor
Inlägg: 4689
Blev medlem: 6 november 2006, 21:43:35
Ort: Munkedal, Sverige (Sweden)
Kontakt:

Re: Hur får man en korrekt och proper Input Capture funktion i STM32?

Inlägg av Swech »

Finns det ingen färdig absolutbeloppsfunktion i c ?

Swecb
DanielM
Inlägg: 2166
Blev medlem: 5 september 2019, 14:19:58

Re: Hur får man en korrekt och proper Input Capture funktion i STM32?

Inlägg av DanielM »

hummel skrev: 24 mars 2022, 14:43:50 Det bör fungera bra med interrupt om din ISR är snabb. Hur många klockcykler tar din ISR?
Jag använder inte interrupts. Jag använder DMA.
Swech skrev: 24 mars 2022, 15:36:25 Finns det ingen färdig absolutbeloppsfunktion i c ?

Swecb
Jo, abs() finns. Men med tanke på att allt är unsigned så fungerar if-satser bättre.

Edit: Nu har jag löst problemet.

Jag använder detta. Ser ni skillnaden från ovan?

Kod: Markera allt

        /* Check absolute value */
	if(input_capture[i1] > input_capture[i0])
		return input_capture[i1] - input_capture[i0];
	else if(input_capture[i1] < input_capture[i0])
		return input_capture[i1] + 0xFFFF - input_capture[i0];
	else
		return 0;
Resultat:
Skärmklipp.PNG
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
hummel
Inlägg: 2259
Blev medlem: 28 november 2009, 10:40:52
Ort: Stockholm

Re: Hur får man en korrekt och proper Input Capture funktion i STM32?

Inlägg av hummel »

Du gissar alltså att interrupt är dåligt i detta fall?
Du gör som du vill. :-)

Din funktion är definierad att returnera uint16_t men din beräkning ser ut att göra om resultat till en signed 32 bitars int.
DanielM
Inlägg: 2166
Blev medlem: 5 september 2019, 14:19:58

Re: Hur får man en korrekt och proper Input Capture funktion i STM32?

Inlägg av DanielM »

Gissar? Jag vet att en interrupt är dålig om den måste anropas hela tiden :)

Jag har fått det att fungera nu. Ja, den returnerar 16 bitar data, men det är inget problem att kasta om det till signed 32-bit. :)

Vad är bäst SysTick, eller använda en vanlig Timer som klocka?
hummel
Inlägg: 2259
Blev medlem: 28 november 2009, 10:40:52
Ort: Stockholm

Re: Hur får man en korrekt och proper Input Capture funktion i STM32?

Inlägg av hummel »

Menar du det du skriver eller försöker du vara rolig på något sätt?
ToPNoTCH
Inlägg: 4847
Blev medlem: 21 december 2009, 17:59:48

Re: Hur får man en korrekt och proper Input Capture funktion i STM32?

Inlägg av ToPNoTCH »

Om input_capture[i1] == input_capture[i0] så får du noll som svar, vill du det ?
DanielM
Inlägg: 2166
Blev medlem: 5 september 2019, 14:19:58

Re: Hur får man en korrekt och proper Input Capture funktion i STM32?

Inlägg av DanielM »

ToPNoTCH skrev: 28 mars 2022, 00:32:34 Om input_capture[i1] == input_capture[i0] så får du noll som svar, vill du det ?
Jag vill ha det. För det är vad det blir på riktigt. Men det kommer aldrig bli det...i praktiken.
hummel skrev: 27 mars 2022, 23:01:19 Menar du det du skriver eller försöker du vara rolig på något sätt?
Men tänk en interrupt som anropas 1000 gånger per sekund. Klarar den av det eller? Nej, jag har testat interrupt på en ADC och det var det dummaste jag hade gjort.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 45168
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Hur får man en korrekt och proper Input Capture funktion i STM32?

Inlägg av TomasL »

Varför skulle den inte klara det, det gäller ju att göra smarta funktioner.
Användarvisningsbild
sodjan
EF Sponsor
Inlägg: 43148
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping
Kontakt:

Re: Hur får man en korrekt och proper Input Capture funktion i STM32?

Inlägg av sodjan »

Det är ungefär lika enkelt/svårt att göra någonting 1000 gånger/sek
oavsett om det sker via interrupt eller på något annat sätt.
Interrupt kan även vara snabbare, speciellt på en processor med
snabb "context switch". Och man slipper overhead från beräkningar
av delay eller intervall.
Användarvisningsbild
Klas-Kenny
Inlägg: 11292
Blev medlem: 17 maj 2010, 19:06:14
Ort: Växjö/Alvesta

Re: Hur får man en korrekt och proper Input Capture funktion i STM32?

Inlägg av Klas-Kenny »

Jag har en kommersiell produkt ute som har tolv frekvensingångar, klarar typ 20 kHz på varje och görs helt med interrupt, så typ 200 kHz interrupt som mest, funkar fint.

Implementerat med en PIC32 klockad med 80 MHz vill jag minnas
Men det krävdes såklart lite eftertanke för att göra interruptet effektivt.
Användarvisningsbild
Icecap
Inlägg: 26106
Blev medlem: 10 januari 2005, 14:52:15
Ort: Aabenraa, Danmark

Re: Hur får man en korrekt och proper Input Capture funktion i STM32?

Inlägg av Icecap »

"Nej, jag har testat interrupt på en ADC och det var det dummaste jag hade gjort."

Inte en chans! Du har gjort många dummare ting.

Men 1000 interrupts/sekund är inget - om man gör just vad de ska o inget annat.

Och en interrupt kallas inte, den aktiveras av hårdvaran.

Jag har gjort 3 st rotary-encoder dekodning mha. en 1kHz timerinterrupr. Nemas problemas - om man håller det rent o snyggt.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 45168
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Hur får man en korrekt och proper Input Capture funktion i STM32?

Inlägg av TomasL »

Och 230kbit/s seriell kommunikation via interrupt, heller inga problem på en 80MHz MIPS
Användarvisningsbild
AndLi
Inlägg: 17045
Blev medlem: 11 februari 2004, 18:17:59
Ort: Knivsta
Kontakt:

Re: Hur får man en korrekt och proper Input Capture funktion i STM32?

Inlägg av AndLi »

Fasiken jag har genererat en PPM signal i typ 38 kbit/s med en atmega1284 i 20Mhz med timerar och interrupt... (bitnangad mer eller mindre alltså)

Det gäller ju bara att ha koll på att klockcyklerna räcker till och inte använda för svulstiga libbar....( det var skrivet i c)
Skriv svar