cancel
Showing results for 
Search instead for 
Did you mean: 

Problems with ADC interrupt

Keyshav Mor
Associate II
Posted on December 27, 2016 at 06:55

I am trying to generate an instantaneous PWM signal using ADC Single channel interrupt on stm32f103rbt6 nucleo board. I want to vary the speed of the dc motor using the instantaneous adc interrupt. But I am getting latency in my code, which is right now trying to just start and stop the motor on adc interrupt. Can anyone please help me? Here is the code. There is a lot of latency in the code.

/* Private variables ---------------------------------------------------------*/
uint32_t ADC_raw;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void Error_Handler(void);
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc1)
{
 if (__HAL_ADC_GET_FLAG(hadc1, ADC_FLAG_EOC))
 {
 ADC_raw = HAL_ADC_GetValue(hadc1);
 HAL_ADC_Start_IT(hadc1);
 }
}
/* USER CODE END PFP */
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
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();
/* Configure the system clock */
 SystemClock_Config();
/* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_ADC1_Init();
 MX_TIM1_Init();
/* USER CODE BEGIN 2 */
HAL_ADC_Start_IT((ADC_HandleTypeDef*) &hadc1);
 /* USER CODE END 2 */
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_4);
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
 HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_5);
 /* USER CODE END WHILE */
 HAL_Delay(71);
 /* USER CODE BEGIN 3 */
 HAL_ADC_Start_IT((ADC_HandleTypeDef*) &hadc1);
 }
 /* USER CODE END 3 */
}
void ADC1_2_IRQHandler(void)
{
 /* USER CODE BEGIN ADC1_2_IRQn 0 */
uint32_t adcValue;
adcValue=HAL_ADC_GetValue(&hadc1);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);
if(adcValue>1000)
{
 uint32_t motor=0;
for(motor=0;motor<=1000;motor++)
 {
 __HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_4,motor);
 HAL_Delay(10);
 // HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_4);
 }
 //HAL_Delay(500);
}
 /* USER CODE END ADC1_2_IRQn 0 */
 HAL_ADC_IRQHandler(&hadc1);
 /* USER CODE BEGIN ADC1_2_IRQn 1 */
/* USER CODE END ADC1_2_IRQn 1 */
}
void ADC1_2_IRQHandler2(void)
{
 uint32_t adcValue;
 adcValue=HAL_ADC_GetValue(&hadc1);
 HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);
 if(adcValue<1000)
 {
 uint32_t motor=0;
for(motor=0;motor<=1000;motor=motor+100)
{
 __HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_4,motor);
 HAL_Delay(10);
}
 HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
}
 HAL_ADC_IRQHandler(&hadc1);
}
/**
* @brief This function handles EXTI line[15:10] interrupts.
*/
void EXTI15_10_IRQHandler(void)
{
 /* USER CODE BEGIN EXTI15_10_IRQn 0 */
 HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_4);
 uint32_t motor=0;
 HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET);
 for(motor=0;motor<=1000;motor++)
 {
 __HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_4,motor);
 HAL_Delay(10);
 // HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_4);
 }
 //HAL_Delay(500);
 HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_4);
 HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET);
 /* USER CODE END EXTI15_10_IRQn 0 */
 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
 /* USER CODE BEGIN EXTI15_10_IRQn 1 */
/* USER CODE END EXTI15_10_IRQn 1 */
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

#stm32f103 #adc
10 REPLIES 10
Jeroen3
Senior
Posted on December 27, 2016 at 14:19

There is this big elephant in the room about using the for loop with 1000 times the 10 tick blocking delay inside the Interrupt Service Routine. What you are trying to to here is also a mystery for me.

for(motor=0;motor<=1000;motor=motor+100)
{
 __HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_4,motor);
 HAL_Delay(10);
}

It looks like you want to do a sweep of the duty cycle on TIM1_CH4. But this is certainly not the way.

Try doing this (without the for) in the TIM1_Update ISR. Since you have to give TIM1 at least some time to do run it's cycle!

Also if you have not setup the NVIC for preemption, no other code will execute while this for loop is running. (eg: systick never increments the HAL delay tick)

Seb
ST Employee
Posted on December 27, 2016 at 14:25

An interrupt must be as short as possible. Hardly the case here with a HAL_Delay.

How about running ADC in continuous mode without any interrupt. ADC should yield an answer every 1 or 2 us, which is probably shorter than the PWM period.

Then if you'd like to have an interrupt, get it from EXTI or your motor feedback incoming signal. Inside this interrupt, read the ADC latest converted value, adjust want you want and return to main loop.

This way, leftover Core time can be used for other tasks.

