web-dev-qa-db-fra.com

Temporisateurs STM32F4 - Calcul de la période et de la pré-échelle et génération d'un retard de 1 ms

J'utilise STM32F407VGT6 Avec CubeMX.
Je commençais donc avec des minuteries à usage général et je suis coincé avec des valeurs de pré-échelle et de période.

Fondamentalement, je veux générer une interruption de minuterie toutes les n (où n = 1,2,3 ..) ms et effectuer certaines tâches.
Il y a beaucoup de variations dans la formule pour calculer la valeur de la période et de la pré-échelle

Certaines versions de formule sont:

TIMupdateFreq (HZ) = Horloge/((PSC-1) * (Période-1))
Événement de mise à jour = TIM clk/((PSC + 1) * (ARR + 1) * (RCR + 1))
Prescaler = ((((ClockSpeed)/((période)/(1/fréquence))) + 0,5) - 1)

Pour en venir à la question, mon horloge principale fonctionne à 168 MHz Mais je peux voir que le minuteur est connecté à APB1 Bus Qui fonctionne à 84 MHz.

J'ai essayé un code qui génère un retard de 1 ms (dit par l'auteur) et après avoir utilisé ces valeurs pour la pré-échelle et la période, j'ai généré un code qui génère également un retard de 1 ms (par intuition - pas de portée).

Le code utilise une valeur de mise à l'échelle de 41999 et une période de 1999.

Alors,
PSC - 41999
ARR - 1999
Application de ceci à la deuxième formule

Update Event = TIM clk/((PSC+1)*(ARR+1)*(RCR+1))

Update Event = 84000000/(42000*2000) = 1 (ce délai est-il de 1 ms ??)

OK alors maintenant j'essaie de comprendre comment ces PSC = 41999 Et Period = 1999 Sont sélectionnés ?? Est-ce purement basé sur l'hypothèse que dans la formule que j'utilise, je dois assumer une variable. ?? Comment calculer la pré-échelle et la période si je veux dire 1,5 ou 2,3 ​​ou 4,9 quelque chose comme ce timing précis. ??

MODIFIER

De plus, lorsque j'ai utilisé PSC=41999 and Period =999 La valeur de l'événement de mise à jour est 2.

Update Event = 84000000/(42000*1000) = 2

Mais mon retard est deux fois par seconde. soit 500 ms

et quand j'utilise PSC=39999 and Period =4199 La valeur de l'événement de mise à jour est 0,5.

Update Event = 84000000/(40000*4200) = 0.5

et mon retard de 2 ms.

Merci d'avance

4
Ehsan Habib

TIMupdateFreq (HZ) = Horloge/((PSC-1) * (Période-1))

C'est évidemment faux. Les compteurs passent de 0 à la valeur de registre (inclus), il y a toujours un cycle de plus que la valeur de registre, pas un de moins.

Événement de mise à jour = TIM clk/((PSC + 1) * (ARR + 1) * (RCR + 1))

Celui-ci est meilleur, mais les temporisateurs à usage général n'ont pas de registres RCR. Vous pouvez supposer RCR=0 Et omettre *(RCR+1) de la formule.

Prescaler = ((((ClockSpeed)/((période)/(1/fréquence))) + 0,5) - 1)

Cela tente d'arrondir le résultat, lorsqu'aucune solution entière n'est possible. Plus d'informations plus tard.

Update Event = 84000000/(42000*2000) = 1 (ce délai est-il de 1 ms ??)

Non, il s'agit d'un retard d'une seconde (1 s) ou d'une fréquence de 1 Hz.

comment ces PSC = 41999 et Period = 1999 sont sélectionnés?

Prenez la formule simple,

Updatefrequency = TIM clk/((PSC+1)*(ARR+1))

réorganiser

(PSC+1)*(ARR+1) = TIMclk/Updatefrequency

alors vous avez une valeur connue sur le côté droit, mais deux inconnues sur le côté gauche. La solution triviale serait de définir l'un d'eux, par exemple PSC à 0 et ARR à la valeur de droite - 1.

Malheureusement, la plupart des temporisateurs n'ont que des registres 16 bits, donc cela ne fonctionnera pas lorsque TIMclk/Updatefrequency > 65535. PSC et ARR doivent se situer entre 0 et 65 535. Il faudrait trouver une factorisation qui réponde à ces contraintes.

Voyons un exemple, vous voudriez un délai de 2,3 secondes. Notez que 2,3 s est la période, pas la fréquence, vous devez donc mettre son inverse dans la formule.

(PSC+1) * (ARR+1) = 84000000 / (1 / 2.3) = 84000000 * 2.3 = 193200000

Heureusement, il y a beaucoup de zéros à la fin, vous pouvez simplement choisir par exemple 10000 En tant que prédécaleur (PSC=9999), Et ARR devient 19320-1 = 19319. Si le rapport souhaité n'est pas un nombre rond de Nice, vous devez alors recourir à factorisation entière , ou écrire un petit programme pour rechercher tous les diviseurs possibles (for(i=0;i<65536;i++) ...).

Il peut également arriver qu'il n'y ait pas de solutions entières précises du tout, alors vous pouvez toujours parcourir toutes les valeurs de prédimensionnement possibles et voir celle qui donne la plus petite erreur.

Update Event = 84000000/(42000*1000) = 2

Mais mon retard est deux fois par seconde. soit 500 ms

Notez les dimensions. Vous utilisez fréquences dans la formule, vous divisez la fréquence d'entrée de 84 MHz avec quelques valeurs, et obtenez 2 Hz en conséquence. La fréquence de 2 Hz signifie deux événements par seconde, donc les événements sont en effet séparés de 500 ms.

