2017-04-18 1 views
2

J'ai utilisé le générateur de code d'initialisation STM32Cube pour générer une fonction de temporisateur initialisée. Pour générer un signal PWM de cycle de service fixe, j'ai ajouté HAL_TIM_Base_Start(&htim1); //Starts the TIM Base generation et HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1)//Starts the PWM signal generation à la fonction d'initialisation du temporisateur comme indiqué ci-dessous.Utilisation du temporisateur STM32 HAL et ajustement du cycle de service d'un signal PWM

/* Private variables ---------------------------------------------------------*/ 
int pulse_width=0; 

/* TIM1 init function */ 
static void MX_TIM1_Init(void) 
{ 

    TIM_ClockConfigTypeDef sClockSourceConfig; 
    TIM_MasterConfigTypeDef sMasterConfig; 
    TIM_OC_InitTypeDef sConfigOC; 
    TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig; 

    htim1.Instance = TIM1; 
    htim1.Init.Prescaler = 0;//we want a max frequency for timer, so we set prescaller to 0   
    //And our timer will have tick frequency 
    htim1.Init.CounterMode = TIM_COUNTERMODE_UP; 
    htim1.Init.Period = 1066;//max value for timer is 16bit = 65535, TIM_Period = timer_tick_frequency/PWM_frequency - 1 
    //In our case, for 15Khz PWM_frequency, set Period to TIM_Period = 16MHz/15KHz - 1 = 1066 
    htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; 
    htim1.Init.RepetitionCounter = 0; 
    if (HAL_TIM_Base_Init(&htim1) != HAL_OK)/* to use the Timer to generate a simple time base for TIM1 */ 
    { 
    Error_Handler(); 
    } 

    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;//the default clock is the internal clock from the APBx, using this function 
    if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)//Initializes the TIM PWM Time Base according to the specified 
//parameters in the TIM_HandleTypeDef and create the associated handle. 
    { 
    Error_Handler(); 
    } 

    if (HAL_TIM_PWM_Init(&htim1) != HAL_OK) 
    { 
    Error_Handler(); 
    } 

    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; 
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; 
    if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK) 
    { 
    Error_Handler(); 
    } 

    //sConfig: TIM PWM configuration structure 
    //set duty cycle: pulse_length = ((1066 + 1) * duty_cycle)/(100 - 1) 
    sConfigOC.OCMode = TIM_OCMODE_PWM1; 
    sConfigOC.Pulse = pulse_width;/* 50% duty cycle is 538, set to 0 initially*/// 
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; 
    sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH; 
    sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; 
    sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; 
    sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; 
    if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) 
    { 
    Error_Handler(); 
    } 

    if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) 
    { 
    Error_Handler(); 
    } 

    sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_ENABLE; 
    sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_ENABLE; 
    sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_1; 
    sBreakDeadTimeConfig.DeadTime = 0; 
    sBreakDeadTimeConfig.BreakState = TIM_BREAK_ENABLE; 
    sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH; 
    sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE; 
    if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK) 
    { 
    Error_Handler(); 
    } 

    HAL_TIM_MspPostInit(&htim1);//output pin assignment 
    HAL_TIM_Base_Start(&htim1); //Starts the TIM Base generation 
    if (HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1) != HAL_OK)//Starts the PWM signal generation 
    { 
    /* PWM Generation Error */ 
    Error_Handler(); 
    } 

    /* Start channel 2 */ 
    if (HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2) != HAL_OK) 
    { 
    /* PWM Generation Error */ 
    Error_Handler(); 
    } 

} 

Ceci est suffisant pour faire fonctionner le PWM à un cycle de service fixe spécifié dans les commentaires ci-dessus lorsque je coder en dur la valeur correcte pour remplacer la valeur pulse_width dans sConfigOC.Pulse = pulse_width. Dans une autre fonction, j'ai un algorithme qui mettrait à jour la variable globale pulse_width. La fonction est appelée: adjust_PWM();. L'algorithme calcule les valeurs mesurées à partir de l'ADC et stockées en tant que variables globales. Cette fonction est appelée: Data_Update();. En main(), après toutes les fonctions sont initialisées. J'appelle ces trois fonctions sans fin

Data_Update(); 
adjust_PWM(); 
MX_TIM1_Init(); 

J'ai essayé et obtenu des formes d'ondes étranges sur l'oscilloscope, mais cela pourrait être parce que les broches ADC où flottant, ce qui provoque des mesures flottantes interférer avec le cycle de service par l'algorithme. Le fait de rappeler en continu l'initialisation de la minuterie interromprait le signal PWM. Y a-t-il une meilleure façon de changer le cycle de service tout en exécutant le code sans utiliser de variables globales, ou sans initialiser la minuterie chaque fois que je veux mettre à jour le cycle de service. Tout lien serait apprécié.

