cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F072xB GPIO issue | USB pins do not work as GPIO

JCame.1
Associate III

Hi all,

First some context:

I am attempting to use the USB designated pins (PA11 and PA12) as generic GPIO inputs at start-up as a way of determining if a host is connected to the device and make a decision whether to run the main code or not - I will have external pull-up resistors on the DP and DM lines to aid this. Whilst waiting for the board to be made I wanted to make sure I could read the pin levels at start-up - for this I used the STM32F0-Discovery board.

This is where I encountered my issue. I initially tried setting pins PA11 and PA12 as inputs with a pull-up resistor, I would then use a 'flying wire' to alternate the pin voltage between 0V and 5V. I then set up the code such that the LEDs would change state depending on the input pins. The code can be seen here:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
 
/* Includes ------------------------------------------------------------------*/
#include "main.h"
 
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
 
/* USER CODE END Includes */
 
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
 
/* USER CODE END PTD */
 
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
 
/* USER CODE END PD */
 
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
 
/* USER CODE END PM */
 
/* Private variables ---------------------------------------------------------*/
 
/* USER CODE BEGIN PV */
 
/* USER CODE END PV */
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
/* USER CODE BEGIN PFP */
 
/* USER CODE END PFP */
 
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
 
/* USER CODE END 0 */
 
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
 
  /* 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();
  /* USER CODE BEGIN 2 */
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
      HAL_GPIO_WritePin(LED_USB_GPIO_Port, LED_USB_Pin, HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11));
      HAL_GPIO_WritePin(LED_AXIOM_GPIO_Port, LED_AXIOM_Pin, HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_12));
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
 
/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6;
  RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  {
    Error_Handler();
  }
}
 
/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
 
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
 
  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, LED_USB_Pin|LED_AXIOM_Pin, GPIO_PIN_RESET);
 
  /*Configure GPIO pins : LED_USB_Pin LED_AXIOM_Pin */
  GPIO_InitStruct.Pin = LED_USB_Pin|LED_AXIOM_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
 
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11|GPIO_PIN_12, GPIO_PIN_SET);
 
  /*Configure GPIO pin : PA11 */
  GPIO_InitStruct.Pin = GPIO_PIN_11;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
  /*Configure GPIO pin : PA12 */
  GPIO_InitStruct.Pin = GPIO_PIN_12;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
}
 
/* USER CODE BEGIN 4 */
 
/* USER CODE END 4 */
 
/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
 
  /* 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(char *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****/

No matter what voltage/logic level I applied to the pins the LEDs did not change state.

I then set PA11 and PA12 as output push-pulls, made them write high, and used a logic analyser to scope the pins. With this set-up the pins should have been outputting a logic 1, however they remained at 0!

In short, I do not seem to be able to use these pins as regular GPIOs. I have tried looking through the documentation for any reference to this behaviour but came up with nothing. I know it is not how I am setting the pins up as applying the same process/config to PA9 and PA10 yields the expected results.

Any ideas?

James

1 ACCEPTED SOLUTION

Accepted Solutions

> for this I used the STM32F0-Discovery board.

https://www.st.com/en/evaluation-tools/stm32f0discovery.html ?

Cube is good only for the "typical" applications you can click in CubeMX. Outside of those, it just gets into way.

USB_DP and USB_DM are special functions outside of the GPIO AF matrix. The GPIO is overriden by the USB module, by clearing USB_CNTR.PDWN, so you have to set that bit to enable the "normal" function of the two pins.

Also,

> I am attempting to use the USB designated pins (PA11 and PA12) as generic GPIO inputs at start-up as a way of determining if a host is connected to the device

I don't think it's a good idea, as such method does not have any support in the standard. Use VBUS instead.

JW

View solution in original post

15 REPLIES 15
TDK
Guru

Your code initializes all 4 pins as output, including PA11 and PA12, so it makes sense that two of them aren't changing value.

  /*Configure GPIO pin : PA11 */
  GPIO_InitStruct.Pin = GPIO_PIN_11;
//  GPIO_InitStruct.Pin = GPIO_PIN_10;
//  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
  /*Configure GPIO pin : PA12 */
  GPIO_InitStruct.Pin = GPIO_PIN_12;
//  GPIO_InitStruct.Pin = GPIO_PIN_9;
//  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 

There should be nothing preventing you from using PA11/PA12 as GPIO.

If you feel a post has answered your question, please click "Accept as Solution".
JCame.1
Associate III

Apologies TDK - I had left in some of my test code where I set the pins as outputs. I have changed them back to inputs in the above code to more accurately match my initial test conditions.

JCame.1
Associate III

Apologies TDK - I had left in some of my test code where I set the pins as outputs. I have changed them back to inputs in the above code to more accurately match my initial test conditions.

