Important changes to forums

We’re making some changes to the Mbed forums. From 10th December 2019 all new discussions will take place on our new forum site. You can continue to reply to existing threads for the next two weeks. After that we will archive this forum so you can return to useful posts in the future.

Affordable and flexible platform to ease prototyping using a STM32F401RET6 microcontroller.

Nucleo F401RE and Quadrature encoders

22 Oct 2018

Hi to everyone, On F446RE Nigel's code works perfectly, and following his suggestion, I enabled both 32bit TIMs (TIM2 and TIM5), and I confirm that TIM5 doesn't work in mbed. I tried with Truestudio and TIM2 + TIM5 work. This is the code for mbed:

TIM2 activated in 446RE

#include "mbed.h"
 
// Hardware Quadrature Encoder ABZ for Nucleo F401RE
// Output on debug port to host PC @ 9600 baud
// 
// By Nigel Webb, November 2014
  
/* Connections TIM2
   PA_15 = Encoder A
   PB_9 = Encoder B
   PA_5 = Encoder Z 
*/ 
/* Connections TIM5 -NOT WORKING IN MBED
   PA_0 = Encoder A
   PA_1 = Encoder B
   PA_4 = Encoder Z 
*/
 
InterruptIn ZPulse_TIM2(PA_5) ; // Setup Interrupt for Z Pulse (TIM2)

void EncoderInitialise(void) {
    
     //TIM2
    // configure GPIO PA15 & PB9 as inputs for Encoder 
            RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;  // Enable clock for GPIOA
            GPIOA->MODER   |= GPIO_MODER_MODER15_1;    // PA15 as Alternate Function
            GPIOA->OTYPER  |= GPIO_OTYPER_OT_15;        // PA05 as Inputs
            GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR15;      // Low speed
            GPIOA->PUPDR   |= GPIO_PUPDR_PUPDR15_1  ;// Pull Down
            GPIOA->AFR[0]  |= 0x00000000 ;        // AF01 for PA15
            GPIOA->AFR[1]  |= 0x10000000 ;        // AF01 for PA15

            RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;        // Enable clock for GPIOB
            GPIOB->MODER    |= GPIO_MODER_MODER9_1 ; //PB09  as Alternate Function   /*!< GPIO port mode register,
            GPIOB->OTYPER   |= GPIO_OTYPER_OT_9 ;    // PB09 as Inputs               /*!< GPIO port output type register,
            GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9;// Low speed                        /*!< GPIO port output speed register
            GPIOB->PUPDR   |= GPIO_PUPDR_PUPDR9_1  ;// Pull Down
            GPIOB->AFR[0]  |= 0x00000000 ;        // AF01 for PB9
            GPIOB->AFR[1]  |= 0x00000010;         // AF01 for PB9
                                                    //                                  /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
   
    // configure TIM2 as Encoder input
    RCC->APB1ENR |= 0x00000001;  // Enable clock for TIM2
 
    TIM2->CR1   = 0x0001;     // CEN(Counter ENable)='1'     < TIM control register 1
    TIM2->SMCR  = 0x0003;     // SMS='011' (Encoder mode 3)  < TIM slave mode control register
    TIM2->CCMR1 = 0xF1F1;     // CC1S='01' CC2S='01'         < TIM capture/compare mode register 1
    TIM2->CCMR2 = 0x0000;     //                             < TIM capture/compare mode register 2
    TIM2->CCER  = 0x0011;     // CC1P CC2P                   < TIM capture/compare enable register
    TIM2->PSC   = 0x0000;     // Prescaler = (0+1)           < TIM prescaler
    TIM2->ARR   = 0xffffffff; // reload at 0xfffffff         < TIM auto-reload register
  
    TIM2->CNT = 0x0000;  //reset the counter before we use it  
   
}
 
// Z Pulse routine
void ZeroEncoderCount() {
    TIM2->CNT=0 ; //reset count to zero
}
 
