Getting started with USB Type-C only Source

Revision as of 05:52, 31 March 2022 by Registered User

Target description

This tutorial will help you to:

  • Use the X-NUCLEO-SRC1M1 shield that includes a TCPP02-M18 protection circuit and provides a USB Type-C® connector
  • Create a USB legacy 3A@5V Source application with the NUCLEO-F446RE board that does not include any UCPD peripheral and the X-NUCLEO-SRC1M1 shield by using STM32CubeIDE software

Prerequisites

  • Computer with Windows 7 (or higher)

Hardware

  • NUCLEO-F446RE (tested on rev C-04) [1]
  • X-NUCLEO-SRC1M1 shield [2]
  • A USB-PD sink device to test our USB Source device (it can be the sink created in this wiki article, or a mobile phone or device)
  • USB cable Type-A to Mini-B
  • USB Type-C® to Type-C® cable

Software

  • STM32CubeIDE (tested with V1.8.0) [3]
  • X-CUBE-TCPP MCU Firmware Package (BSP) [4]

Literature

  • UM1724 NUCLEO-F446RE User Manual
  • UM2973 X-NUCLEO-SRC1M1 User Manual


Create a USB-PD Source Device

Clock.png Total 50min

1. Creating the project

Clock.png 5min

Open STM32CubeIDE and create a New STM32 Project. As a target selection, choose the NUCLEO-F446RE from the Board Selector Tab

USBnoPD 0 Newproject.png


Click "Next", then enter your project's name. Leave the other fields as default and click "Finish".

USBnoPD 1 Newproject.png


When prompted for initializing peripherals with their default mode, click No.

2. Configuring the system

Clock.png 15min

At this point, your project is created and you are left with the STM32CubeMX view. In the next steps, we will configure the peripherals and options needed for the project.

2.1. Clear the pinout

To start from a blank configuration, click on the Pinout menu and select Clear Pinouts. This will reset the pinouts in the Pinout view.

USBPD 0-pinoutConf.png


2.2. Configure the system timebase

For this simple example, we will use SysTick as the system timebase. In the System Core section, select SYS and change the Timebase Source to SysTick.

USBnoPD 0 sysConf v2.png


2.3. Configure GPIO

Three GPIO are used

  • Click left on the pin PC8 and select GPIO_Output. With a right click select Enter user label to name it as ENABLE. This output will drive the TCPP02 chip enable input.
  • Click left on the pin PA5 and select GPIO_Output. With a right click select Enter user label to name it as GREEN_LED. This output will drive the green led on the Nucleo board to indicate that Vbus is ON.
  • Click left on the pin PA10 and select GPIO_EXT10, and with a right click select Enter user label to name it as 'FLGN'. This interrupt input will receive any TCPP02 fault flag.

in the System Core section, select GPIO, and then change PA10 mode to "External interrupt Mode with Falling Edge detection".


Finally, in the NVIC tab, activate the EXTI lines[15-10] interrupts

USBnoPD 1 GPIOConf v2.png


2.4. Configure ADC peripheral

Five signals shall be monitored to ensure proper and safe USB 3A-5V delivery. CC1 and CC2 configurations lines voltage, Vbus and Vprovider voltages and Iana the current through Vbus. Then an ADC needs to be configured: In the pin view, with a left click, select the pin PC0, configure it as ADC1 Channel 10 (CC2). Repeat the operation for PC1 as ADC1 Channel 11 (IANA); for PA0 as ADC1 Channel 0 (VBusc); for PA1 as ADC1 Channel 1(Vprov); and for PA4 as ADC1 Channel 4 (CC1) Note: it is not mandatory to set their name as each value will be obtained by DMA in function of its rank.

USBnoPD 0 ADCConf.png


2.4.1. ADC parameters configuration

In the Analog section clock on ADC and select, in the Parameter Settings tab:

  • Scan conversion Mode: Enabled
  • Number of conversions: 5


Then, define for each ADC input its channel and 144 Cycles Sampling time

  • Rank 1: CC2: Channel 10
  • Rank 2: CC1: Channel 4
  • Rank 3: Vbus: Channel 0
  • Rank 4: Iana: Channel 11
  • Rank 5: Vprov: Channel 1
USBnoPD 2 ADCConf.png


2.4.2. DMA Configuration for ADC1

in the System Core section, select the DMA, click on "ADD" and select ADC1 in the DMA2 request column.

USBnoPD 0 DMAConf.png


And set the DMA Mode: Circular

USBnoPD 1 DMAConf.png


Then back to the ADC1 parameters configuration, set DMA Continuous Requests to ENABLED