+1

Le meilleur moyen est de se débarrasser du bloatware ST "HAL" et de programmer directement les registres. Cela économise la moitié du code en réalité moins d'effort. – Olaf

+0

@Olaf programme directement les registres? pouvez-vous élaborer pour quelqu'un qui est plus axé sur le matériel? – Nadim

+0

Lisez le manuel de référence (vous devez le faire de toute façon), n'incluez que le CMSIS et les en-têtes de définition de registre de ST et écrivez/lisez directement les registres des modules périphériques. En tant que personne axée sur le matériel, cela devrait également vous convenir beaucoup mieux. De cette façon, vous n'avez pas à jouer avec ce bloatware ** et ** le matériel, mais seulement le matériel. – Olaf

Répondre

2

Ne pas Reinit la minuterie lorsque vous souhaitez modifier un paramètre, HAL a une macro dédié à cet effet appelé:

/** 
    * @brief Sets the TIM Capture Compare Register value on runtime without 
    *   calling another time ConfigChannel function. 
    * @param __HANDLE__: TIM handle. 
    * @param __CHANNEL__ : TIM Channels to be configured. 
    *   This parameter can be one of the following values: 
    *   @arg TIM_CHANNEL_1: TIM Channel 1 selected 
    *   @arg TIM_CHANNEL_2: TIM Channel 2 selected 
    *   @arg TIM_CHANNEL_3: TIM Channel 3 selected 
    *   @arg TIM_CHANNEL_4: TIM Channel 4 selected 
    * @param __COMPARE__: specifies the Capture Compare register new value. 
    * @retval None 
    */ 
#define __HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__) \ 
(*(__IO uint32_t *)(&((__HANDLE__)->Instance->CCR1) + ((__CHANNEL__) >> 2)) = (__COMPARE__)) 

Pour la minuterie 1 - Canal 1 et minuterie 1 - Canal 2, il devrait ressembler à:

Data_Update(); 
adjust_PWM(); 

__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, pulse_width); 
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, pulse_width); 
0

Écrivez votre propre fonction pour mettre à jour le registre qui régit le cycle de service. Vous devrez mettre à jour manuellement le registre CCRx approprié (x est le canal PWM que vous utilisez, qui est CCR1 dans votre cas).

Le registre ARR est le registre que vous référerez lors du calcul de la nouvelle valeur du registre CCR en fonction du cycle de service souhaité.

void adjust_PWM_DC(TIM_HandleTypeDef* const pwmHandle, const float DC) 
{ 
    assert(pwmHandle != NULL); 
    assert((DC >= 0.0F) && (DC <= 100.0F)); 

    /* The duty cycle value is a percentage of the reload register value (ARR). Rounding is used.*/ 
    uint32_t newRegVal = (uint32_t)roundf((float32_t)(pwmHandle->Instance->ARR) * (DC * 0.01F)); 

    /*In case of the DC being calculated as higher than the reload register, cap it to the reload register*/ 
    if(newRegVal > pwmHandle->Instance->ARR){ 
     newRegVal = pwmHandle->Instance->ARR); 
    } 

    /*Assign the new DC count to the capture compare register.*/ 
    pwmHandle->Instance->CCR1 = (uint32_t)(roundf(newRegVal)); /*Change CCR1 to appropriate channel, or pass it in with function.*/ 
} 
+0

Alors que les M4F et M7 prennent en charge le virgule flottante, ils représentent néanmoins un fardeau pour le code intégré.Ne les utilisez pas, du moins pas dans un tel code. En règle générale, seul le code de l'application de niveau supérieur doit traiter des flottants et seulement ** si nécessaire **. – Olaf

+0

@Olaf Il existe des cas où les points flottants sont dangereux et doivent être évités. Bien qu'ils soient beaucoup plus simples à comprendre et à bien servir leur but dans ce morceau de code illustratif. Si le flottement doux est un problème, alors le DC [0; 100] peut être mappé sur [0; 0xFFFFU] ou point fixe etc. Ceci sera cependant plus lent que le matériel flottant ce dernier conservant également plus de précision. – Flip

+0

La vitesse du traitement de flotteur n'est pas le seul problème et je n'ai pas parlé de danger (qui sont deux questions). Ce n'est pas l'endroit pour le tutorat, mais un programmeur embarqué expérimenté va éviter les flotteurs où raisonnable, ce qui est très le problème. – Olaf