8

Il n'y a pas de "variations". Une seule formule existe:

Period = (PSC+1)*(ARR+1) / TmerClockFreq en secondes Period = 1000 * (PSC+1)*(ARR+1) / TmerClockFreq en millisecondes

Vous devez donc trouver l'ARR & PSC qui vous donnera le temps le plus proche possible de la période requise

2
P__J__

Je pensais que je jetterais une réponse plus complète ici. Pour une horloge à 84 MHz, il y a beaucoup combinaisons de pré-échelle et de période qui fonctionneront. Voici quelques exemples:

  PSC    ARR            F         ERROR EXACT
   1  41999  1000.000000  0.0000000000   YES
   2  27999  1000.000000  0.0000000000   YES
   3  20999  1000.000000  0.0000000000   YES
   4  16799  1000.000000  0.0000000000   YES
   5  13999  1000.000000  0.0000000000   YES
   6  11999  1000.000000  0.0000000000   YES
   7  10499  1000.000000  0.0000000000   YES
   9   8399  1000.000000  0.0000000000   YES
  11   6999  1000.000000  0.0000000000   YES
  13   5999  1000.000000  0.0000000000   YES
  14   5599  1000.000000  0.0000000000   YES
  15   5249  1000.000000  0.0000000000   YES
  19   4199  1000.000000  0.0000000000   YES

Comment puis-je trouver cela? Même les outils commerciaux comme celui de MikroElektronica ne proposent qu'une seule combinaison exacte (ou inexacte). Comment les trouver tous? J'ai simplement écrit un programme python pour les calculer tous. Il classe chacun comme exact, ou note l'erreur relative de ceux qui sont inexacts. En changeant la tolérance en haut du programme, vous pouvez "resserrer" ou "desserrer" les calculs si nécessaire.

Voici le programme dans son intégralité:

import numpy as np
import pandas as pd

TARGET_F = 1000  # In Hz so 50.0 is 0.020 seconds period and 0.25 is 4 seconds period
CLOCK_MCU = 84000000
TOLERANCE = 0.0001

# -----------------------------------------------------


def abs_error(num1, num2):
    return abs((num1 - num2) / num1)


def hertz(clock, prescaler, period):
    f = clock / (prescaler * period)
    return f


def perfect_divisors():
    exacts = []
    for psc in range(1, 65536):
        arr = CLOCK_MCU / (TARGET_F * psc)
        if CLOCK_MCU % psc == 0:
            if arr <= 65536:
                exacts.append(psc)
    return exacts


def add_exact_period(prescaler):
    entries = []
    arr = CLOCK_MCU / (TARGET_F * prescaler)
    if arr == int(arr):
        entry = [prescaler, arr, TARGET_F, 0.0]
        entries.append(entry)
    return entries


def possible_prescaler_value():
    possibles = []
    for psc in range(1, 65536):
        if psc in exact_prescalers:
            continue
        h1 = hertz(CLOCK_MCU, psc, 1)
        h2 = hertz(CLOCK_MCU, psc, 65536)
        if h1 >= TARGET_F >= h2:
            possibles.append(psc)
    return possibles


def close_divisor(psc, tolerance):
    arr = CLOCK_MCU / (TARGET_F * psc)
    error = abs_error(int(arr), arr)
    if error < tolerance and arr < 65536.0:
        h = hertz(CLOCK_MCU, psc, int(arr))
        return psc, int(arr), h, error
    else:
        return None


#  ------------------------------------------------------------------------

# Make a dataframe to hold results as we compute them
df = pd.DataFrame(columns=['PSC', 'ARR', 'F', 'ERROR'], dtype=np.double)

# Get exact prescalars first.
exact_prescalers = perfect_divisors()
exact_values = []
for index in range(len(exact_prescalers)):
    rows = add_exact_period(exact_prescalers[index])
    for rowindex in range(len(rows)):
        df = df.append(pd.DataFrame(np.array(rows[rowindex]).reshape(1, 4), columns=df.columns))

# Get possible prescalers.
poss_prescalers = possible_prescaler_value()
close_prescalers = []
for index in range(len(poss_prescalers)):
    value = close_divisor(poss_prescalers[index], TOLERANCE)
    if value is not None:
        close_prescalers.append((value[0], value[1], value[2], value[3]))
df = df.append(pd.DataFrame(np.array(close_prescalers).reshape(len(close_prescalers), 4), columns=df.columns))

#  Adjust PSC and ARR values by -1 to reflect the way you'd code them.
df['PSC'] = df['PSC'] - 1
df['ARR'] = df['ARR'] - 1

#  Sort first by errors (zeroes and lowest errors at top of list, and
#  then by prescaler value (ascending).
df = df.sort_values(['ERROR', 'PSC'])

# Make and populate column indicating if combination is exact.
df['EXACT'] = pd.Series("?", index=df.index)
df['EXACT'] = np.where(df['ERROR'] == 0.0, "YES", "NO")

#  Format for output.
df['PSC'] = df['PSC'].map('{:.0f}'.format)
df['ARR'] = df['ARR'].map('{:.0f}'.format)
df['F'] = df['F'].map('{:.6f}'.format)
df['ERROR'] = df['ERROR'].map('{:.10f}'.format)

output = df.to_string()
print(output)
print()
print('these are the ', df.shape[0], ' total combination meeting your tolerance requirement')
exit(0)

En utilisant ce programme, tout le monde pourra calculer ces valeurs en toute confiance. J'espère que cela se révèle utile.

1
TomServo