USBnoPD 3 ADCConf.png


Finally in the User Constants' tab, click on "ADD" to create the "USBPD_CONFIG_MX" constant and affect it the value "True"


2.5. Configure TIM2 timer

in the Timers section, select timer2: "TIM2", afect the "Internal clock" as clock source. In the Parameter Settings tab,

  • Set the Prescaler value to 2000
  • The counter period to 50
  • The Internal clock division to "Division by 4"


Finally, select the NVIC Settings tab to enable the TIM2 global interrupt

USBnoPD 1 Tim2Conf.png


2.6. Configure I2C peripheral

As the X-NUCLEO-SRC1M1 shield includes a TCPP02-M18 that communicates via I2C, we need to enable the I2C peripheral in our project.

In the Connectivity section, enable I212 peripheral, in I2C mode. Leave the configuration as default, as the X-NUCLEO-SRC1M1 BSP will reconfigure it.

USBnoPD 0 I2CConf.png


Note: We need to enable the I2C1 peripheral in the CubeMX view for code generation to include the I2C drivers as we do for the ADC.

2.7. Check the clock configuration

Under Clock Configuration main tab, change system clock mux to PLLCLK. It will set HCLK clock to 64MHz.


3. Configure project

Clock.png 5min

Under the Project Manager main tab, under the Advanced Settings tab, as we do not need I2C initialization functions (handled by the BSP drivers), uncheck Generate Code for the MX_I2C1_Init.

USBnoPD 0 ConfProjet.png


4. Add BSP to the project

Clock.png 5min

Board Support Package (BSP) for the X-NUCLEO-SRC1M1 shield needs to be added to the project. The files need to be copied manually in the project's folder.
Get the latest BSP from GitHub x-cube-tcpp.

Manually copy the three following folders:

x-cube-tcpp
└── Drivers
    └── BSP
        ├── 📁X-NUCLEO-SRC1M1
        └── Components
            └── 📁tcpp0203

Into:

<ProjectFolder>
└── Drivers
    └── BSP
        ├── 📁X-NUCLEO-SRC1M1
        └── Components
            └── 📁tcpp0203

Then, create a file named ".extSettings" at the project's root folder (please mind the dot character in the filename) and fill it with the following code:

[ProjectFiles]
HeaderPath=Drivers\BSP\X-NUCLEO-SRC1M1;Drivers\BSP\STM32F4xx_Nucleo;Drivers\BSP\Components\tcpp0203
[Others]
Define=TCPP0203_SUPPORT;USE_STM32F4XX_NUCLEO
HALModule=
[Groups]
Drivers/BSP/X-NUCLEO-SRC1M1=Drivers/BSP/X-NUCLEO-SRC1M1/src1m1_usbpd_pwr.c;Drivers/BSP/X-NUCLEO-SRC1M1/src1m1_bus.c;
Drivers/BSP/Components/tcpp0203=Drivers/BSP/Components/tcpp0203/tcpp0203.c;Drivers/BSP/Components/tcpp0203/tcpp0203_reg.c

This file is used to tell the code generator to include the BSP files when generating the project.

Info white.png Information
You can double-click the code zones to select it all, then copy it with Ctrl+c.

5. Generate code

Clock.png 5min

Save your file with Ctrl+s and select generate code if prompted. You can also generate code from the STM32CubeIDE menu by clicking Project/Generate Code, or by pressing Alt+K.

USBPD 0-projGen.png



In this project, different folders can be found:

  • The Core folder contains the source files for the core of the project.
  • The Drivers folder contains the HAL drivers for the STM32, and the BSP for the Nucleo board and X-NUCLEO-SRC1M1 shield.

The Drivers folder in the Explorer view of the project must contain the BSP folders added earlier.


6. Complete application

Clock.png 15min

Now that the peripherals are initialized by STM32CubeMX, some minimum level of the application needs to be added:

  • src1m1_conf.h file needs to be created from its template, and added to the project
  • User code needs to be added in several files

6.1. Add SRC1M1 configuration file

In the Drivers\BSP\X-NUCLEO-SRC1M1 folder you will find src1m1_conf_template.h. Copy it to Core\Inc folder, and rename it src1m1_conf.h. It is the configuration file used for the X-NUCLEO-SRC1M1 BSP.

6.2. Modification in stm32f4xx_it.c

Info white.png Information
You can double-click the code zones to select it all, then copy it with Ctrl+c.

Add the following code between the /* USER CODE BEGIN-END Includes */ tags:

/* USER CODE BEGIN Includes */
#include "src1m1_conf.h"
/* USER CODE END Includes */

