Issue with timer Input Capture mode in STM32 - c

I would like to trigger a timer with an external signal which happens every 1ms. Then, the timer has to count up to 90us at every rising edge of the external signal. The question is, can I do that using a general purpose timer configured as Input Compare? I don’t understand which callBack to use for this purpose.
I’m using the HAL library and TIM2 peripheral in STM32F446 microcotnroller.
This is how I configured my timer peripheral
void TIMER2_Config(void)
{
TIM_IC_InitTypeDef timer2IC_Config;
htimer2.Instance = TIM2;
htimer2.Init.CounterMode = TIM_COUNTERMODE_UP;
htimer2.Init.Period = 89; //Fck=50MHz, Timer period = 90us
htimer2.Init.Prescaler = 49;
if ( HAL_TIM_IC_Init(&htimer2) != OK)
Error_handler();
timer2IC_Config.ICPolarity = TIM_ICPOLARITY_RISING;
timer2IC_Config.ICPrescaler = TIM_ICPSC_DIV1;
if (HAL_TIM_IC_ConfigChannel(&htimer2, &timer2IC_Config, TIM_CHANNEL_1) != OK)
Error_handler();
}

What you are asking for is well within the features of this peripheral, but you must remember that the HAL library is not capable of using the full features of the chip. Sometimes you have to use access the registers directly (the LL library is another way to do this).
To have the external signal start the timer you need to use trigger mode, not input capture. Input capture means record the value of the timer which is already started. You need to set the field TIMx_CCMRx_CCxS to 0b11 (3) to make the input a trigger, then set the field TIMx_SMCR_TS to select the channel you are using, and TIMx_SMCR_SMS to 0b110 (6) to select start on trigger mode.
Next set up the prescaler and reload register to to count for the 90 microsecond delay that you want, and set TIMx_CR1_OPM to 1 to stop the counter wrapping when it reaches the limit.
Next set TIMx_CR2_MMS to 0b010 to output a trigger on the update event.
Finally you can set the ADCx_CR2_EXTSEL bits to 0b00110 to trigger on TIM2_TRGO trigger output.
This is all a bit complicated, but the reference manual is very thorough and you should read the whole chapter through and check every field in the register description section. I would recommend not mixing the HAL library with direct register access, it will probably interfere with what you are trying to do.

Related

ASF4 Microchip API timer driver reset function

I'm using ASF4 API hal_timer for a ARM Cortex M4. I'm using the timer driver to timing a data sequence.
Why does no reset function exist? I'm using the timer on a TIMER_TASK_ONE_SHOT mode and want to reset it when ever I need to.
I thought a simple
timer_start(&TIMER_0);
timer_stop(&TIMER_0);
would do the trick but does not seem to work.
Is it necessary to re-initialize the timer for each timing event?
I'm probably missing something obvious. Am I approaching this problem incorrectly reason being why the method timer_reset() doesn't exist?
I have no experience of this API, but looking at the documentation it is apparent that a single timer can have multiple tasks on different periods, so resetting TIMER_0 makes little semantic sense; rather you need to reset the individual timer task attached to the timer - of which there may be more than one.
From the documentation (which is poor and contains errors), and the source code which is more reliable:
timer_task_instance.time_label = TIMER_0.time ;
where the timer_task_instance is the struct timer_task instance you want to reset. This sets the start time to the current time.
Probably best to wrap that in a function:
// Restart current interval, return interval.
uint32_t timer_restart( struct timer_descriptor* desc, struct timer_task* tsk )
{
tsk->time_label = desc->time
return tsk->interval ;
}
Then:
timer_restart( &TIMER_0, &timer_task_instance ) ;
Assuming you're using the (edited) example from the ASF4 Reference Manual:
/* TIMER_0 example */
static struct timer_task TIMER_0_task;
static void TIMER_0_task_cb(const struct timer_task *const timer_task)
{
// task you want to delay using non-existent reset function.
}
void TIMER_0_example(void)
{
TIMER_0_task.interval = 100;
TIMER_0_task.cb = TIMER_0_task_cb;
TIMER_0_task.mode = TIMER_TASK_ONE_SHOT;
timer_add_task(&TIMER_0, &TIMER_0_task);
timer_start(&TIMER_0);
}
Instead of resetting, which isn't supported by the API, you could use:
timer_remove_task(&TIMER_0, &TIMER_0_task);
timer_add_task(&TIMER_0, &TIMER_0_task);
which will effectively restart the delay associated with TIMER_0_task.
Under the hood, timer tasks are maintained as an ordered list, in order of when each task will expire, and using the functions provided by the API maintains the list order.

How do I implement a timer which turn a signal on and off (PWG) every few seconds on an Atmega324a microcontroller?

