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. TIM16 och TIM17 ingår i bussen APB2. Som ni kan se så har jag valt prescaler till 4799 och det har med att APB2 är inställd på 48 MHz. 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. 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;
}
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);
}
Här kommer det magiska
Kod: Markera allt
handler_tim17->hdma[TIM_DMA_ID_CC1]->Instance->CNDTR
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;
}
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
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?