{"id":12041,"date":"2024-01-31T08:45:26","date_gmt":"2024-01-31T06:45:26","guid":{"rendered":"https:\/\/deepbluembedded.com\/?p=12041"},"modified":"2024-02-01T09:40:14","modified_gmt":"2024-02-01T07:40:14","slug":"stm32-adc-injected-channel-conversion-mode-example","status":"publish","type":"post","link":"https:\/\/deepbluembedded.com\/stm32-adc-injected-channel-conversion-mode-example\/","title":{"rendered":"STM32 ADC Injected Channel Conversion Mode With Example"},"content":{"rendered":"\n
In this tutorial, we’ll discuss the STM32 ADC Injected Channel Conversion Mode<\/strong>, what makes it different from regular ADC channels, when to consider using an ADC channel as a regular vs injected, and how to configure the STM32 ADC injected channel to trigger on various events.<\/p>\n\n\n\n The practical example we’ll implement in this tutorial is an STM32 power LED dimmer (10W\/12v) with shunt resistor current measurement. The driving PWM signal will be configured as center-aligned, and the current measurement ADC channel will be an injected channel with a timer trigger in the middle of the PWM HIGH period. Without further ado, let’s get right into it!<\/p>\n\n\n The STM32 ADC gives you, the system designer\/programmer, the option to set several analog input channels as “injected channels”. A channel can either be configured as a regular or injected channel at any given time. When configured as an injected ADC channel, it’ll have a higher priority than regular channels and can also suspend any ongoing regular channel conversion when the injected channel\/group is triggered.<\/p>\n\n\n\n Below is an example diagram showing you a system where ADC1 is configured to have a regular group (CH0 to CH3) being converted while an injected channel (CH7) is triggered from a hardware source. The ADC suspends the ongoing conversion of the regular channels group, CH1 conversion is omitted, the injected CH7 is immediately sampled and converted, and then the regular group conversion is resumed starting from CH1 which was interrupted at the time of triggered-injection has occurred.<\/p>\n\n\n\n <\/p>\n\n\n\n The STM32F103xx, for example, can have up to 16 regular channels as well as up to 4 injected channels per ADC module. This number will vary from one target microcontroller to another, so you need to refer to the MCU’s datasheet to know its actual capabilities.<\/p>\n\n\n Here is a summary of the features that differentiate the injected channels from regular channels. This may be the answer to your question “Should I configure my channel as a regular or injected channel for XYZ application?”. The injected ADC channels will have the following features:<\/p>\n\n\n\n The injected ADC channels\/group can be configured to be “Triggered<\/strong>” using a configurable trigger source or “Auto<\/strong>” injected after the completion of a regular group conversion process.<\/p>\n\n\n\n Triggered-Injection<\/strong><\/p>\n\n\n\n In the triggered-injection mode: The injected channel\/group starts conversion immediately when a configured trigger source is fired<\/strong> (it can be a timer, EXTI pin, or any other trigger source). If an external injected trigger occurs during the regular group channel conversion, the current conversion is reset and the injected channel sequence is converted in Scan once mode.<\/p>\n\n\n\n Then, the regular group channel conversion is resumed from the last interrupted regular conversion. If a regular event occurs during an injected conversion, it doesn\u2019t interrupt it but the regular sequence is executed at the end of the injected sequence.<\/p>\n\n\n\n Auto-Injection<\/strong><\/p>\n\n\n\n In the auto-injection mode: the injected group channels are automatically converted after the regular group channels<\/strong>. This can be used to convert a sequence of up to 20 conversions, given that we can have up to 16 regular channels + up to 4 injected channels (in STM32F103xx devices).<\/p>\n\n\n The STM32 ADC injected channels conversion is considered a bit more advanced feature that can only be useful when you’re building an advanced application that really requires the features of injected channels over regular ADC channels.<\/p>\n\n\n\n Here we’ll discuss a couple of example projects (use cases) where we can incorporate the STM32 ADC injected channels conversion feature to solve real-world problems that could not have been solved otherwise. Where we need to have a high-priority ADC conversion with specific triggering configurations. This is exactly where the injected mode really shines.<\/p>\n\n\n The first application example for using ADC injected channels is the 3-phase current measurement for the ESC design shown in the project linked below. In which, I’m using dual shunt resistors to read the phase currents (IU<\/sub>, IV<\/sub>, IW<\/sub>) of a PMSM motor. The current measurement has to be triggered only when the low-side MOSFETs are conducting (turned ON). Reading the ADC channels anytime else will result in meaningless values that’ll cause the system to fail.<\/p>\n\n\n\n The time window for the phase current measurement is changing in width as the 3-phase PWM signals change in terms of the duty cycle. Therefore, we need to use the PWM’s timer to generate a trigger for the ADC in such a way that makes the injected channels start the conversion when the PWM signal going to the low-side MOSFET is HIGH.<\/p>\n\n\n This article will give more in-depth information about the hardware design of an STM32-based ESC board for BLDC & PMSM motor drive applications.<\/p>\n\n<\/div><\/div><\/div>\n<\/div>\n<\/div><\/div>\n\n (This is the project we’ll implement in this tutorial)<\/p>\n\n\n\n Another example use case for the ADC injected channel mode is reading the DC current of a high-power LED with a shunt resistor. The ADC injected channel will be hooked up to the shunt resistor to read the voltage developed on it (only while the driver transistor is turned ON). The ADC injected channel has to be triggered when the PWM signal going to the driver BJT is HIGH only.<\/p>\n\n\n\n Can we build this system using a regular channel? Of course, we can. A regular ADC channel triggered by a timer in the middle of the PWM’s HIGH period will do the job. However, we’ll use the ADC’s injected channel mode instead to showcase it!<\/p>\n\n\n The STM32 ADC Injected channel conversion mode is a bit more complex to use than regular ADC channels as you’ll see in the example project for this tutorial. Make sure your application really needs it before you consider using an ADC channel as injected instead of a regular one.<\/p>\n\n<\/div><\/div>\n\n\n In this example project, we’ll use an STM32 microcontroller to control the brightness of a high-power LED (10W\/12v) while monitoring the DC current going through the LED. For this application, we’ll set up a center-aligned PWM output signal to control a BJT that drives the LED on and off.<\/p>\n\n\n\n We’ll use a shunt power resistor (1\u03a9) to measure the current going through the load by reading the voltage developed across the shunt resistor’s terminals. Below is a simplified diagram showing you an overview of the system we’ll be building today.<\/p>\n\n\n\n <\/p>\n\n\n\n The voltage on the shunt resistor will reflect the current going through the load as we all know. But this is only valid while the BJT transistor is conducting or in other words: during the PWM’s HIGH poriod. That’s why we need to set up the PWM in center-aligned mode, and configure the ADC analog input channel as injected channel with a timer trigger to start the ADC conversion at the middle of the PWM’s HIGH period.<\/p>\n\n\n\n <\/p>\n\n\n\n We’ll add a regular ADC channel to read a potentiometer that will be used for controlling the LED’s brightness. We’ll also add a UART serial output to send the measured load current to the PC over the serial port for visualization and validation. And that’s everything we’re going to implement in this project.<\/p>\n\n\n\n Example Project Steps Summary:<\/p>\n\n\n\n And now, let’s build this system step-by-step<\/span><\/strong><\/span><\/p>\n\n\n Step #1<\/strong><\/p>\n\n<\/div>\n\n\n Open STM32CubeMX<\/strong>, create a new project, and select the target microcontroller. <\/p>\n\n\n Step #2<\/strong><\/p>\n\n<\/div>\n\n\n Configure The ADC1<\/strong> Peripheral, Enable the regular Channel-7<\/strong> in continuous conversion mode. Enable Channel-4<\/strong> as an injected channel in discontinuous mode with Timer2 TRGO<\/strong> event as a trigger source. In the NVIC tab, enable the ADC1 global interrupt.<\/p>\n\n\n\n <\/p>\n\n\n Step #3<\/strong><\/p>\n\n<\/div>\n\n\n For Timer2, we’ll enable PWM<\/strong> output on CH2<\/strong> and output compare with no output<\/strong> on CH1<\/strong>. The PWM output mode will be set as Center-Aligned<\/strong> mode1.<\/p>\n\n\n\n We’ll set the ARR<\/strong> register (MAX PWM period) to 4095<\/strong> just to match the ADC’s resolution so we can write the potentiometer reading directly as a duty cycle for the PWM output. Finally, choose the TRGO event to be “OC1 Event<\/strong>“. And that’s all about it.<\/p>\n\n\n\n <\/p>\n\n\n Step #4<\/strong><\/p>\n\n<\/div>\n\n\n Configure UART1<\/strong> To Operate @115200bps in TX-only mode (Asynchronous). Which will be used to send messages over the serial port to the PC.<\/p>\n\n\n\n <\/p>\n\n\n Step #5<\/strong><\/p>\n\n<\/div>\n\n\n Go to the RCC<\/strong> clock configuration page and enable the HSE external crystal oscillator<\/strong> input.<\/p>\n\n\n\n <\/p>\n\n\n Step #6<\/strong><\/p>\n\n<\/div>\n\n\n Go to the clock configurations page, and select the HSE<\/strong> as a clock source, PLL<\/strong> output, and type in 72<\/strong>MHz for the desired output system frequency. Hit the “ <\/p>\n\n\n Step #7<\/strong><\/p>\n\n<\/div>\n\n\n Name & Generate The Project Initialization Code For CubeIDE or The IDE You’re Using.<\/p>\n\n\n Step #8<\/strong><\/p>\n\n<\/div>\n\n\n In the STM32CubeIDE project navigator, right-click the project’s name, and select properties<\/strong>. We need to do the following configuration to enable printing floating-point variables using the STDIO <\/p>\n\n\n Here is The Application Code For This LAB (main.c)<\/strong><\/span><\/p>\n\n\n\n <\/p>\n\n\n There are so many things going on in the demo video that you need to keep your eye on. Here is a brief description of what each element is doing in this footage:<\/p>\n\n\n\n Yellow Trace<\/mark><\/strong>: GPIO (B1) showing the ADC trigger timing relative to the PWM signal. This is not ideal of course as we’re toggling the pin in the injected ADC channel’s ISR after the completion of the conversion not at the time of the trigger’s arrival. But it’s close enough to validate what we’ve designed the system for.<\/p>\n\n\n\n Blue Trace<\/mark><\/strong>: the center-aligned PWM output signal that drives the BJT to control the power LED’s brightness.<\/p>\n\n\n\n Multimeter<\/mark><\/strong>: Measures the DC current going through the power LED.<\/p>\n\n\n\n Serial Monitor<\/mark><\/strong>: It shows the UART messages sent from the STM32 microcontroller that displays the DC current measured by the microcontroller’s ADC from the shunt resistor with the injected channel configuration that’s triggered in the middle of the PWM HIGH period.<\/p>\n\n\n\nTable of Contents<\/h2>\n
\n
\n\n\nSTM32 ADC Injected Channel Conversion Mode<\/strong><\/h2>\n\n\n
STM32 ADC Injected Vs Regular Channels<\/strong><\/h3>\n\n\n
\n
STM32 ADC Auto-Injection Vs Triggered-Injection<\/strong><\/h3>\n\n\n
STM32 ADC Injected Channels Applications (Use Cases)<\/strong><\/h3>\n\n\n
Application #1<\/strong><\/h4>\n\n\n
Application #2<\/strong><\/h4>\n\n\n
\n\n\nSTM32 ADC Injected Channel Conversion Example Project<\/strong><\/h2>\n\n\n
\n
\n\n\nSTM32 ADC Injected Channel With PWM Trigger Example<\/strong><\/strong><\/h2>\n\n\n
Enter<\/code>” key, and let the application solve for the required PLL dividers\/multipliers to achieve the desired clock rate.<\/p>\n\n\n\n
sprintf()<\/code> function.<\/p>\n\n\n\n
STM32 ADC Injected Channel With PWM Trigger Example Code<\/strong><\/h3>\n\n\n
\/*\n * LAB Name: STM32 ADC Injected Channel Example (R_Shunt Load Current Measurement)\n * Author: Khaled Magdy\n * For More Info Visit: www.DeepBlueMbedded.com\n*\/\n#include \"main.h\"\n#include <stdio.h>\n\nADC_HandleTypeDef hadc1;\nTIM_HandleTypeDef htim2;\nUART_HandleTypeDef huart1;\nchar UART1_TxBuffer[30];\nvolatile uint16_t InjADC_Reading = 0;\nuint16_t AD_RES = 0;\nfloat I_dc = 0.0;\n\nvoid SystemClock_Config(void);\nstatic void MX_GPIO_Init(void);\nstatic void MX_ADC1_Init(void);\nstatic void MX_TIM2_Init(void);\nstatic void MX_USART1_UART_Init(void);\n\nint main(void)\n{\n HAL_Init();\n SystemClock_Config();\n MX_GPIO_Init();\n MX_ADC1_Init();\n MX_TIM2_Init();\n MX_USART1_UART_Init();\n HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2); \/\/ Start PWM: TIM2 (CH2)\n HAL_TIM_OC_Start(&htim2, TIM_CHANNEL_1); \/\/ Start Output Compare (CH1)\n TIM2->CCR1 = 1; \/\/ Set The OC1 Trigger Point To The Middle of The PWM Waveform\n HAL_ADC_Start(&hadc1); \/\/ Start ADC Conversion (Regular Channel, Continuous Mode, Polling)\n HAL_ADCEx_InjectedStart_IT(&hadc1); \/\/ Start ADC Conversion (Injected Channel, Tim2-PWM-Triggered, Interrupt Mode)\n while (1)\n {\n\t HAL_ADC_PollForConversion(&hadc1, 1); \/\/ Poll ADC1 Peripheral & TimeOut = 1mSec\n\t AD_RES = HAL_ADC_GetValue(&hadc1); \/\/ Read The ADC Regular Channel (CH7)\n\t TIM2->CCR2 = AD_RES; \/\/ Update PWM DutyCycle Using ADC Value (Potentiometer On CH7)\n\t I_dc = ((InjADC_Reading*3.30)\/4.095); \/\/ Convert The Injected ADC Channel Reading To I_dc\n\t sprintf(UART1_TxBuffer, \"I_dc = %f mA\\r\\n\", I_dc); \/\/ Print The Current Measurement To Serial Port\n\t HAL_UART_Transmit(&huart1, (uint8_t*)UART1_TxBuffer, sizeof(UART1_TxBuffer), 10);\n\t HAL_Delay(1);\n }\n}\n\nvoid HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc)\n{\n\tHAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); \/\/ Toggle This Pin To Check The ADC Trigger Time On DSO\n\tInjADC_Reading = HAL_ADCEx_InjectedGetValue(&hadc1, ADC_INJECTED_RANK_1); \/\/ Read The Injected Channel Result\n\tHAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);\n}<\/pre>\n\n\n\n
STM32 ADC Timer Trigger Example Testing<\/strong><\/h3>\n\n\n