cancel
Showing results for 
Search instead for 
Did you mean: 

STM32CUBEU5: USB conflicts with user ADC1 use

tjaekel
Senior III

When I use STM32Uxx boards with USB device and STM32CubeU5 drivers/libraries - I cannot make use of ADC1.
I want to use ADC1 also (not just USB), to measure VCORE and Temperature.

It is a SW issue (and it can be fixed), not a HW issue.

Details:

I use STM32U5xx (e.g. NUCLEO-U5A5ZJ-Q, with user USB-C as USB VCP UART. The related demo for it is in STM32CubeU5 USBX/Ux_Device_CDC_ACM (starting from it for my project).

It is a USB device VCP UART demo.

The ADC1 is used as USB VSENSE!

So, even you configure ADC1 for your own purposes, e.g. you provide your own MX_ADC1_Init() and void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc) - it is not really acting: your config is overwritten by the USB driver:

See in file ".../Drivers/BSP/STM32U5xx_Nucleo/stem32u5xx_nucleo_usbpd_pwr.c" that the same ADC1 is initialized again. It overwrites your settings for ADC1. And the measurement done for USB VSENSE seems to change your ADC1 config (e.g. back to one channel only).

In function "int32_t BSP_USBPD_PWR_VBUSInit(uint32_t PortNum)" the ADC is initialized again.

And when the USB driver is activated, e.g. you plug in the USB (anytime later), the function int32_t BSP_USBPD_PWR_VBUSGetVoltage(uint32_t PortNum, uint32_t *pVoltage) is called (to measure the USB VBUS voltage). This might be not aligned with your ADC1 use.
My ADC1 stuff worked fine until I have plugged on USB (USB modifies ADC1).

Solution:

  • modify the USB drivers so that their functions are aligned with your purpose, e.g. for me also to measure VBAT, VCORE and esp. Temperature sensor (not just external pin PC2 with a voltage divider for VBUS)
  • or disable all USB related ADC1 stuff - fake the result of function int32_t BSP_USBPD_PWR_VBUSGetVoltage(uint32_t PortNum, uint32_t *pVoltage) without to touch the ADC1 (return the right fix value from this function, w/o to measure really, so that driver is happy)

My project, which uses USB and ADC1 to measure the VREF, VBAT and Temperature (USB not using anymore ADC1 at all) is here:

GitHub - tjaekel/STM32U5A5-VCP_UART: VCP UART on U5A5 NUCLEO and my board

You can find a better solution for a shared use of ADC1 (for USB and for your code), potentially a need to modify the USB driver a bit (at least to be aware what the USB driver will do on ADC1).

 

1 ACCEPTED SOLUTION

Accepted Solutions

Hi F.Belaid,

you are right: the "issue" is in the BSP. It is not a big deal, just to make users aware of: "using ADC1 in combination with USB - needs a modification of BSP files".

It can be fixed (in BSP files). The current BSP files do not support really the use of ADC1  - in particular not in combination with USB.

What happens... (details):

  1. User can have its own static MX_ADC1_Init(), e.g. generated in main.c
  2. There is also a (global) hadc1 handler.
  3. And in stm32u5xx_hal_msp.c we have the HAL_ADC_MspInit()
  4. But this code is never effective: it will be "overridden" by the USB, e.g. code in stm32u5xx_nucleo_usbpd_pwr.c

The BSP drivers, e.g. file Drivers/BSP/STM32U5xx_Nucleo/stm32u5xx_nucleo_usbpd_pwr.c - they provide again a

    static MX_ADC1_Init()

as well as a static ADC_HandleTypedef hadc1.

OK, the USB based MX_ADC1_Init() uses at the end the HAL_ADC_MspInit() (provided via the stm32u5xx_hal_msp.c file, the same us user ADC config would use). But due to fact that MX_ADC1_Init() is static in both files (and hadc1 is a static on each file) - the "USB wins": every time you plug-in USB - it will reconfigure the ADC1 again (and overrides my user ADC1 config).

At the end: ADC1 is a different config, using a different hadc1 and all the user code for ADC1 is "ineffective".

So, there is a need to modify the BSP files - when users want to use ADC1  - and USB, e.g. as in my case to measure Vref, VBAT or TEMPerature sensor parallel with USB VBUS monitoring.

In Drivers/BSP/STM32U5xx_Nucleo/stm32u5xx_nucleo_usbpd_pwr.c is a function call as:

     BSP_USB_PWR_VbusGetVoltage()

It starts the conversion and expects just one single value measured: the PC2 (IN2) pin with the voltage divider, for monitoring the external VBUS voltage.

And the function BSP_USBPD_PWR_VBUSInit() starts also the ADC conversion.

But all this code assumes there is just one channel (PC2) - just one RANK_1 ! Even the user configures ADC1 with several RANKs - it would be "lost".

Solution

It would be nice - "cool" - if you (STM) can provide a BSP which has already four RANKs: three for VREF, VBAT, TEMPsensor and the additional one for PC2 (as IN2) for VBUS measurement.

And modify BSP_USBPD_PWR_VBusGetVoltage() accordingly, in a way, that USB gets the voltage for PC2 (on the right RANK, e.g. RANK_4).
User can now also get the VBAT and esp. the TEMPsensor without to modify the BSP (just add BSP functions to Get Temperature: ADC1 is anyway already used for PC2 VBUS: why not extending BSP to get also the Temperature?)

Remark

In general, you (as STM company) follow properly the approach to have a BSP (to handle the specifics of a board).