Add the following code between the /*USER CODE BEGIN-END EV * tags:

/* USER CODE BEGIN EV */
  extern USB_C_Typedef USB_C;
  extern uint16_t value_adc[];
  extern uint16_t value_adc_filtered[];
/* USER CODE END EV */

In the TIM2 IRQ Handler, add the following code between the /*USE CODE BEGIN TIM2_IRQn 0-END TIM2_IRQn 0*/ tags:

/* USER CODE BEGIN TIM2_IRQn 0 */
	if (USB_C.Attaching_CC1>=1
        {
		USB_C.Attaching_CC1++;
	}
	if (USB_C.Attaching_CC2>=1)
        {
		USB_C.Attaching_CC2++;
        }
  	if (USB_C.Detaching)
  	{
                USB_C.Detaching++;
  	}	
	__HAL_TIM_SET_COUNTER(&htim2, 0);
  	/* USER CODE END TIM2_IRQn 0 */

In the EXTI10-15 IRQ Handler, add the following code between the /*USE CODE BEGIN EXTI10_IRQn 0-END EXTI10_IRQn 0*/ tags:

/* USER CODE BEGIN EXTI10_IRQn 0 */
	//TCPP FLGn flag Active low
	USB_C.Discharging=1;
	BSP_USBPD_PWR_VBUSMOSOff(0);
	BSP_USB_PWR_VBUSDischargeOn(0);
	HAL_GPIO_WritePin(GREEN_LED_GPIO_Port,GREEN_LED_Pin,GPIO_PIN_RESET);
	if (USB_C.Attached_CC1==1)
        {
		USB_C.TCPP_Fault=1;
        }
	if (USB_C.Attached_CC2==1)
        {
		USB_C.TCPP_Fault=2;
        }
/* USER CODE END EXTI10_IRQn 0 */

In the DMA2 IRQ Handler, add the following code between the /*USE CODE BEGIN DMA2_Stream0_IRQn 0-END DMA2_Stream0_IRQn 0*/ tags:

/* USER CODE BEGIN DMA2_Stream0_IRQn 0 */
//Filtering ADC
  uint8_t i;
  for (i=0;i<5;i++)
  {
	  value_adc_filtered[i]=(value_adc_filtered[i]+value_adc[i])/2;
  }
  //Connection detection
  if ((USB_C.Attached_CC1==0)&&(USB_C.Attached_CC2==0)&&(USB_C.TCPP_Fault==0)&&
		  (PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_VPROV],SRC1M1_VSENSE_RA,SRC1M1_VSENSE_RB)>VPROV_MIN)) 		//No fault, No connection, Vbus=Vsafe0
  {
	  if (((PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC1],SRC1M1_NORA,SRC1M1_NORB)>CC_MIN)&&((PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC1],SRC1M1_NORA,SRC1M1_NORB)<CC_MAX))&&
			  ((PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC2],SRC1M1_NORA,SRC1M1_NORB)<CC_MAX_Ra)|(PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC2],SRC1M1_NORA,SRC1M1_NORB)>CC_OPEN_MIN))))
	  {
		  if (USB_C.Attaching_CC1==0)
		  {
			  USB_C.Attaching_CC1=1;
			  HAL_TIM_Base_Start_IT(&htim2);
		  }
		  if (USB_C.Attaching_CC1>=110)
		  {
			  HAL_TIM_Base_Stop_IT(&htim2);
			  BSP_USBPD_PWR_VBUSDischargeOff(0);
			  BSP_USBPD_PWR_VBUSOn(0);														//TCPP Vbus MOS ON
			  USB_C.Attaching_CC1=0;
			  USB_C.Attaching_CC2=0;
			  USB_C.Attached_CC1=1;
			  USB_C.Attached_CC2=0;
			  USB_C.Detaching=0;
			  HAL_GPIO_WritePin(GREEN_LED_GPIO_Port,GREEN_LED_Pin,GPIO_PIN_SET);
		  }
	  }
	  else if (((PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC2],SRC1M1_NORA,SRC1M1_NORB)>CC_MIN)&&((PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC2],SRC1M1_NORA,SRC1M1_NORB)<CC_MAX))&&
			  ((PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC1],SRC1M1_NORA,SRC1M1_NORB)<CC_MAX_Ra)|(PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC1],SRC1M1_NORA,SRC1M1_NORB)>CC_OPEN_MIN))))
	  {
		  if (USB_C.Attaching_CC2==0)
		  {
			  USB_C.Attaching_CC2=1;
			  HAL_TIM_Base_Start_IT(&htim2);												//for Vbus=Vsafe0
		  }
		  if (USB_C.Attaching_CC2>=110)
		  {
			  HAL_TIM_Base_Stop_IT(&htim2);
			  BSP_USBPD_PWR_VBUSDischargeOff(0);
			  BSP_USBPD_PWR_VBUSOn(0);														//TCPP Vbus MOS ON
			  USB_C.Attaching_CC1=0;
			  USB_C.Attaching_CC2=0;
			  USB_C.Attached_CC1=0;
			  USB_C.Attached_CC2=1;
		      USB_C.Detaching=0;
			  HAL_GPIO_WritePin(GREEN_LED_GPIO_Port,GREEN_LED_Pin,GPIO_PIN_SET);
		  }
	  }
  }
  //debouncing connection CC1
  if ((USB_C.Attaching_CC1>=1)&&(PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC1],SRC1M1_NORA,SRC1M1_NORB)>CC_MAX))
  {
	  USB_C.Attaching_CC1=0;																//If glitch detected : time restarts at beginning
  }
  //debouncing connection CC2
  if ((USB_C.Attaching_CC2>=1)&&(PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC2],SRC1M1_NORA,SRC1M1_NORB)>CC_MAX))
  {
	  USB_C.Attaching_CC2=0;																//If glitch detected : time restarts at beginning
  }

  //----------------------------------------------------------------
  //Disconnection detection
  if (((USB_C.Attached_CC1==1)&&(PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC1],SRC1M1_NORA,SRC1M1_NORB)>CC_MAX))|
		  ((USB_C.Attached_CC2==1)&&(PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC2],SRC1M1_NORA,SRC1M1_NORB)>CC_MAX)))
  {
	  if (USB_C.Detaching==0)
	  {
		  USB_C.Detaching=1;
		  HAL_TIM_Base_Start_IT(&htim2);
	  }
	  if (USB_C.Detaching>=10)
	  {
		  USB_C.Attached_CC1=0;
		  USB_C.Attached_CC2=0;
		  USB_C.Discharging=1;
		  USB_C.Detaching=0;
		  BSP_USBPD_PWR_VBUSOff(0);
		  BSP_USBPD_PWR_VBUSDischargeOn(0);
		  HAL_GPIO_WritePin(GREEN_LED_GPIO_Port,GREEN_LED_Pin,GPIO_PIN_RESET);
	  }
  }
  if (((USB_C.Attached_CC1==1)&&(PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC1],SRC1M1_NORA,SRC1M1_NORB)<CC_MAX))|
		  ((USB_C.Attached_CC2==1)&&(PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC2],SRC1M1_NORA,SRC1M1_NORB)<CC_MAX)))
  {
	  USB_C.Detaching=0;
  }
  //-----------------------------------------------------------------
  //Fault Vbus too high or I too high (detected by ADC)
  if (((USB_C.Discharging==0)&&(USB_C.TCPP_Fault==0))&&((PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_VBUSC],SRC1M1_VSENSE_RA,SRC1M1_VSENSE_RB)>VBUS_MAX)|
		  (PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_VPROV],SRC1M1_VSENSE_RA,SRC1M1_VSENSE_RB)<VPROV_MIN)))//|(value[ADC_ISENSE]>ISENSE_MAX)
  {
	  if (USB_C.Attached_CC1==1)
	  {
		  USB_C.Discharging=1;
		  USB_C.Detaching=0;
		  USB_C.TCPP_Fault=1;
		  BSP_USBPD_PWR_VBUSOff(0);														//Open MOS
		  BSP_USBPD_PWR_VBUSDischargeOn(0);													//Close Vbus discharge path
		  HAL_GPIO_WritePin(GREEN_LED_GPIO_Port,GREEN_LED_Pin,GPIO_PIN_RESET);
	  }
	  if (USB_C.Attached_CC2==1)
	  {
		  USB_C.Discharging=1;
		  USB_C.Detaching=0;
		  USB_C.TCPP_Fault=2;
		  BSP_USBPD_PWR_VBUSOff(0);														//Open MOS
		  BSP_USBPD_PWR_VBUSDischargeOn(0);													//Close Vbus discharge path
		  HAL_GPIO_WritePin(GREEN_LED_GPIO_Port,GREEN_LED_Pin,GPIO_PIN_RESET);
	  }
  }

  //-------------------------------------------------------------------
  //End of Vbus discharge -> TCPP in Low Power
  if (USB_C.Discharging==1)		//If Vbus discharge was Requested
  {
	  if (PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_VBUSC],SRC1M1_VSENSE_RA,SRC1M1_VSENSE_RB)<VSAFE_MAX)	//Vbus is 0
	  {
		  USB_C.Discharging=0;
		  USB_C.Detaching=0;
		  BSP_USBPD_PWR_VBUSDischargeOff(0);
		  HAL_GPIO_WritePin(GREEN_LED_GPIO_Port,GREEN_LED_Pin,GPIO_PIN_RESET);
	  }
  }
  if (((USB_C.TCPP_Fault==1)&&(PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC1],SRC1M1_NORA,SRC1M1_NORB)>CC_OPEN_MIN))|
		  ((USB_C.TCPP_Fault==2)&&(PWR_TCPP0203_ConvertADCDataToVoltage(value_adc_filtered[ADC_CC2],SRC1M1_NORA,SRC1M1_NORB)>CC_OPEN_MIN)))
  {
	  USB_C.Attached_CC1=0;
	  USB_C.Attached_CC2=0;
	  USB_C.Detaching=0;
	  USB_C.TCPP_Fault=0;
	  HAL_GPIO_WritePin(GREEN_LED_GPIO_Port,GREEN_LED_Pin,GPIO_PIN_RESET);
  }
  //-------------------------------------------------------------------

  /* USER CODE END DMA2_Stream0_IRQn 0 */

