second timer - Trigger - every 100microseconds
when I create two timer interrupt with above condition, problem one timer interrupt function is disturbing other timer interrupt.
Here I used the Hardware timer library from Arduino_STM32 core library
For Hardware details, please refer this post link
cc: maxgerhardt
The timers run in parallel, in hardware, when you use different timer instances (e.g., TIM2, TIM3, etc.).
But the CPU that executes the interrupt service routines (aka, the functions that get triggered by the timer) does not run in parallel. So, if one takes a long time, it will block the execution of the other ISR too, or get interrupted itself (depending on interrupt priority in the NVIC).
Do you have an exact piece of code that behaves unexpectedly? Hard to say anything otherwise.
Oh no. Very wrong approach. analogRead() will be blocking for quite some time to start the ADC and wait for the measurement result. Thus you’ll be blocking OnTimer1Interrupt() to be executed (if its priority is lower). Blocking is exactly what you should not do inside an interrupt. This needs to finish as fast as possible. Starting the ADC conversion in there is far too late.
The Timer and ADC peripherals of the STM32(F1) can play together very nicely here: The timer can generate TRGO (trigger out) events at a specified rate, e.g., 100Hz. The ADC can then be setup to start a conversion from a trigger, that will e.g. be the TRGO of a timer. Then, when the ADC conversion (measurement) finishes, an ADC interrupt fires and a function of yours can be invoked so that you can retrieve the prepared result and save it in a buffer.
In fact, even the “save ADC result into a buffer” can be done automatically if you involve the DMA (direct memory access) peripheral with a Peripheral2Memory transfer.
The point is that you want to push as many responsibilities you can from the software (CPU) to hardware (ADC, TIM, DMA, …). Don’t fetch the ADC result into a buffer by software if there’s a peripheral that can do it for you in hardware. Don’t start the ADC by software if there’s a peripheral that can do it for you in hardware.
The only problem here is that this whole setup is not covered by the Arduino APIs at all. They don’t give you the possibility to configure the ADC for e.g., continuous conversion, or being triggered by a timer / TRGO. Other Arduino cores are better here, providing a dedicated ADC library with more functionality, but they are also outdated.
The same thing can be said about you generating your sine wave. After all, this can be realized by periodically triggering a DMA transfer from memory (RAM) to the GPIO->ODR register.
With your suggest I tried different way of ADC setup to read data. Let me explain
Setting up Timer 3 to Output TRGO
Setting up ADC to Trigger on Timer 3 TRGO
From the ADC Conversion Complete Interrupt I will store the ADC data
Start the ADC
Problem
If I Enable ADC interrupt in NVIC, STM32 get into Hang or stuck mode on same line. Also I tried by set ADC interrupt to lower priority HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
#include <Arduino.h>
ADC_HandleTypeDef hadc1;
TIM_HandleTypeDef htim3;
void init_adc_and_tim() {
__HAL_RCC_ADC1_CLK_ENABLE();
ADC_ChannelConfTypeDef sConfig = {0};
/* Common config */
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
if (HAL_ADC_Init(&hadc1) != HAL_OK) {
Error_Handler();
}
/* Configure Regular Channel */
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){
Error_Handler();
}
/* TIM3 Init */
__HAL_RCC_TIM3_CLK_ENABLE();
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM3_Init 1 */
/* USER CODE END TIM3_Init 1 */
htim3.Instance = TIM3;
htim3.Init.Prescaler = 7200 - 1;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 100 - 1;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK) {
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK) {
Error_Handler();
}
// Enable interrupt for ADC1_2
HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
}
/* interrupt handler for ADC */
extern "C" void ADC1_2_IRQHandler(void) {
HAL_ADC_IRQHandler(&hadc1);
}
static volatile uint16_t adcValue = 0;
static volatile bool adcChanged = false;
/* called by the HAL ADC in interrupt context when conversion is complete */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
if (hadc->Instance == ADC1) {
adcValue = (uint16_t) HAL_ADC_GetValue(hadc);
adcChanged = true;
}
}
void setup() {
Serial.begin(921600);
// use one analogRead to setup pin correctly in analog mode
(void) analogRead(PA0);
init_adc_and_tim();
// kick off timer to generate TRGO events
HAL_TIM_Base_Start(&htim3);
// kick off process once
HAL_ADC_Start_IT(&hadc1);
}
void loop() {
if (adcChanged) {
__disable_irq(); // so that the value doesn't change as we read it out
String line = "[" + String(millis()) + "] New ADC value: " + String(adcValue);
adcChanged = false;
__enable_irq();
Serial.println(line);
}
}
It consistently produces a new ADC value every 10 milliseconds, which is 100 Hz.
This example still doesn’t use DMA to transfer the value from the ADC peripheral into a e.g. (circular) RAM buffer, but it’s a start. Now, the interrupt processing time for the ADC should be much, much lower than before, because the ADC result is already available.