Autres Realisation d'un system de filetage par µC

Guy69
Compagnon
3 Mars 2009
1 121
FR-71170
bonjour
Qu'est-ce-que va passer quand le codeur s'arrete subitement? On n'aura plus de updates, donc notre PaP va continuer a avancer a sa vitesse actuel, broche a l'arret, jusqu'au butee, ruinant notre piece.
pourquoi? le Pap n'avance que quand on lui dit, et si tu ne le fais avancer QUE lorsqu'il y a changement d'état sur le codeur, il ne va pas avancer tout seul. Y a un truc que je pige pas là.
A+
Guy
 
simon74
Compagnon
3 Mai 2016
958
Savoie
C'est car, comme pour le codeur, y a plusiers facons de faire avec les PaP, entre "faire le tout manuellement" et "laisse faire (d'un facon ou un autre) un timer". Moi, je propose de faire un maximum par timer, en dehors de processeur, et ca va dire que, encore comme avec le codeur, on ne va pas gerer chaque impulsion manuellement.
 
simon74
Compagnon
3 Mai 2016
958
Savoie
Pour coder tout ca, on vas en avoir besoin d'un peu plus (ou meme moins) que le HAL nous donne, il va falloir plonger un peu dans les fichiers stm32f1xx_ll_* au lieu de stm32f1xx_hal_*. Les driveurs "low level", quoi.

Avant d'y plonger la tete en avant, par contre, on verra pour la fonctionnalité de base - un timer pour lire le codeur, et qui nous envoi le positionnement de ce codeur, sans qu'on as besoin d'agir. On regardant dans stm32f1xx_hal_tim.h, on trouve les fonctions necessaires:
C:
/* Timer Encoder functions *****************************************************/
HAL_StatusTypeDef HAL_TIM_Encoder_Init(TIM_HandleTypeDef *htim,  TIM_Encoder_InitTypeDef* sConfig);
HAL_StatusTypeDef HAL_TIM_Encoder_DeInit(TIM_HandleTypeDef *htim);
void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef *htim);
void HAL_TIM_Encoder_MspDeInit(TIM_HandleTypeDef *htim); 
/* Blocking mode: Polling */
HAL_StatusTypeDef HAL_TIM_Encoder_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_StatusTypeDef HAL_TIM_Encoder_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
/* Non-Blocking mode: Interrupt */
HAL_StatusTypeDef HAL_TIM_Encoder_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_StatusTypeDef HAL_TIM_Encoder_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
/* Non-Blocking mode: DMA */
HAL_StatusTypeDef HAL_TIM_Encoder_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData1, uint32_t *pData2, uint16_t Length);
HAL_StatusTypeDef HAL_TIM_Encoder_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);
Au lieu d'utiliser HAL_TIM_Base_Init(), on va utiliser HAL_TIM_Encoder_Init(), en passant l'addresse d'un structure de TIM_Encoder_InitTypeDef, et ca nous fera le necessaire. Par exemple, pour notre codeur de 1000 pas / tour, on pourrait avoir ca:
C:
TIM_HandleTypeDef tim2;
TIM_EncoderInitTypeDef encoder_config;

tim2.Instance =               TIM2;
tim2.Init.Prescaler =         0;
tim2.Init.CounterMode =       TIM_COUNTERMODE_UP;
tim2.Init.Period =            999;
tim2.Init.ClockDivision =     TIM_CLOCKDIVISION_DIV1;
tim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

encoder_config.EncoderMode =  TIM_ENCODERMODE_TI12;
encoder_config.IC1Polarity =  TIM_ICPOLARITY_RISING;
encoder_config.IC1Selection = TIM_ICSELECTION_DIRECTTI;
encoder_config.IC1Prescaler = TIM_ICPSC_DIV1;
encoder_config.IC1Filter =    0x2;
encoder_config.IC2Polarity =  TIM_ICPOLARITY_RISING;
encoder_config.IC2Selection = TIM_ICSELECTION_DIRECTTI;
encoder_config.IC2Prescaler = TIM_ICPSC_DIV1;
encoder_config.IC2Filter =    0x2;

if ((HAL_TIM_Encoder_init(&tim2, &encoder_config) != HAL_OK)) {
  _Error_Handler(__FILE__, __LINE__);
}
A noter que IC1Filter et IC2Filter nous permets de faire du "debouncing" des signaux, c'est a dire qu'il va falloir 4 samples du meme niveau pour que ca soit pris en compte.

Il faut qu'on connect les sorties du codeur sur lee entrees du timer, bien sur. Par default, TIM2 Canal 1 et 2 sont sur PA0 et PA1.
Code:
GPIO_InitTypeDef gpio;
gpio.Pin = GPIO_PIN_0 | GPIO_PIN_1;
gpio.Mode = GPIO_MODE_INPUT;
gpio.Pull = GPIO_PULLUP;
gpio.Speed = GPIO_SPEED_FREQ_MEDIUM;

