Drivers

peripheriques.svg

BROCHAGE

Selection des Broches


Fonctionnement d’un Timer

Un Timer est un compteur qui s’incrémente à chaque front montant d’une horloge.
Cette horloge peut éventuellement être ralentie avec un diviseur de fréquence (Prescaler).
Le Compteur est remis périodiquement à zéro lorsque sa valeur courante de comptage atteint celle d’un registre de comparaison.

timer_principe_1.svg

timer_principe_2.svg

T=[(Presc_buff+1) * (Comp_Reg+1)]*T_CLK


Contrôle du Hacheur

Timer en mode wafeform PWM (Pulse Width Modulation)

Pour générer un signal MLI (Modulation de Largeur d’Impulsion), il suffit de comparer la valeur courant de comptage à celle d’un registre (Duty), et de forcer à 0 ou à 1 une broche de sortie du microcontrôleur en fonction du résultat de cette comparaison.

pwm_2.svg

pwm_3.svg

pwm_4.svg

Commande des Moteurs

  • void motorCommand_Init(void)
  • void motorCommand_SetDuty(int duty)
    • duty rapport cyclique
    • 0 < duty < 200
    • moteurs au repos pour duty=100
code motorCommand
/*
  * motorCommand.h
  *
  *  Created on: 27 mars 2017
  *      Author: kerhoas
  */
 
 #ifndef INC_MOTORCOMMAND_H_
 #define INC_MOTORCOMMAND_H_
 
 #include "main.h"
 
 
 void motorCommand_Init(void);
 void motorCommand_SetDuty(int);
 
 
 #endif /* INC_MOTORCOMMAND_H_ */
 
/*
 * motorCommand.c
 *
 *  Created on: 27 mars 2017
 *      Author: kerhoas
 */



/*
 * MotorCommand.c
 */

#include "motorCommand.h"

static TIM_HandleTypeDef    TimHandle;
static TIM_OC_InitTypeDef   sConfigOC;

//=================================================================
//			PWM INIT
// TIMER 3 (PWM)  : CH1
// ENABLE : Sortie Logique (GPIO) PB5
//=================================================================

void motorCommand_Init(void)
{
	unsigned int uwPrescalerValue = 0;

	/* Compute the prescaler value to have TIM4 counter clock equal to 10MHz */
	  uwPrescalerValue = (unsigned int) ((SystemCoreClock / 10000000) - 1);
	  TimHandle.Instance = TIM3;
	  TimHandle.Init.Period = PWM_VAL_MAX - 1;				 // 100MHz/200=50kHz
	  TimHandle.Init.Prescaler = 0 ;//uwPrescalerValue;
	  TimHandle.Init.ClockDivision = 0;
	  TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;

	  HAL_TIM_Base_Init(&TimHandle);

	  sConfigOC.OCMode = TIM_OCMODE_PWM1;
	  sConfigOC.Pulse = 0x5;// Specifies the pulse value to be loaded into the Capture Compare Register. This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */

	  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
	  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;

	 HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfigOC, TIM_CHANNEL_1);

	 // CHANGEMENT DU RAPPORT CYCLIQUE
	 __HAL_TIM_SetCompare(&TimHandle, TIM_CHANNEL_1, PWM_VAL_MAX/2);	// 100 : moteurs au repos

	  HAL_TIM_PWM_Start(&TimHandle, TIM_CHANNEL_1);	// MOTOR RIGHT

	  // ENABLE MOTEUR (SI INVERSEUR)
	  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, 0);
}

//=================================================================
//			SET DUTY CYCLE
//=================================================================
void motorCommand_SetDuty(int duty)
{
	__HAL_TIM_SetCompare(&TimHandle, TIM_CHANNEL_1, (PWM_VAL_MAX/2)+duty);
}


Mesures

On applique motorCommand_SetDuty(150)

Signal PWM en sortie du microcontrôleur ( Broche PB4 ) :

pwm.png

La fréquence mesurée du signal est 50kHz
En effet, le timer 3 est configuré pour s’incrémenter à une fréquence de 100MHZ.
Sachant que la remise à zéro se fait à 199,
F=100e6/200=50e3

Signal PWM en sortie du Hacheur :

C’est une tension différentielle qui s’applique aux bornes du moteur :

pwm_a.svg

pwm_b.svg

pwm_c.svg


Codeur Incrémental

Timer en mode comptage

Afin d’asservir les moteurs en vitesse ou en position, il faut déjà connaître les mesures correspondantes.

Le Codeur incrémental

hedl5540.png

TPE-Codeur-incremental.jpg

codeur_inc_1.gif

codeur_inc_2.gif

Mesure le la Position