6.3. Add code in main.h

Add the following code between the /* USER CODE BEGIN ET-END ET*/ tags:

/* USER CODE BEGIN ET */
typedef struct
{
    uint8_t Attaching_CC1;   /*Attachement on going - debouncing - Rp detected on CC1*/
    uint8_t Attaching_CC2;   /*Attachement on going - debouncing - Rp detected on CC2*/
    uint8_t Attached_CC1;    /*Attached - Rp on CC1*/
    uint8_t Attached_CC2;    /*Attached - Rp on CC2*/
    uint8_t Detaching;           /*Detachement on going*/
    uint8_t Discharging;        /*Vbus is discharging on going*/
    uint8_t TCPP_Fault;	    /*Hardware detected fault (Isense)
}USB_C_Typedef;
/* USER CODE END ET */

6.4. Add code in main.c

Add the following code between the /*USER CODE BEGIN Includes-END Includes*/ tags:

/* USER CODE BEGIN Includes */
#include "src1m1_usb_pwr.h"
/* USER CODE END Includes */

Add the following code between the /*USER CODE BEGIN PV-END PV*/ tags :

/* USER CODE BEGIN PV */
  uint16_t value_adc[5];
  uint16_t value_adc_filtered[5]={0,0,0,0,0};
  USB_C_Typedef USB_C;
/* USER CODE END PV */

Add the following code between the /*USER CODE BEGIN 2-END 2*/ tags :

/* USER CODE BEGIN 2 */
  HAL_GPIO_WritePin(GREEN_LED_GPIO_Port,GREEN_LED_Pin,GPIO_PIN_RESET);
  HAL_GPIO_WritePin(ENABLE_GPIO_Port,ENABLE_Pin,GPIO_PIN_SET);

  BSP_USBPD_PWR_Init(0);
  BSP_USBPD_PWR_SetPowerMode(0,USBPD_PWR_MODE_NORMAL);

  USB_C.Attaching_CC1=0;
  USB_C.Attaching_CC2=0;
  USB_C.Attached_CC1=0;
  USB_C.Attached_CC2=0;
  USB_C.Detaching=0;
  USB_C.Discharging=0;
  USB_C.TCPP_Fault=0;

  HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&value_adc,5);
/* USER CODE END 2 */

7. Configure the shield's jumpers

Place jumpers on the X-NUCLEO-SRC1M1 shield as shown in the picture.


Next, plug an external 5V source into the green "source" connector.

With this configuration, the board will be powered by the ST-Link of the Nucleo board.
If you want to power your system from the external power supply connected to the "source" terminal, and not from the ST-Link, please add the JP1 jumpers between 1-2 and 3-4.

8. Compile and run the application

The compilation must be performed without error or warnings.
Build the application by clicking on the Built Button.png button (or select Project/Build Project).
Run the application by clicking on the DownloadRun Button.png button (or select Run/Run)

9. Evaluate the application

Clock.png 5min

Plugging an USB C sink device, Vbus is applied after 120ms Unplugging the USB C sink device, Vbus is immediately shutdown and placed in safe mode (0V) During connection, if an overcurrent or malfunction is detected, the Source is placed in safe mode (0V) and needs an USB C disconnection/reconnection to restart.



You can find other applicative examples on GitHub: x-cube-tcpp

10. References


[[Category:STM32 step by step advance|30]] [[Category:USB Power Delivery|20]]