HAL_GPIO_INIT(GPIOA, &gpio);
Puis, au lieu de HAL_TIM_Base_Start_xx(), on utilisera HAL_TIM_Encoder_Start_xx(). Pour le mode "polling" ou "interrupt", un peu de lecture du RM0008 et le code du HAL, on s'on sortira tres bien.

Le mode DMA est un peu plus complex, car il faut aussi initialiser l'accessoire DMA, qui permets le timer de faire un transfert direct vers un endroit en memoire. Sur les stm32f1, y a 2 controleurs DMA avec 12 canaux total de DMA. Si on est sur TIM2, et on veut faire un DMA sur chaque update (TIM2_UP), il nous faut DMA1, canal 2 (voir RM0008, 13.3.7, table 78).

Code:
DMA_Channel_TypeDef channel = DMA1_Channel2;
DMA_HandleTypeDef dma;

dma.Instance = &channel;
dma.Init.Direction = DMA_PERIPH_TO_MEMORY;
dma.Init.PeriphInc = DMA_PINC_DISABLE;
dma.Init.MemInc = DMA_MINC_DISABLE;
dma.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
dma.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
dma.Init.Mode = DMA_NORMAL;
dma.Init.Priority = DMA_PRIORITY_HIGH;

HAL_DMA_Init(&dma);

// Ici, on lie le update du timer avec le canal DMA
__HAL_LINKDMA(&tim2, hdma[TIM_DMA_ID_UPDATE], &dma);
Par contre, demarrer le timer est plus delicat. HAL_TIM_Encoder_Start_DMA() n'est pas si bon que ca, car il transfert par DMA a partir du registre (ou registres) CCR1 / CCR2, sur les changements CCR, et ne transfert pas le direction (bit 4 en registre CR1). Nous, on veut transferer CR1 et CNT sur update; on as besoin de creer notre propre fonction de demarrage. Bof, mais en gros c'est du copier-coller su code de HAL.
C:
HAL_StatusTypeDef Simon_TIM_Encoder_Start_DMA(TIM_HandleTypeDef *htim, uint32_t *pData)
{
  /* Check the parameters */
  assert_param(IS_TIM_DMA_CC_INSTANCE(htim->Instance));

  if((htim->State == HAL_TIM_STATE_BUSY))
  {
     return HAL_BUSY;
  }
  else if((htim->State == HAL_TIM_STATE_READY))
  {
    if((pData == 0U))
    {
      return HAL_ERROR;
    }
    else
    {
      htim->State = HAL_TIM_STATE_BUSY;
    }
  }

      htim->hdma[TIM_DMA_ID_UPDATE]->XferCpltCallback = TIM_DMACaptureCplt;
      htim->hdma[TIM_DMA_ID_UPDATE]->XferErrorCallback = TIM_DMAError ;
      HAL_DMA_Start_IT(htim->hdma[TIM_DMA_ID_UPDATE], (uint32_t)&htim->Instance->CR1, (uint32_t )pData, 0x28);
      __HAL_TIM_ENABLE_DMA(htim, TIM_DMA_UPDATE);

     TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
     TIM_CCxChannelCmd(htim->Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);

      /* Enable the Peripheral */
      __HAL_TIM_ENABLE(htim);
    return HAL_OK;
}
et similaire pour l'arret. Puis on defini un bloc de memoire de 0x28 bytes pour recevoir ces donnees (n'oublient pas les declarations volatile), et on appel notre fonction de demarrage.
 
simon74
Compagnon
3 Mai 2016
958
Savoie
Bon, ca a pris du temps de remettre en route un environnement C/C++ pour ARM qui peut faire avec ce qui genere STM32CubeMX. J'ai maintenant 2 installations de llvm/clang (qui marche tres bien, mais pas avec CubeMX); finalement j'ai cedé et installé un toolchain arm-none-eabi. Enfin bref, j'ai un projet qui compile maintenant, mais j'ai du me tromper sur le clock, c'est enormement lent.

[edit]
Probleme entre clavier et chaise. J'ai oublié de preciser un source pour le HSE.

Donc, le code sera ici : https://github.com/tufty/stm32-noodling

Je m'excuse, mais les commentaires et messages de commit sera en Anglais, car - ummm - c'est difficile de faire les accents avec emacs. Oui, c'est ca.

Et puis, bien sur, j'avait du code qui marchait parfaitement deja hier matin, mais j'ai fait un connerie avec STM32CubeMX et il m'a tout zappé, y compris le repo git locale. Et non, je n'avait pas mon disque de sauvegarde branché.

If it's not backed up, it's not important.
 
Dernière édition:
La dernière réponse à ce sujet date de plus de 6 mois

Dernières discussions

Haut