Skip to main content
Visitor II
July 1, 2021
Solved

STM32F072xB GPIO issue | USB pins do not work as GPIO

  • July 1, 2021
  • 9 replies
  • 4323 views

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

    This topic has been closed for replies.
    Best answer by waclawek.jan

    > 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

    9 replies

    Super User
    July 1, 2021

    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.

    JCame.1Author
    Visitor II
    July 1, 2021

    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.

    JCame.1Author
    Visitor II
    July 1, 2021

    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.

    Super User
    July 1, 2021

    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.

    Super User
    July 1, 2021

    > 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

    JCame.1Author
    Visitor II
    July 1, 2021

    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.

    Super User
    July 1, 2021

    > 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

    JCame.1Author
    Visitor II
    July 2, 2021

    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

    Super User
    July 2, 2021

    > 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?

    JCame.1Author
    Visitor II
    July 2, 2021

    Hi TDK,

    > 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?

    That would be my assumption as well. It almost seems as though the pin on the board isn't physically connected to the chip. I have a lead connecting PA11 to GND on the discovery board as shown in the photo.0693W00000BcgpBQAR.jpg

    Super User
    July 2, 2021

    > It almost seems as though the pin on the board isn't physically connected to the chip.

    By default, it isn't.

    0693W00000BcgsFQAR.pngJW

    JCame.1Author
    Visitor II
    July 2, 2021

    Ah that would explain a lot. Thanks a lot for your help, I should've looked at that document a lot earlier. I'll join that bridge and report back whether it springs back to life (which I am very certain it will).

    Super User
    July 2, 2021

    > STM32F0-Discovery board

    I checked the schematic beforehand and it did not show these solder bridges.

    Turns out the issue was that I was looking at STM32F0-DISCOVERY board (per the OP), whereas the OP actually had the 32F072BDISCOVERY board, despite it saying "stm32f0-discovery" on the board itself. The OP also says STM32F072xB so can't really fault.

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

    vs

    https://www.st.com/en/evaluation-tools/32f072bdiscovery.html

    Super User
    July 2, 2021

    You can because I've already asked :)

    JW

    JCame.1Author
    Visitor II
    July 5, 2021

    I have joined those bridges and now have the expected behaviour, thank you both for your help it is greatly appreciated.

    Apologies for any unnecessary confusion I caused, I was not aware there were 2 evaluation boards (with a similar name) and somehow didn't notice that even with the page JW linked - that'll teach me to take a bit more time reading next time.

    James