I'm programming an Atmega324a Microcontroller and I'm trying to implement a timer (in this case Timer1) which supposed to make a second led connected to my board blink.
I also need to know how to identify the pin the led is attached to
I've found the data sheet:
http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega164A_PA-324A_PA-644A_PA-1284_P_Data-Sheet-40002070A.pdf
but the details are too technical for me to understand and I don't know where to start looking and most importantly, get to the result, which is the code itself.
Also, What does the ISR function do?
Down below is the current Init_timer function for Timer 0. Is it possible for me to enable both timers at the same time?
static void init_timer(void)
{
// Configure Timer0 for CTC mode, 64x prescaler for 1 ms interval
TCCR0A = _BV(WGM01);
TCCR0B = _BV(CS01) | _BV(CS00);
OCR0A = 124;
TIMSK0 = _BV(OCIE0A);
}
int main(void){
MCUSR = 0;
wdt_disable();
init_pins(); // Reset all pins to default state
init_timer(); // Initialize 1 msec timer interrupt
configure_as_output(LOAD_ON);
configure_as_output(LED1);
configure_as_output(LED2);
sei();
.
.
.
}
ISR(TIMER0_COMPA_vect)
{
static uint16_t ms_count = 0;
ms_count++; // milliseconds counter
if (ms_count == TMP107_POLL_PERIOD)
{
tmp107_command(); // send command to temperature sensor
toggle(LED1); // blink status led
ms_count = 0;
}
}
First of all: StackOverflow is a site to ask questions around source code, it is not a service delivering solutions. Please take the tour, it will help you to get satisfactory answers.
But nevermind, because you're new:
For example, you can implement a timer for a pulse width generator in these steps:
Learn to read data sheets. Nobody can relieve you of this burden.
Learn how to use output pins.
Write some tests to make sure you understand output pins.
Select a timer to measure the clock cycles. Apparently you did that already.
Learn to use this timer. Some timers can generate PWM (pulse width modulated) signals in hardware. However, the output pin is likely to be in a fixed location and the range of possible periods may not meet your requirements.
Write some tests to make sure you understand timers and interrupts.
If the required pulse period is too long for the timer, you can add an extra variable to scale down, for example.
Implement the rest of it.
Also, What does the ISR function do?
This function is called "magically" by hardware when the conditions for the interrupt are met. In the case shown, tmp107_command() and toggle(LED1) are called only every TMP107_POLL_PERIOD times.
Is it possible for me to enable both timers at the same time?
Sure.

how do I fix an interrupt that fires multiple times on EXTI9-5?

I'm working on an STM32F303CC. on this mcu I'm using 6 buttons as interrupts. 5 of these buttons are connected to EXTI15-10 and one to EXTI9-5. the buttons on EXTI15-10 are working perfectly fine. although, when I press the button on EXTI9-5 it fires an incredible amount of times (randomly somewhere between 1500 to 4000 times or so).
it clears the flag, and when there's a breakpoint added to the interrupt function it only fires one time.
I've tried to see if it was a hardware bounce, but there is no bouncing, as checked with an oscilloscope. also it has this problem with both falling and rising edge trigger detection.
I expect to fire the interrupt just one time after a press, instead, it fires a very high amount of times.
what could be the problem?
thanks in advance,
Ruben
GPIO config:
/*Configure GPIO pins : BUTTON_6_Pin BUTTON_5_Pin BUTTON_4_Pin BUTTON_3_Pin
BUTTON_2_Pin BUTTON_1_Pin BUTTON_7_Pin */
GPIO_InitStruct.Pin = BUTTON_6_Pin|BUTTON_5_Pin|BUTTON_4_Pin|BUTTON_3_Pin
|BUTTON_2_Pin|BUTTON_1_Pin|BUTTON_7_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HANDLER1:
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
HANDLERS2:
void EXTI9_5_IRQHandler(void)
{
/* USER CODE BE
GIN EXTI9_5_IRQn 0 */
/* USER CODE END EXTI9_5_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_8);
/* USER CODE BEGIN EXTI9_5_IRQn 1 */
/* USER CODE END EXTI9_5_IRQn 1 */
}
/**
* #brief This function handles EXTI line[15:10] interrupts.
*/
void EXTI15_10_IRQHandler(void)
{
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
/* USER CODE END EXTI15_10_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_10);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_14);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
/* USER CODE BEGIN EXTI15_10_IRQn 1 */
/* USER CODE END EXTI15_10_IRQn 1 */
}
what could be the problem?
Have you considered that it may be caused by bounce?
it clears the flag, and when there's a breakpoint added to the interrupt function it only fires one time.
To me this sounds like you need to debounce the button[1][2] or add analogue filtering.
Can you share the circuit diagram with us ?
.
[1] https://www.allaboutcircuits.com/technical-articles/switch-bounce-how-to-deal-with-it/
[2] https://en.wikipedia.org/wiki/Keyboard_technology#Debouncing
This from community.st.com says...
Make sure it checks and clears the flag early in the routine, then do other things. There is a tail-chain issue if you clear it right before returning.
Since this is a timing/cache issue, increasing compiler optimizations can make the problem appear and stepping in the debugger can make the problem disappear. The HAL can mask the problem due to it's extra code (callbacks etc).
Some solutions:
Insert a synchronization barrier (DSB) before leaving the ISR (mentioned here and here)
Clear the interrupt bit as early as possible (here)
Read/write another location (here)
I see the same issue on newer STM32H7 variants.
I see the same thing when I use s software trigger, and that doesn't bounce. If I chain interrupts, and trigger this from inside a higher priority interrupt, I get a single interrupt provided that I set then immediately clear the SWIER bit in the calling routine.
If instead I software trigger this from the main code, I can't set then clear the SWIER bit since the interrupt will occur before I can clear it. I then get two calls, even if I repeatedly clear the SWIER bit and write a 1 to the PR bit in the handler.
I haven't found a solution.

