I generated my code from STM32CubeMx and wanted to generate a update event every 1µs. I work with the internal clock at 48MHz, which should be with Prescaler:0 and Autoreload:47 result to 1µs.
I use a STM32F030 with TrueStudio V.9.0.0
generated code
/* TIM17 init function */
static void MX_TIM17_Init(void)
{
LL_TIM_InitTypeDef TIM_InitStruct;
/* Peripheral clock enable */
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_TIM17);
/* TIM17 interrupt Init */
NVIC_SetPriority(TIM17_IRQn, 3);
NVIC_EnableIRQ(TIM17_IRQn);
TIM_InitStruct.Prescaler = 0;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 47;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
TIM_InitStruct.RepetitionCounter = 0;
LL_TIM_Init(TIM17, &TIM_InitStruct);
LL_TIM_EnableARRPreload(TIM17);
}
I added in my init:
LL_TIM_EnableIT_UPDATE(TIM17);
LL_TIM_EnableCounter(TIM17);
In the IRQ_Handler i toggle a PIN:
void TIM17_IRQHandler(void)
{
/* USER CODE BEGIN TIM17_IRQn 0 */
LL_GPIO_TogglePin(LED_D2_2_GPIO_Port,LED_D2_2_Pin);
/* USER CODE END TIM17_IRQn 0 */
/* USER CODE BEGIN TIM17_IRQn 1 */
/* USER CODE END TIM17_IRQn 1 */
}
After flashing my device with the code it generates a Signal with Frequency 889kHz with Pulsewidth of 564ns measured with Oscilloscope. Changes on Prescaler or Autoreload does not affect this output, it stays right away at T_Pulse=564ns or F=889kHz.
Any idea what I am missing here?
Register output from debugging:
CR1:0x81 CR2:0
DIER:0x01 SR:0x03
CCMR1_O/I:0 CCER:0
PSC:0 ARR:0x2f
RCR:0 CCR1:0
BDTR:0 DCR:0
DMAR:0x81
The solution was the clearance of the FLAG UIE in TIM17->SR in the IRQ_Handler.
I was stuck permanently in the IRQ routine.
Related
My board is the NUCLEO-H743ZI2
I configured TIM4,CH2 as PWM such that TIM4 output should be HI for half a second and LO for half a second. Here is the block.ioc clock and pin configuration
I auto generated the code and tried to add my own to toggle the LED with the H/L of PWM. What I want to do is this:
if(timer_4 == HIGH) LED_state = !LED_state
Here is the actual code:
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_ETH_Init();
MX_USART3_UART_Init();
MX_USB_OTG_FS_PCD_Init();
MX_DAC1_Init();
MX_TIM3_Init();
MX_TIM4_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_2);
uint16_t timer_init_val = __HAL_TIM_GET_COUNTER(&htim4);
uint16_t timer_poll;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
timer_poll = __HAL_TIM_GET_COUNTER(&htim4);
if(timer_poll - timer_init_val >= 500) {
HAL_GPIO_TogglePin(GPIOB,LD1_Pin);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
I know that this most likely does not work. In fact. I was confused because replacing the >=500 with <=100 actually made it seem like it was working..but the LED just blinks at seemingly random intervals.
I have tried to test if !timer{..} (timer reached 0) to toggle the LED but none of my methods have seemed to work.
Am I misunderstanding something about timers? Is there something super obvious I am missing? It does not seem like it would be difficult to blink an LED with a 1sec PWM
What you have written does the following:
for 500 counts of the timer do nothing
after that turn the LED repeatedly on and off (faster than the eye can see) until the timer wraps
when the timer wraps (65.536 seconds?) goto (1).
You can't really do this with GPIO_TogglePin because you will call it many many times during the same value of the counter.
Try something like:
if (((timer_poll - timer_init_val) % 1000) < 500)
{
HAL_GPIO_WritePin(GPIOB, LD1_Pin, 0);
}
else
{
HAL_GPIO_WritePin(GPIOB, LD1_Pin, 1);
}
But this still isn't completely clean when the timer wraps. To sort that out you can adjust the period of the timer to be a multiple of the period of your sequence.
I've been trying to configure & run hardware timer over SAML21 MCU to generate a 100ms delay i.e. ISR is supposed to hit at every 100ms. But it is observed that after starting the timer ISR is hitting at every 10us and changing the Prescaler & Compare register values isn't creating any difference in the 10us interval. Please review my code and let me know where I'm doing wrong.
I'm trying to configure Timer1(TC1) in 16bit mode, using GCLK_GENERATOR_1 as its clock source running at 8MHz frequency(CPU Main Clock:16MHz). The timer is expected to cause overflow interrupt every 100ms.
TcCount16 *tc_hw1 = NULL; /* Pointer to TC1 hardware registers Initilized later */
void init_timer1(void)
{
struct tc_module tc_inst1;
struct tc_config conf_tc1;
tc_get_config_defaults(&conf_tc1);
conf_tc1.clock_source = GCLK_GENERATOR_1;
conf_tc1.clock_prescaler = TC_CLOCK_PRESCALER_DIV64; /* 8MHz/64 = 125KHz*/
conf_tc1.reload_action = TC_RELOAD_ACTION_GCLK;
conf_tc1.counter_size = TC_COUNTER_SIZE_16BIT;
conf_tc1.count_direction = TC_COUNT_DIRECTION_UP;
conf_tc1.counter_16_bit.value = 0x0000;
/** Rest of the settings are used as defaults **/
while (tc_init(&tc_inst1, TC1, &conf_tc1) != STATUS_OK){
}
tc_set_top_value(&tc_inst1, 12500); /* Set counter compare top value */
/* Enable interrupt & Set Priority */
tc_hw1 = &(tc_inst1.hw->COUNT16); /* Initialize pointer to TC1 hardware register */
tc_hw1->INTENSET.reg |= TC_INTFLAG_OVF; /* Enable Overflow Interrupt */
NVIC_SetPriority(TC1_IRQn, 2);
NVIC_EnableIRQ(TC1_IRQn);
tc_enable(&tc_inst1); /*Start The TIMER*/
}
void TC1_Handler(void)
{
if((tc_hw1->INTFLAG.reg) & (TC_INTFLAG_OVF))
{
port_pin_toggle_output_level(PIN_PB03);
}
system_interrupt_clear_pending(SYSTEM_INTERRUPT_MODULE_TC1);
}
Debugger Information: I can see that the timer register is configured correctly but the COUNT register is not incrementing itself every time I pause to capture the debug info it shows only 0x0000 values.
Please help. Thanks!
I resolved the issue actually it's kind of mandatory to clear the timer OVF bit in the INTFLAG register. So the interrupt handler should've been like this:
void TC1_Handler(void)
{
if((tc_hw1->INTFLAG.reg) & (TC_INTFLAG_OVF))
{
tc_hw1->INTFLAG.reg = TC_INTFLAG_OVF; /*Clears the flag by writing 1 to it*/
port_pin_toggle_output_level(PIN_PB03);
}
system_interrupt_clear_pending(SYSTEM_INTERRUPT_MODULE_TC1); /*Not necessarily needed*/
}
Every time I enable the timer it instantly activates the Interrupt. No matter how I try to prescale it. only ARR seems to work but 16 bit with 0,5MHz clock gives me ~160ms maneuver.
#define SYSCLK_FREQ 524288
void timer_init(uint16_t detonation_delay_ms);
int main(void){
RCC->APB2ENR = RCC_APB2ENR_TIM22EN;
TIM22->PSC = (SYSCLK_FREQ/1000)-1;
NVIC_EnableIRQ(TIM22_IRQn);
NVIC_SetPriority(TIM22_IRQn,4);
}
/* calling function */
timer_init(65535);
/* calling function */
void timer_init(uint16_t detonation_delay_ms){
TIM22->CR1 &= ~TIM_CR1_CEN;
TIM22->SR=0;
TIM22->ARR = detonation_delay_ms;
TIM22->CR1 |= TIM_CR1_CEN;
TIM22->SR = 0;
}
void TIM22_IRQHandler(void){
TIM22->CR1 &= ~TIM_CR1_CEN;
TIM22->SR=0;
GPIOB->BSRR = GPIO_BSRR_BS_7;
}
I wish that calling function makes the timer tick till the called value in milisec. But no matter how I set it up it ends up with no scaled timer and instant interrupt after calling it.
Correct way to do it?
TIM22->DIER = TIM_DIER_UIE;
TIM22->ARR = 65535-detonation_delay_ms;
TIM22->EGR = TIM_EGR_UG;
TIM22->CR1 |= TIM_CR1_OPM | TIM_CR1_CEN;
TIM22->SR=0;
Do not delay in interrupts
you enable the timer then set the ARR which is wrong - first set ARR and prescaller, then generate the UG event using the EGR register, then enable the timer.
Works like a charm. Just because I got help here will describe for future interested people.
The way to get the interrupt working for the timers is to generate interrupt 'by hand' once. It's okay to do it because you can control what's happening during the interrupt by a single 'if'.
/* TIMER Enable */
RCC->APB2ENR = RCC_APB2ENR_TIM22EN;
I had a problem with the above declaration, dunno why but it wasn't working after declaring some more modules before it. Had to put it on higher on the list. Manual does not say why it happened.
/* Configure TIM22 interrupt */
TIM22->PSC = (SYSCLK_FREQ/1000)-1; /* Prescaler TIMERA 22 */
TIM22->DIER = TIM_DIER_UIE;
TIM22->CNT = 0;
TIM22->EGR = TIM_EGR_UG;
NVIC_EnableIRQ(TIM22_IRQn); /* Zalaczenie przerwania od TIMER'a */
NVIC_SetPriority(TIM22_IRQn,4); /* Ustawienie priorytetu przerwania od TIMER'a */
The prescaler meant to be 1ms so I divided 524288 of my core speed. Then enabling the interrupt, resetting the counter to make sure it starts from 0 and then manually generating the interrupt. And it does the interrupt 'loop' once but with a single 'if' and variable, I can control what it does.
So what I do, I am calling a function that set the clock and enables the count inside another function enable = 1; timer_init(ms);
Then comes the function call
void timer_init(uint16_t ms)
{
TIM22->CNT = 65535-ms;
TIM22->CR1 |= TIM_CR1_CEN;
}
void TIM22_IRQHandler(void)
/* Up-Counter milisec */
{
if(TIM22->CR1 & TIM_CR1_CEN) {TIM22->CR1 &= ~TIM_CR1_CEN;}
if(TIM22->SR & TIM_SR_UIF) {
if(enable == 1){
GPIOB->BSRR = GPIO_BSRR_BS_7;
}
}
TIM22->SR=0;
}
And the interrupt.
Thanks a lot!Have fun with registers!
I have been trying exhaustively to program my STM32F7xx microcontroller to use DMA to transmit to UART. Three things are going on and I cannot explain or understand why this is happening, and hope somebody can help me out with this issue.
In the main while loop, I am printing three interrupt status flags. These flags are set if the corresponding ISR has been called. I added this to check if the ISR was called without adding blocking statements in the ISRs. None of the interrupts, however, are called.
The DMA only transmits 1 sequence of 513 bytes. When I modify the while loop in my main to only contain HAL_UART_Transmit_DMA(&handleUart4, dmxBuffer, 513);, nothing changes, the function is only called/executed once.
In the while loop, I print the status of the ISR flags. After printing, the CPU stops/locks/shutdown/exits the while loop. At first, I thought I was congesting the AHB by using the UART to my terminal and the UART for the DMA controller. I disabled my terminal, and used LEDs, this didn't change anything.
Currently, the only running hypothesis I have is that my CPU somehow has interrupts disabled.
#include "stm32f7xx.h"
#include "mbed.h"
uint8_t dmxBuffer[513];
volatile bool irqA = false;
volatile bool irqB = false;
volatile bool irqC = false;
Serial pc(USBTX, USBRX, 115200);
UART_HandleTypeDef handleUart4;
DMA_HandleTypeDef handleDma;
void initialiseGPIO()
{
GPIO_InitTypeDef GPIO_InitStruct;
__GPIOA_CLK_ENABLE();
/**UART4 GPIO Configuration
PA0 ------> USART4_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
void initialiseDMAController()
{
/* DMA controller clock enable */
__DMA1_CLK_ENABLE();
/* Peripheral DMA init*/
handleDma.Instance = DMA1_Stream4;
handleDma.Init.Channel = DMA_CHANNEL_4;
handleDma.Init.Direction = DMA_MEMORY_TO_PERIPH;
handleDma.Init.PeriphInc = DMA_PINC_DISABLE;
handleDma.Init.MemInc = DMA_MINC_ENABLE;
handleDma.Init.PeriphDataAlignment = DMA_MDATAALIGN_BYTE;
handleDma.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
handleDma.Init.Mode = DMA_NORMAL;
handleDma.Init.Priority = DMA_PRIORITY_MEDIUM;
handleDma.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&handleDma);
//Define
__HAL_LINKDMA(&handleUart4,hdmatx,handleDma);
/* DMA interrupt init */
HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
}
void initialiseUart()
{
__UART4_CLK_ENABLE();
handleUart4.Instance = UART4;
handleUart4.Init.BaudRate = 250000;
handleUart4.Init.WordLength = UART_WORDLENGTH_8B;
handleUart4.Init.StopBits = UART_STOPBITS_2;
handleUart4.Init.Parity = UART_PARITY_NONE;
handleUart4.Init.Mode = UART_MODE_TX;
handleUart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
handleUart4.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&handleUart4);
/* Peripheral interrupt init*/
HAL_NVIC_SetPriority(UART4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(UART4_IRQn);
}
/* This function handles DMA1 stream4 global interrupt. */
void DMA1_Stream4_IRQHandler(void)
{
irqA = true;
HAL_DMA_IRQHandler(&handleDma);
}
/* This function handles the UART4 interups */
void UART4_IRQHandler(void)
{
irqB = true;
HAL_UART_IRQHandler(&handleUart4);
}
//HAL_UART_TxCpltCallback
/* This callback function is called when the DMA successfully transmits all scheduled bytes. */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
irqC = true;
}
int main(void)
{
/* Reset of all peripherals */
HAL_Init();
//Initialise peripherals
initialiseGPIO();
initialiseDMAController();
initialiseUart();
//Fill buffer with test data
for (int x = 0; x < 100; x++)
{
dmxBuffer[x] = x;
}
//Now instruct the UART peripheral to transmit 513 bytes using the DMA controller.
HAL_UART_Transmit_DMA(&handleUart4, dmxBuffer, 513);
while(1)
{
pc.printf("irqA: %d - irqB: %d - irqC: %d\r\n", irqA, irqB, irqC);
wait_ms(100); //Wait to see if any of the interupt handlers / callback functions are called
//Check if all bytes are sent, if so, retransmit
if (irqC)
{
irqC = false;
HAL_UART_Transmit_DMA(&handleUart4, dmxBuffer, 513);
}
}
}
Check the interrupt vector table
Verify that the vector table does indeed contain a pointer to your handler function, not to some generic placeholder with an infinite loop (that makes the program hang).
Search for the name of the interrupt handler function in the entire source code. Is there any other object or #define that could interfere with the function definition, or the vector table entry?
Change the name of the handler, both the function definition and the vector table entry. Does it still compile? When not, does adding extern "C" to the function prototype help?
Look up the address of the handler in the .map file, and the offset entry for the interrupt in the vector table provided in the Reference Manual (Nested vectored interrupt controller (NVIC) / Interrupt and exception vectors). Check the contents of the compiled program binary file at the given offset. Does it match the address found in the .map file + 1?
Check the value at NVIC->VTOR plus the offset while running the program. It should be the same as the one found in the binary. If not, see that the VTOR register is set to the beginning of the right vector table.
Can a interrupt flag be set by the code as in the example below or is that line just an error of thinking? This is just the main function. Below this code snipet are the interrupt it self, is it correct and nessasary to clear the interrupt flag in the end of the code?
if(duty != (uint8_t) (SOFT_PWM_PERIOD - 1))
{
// Request an immediate interrupt if the timer counter has
// already the initial period. This helps minimize glitches
// when changing duty cycles
if(duty < TMR4)
PIR3bits.TMR4IF = 1;
// Finally (re-)start the timer
T4CON =
0 << 3 | // 1x post-scaler
1 << 2 | // Active
2 /* << 0 */; // 16x pre-scaler
IPR3bits.TMR4IP = 1; // TMR4 Overflow Interrupt Priority bit High
PIE3bits.TMR4IE = 1; // TMR4 Overflow Interrupt Enable bit
}
The Intrrupt code ->
// Deal with PWM timer interrupts. Add this to the high-priority interrupt handler.
void SoftPWM_Interrupt(void)
{
volatile uint8_t _SoftPWM_Toggle; // Is this variable really accessed by both the ISR and mainline functions? (C.G)
/* Has a flank been reached yet? */
if(PIR3bits.TMR4IF)
{
/* Alternate between the low and high periods */
PR4 ^= _SoftPWM_Toggle;
/* Try to deal gracefully with the new period already having been reached. */
/* The hardware timer works by checking if TMR4 = PR4 when it is time to increase */
/* counter, in which case TMR4 is reset instead. Thus if is already TMR4 > PR4 due to */
/* interrupt latency then we've missed the period and an extra interrupt is needed. */
/* First acknowledging the flag and then conditionally setting it is necessary to */
/* avoid a race between reading TMR4 and changing the flag. */
/* Finally the the TMR4 > PR4 test is actually implemented as skip if TMR4 < PR4 + 1 */
/* but the increment cannot overflow since the interrupt won't be used for 0% or 100% */
/* duty cycles */
PIR3bits.TMR4IF = 0;
_asm
INCF PR4,0,ACCESS
CPFSLT TMR4,ACCESS
_endasm
/* if(TMR4 > PR4) */
PIR3bits.TMR4IF = 1; // Cant only the harware set this flag? (C.G)
/* Finally toggle the output pin */
SOFT_PWM_PIN ^= 1;
/*Important?*/
PIR3bits.TMR4IF = 0;
}
}
Yes you can set an interrupt flag by soft. But IMO it is not really a good practice to do it...
If you really want the behavior of your ISR to be executed in the normal context, why don't you externalize your ISR code in a function that you can call in your main function?
About the interrupt flag, if you don't clear it, the ISR will be executed in loop and you will never go back to your main program.