Le comptage des fronts montants des signaux A et B issus du codeur incrémental permet de connaître la position du rotor du moteur.
La précision des codeurs étant de 500 impulsions/tours, le OU EXCLUSIF entre A et B permet d’obtenir une précision de 1000 impulsions par tours.
le signal index déclenche une interruption de type EXTI permettant d’incrémenter une variable et de remettre à zéro le compteur dans le cas d’une mesure de position.

encoder_3.svg

encoder_2.svg

Mesure de la Vitesse

  • La vitesse est la dérivée de la position.
  • Numériquement cela se traduit par SPEED=(POS_act-POS_prec)/Te
  • Ce résultat est faux au moment de la remise à zéro du timer.
  • L’algorithme suivant est donc nécessaire :

SENS DIRECT

mes_vitesse_direct.svg

mes_vitesse_algo.svg

  • Speed=10000-5000=5000 –> Speed>0 –> Speed<16384 –> Speed=10000
  • Speed=25000-20000=5000 –> Speed>0 –> Speed<16384 –> Speed=10000
  • Speed=2232-30000=-27768 –> Speed<0 –> Speed<-16384 –> Speed=-27768+32768=5000 –> Speed=10000

SENS INVERSE

mes_vitesse_inverse.svg

  • Speed=5000-10000=-5000 –> Speed<0 –> Speed>-16384 –> Speed=-10000

  • Speed=20000-25000=-5000 –> Speed<0 –> Speed>-16384 –> Speed=-10000

  • Speed=30000-2232=27768 –> Speed>0 –> Speed>16384 –> Speed=27768-32768=-5000 –> Speed=-10000

  • void quadEncoder_Init(void)

  • int16_t quadEncoder_GetPos16(void)

    • Retourne une position sur 16 bits (moins d’un tour)
  • int32_t quadEncoder_GetPos32(void)

    • Retourne une position sur 32 bits ; à utiliser en cas d’asservissement de position
  • int16_t quadEncoder_GetSpeed(void)

    • Retourne l’image de la vitesse ; à appeler périodiquement
  • void quadEncoder_CallbackIndex(void)

    • Fonction de Callback appelée depuis l’interruption EXTI générée par l’index
    • mise à jour de la variable indexL

code quadEncoder

code quadEncoder
/*
 * quadEncoder.h
 *
 *  Created on: 30 juin 2016
 *      Author: kerhoas
 */

#ifndef INC_QUADENCODER_H_
#define INC_QUADENCODER_H_



#include "main.h"

void quadEncoder_Init(void);
uint16_t quadEncoder_GetPos16(void);
int32_t quadEncoder_GetPos32(void);
int16_t quadEncoder_GetSpeed(void);
void quadEncoder_CallbackIndexZ(void);
void quadEncoder_PosCalc(int*);


#endif /* INC_QUADENCODER_H_ */




/*
 * QuadEncoder.c
 */
#include "quadEncoder.h"

#define TE_ms	5	// Période d'échantillonage en ms
#define USE_QUAD_ENCODER_1250_CPR 1


#if USE_QUAD_ENCODER_1250_CPR
#define COUNT_PER_ROUND 1250
#define MAX_CNT_PER_REV (COUNT_PER_ROUND * 4 - 1)
#define MAX_COUNT (int)(((unsigned long)MAX_CNT_PER_REV*6555)/1000)
#define HALF_MAX_COUNT (MAX_COUNT>>1)
#define COEFF	6555
#endif

#if USE_QUAD_ENCODER_1000_CPR
#define COUNT_PER_ROUND 1000
#define MAX_CNT_PER_REV (COUNT_PER_ROUND * 4 - 1)
#define MAX_COUNT (int)(((unsigned long)MAX_CNT_PER_REV*8192)/1000)
#define HALF_MAX_COUNT (MAX_COUNT>>1)
#define COEFF 8192
#endif

#if USE_QUAD_ENCODER_500_CPR
#define COUNT_PER_ROUND 500
#define MAX_CNT_PER_REV (COUNT_PER_ROUND * 4 - 1)
#define MAX_COUNT (int)(((unsigned long)MAX_CNT_PER_REV*16392)/1000)
#define HALF_MAX_COUNT (MAX_COUNT>>1)
#define COEFF 16392
#endif

#if USE_QUAD_ENCODER_250_CPR
#define COUNT_PER_ROUND 250
#define MAX_CNT_PER_REV (COUNT_PER_ROUND * 4 - 1)
#define MAX_COUNT (int)(((unsigned long)MAX_CNT_PER_REV*32768)/1000)
#define HALF_MAX_COUNT (MAX_COUNT>>1)
#define COEFF 32768
#endif



TIM_HandleTypeDef    TimEncoderHandle;