int main() {
    EncoderInitialise() ;
    
   
    ZPulse_TIM2.rise(&ZeroEncoderCount) ; //Setup Interrupt for rising edge of Z pulse TIM2
    ZPulse_TIM2.mode(PullDown) ; // Set input as pull down
     
    unsigned int EncoderPositionTIM2 ;

    while (true) {
        // Print Encoder Quadrature count to debug port every 0.5 seconds
        EncoderPositionTIM2 = TIM2->CNT ; // Get current position from Encoder
                        
                printf("Encoder Position TIM2 %i\r\n  ", EncoderPositionTIM2);
                     
        
        wait(0.5);
    }
       
}

This is code in TrueStudio for both TIM2 and TIM5, if someone could be interested in it:

Generic C code for both TIM2 and TIM5 with F446RE

/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  ** This notice applies to any and all portions of this file
  * that are not between comment pairs USER CODE BEGIN and
  * USER CODE END. Other portions of this file, whether 
  * inserted by the user or by software development tools
  * are owned by their respective copyright owners.
  *
  * COPYRIGHT(c) 2018 STMicroelectronics
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f4xx_hal.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim5;

UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
#define ZPulse_Pin GPIO_PIN_4
#define ZPulse_GPIO_Port GPIOA
#define ZPulse_EXTI_IRQn EXTI4_IRQn
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM5_Init(void);
static void MX_TIM2_Init(void);

/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/

