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 ...).