static int indexZ=-1;

int first_index_reached = 0;

//================================================================
//		TIMER INIT
//================================================================

void quadEncoder_Init(void)
{
	TIM_Encoder_InitTypeDef sConfig;

	TimEncoderHandle.Instance = TIM1;
	TimEncoderHandle.Init.Prescaler = 0;
	TimEncoderHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
	TimEncoderHandle.Init.Period = COUNT_PER_ROUND*4;
	TimEncoderHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

	sConfig.EncoderMode = TIM_ENCODERMODE_TI12;
	sConfig.IC1Polarity = TIM_INPUTCHANNELPOLARITY_RISING;
	sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
	sConfig.IC1Prescaler = TIM_ICPSC_DIV4;
	sConfig.IC1Filter = 0x0F;
	sConfig.IC2Polarity = TIM_INPUTCHANNELPOLARITY_RISING;
	sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;//TIM_ICSELECTION_DIRECTTI; //TIM_TI1SELECTION_XORCOMBINATION
	sConfig.IC2Prescaler = TIM_ICPSC_DIV4;
	sConfig.IC2Filter = 0x0F;

	HAL_TIM_Encoder_Init(&TimEncoderHandle, &sConfig);
	HAL_TIM_Encoder_Start(&TimEncoderHandle,TIM_CHANNEL_1);
	HAL_TIM_Encoder_Start(&TimEncoderHandle,TIM_CHANNEL_2);
}
//================================================================
//		POSITION LEFT CALC
//================================================================
void quadEncoder_PosCalc(int* AngPos)
{
	int POSCNTcopy = 0;
	POSCNTcopy = (int)TIM1->CNT;
	AngPos[1] = AngPos[0];
	AngPos[0] = (unsigned int)(((unsigned long)POSCNTcopy * COEFF)/1000); // 0 <= POSCNT <= 4999 to 0 <= AngPos <= 32767
}
//================================================================
//		POSITION LEFT 16 BITS
//================================================================
uint16_t quadEncoder_GetPos16(void)
{
	uint16_t Pos = 0;
	Pos=TIM1->CNT;
	return Pos;
}
//================================================================
//		POSITION LEFT 32 BITS	(pos 16 bits + nombre de tours)
//================================================================
int32_t quadEncoder_GetPos32(void)
{
	int32_t  Pos = 0;
	Pos=indexZ*4*COUNT_PER_ROUND + (int32_t) quadEncoder_GetPos16();
	return Pos;
}
//================================================================
//		SPEED LEFT
//--> get pos  ; must be called every Te
//================================================================
int16_t quadEncoder_GetSpeed(void)
{
	static int AngPos[2] = {0,0};
	static int16_t Speed=0;
	int speed_rpm;


	quadEncoder_PosCalc(AngPos);
	Speed = AngPos[0] - AngPos[1];
	if (Speed >= 0)
	{
		if (Speed >= HALF_MAX_COUNT)
			{
			Speed = Speed - MAX_COUNT;
			}
	}
	else
	{
		if (Speed < -HALF_MAX_COUNT)
			{
			Speed = Speed + MAX_COUNT;
			}
	}

	//***********************************
	// CONVERT RPM
	// 1 tour = 32767
	// Nbre de Tours pendant Te: DELTA_pos/32767
	// Nbre de Tours pendant 1s (Te en ms) : (DELTA_pos/32767)*(1000/Te)
	// Nbre de Tours par minute : : (DELTA_pos/32767)*((60*1000)/Te)

	speed_rpm=(Speed*60*1000)/(32767*TE_ms);


	//************************************

	return speed_rpm;
}
//================================================================
//		MAJ indexZ Left
//================================================================
void quadEncoder_CallbackIndexZ()
{
		if (__HAL_TIM_DIRECTION_STATUS(&TimEncoderHandle)==1)
		{
			indexZ--;
		}
		else
		{
			indexZ++;
		}
		__HAL_TIM_SetCounter(&TimEncoderHandle, 0);		// RAZ Counter
		HAL_TIM_Encoder_Start(&TimEncoderHandle,TIM_CHANNEL_1);
		HAL_TIM_Encoder_Start(&TimEncoderHandle,TIM_CHANNEL_2);

		first_index_reached = 1;
}
//================================================================



Mesures

Signaux A et B du codeur incrémental :

codeur_inc.png

La période mesurée du signal est de 40us.
Il me faut donc 500*40us pour faire un tour ( 500 impulsions par tour ), soit 20ms pour un tour.
Pour connaitre la vitesse en tours par minute, je fais la règle de 3 :
N=60/0.02
N=3000 tr/min

Signaux A et index du codeur incrémental :

codeur_inc_3.png

codeur_inc_2.png