/* USER CODE END PFP */

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  *
  * @retval None
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	  uint32_t PositionTIM2;
	  uint32_t PositionTIM5;

  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  MX_TIM5_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
  TIM2->CNT=0;
  TIM5->CNT=0;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  PositionTIM2 =  TIM2->CNT ; // Get current position from Encoder
	  PositionTIM5 =  TIM5->CNT ; // Get current position from Encoder


	  	 uint8_t bufferTIM2[20];
	  	 uint8_t bufferTIM5[20];
	  	 sprintf(bufferTIM2,"TIM2: %i   ", PositionTIM2);
	  	 HAL_UART_Transmit(&huart2,(uint8_t *)bufferTIM2,sizeof(bufferTIM2),0xffff);
	  	 sprintf(bufferTIM5,"TIM5: %i\r\n", PositionTIM5);
	  	 HAL_UART_Transmit(&huart2,(uint8_t *)bufferTIM5,sizeof(bufferTIM5),0xffff);
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{

  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;

    /**Configure the main internal regulator output voltage 
    */
  __HAL_RCC_PWR_CLK_ENABLE();

  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = 16;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 16;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure the Systick interrupt time 
    */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

    /**Configure the Systick 
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

/* TIM2 init function */
static void MX_TIM2_Init(void)
{
  //RCC->APB1ENR |=RCC_APB1ENR_TIM2EN;
  RCC->APB1ENR|= 0x00000001;
  htim2.Instance = TIM2;
  htim2.Instance->CR1=0x0001;
  htim2.Instance->SMCR=0x0003;
  htim2.Instance->CCMR1=0xF1F1;
  htim2.Instance->CCMR2=0x0000;
  htim2.Instance->CCER  = TIM_CCER_CC1E | TIM_CCER_CC2E;
  htim2.Instance->PSC=0;
  htim2.Instance->ARR=0xffffffff;
  htim2.Instance->CNT=0x0000;
}

/* TIM5 init function */
static void MX_TIM5_Init(void)
{
  RCC->APB1ENR|= 0x00000008;
  htim5.Instance = TIM5;
  htim5.Init.Prescaler = 0;
  htim5.Instance->CR1=0x0001;
  htim5.Instance->SMCR=0x0003;
  htim5.Instance->CCMR1=0xF1F1;
  htim5.Instance->CCMR2=0x0000;
  htim5.Instance->CCER=0x0011;
  htim5.Instance->PSC=0;
  htim5.Instance->ARR=0xffffffff;
  htim5.Instance->CNT=0x0000;
}

/* USART2 init function */
static void MX_USART2_UART_Init(void)
{

  huart2.Instance = USART2;
  huart2.Init.BaudRate = 9600;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

/** Configure pins as 
        * Analog 
        * Input 
        * Output
        * EVENT_OUT
        * EXTI
*/
static void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct;

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  //  TIM2 (32bit) works on PA15 (CH1) and PB9 (CH2) pins


     	  	RCC->AHB1ENR |=	RCC_AHB1ENR_GPIOAEN;  // Enable clock for GPIOA
     	  	GPIOA->MODER   |= GPIO_MODER_MODER15_1;    // PA15 as Alternate Function
  		    GPIOA->OTYPER  |= GPIO_OTYPER_OT_15;		// PA05 as Inputs
   		    GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR15;      // Low speed
   		    GPIOA->PUPDR   |= GPIO_PUPDR_PUPDR15_1  ;// Pull Down
   		    GPIOA->AFR[0]  |= 0x00000000 ;		  // AF01 for PA15
   		   // GPIOA->AFR[1]  |= GPIO_AF1_TIM2;	  /// AF01 for PA15, NOT WORKING
   		    GPIOA->AFR[1]  |= 0x10000000 ;        // AF01 for PA15

   		    RCC->AHB1ENR |=	RCC_AHB1ENR_GPIOBEN;		// Enable clock for GPIOB
   		    GPIOB->MODER	|= GPIO_MODER_MODER9_1 ; //PB09  as Alternate Function   /*!< GPIO port mode register,
   		    GPIOB->OTYPER	|= GPIO_OTYPER_OT_9 ;    // PB09 as Inputs               /*!< GPIO port output type register,
   		    GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9;// Low speed                        /*!< GPIO port output speed register
   		    GPIOB->PUPDR   |= GPIO_PUPDR_PUPDR9_1  ;// Pull Down
   		    GPIOB->AFR[0]  |= 0x00000000 ;		  // AF01 for PB9
  			//GPIOB->AFR[1]  |= GPIO_AF1_TIM2 ;   // AF01 for PB9, NOT WORKING
  			GPIOB->AFR[1]  |= 0x00000010;		  // AF01 for PB9

  			// TIM5 works on PA0 (CH1) and PA1 (CH2) pins

   			RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //Enable clock for GPIOA
//   		RCC->AHB1ENR |= 0x00000001;  // Enable clock for GPIOA
   		    GPIOA->MODER   |= GPIO_MODER_MODER0_1 | GPIO_MODER_MODER1_1 ;           //PA0 & PA1 as Alternate Function
   		    GPIOA->OTYPER  |= GPIO_OTYPER_OT_0 | GPIO_OTYPER_OT_1 ;                 //PA0 & PA1 as Inputs
   		    GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR0 | GPIO_OSPEEDER_OSPEEDR1 ;     // Low speed
   		    GPIOA->PUPDR   |= GPIO_PUPDR_PUPDR0_1 | GPIO_PUPDR_PUPDR1_1 ;           // Pull Down
   		    //GPIOA->AFR[0]  |= GPIO_AF2_TIM5; // AF01 for PA0 & PA1, NOT WORKING
   		    GPIOA->AFR[0]  |= 0x00000022 ;//  AF02 for PA0 & PA1
   		    GPIOA->AFR[1]  |= 0x00000000 ;//  AF02 for PA0 & PA1

  /*Configure GPIO pins : ZPulse_Pin (PA04) ZPulseTIM2_Pin (PA05)*/
  GPIO_InitStruct.Pin = ZPulse_Pin|ZPulseTIM2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
 // HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  HAL_GPIO_Init(ZPulse_GPIO_Port, &GPIO_InitStruct);


  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI4_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI4_IRQn);

  HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);


}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @param  file: The file name as string.
  * @param  line: The line in file as a number.
  * @retval None
  */
void _Error_Handler(char *file, int line)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  while(1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{ 
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

I have only a little trouble, after some cycles TIM2 goes to -1 value (even if no hardware is connected), I supposed it should be some noise on the signal, but I don't know the effective reason (in fact when I plug the encoder on TIM2, TIM5 remain 0 ...).

You need to log in to post a reply