You can use also a Timer Input Capture interrupt instead of the EXTI... depends on what is simplest to do.

Input capture will snapshot the timer value at input selected edge. Edge can also be filtered or pre-divided which might reduce the interrupt frequency when needed.

Good luck!

Posted on December 28, 2016 at 08:34

thank you Seb and jereon  I used your advice with a little tweak and it works to expectation now. I removed the for loop and then the results are satisfactory. Now I want to multichannel scan mode conversion as I would be using at least 4 potentiometers. Can you help me? I have used interrupts and watching the variables on stm studio but only 1 channel is getting converted. Rest two are giving garbage values. can you help?

here is the call back function.

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc1)

{

if(hadc1->Instance == ADC1)

{

ADC_Raw[0]= HAL_ADC_GetValue(hadc1);

ADC_Raw[1]= HAL_ADC_GetValue(hadc1);

ADC_Raw[2]= HAL_ADC_GetValue(hadc1);

}

}
Posted on December 28, 2016 at 08:49

The simplest way is to program a DMA to cycle indefinitely through ADC_Raw buffer. There should be some ADC DMA examples in the HAL examples of Nucleo or Discovery boards.

Posted on December 28, 2016 at 09:17

won't that be slower than polling and interrupt? Just asking. Never used DMA before.

Posted on December 28, 2016 at 09:36

It will be faster. The DMA will initiate a peripheral to memory copy directly after conversion complete. Which is the same bit as the IRQ. The DMA only waits for other bus operations, the interrupt has to wait for other interrupts as well.

Eg:

TIMx triggers ADCx sequential conversion.

On each ADCx conversion complete the DMA request flag is set, and the DMA copies ADC_DR to the buffer, and increments the iterator.

The DMA (half)complete IRQ is used to process/filter the data.

Posted on December 28, 2016 at 10:13

I am trying as you and Seb Kindly suggested but right now everything is garbage. here is a sneak peek. Should I include a callback function in this?

&sharpinclude 'main.h'

&sharpinclude 'stm32f1xx_hal.h'

&sharpinclude 'adc.h'

&sharpinclude 'dma.h'

&sharpinclude 'gpio.h'

/* USER CODE BEGIN Includes */

uint32_t ADC_Buffer[3];

uint16_t i=0;

uint32_t Speed;

uint32_t Ramp;

uint32_t SpeedLimit;

/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* Private variables ---------------------------------------------------------*/

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/

void SystemClock_Config(void);

void Error_Handler(void);

/* USER CODE BEGIN PFP */

/* Private function prototypes -----------------------------------------------*/

/* USER CODE END PFP */

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

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();

/* Configure the system clock */

SystemClock_Config();

/* Initialize all configured peripherals */

MX_GPIO_Init();

MX_DMA_Init();

MX_ADC1_Init();

/* USER CODE BEGIN 2 */

HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC_Buffer,2);

/* USER CODE END 2 */

/* Infinite loop */

/* USER CODE BEGIN WHILE */

while (1)

{

/* USER CODE END WHILE */

HAL_Delay(300);

}

/* USER CODE END 3 */

}

Would this work in the case I use a complete conversion call back?

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc1)

{

if (hadc1->Instance == ADC1)

{

ADC_Buffer[0]=HAL_ADC_GetValue(hadc1);

ADC_Buffer[1]=HAL_ADC_GetValue(hadc1);

ADC_Buffer[2]=HAL_ADC_GetValue(hadc1);

}

}
Jeroen3
Senior
Posted on December 28, 2016 at 11:37

With DMA you have to use the DMA Interrupt, not the ADC interrupt. Since the ADC performs 4 conversions (with interrupt), but you are only interested in all 4 together, thus when the DMA is finished.

I'm not familiar with the HAL and DMA usage. I do this bare metal. There are so many per-chip-exceptions you can't really create a suitable HAL for this without using half the ROM.

Posted on December 29, 2016 at 19:13

Dear Keyshav, I have recently started working with DMA and HAL drivers. If your DMA is properly configured in:

MX_DMA_Init();

 ten, after DMA completes all transfers (in your case two of them)

HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC_Buffer,2);

You will be able to get a callback: XferCpltCallback

HAL_DMA_RegisterCallback(DMA_HandleTypeDef *hdma, HAL_DMA_CallbackIDTypeDef CallbackID, void (* pCallback)(DMA_HandleTypeDef *_hdma))

So, you have to setup this callback like this:

HAL_DMA_RegisterCallback(

&hadc1

, HAL_DMA_XFER_CPLT_CB_ID,YourCallbackFunction);

Now just write this function somwhere and put your code there

void YourCallbackFunction()

{

//TODO

}

PS. How the hell do you enable code listing here???