This is what is confusing me the most - I can't think of any logical reason why the pins aren't being driven correctly, even when set as outputs.

EDIT: setting up pins PA11/PA12 as inputs with pull ups should produce a logic high (when looking on my logic analyser) however both are seen low. Repeating the same config for PA9/PA10 results in reading a logic high as expected.

TDK
Guru

Do you have a USB cable plugged in to those pins?

Set them as output and verify that ODR gets set correctly on both set and reset and that IDR matches it.

If you feel a post has answered your question, please click "Accept as Solution".

> for this I used the STM32F0-Discovery board.

https://www.st.com/en/evaluation-tools/stm32f0discovery.html ?

Cube is good only for the "typical" applications you can click in CubeMX. Outside of those, it just gets into way.

USB_DP and USB_DM are special functions outside of the GPIO AF matrix. The GPIO is overriden by the USB module, by clearing USB_CNTR.PDWN, so you have to set that bit to enable the "normal" function of the two pins.

Also,

> I am attempting to use the USB designated pins (PA11 and PA12) as generic GPIO inputs at start-up as a way of determining if a host is connected to the device

I don't think it's a good idea, as such method does not have any support in the standard. Use VBUS instead.

JW

Hi JW,

I knew I was missing something like that! Is there any specific sequence of events that needs to be followed, or is it a matter of just setting USB_CNTR.PDWN?

> I don't think it's a good idea, as such method does not have any support in the standard. Use VBUS instead.

Unfortunately I cannot use VBUS to detect the host. The micro (which in essence allows a USB host to control our main chip over SPI via the STM32) is being used on a board where a host may connect directly to the main chip over I2C meaning the STM32 USB chip should not interfere. This means that the chip will be powered even with no host present and a requirement from the design team was no pin headers.

> is it a matter of just setting USB_CNTR.PDWN?

I'm not going to test but IMO yes.

> This means that the chip will be powered even with no host present and a requirement from the design team was no pin headers.

I don't understand the remark about pin headers. You don't need to power the STM32 from VBUS, simply use it as a logic signal (after conditioning it, maybe a resistor divider would do).

JW

Hi JW,

I implemented the following code:

int main(void)
{
    //gpio clocks
    RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
    RCC->AHBENR |= RCC_AHBENR_GPIOCEN;
    //usb peripheral clock
    RCC->APB1ENR |= RCC_APB1ENR_USBEN;
 
    //PA11 and PA12
    GPIOA->MODER &= ~(GPIO_MODER_MODER11_Msk | GPIO_MODER_MODER12_Msk);
    GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR11_Msk | GPIO_PUPDR_PUPDR12_Msk);
    GPIOA->PUPDR |= (GPIO_PUPDR_PUPDR11_0 | GPIO_PUPDR_PUPDR12_0);
 
    //PC7 and PC8
    GPIOC->MODER &= ~(GPIO_MODER_MODER7_Msk | GPIO_MODER_MODER8_Msk);
    GPIOC->MODER |= (GPIO_MODER_MODER7_0 | GPIO_MODER_MODER8_0);
    GPIOC->OTYPER &= ~(GPIO_OTYPER_OT_7 | GPIO_OTYPER_OT_8);
    GPIOC->ODR |= GPIO_ODR_7 | GPIO_ODR_8;
 
    // turn off USB?
    USB->CNTR |= USB_CNTR_PDWN;
 
  /* Infinite loop */
  while (1)
  {
      if((GPIOA->IDR & GPIO_IDR_11) == GPIO_IDR_11)
      {
          GPIOC->ODR |= GPIO_ODR_7;
      }
      else
      {
          GPIOC->ODR &= ~GPIO_ODR_7;
      }
 
      if((GPIOA->IDR & GPIO_IDR_12) == GPIO_IDR_12)
      {
          GPIOC->ODR |= GPIO_ODR_8;
      }
      else
      {
          GPIOC->ODR &= ~GPIO_ODR_8;
      }
  }
}

However I am still not able to correctly read pins PA11 and PA12. The IDR register seems to only change depending on whether I have a pull-up or pull-down resistor enabled - e.g. pins set up with pull-ups enabled, fly a wire from GND to PA11, pause program, IDR register for GPIOA reports it as a logic high (1). I have stripped the test program to not use any HAL libraries to try to eliminate that as a factor. Is there anything obvious that I am missing?

James

TDK
Guru

> The IDR register seems to only change depending on whether I have a pull-up or pull-down resistor enabled

IDR following the pull resistor but not your input would suggest you're not applying the input to the pin. Where exactly are you applying the flying pin?

If you feel a post has answered your question, please click "Accept as Solution".