Change timer period while running application STM32F4 [C]

I want change my timer period while running program
I make different measures requiring different timer periods.
After initialization:
TIM_TimeBaseInitStructure.TIM_Period = period - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 8399+1;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
In main function I set: period = 10000;
Then, I receive new value via UART and try to set another value:
arr3[0] = received_str[11];
arr3[1] = received_str[12];
arr3[2] = received_str[13];
arr3[3] = received_str[14];
arr3[4] = received_str[15];
arr3[5] = '\0';
per = atoi(arr3);
period = per;
But timer period don't changes. How can I do it?
This is the problem with HAL libraries. People who use them have no clue what is behind it.
What is the timer period?
It is the combination of the PCS (prescaller) and ARR (auto reload register). The period is calculated as (ARR + 1) * (PSC + 1) / TimerClockFreq.
When you try to change the period when the timer is running you need to make sure that it is done in the safe moment to prevent glitches. The safest moment is then the UG event happens.
You have two ways of achieving it:
UG interrupt. In the interrupt routine if the ARR or PSC have changed - you should update the register. Bare in mind that the change may happen in the next cycle if the registers are shadowed.
Using the timers DMA burst more. It is more complicated to config - but the hardware take care of the registers update on the selected event. The change is instant and register shadowing does not affect it. More details read RM chapter about the timers DMA burst mode.
If you want to use more advanced hardware features forget about the HAL and program it using the bare registers having the full control.
At run time by updating auto reload register we can change the timer value.
I have done this practically.
TIM5->ARR = Value; //This is for timer5

Windows: Disabling CPU idle C-states from kernel-mode driver

I'm writing an audio device driver which needs to process device interrupts in real-time. When the CPU enters C3 state, interrupts are delayed, causing problems to the driver. Is there a way for the driver to tell the OS not to enter idle C-states?
What I've found is that idle C-states can be disabled from user-space:
const DWORD DISABLED = 1;
const DWORD ENABLED = 0;
GUID *scheme;
PowerGetActiveScheme(NULL, &scheme);
PowerWriteACValueIndex(NULL, scheme, &GUID_PROCESSOR_SETTINGS_SUBGROUP, &GUID_PROCESSOR_IDLE_DISABLE, DISABLED);
PowerSetActiveScheme(NULL, scheme);
However, it is a global setting, which can be overwritten by the user or another application (e.g. when the user changes the Power Plan).
What I need is something like PoRegisterSystemState, but not for S- and P-states, but for C-states. (ref: https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/preventing-system-power-state-changes)
Is there any way to achieve this?
=====
It turns out there isn't a supported way to disable idle C-states from kernel space, and there isn't a service in user space to provide common API to do this.
The way to control C-states is from "Processor Power Management" in "Change advanced power settings" dialog, through registry, or via C API PowerWriteACValueIndex / PowerWriteDCValueIndex.
The original problem was delayed interrupts in all but C1 idle state, so I needed to disable both C2, C3 and deeper idle states. The problem with disabling all idle C-states, including C1 (as shown in the example code PowerWriteACValueIndex(NULL, scheme, &GUID_PROCESSOR_SETTINGS_SUBGROUP, &GUID_PROCESSOR_IDLE_DISABLE, DISABLED)) is that the CPU usage is reported as 100%, and some applications (DAWs) get confused.
The solution for my problem is to disable all but C1 idle state, which can be done by setting the following values in the Processor Power Management:
- Processor idle threshold scaling -> Disable scaling;
- Processor idle promote threshold -> 100%;
- Processor idle demote threshold -> 100%.
Perhaps I'll create a service that does just that, that will use the PowerWriteACValueIndex / PowerWriteDCValueIndex API.

Resources