But I saw in STM32U575-EV project (for USB_ECM) that you "violate" a bit this approach:

in file Application/User/NetXDuo/App/app_netxduo.c

where you want to toggle an LED, requested via the HTTP web page - it is very hard-coded: it uses the PH7 pin - directly, which is only available on the STM32U575-EV board. So, it fails when people porting this project, e.g. to a NUCLEO board: the LED is "so hard-coded" and does not follow, neither using a BSP function (for toggling LED from a Middleware code via BSP, instead of "hard-coded" pin usage).

Here, how I had to fix as (porting the USB_ECM project to a NUCLEO board):

/* Check if requested data equal LED_ON */

if (strncmp((char const *)request_data, LED_ON, sizeof(LED_ON)) == 0)

{

#ifdef NUCLEO_BOARD

HAL_GPIO_WritePin(LED_BLUE_GPIO_Port, LED_GREEN_Pin, GPIO_PIN_SET);

#else

HAL_GPIO_WritePin(GPIOH, GPIO_PIN_7, GPIO_PIN_SET); //where bad approach!

#endif

}

 

View solution in original post

2 REPLIES 2
FBL
ST Employee

Hello @tjaekel 

Do you have issues with example with the configuration provided? I'm afraid to tell you that BSP drivers are meant to explore features not covering all uses cases for each purpose. So, maybe if you would like to use ADC1, in this context you may need to customize the BSP driver for your own purpose.  However, if facing issues with HAL/LL drivers, we need to track it down. Maybe this would be an enhancement for the BSP driver. I will share it with the team.

Thank you for your understanding

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Hi F.Belaid,

you are right: the "issue" is in the BSP. It is not a big deal, just to make users aware of: "using ADC1 in combination with USB - needs a modification of BSP files".

It can be fixed (in BSP files). The current BSP files do not support really the use of ADC1  - in particular not in combination with USB.

What happens... (details):

  1. User can have its own static MX_ADC1_Init(), e.g. generated in main.c
  2. There is also a (global) hadc1 handler.
  3. And in stm32u5xx_hal_msp.c we have the HAL_ADC_MspInit()
  4. But this code is never effective: it will be "overridden" by the USB, e.g. code in stm32u5xx_nucleo_usbpd_pwr.c

The BSP drivers, e.g. file Drivers/BSP/STM32U5xx_Nucleo/stm32u5xx_nucleo_usbpd_pwr.c - they provide again a

    static MX_ADC1_Init()

as well as a static ADC_HandleTypedef hadc1.

OK, the USB based MX_ADC1_Init() uses at the end the HAL_ADC_MspInit() (provided via the stm32u5xx_hal_msp.c file, the same us user ADC config would use). But due to fact that MX_ADC1_Init() is static in both files (and hadc1 is a static on each file) - the "USB wins": every time you plug-in USB - it will reconfigure the ADC1 again (and overrides my user ADC1 config).

At the end: ADC1 is a different config, using a different hadc1 and all the user code for ADC1 is "ineffective".

So, there is a need to modify the BSP files - when users want to use ADC1  - and USB, e.g. as in my case to measure Vref, VBAT or TEMPerature sensor parallel with USB VBUS monitoring.

In Drivers/BSP/STM32U5xx_Nucleo/stm32u5xx_nucleo_usbpd_pwr.c is a function call as:

     BSP_USB_PWR_VbusGetVoltage()

It starts the conversion and expects just one single value measured: the PC2 (IN2) pin with the voltage divider, for monitoring the external VBUS voltage.

And the function BSP_USBPD_PWR_VBUSInit() starts also the ADC conversion.

But all this code assumes there is just one channel (PC2) - just one RANK_1 ! Even the user configures ADC1 with several RANKs - it would be "lost".

Solution

It would be nice - "cool" - if you (STM) can provide a BSP which has already four RANKs: three for VREF, VBAT, TEMPsensor and the additional one for PC2 (as IN2) for VBUS measurement.

And modify BSP_USBPD_PWR_VBusGetVoltage() accordingly, in a way, that USB gets the voltage for PC2 (on the right RANK, e.g. RANK_4).
User can now also get the VBAT and esp. the TEMPsensor without to modify the BSP (just add BSP functions to Get Temperature: ADC1 is anyway already used for PC2 VBUS: why not extending BSP to get also the Temperature?)

Remark

In general, you (as STM company) follow properly the approach to have a BSP (to handle the specifics of a board).

But I saw in STM32U575-EV project (for USB_ECM) that you "violate" a bit this approach:

in file Application/User/NetXDuo/App/app_netxduo.c

where you want to toggle an LED, requested via the HTTP web page - it is very hard-coded: it uses the PH7 pin - directly, which is only available on the STM32U575-EV board. So, it fails when people porting this project, e.g. to a NUCLEO board: the LED is "so hard-coded" and does not follow, neither using a BSP function (for toggling LED from a Middleware code via BSP, instead of "hard-coded" pin usage).

Here, how I had to fix as (porting the USB_ECM project to a NUCLEO board):

/* Check if requested data equal LED_ON */

if (strncmp((char const *)request_data, LED_ON, sizeof(LED_ON)) == 0)

{

#ifdef NUCLEO_BOARD

HAL_GPIO_WritePin(LED_BLUE_GPIO_Port, LED_GREEN_Pin, GPIO_PIN_SET);

#else

HAL_GPIO_WritePin(GPIOH, GPIO_PIN_7, GPIO_PIN_SET); //where bad approach!

#endif

}