I am using the STM32F429I-Discovery board, the board has a pushbutton connected to PA0, which in turn is connected to External Interrupt Line 0 (EXTI0).
Using the HAL Libraries, I can toggle a LED either on the Falling Edge or on the Rising edge using external interrupts. Eg, the LED either changes state as soon as I press the pushbutton, or only once I release the pushbutton.
What I want to do is to Interrupt on the Rising edge, start a timer, and then interrupt on the falling edge again, to stop the timer. I have got no idea how to achieve this?
There is also an option to trigger on both rising and falling edges. I do not know if there should only be one interrupt, and I then figure out if it was a rising or falling edge (probably by accessing the registers directly), or should there be two configured interrupts - One as Rising Edge and one as Falling Edge?
Below are the external interrupt code; firstly to set a GPIO up as an external interrupt, then to detect the interrupt and then to handle the interrupt (callback).
static void EXTILine0_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOA clock */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* Configure PA0 pin as input floating */
GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable and set EXTI Line0 Interrupt to the lowest priority */
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
/* Clears the interrupt after calling this I think */
void EXTI0_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(KEY_BUTTON_PIN);
}
/**
* #brief EXTI line detection callbacks
* #param GPIO_Pin: Specifies the pins connected EXTI line
* #retval None
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == KEY_BUTTON_PIN)
{
/* Toggle LED3 */
BSP_LED_Toggle(LED3);
}
}
Can someone please point out how I can achieve this?
What you're looking for is called "input capture" and can be achieved with a timer directly without the need for external interrupts. On the STM32F429 PA0 is internally mapped to Timer 2 Channel 1.
The sConfigIC structure is responsible for handling input capture related configuration stuff.
The initialization looks something like this:
/* TIM2 init function */
void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
TIM_IC_InitTypeDef sConfigIC;
/* Peripheral clock enable */
__TIM2_CLK_ENABLE();
/* Peripheral interrupt init*/
HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
/**TIM2 GPIO Configuration
PA0/WKUP ------> TIM2_CH1
*/
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0xFFFFFFFF;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim2);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);
HAL_TIM_IC_Init(&htim2);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);
/* Input capture stuff HERE
Change polarity as needed */
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1);
}
And further the interrupt function:
/* IRQ */
void TIM2_IRQHandler(void)
{
// Check for interrupt flags here
}
Within the interrupt you've got to check for the CC1IF flag. The timer value gets stored in the capture and compare register called CCR1.
/edit
Don't forget to start the timer and input capture channel with:
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
This enables the timer, the according channel for input capture and the interrupt.
First, you have to setup a timer, e.g.
TIM_HandleTypeDef htim1;
void TIM1_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
htim1.Instance = TIM1;
htim1.Init.Prescaler = 71;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 65535;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
HAL_TIM_Base_Init(&htim1);
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);
}
Code is taken from an STM32F1, so maybe you have to adapt a little, just have a look in the HAL handbook.
The interrupt for rising and falling edge is the same, so you have to check the state of the pin in your interrupt handler.
To start the timer
HAL_TIM_Base_Start(&htim1);
and to stop
HAL_TIM_Base_Stop(&htim1);
the counter value is stored in
TIM1->CNT
A bit of background: Using the STM32F429I the code below is to display how long you press the blue user button for, the count is given as milliseconds. The PCB has a hardware debounce circuit so the fastest response I was able to get is around 50ms.
As stated earlier PA0 is connected to EXTILine0.
I set the PA0 line up to interrupt on both rising and falling edges.
static void EXTILine0_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOA clock */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* Configure PA0 pin as input floating */
GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING_FALLING;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Pin = GPIO_PIN_0;
HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Enable and set EXTI Line0 Interrupt to the lowest priority */
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
When a interrupt then occurs, I read the amount of counts that are currently stored in HAL_GetTick(), this function clocks every 1 millisecond. I also read whether the pin is high or low to determine if the interrupt was on a falling or on a rising edge.
uint16_t beginCount;
uint16_t stopCount;
void EXTI0_IRQHandler(void)
{
uint16_t var;
var = HAL_GetTick();
uint16_t calcCount = 0;
unsigned char buffer[10];
BSP_LCD_Clear(LCD_COLOR_WHITE);
// The Pin Goes high when the pushbutton is pressed.
if (HAL_GPIO_ReadPin(GPIOA, KEY_BUTTON_PIN) == 0x01)
{
beginCount = 0;
beginCount = var;
BSP_LCD_SetTextColor(LCD_COLOR_GREEN);
BSP_LCD_DisplayStringAtLine(6, "Rising Edge" );
}
else
{
stopCount = 0;
stopCount = var;
BSP_LCD_SetTextColor(LCD_COLOR_RED);
BSP_LCD_DisplayStringAtLine(7, (uint8_t*)"Falling Edge");
// Calculate Counts and covert to seconds - What if the counter overflows?
calcCount = stopCount - beginCount;
sprintf(buffer, "%d", calcCount); // Convert the integer to string and put it in variable buffer
BSP_LCD_DisplayStringAtLine(8, (&buffer) ); // Display the value stored at buffer's location
}
HAL_GPIO_EXTI_IRQHandler(KEY_BUTTON_PIN);
}
Lastly the interrupt callback triggers and just toggles the LED on the board.
The counter can only go to something like 65 seconds, after that it would overflow and my 'calculated' time would be incorrect. This methods works OK for what I intend to do with it. I want to measure 20-300 milliseconds with a accuracy of a few milliseconds. I still have to put in a catch for if the timer overflows between two measurements.
Anything fundamentally wrong with this approach? I am not very experienced with C, and not at all with STM32.
Related
I'm building a tachometer using a hall sensor and I tied it to PE13 or Tim1 Channel 3.
Everything seems to be working as the interrupt TIM1_CC_IRQHandler fires at the expected rate, but whenever I run LL_TIM_IC_GetCaptureCH3(TIM1) I only get 0 back from the function call.
Did I miss something in my setup code? Or am I misunderstanding how to use the timer and the LL drivers?
Setup code:
// Enable Timer and Peripheral Clocks
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOE);
// Ensure Pins are Configured Correctly
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/**TIM1 GPIO Configuration
PE13 ------> TIM1_CH3
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_13;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
LL_GPIO_Init(GPIOE, &GPIO_InitStruct);
// Register FanTach CLI Comand
FreeRTOS_CLIRegisterCommand( &xFANTACH );
// Configure Timer for Capture Compare
LL_TIM_InitTypeDef TIM_InitStruct = {0};
TIM_InitStruct.Prescaler = 0;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 0;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
TIM_InitStruct.RepetitionCounter = 0;
LL_TIM_Init(TIM1, &TIM_InitStruct);
LL_TIM_DisableARRPreload(TIM1);
LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_SetTriggerOutput(TIM1, LL_TIM_TRGO_RESET);
LL_TIM_DisableMasterSlaveMode(TIM1);
LL_TIM_IC_SetActiveInput(TIM1, LL_TIM_CHANNEL_CH3, LL_TIM_ACTIVEINPUT_DIRECTTI);
LL_TIM_IC_SetPrescaler(TIM1, LL_TIM_CHANNEL_CH3, LL_TIM_ICPSC_DIV1);
LL_TIM_IC_SetFilter(TIM1, LL_TIM_CHANNEL_CH3, LL_TIM_IC_FILTER_FDIV1);
LL_TIM_IC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH3, LL_TIM_IC_POLARITY_RISING);
/* Set the pre-scaler value to have TIM2 counter clock equal to 10 MHz */
// To get TIM1 counter clock at 10 MHz, the Prescaler is computed as following:
// Prescaler = (TIM1CLK / TIM2 counter clock) - 1
// Prescaler = (TIM1CLK / 10 MHz) - 1
LL_TIM_SetPrescaler(TIM1, __LL_TIM_CALC_PSC(get_pclk2_freq(), 10000000));
//Enable Interrupts
NVIC_SetPriority(TIM1_UP_TIM10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), PreemptPriority, SubPriority));
NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
NVIC_SetPriority(TIM1_CC_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), PreemptPriority, SubPriority));
NVIC_EnableIRQ(TIM1_CC_IRQn);
/* Enable Capture Compare channel 3 for Timer 1 */
LL_TIM_EnableIT_CC3(TIM1);
//Start Input Capture
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH3);
//Enable Counter
LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP);
LL_TIM_EnableCounter(TIM1);
}
If anyone stumbles upon this with the same problem, I solved this in two steps:
LL_TIM_DisableARRPreload should be LL_TIM_EnableARRPreload
TIM_InitStruct.Autoreload needs to be set to your max counter value
This allows the timer to automatically reset and gives the up counter a maximum value. For my work I set it to 0xFFFF.
I have old programmable timer/ counter (PD71054 - count rate is set to 3Mhz), and now I would like to program a timer in STM32 (STM32F103RET6) to replace PD71054. I need to measure the duration of the square wave (PA0 pin), I found information about on of timer function - capture, but I do not know how to use it. And unfortunately I did't find good examples how to use it (SPL library). I tried to checking the edges of the signal. If a rising edge is detected, TIM5 will be started, if a falling edge is detected TIM5 will be stopped and the counted value will be saved. But I don’t know if this solution is good and how to set an interrupt for both edges of signal and have two different functions to handle for each of the slopes. signal example
int main(void)
{
TIM5_Config();
NVIC_Config ();
//IWDG_ReloadCounter();
RCC_Config();
RCC_APB1PeriphClockCmd(RCC_APB1PeriphClockCmd, ENABLE);
while(1){
}
}
RCC_Config()
{
BKP_DeInit();
// Reset RCC
RCC_DeInit ();
// HSI ON
RCC_HSICmd(ENABLE);
// Wait for HSI
while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET){};
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
FLASH_SetLatency(FLASH_Latency_2);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK2Config(RCC_HCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div1);
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
}
void IWDG_Config (void) {
// watchdoga IWGD
IWDG_WriteAccessCmd (IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_4); // 40kHz/4 = 10kHz
IWDG_SetReload(0x28A); // 65ms
IWDG_Enable(); // Start IWDG oraz LSI
}
void TIM5_IRQHandler(void)
{
TIM_Cmd(TIM5, ENABLE);
}
void TIM5_Config (void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
// Ustawienia podstawy czasu TIM5
TIM_TimeBaseStructure.TIM_Period= 0xFFFF; //65535
TIM_TimeBaseStructure.TIM_Prescaler = 23; // fclk = 72MHz/24 =okolo 3MHz
TIM_TimeBaseStructure.TIM_ClockDivision = 1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
// Zrodlo przerwania kanal 1
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);
TIM_Cmd(TIM5,ENABLE);
// TIM5_CH1, Rising edge, no filter
TIM_TIxExternalClockConfig(TIM5, TIM_TIxExternalCLK1Source_TI1, TIM_ICPolarity_Rising, 0x00);
TIM5_ICInitStructure.TIM_Channel=TIM_Channel_1;
TIM5_ICInitStructure.TIM_ICFilter=0;
TIM5_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
TIM5_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;
TIM5_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
}
void NVIC_Config (void)
{
NVIC_InitStructure.NVIC_IRQChannel=TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority= 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
Sorry I dont have time to go into details about TIMER capture methods.I can offer easer way to do this, at least you can try. Set PA0 pin as EXTI on rising, falling edges. So IRQ will be generated on rising and falling edges. Also setup TIM1 or TIM2 , basically 32bit timer with needed precision, just start timer and its counter. So at rising edge, you can easily reset timer TIM1->CNT = 0 it will be your start time when SIGNAL rise. And then when falling edge appear fetch TIM->CNT . Thats all. Just don't forget to control extra flags (own created variables) wich will help to separate rising and falling edges. Also you could even count those pulses in those events, but its not reliable way.
This is only a snippet, abstract, it should help to understand an idea.
void enable_timer(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
TIM_TimeBaseInitTypeDef timerInitStructure;
timerInitStructure.TIM_Prescaler = 48;
timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
timerInitStructure.TIM_Period = 0xFFFFFFFF; //32 bit timer , precision 1us
timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM1, &timerInitStructure);
TIM_Cmd(TIM1, ENABLE);
}
EXTI handler:
void EXTI0_1_IRQHandler (void)
{
if (EXTI_GetITStatus(EXTI_LINE0) != RESET){
EXTI_ClearITPendingBit(EXTI_LINE0); //clear the irq flag
if(rising_edge)
TIM1->CNT = 0;
if(falling_edge)
fetcher = TIM1->CNT; // here you get your pulse duration time in us
}
}
I'm trying to generate a square wave / quadrature signal ( 2 square wave with 90 degres offset). The board is a STM32F103C8
I'm not focused on the frequency yet, I just want to have a clean quadrature signal.
My code is not complicated at the moment and here are the 2 mains fonctions in order to initialize the timer:
void init_SW()
{
GPIO_InitTypeDef GPIO_InitStruct;
// Step 1: Initialize GPIO as input for rotary encoder
// PB7 (TIM4_CH2) (encoder pin A), PB6 (TIM4_CH1) (encoder pin B)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
// Step 2: Setup TIM4 for encoder input
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructInit (&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = 3;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_Cmd(TIM4, ENABLE);
TIM_TimeBaseInit (TIM4, &TIM_TimeBaseStructure);
/*
TIM4->CCR3=0 ;
TIM4->CCR4=(TIM4->ARR+1)/2;
TIM4->CCER;
*/
}
and:
void timer_ccr_init (void)
{
TIM_OCInitTypeDef TIM_OCInitStructure;
/* always initialise local variables before use */
TIM_OCStructInit (&TIM_OCInitStructure);
/* Common settings for all channels */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
/* Channel1 */
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OC1Init (TIM4, &TIM_OCInitStructure);
/* Channel2 - 90 degres after*/
TIM_OCInitStructure.TIM_Pulse = 1;
TIM_OC2Init (TIM4, &TIM_OCInitStructure);
TIM4->CCER;
}
Do you have any idea where I messed up?
I'm not quite familiar with the F1 line or the Standard Peripheral Library, but I think this is wrong
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
To connect the timer output signals to the actual pins, you'd rather set them to Alternate Function mode.
This line is at the wrong place:
TIM_Cmd(TIM4, ENABLE);
Enable the timer at the end, when all channels are set up. Because you are not using the prescaler, and the period is quite short, one channel might do a few cycles while you'd be still setting up the other. However, this would be no problem, if you'd enable the channels simultaneously (this is possible at the register level, but STL might not be able to do it), but
TIM4->CCER;
on a line alone has no effect (reads the register and discards the value). You should actually set some bits in this register to enable some channels.
/* Channel2 - 90 degres after*/
TIM_OCInitStructure.TIM_Pulse = 1;
Because the period is 4 cycles (0 to 3), it would result in a 45 degrees offset. Channel 1 toggled in cycle 0, channel 2 in cycle 1, nothing happens in cycles 2 and 3.
The code below uses the capture compare feature of TIM1 channel 3 to capture rising edge on PE10, but its not working, the interrupt handler its not called. I am not that good at this embedded stuff, so can somebody tell me if I am settings this correctly ?
#include "STM32/stm32f4xx_tim.h"
void TIM1_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* TIM1 clock enable */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
/* GPIOA clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
/* TIM1 channel 3 pin (PE.10) configuration */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOE, &GPIO_InitStructure);
/* Connect TIM pins to AF2 */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource10, GPIO_AF_TIM1);
/* Enable the TIM1 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_ICInit(TIM1, &TIM_ICInitStructure);
/* TIM enable counter */
TIM_Cmd(TIM1, ENABLE);
/* Enable the CC3 Interrupt Request */
TIM_ITConfig(TIM1, TIM_IT_CC3, ENABLE);
}
void TIM1_CC_IRQHandler(void)
{
// .................
}
int StartCapture()
{
TIM1_Config();
while(1); /* Infinite loop */
}
If your TIM1_CC_IRQHandler() is getting called ONCE, then Dracog71's answer is correct. However, if your IRQHandler() is never firing, then there's something wrong in the initialization.
Here's a list of things you have to get right or the IRQ won't fire.
1) Make sure that the GPIO[A:E] clock is enabled for the pin you are using.
2) Likewise, make sure the clock is enabled for the TIMx you are using. Check where your TIMx lives. If you call RCC_APB2PeriphClockCmd() for a timer that lives on APB1, the compiler won't throw an error, but your timer will never work.
3) Double-check your pin's AF matches the datasheet's Alternative Function Mapping table (eg. AF1 == TIM1_CH3).
4) In your TIM_ICInitStructure, make sure TIM_Channel matches the channel you have chosen (in your case TIM_Channel_3).
5) Configure and enable the correct IRQ channel in the NVIC, eg. NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
6) Enable the interrupt: TIM_ITConfig(TIM1, TIM_IT_CC3, ENABLE);
7) Enable your timer: TIM_Cmd(TIM1, ENABLE);
Well, I guess my answer isn't required after 6 years. But maybe somebody else facing same problem will find it.
There may be a name conflict if you write in c++. If original prototype of interupt function is defined like C-function, and your implementation is C++function so it's just two different functions with the same name, and interupt will try to call an empty C-function or even may stack in endless loop. So that is why your c++ function may be newer called.
To solve this, just define your function like extern "C".
Maybe the interrupt enters the functions but does not know what to do, or enter ant he flag is still active and just enters once, you're missing the clear interrupt flag, try this:
/**
* #brief Capture Compare Handler
* #param None
* #retval None
*/
void TIM1_CC_IRQHandler()
{
if (TIM_GetITStatus(TIM1, TIM_IT_CC3) != RESET) <-- Add this
{
TIM_ClearITPendingBit(TIM1, TIM_IT_CC3); <-- This is important!
//TODO
}
}
I'm trying to configure SPI to work with a ST7565 GLCD library which is given here. For now, I'm trying to use SPI1 to achieve this. When the init function -which is given below- is called in main(), it causes the program to loop in assert_failed function.
void init_SPI1(void){
GPIO_InitTypeDef GPIO_InitStruct;
SPI_InitTypeDef SPI_InitStruct;
// enable clock for used IO pins
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/* configure pins used by SPI1
* PA5 = SCK
* PA6 = MISO
* PA7 = MOSI
*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// connect SPI1 pins to SPI alternate function
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
// enable clock for used IO pins
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
/* Configure the chip select pin
in this case we will use PE7 */
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOE, &GPIO_InitStruct);
GPIOE->BSRRL |= GPIO_Pin_7; // set PE7 high
// enable peripheral clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
/* configure SPI1 in Mode 0
* CPOL = 0 --> clock is low when idle
* CPHA = 0 --> data is sampled at the first edge
*/
SPI_InitStruct.SPI_Direction = SPI_Direction_1Line_Tx; // set to full duplex mode, seperate MOSI and MISO lines
SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // transmit in master mode, NSS pin has to be always high
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // one packet of data is 8 bits wide
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; // clock is low when idle
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; // data sampled at first edge
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; // set the NSS management to internal and pull internal NSS high
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; // SPI frequency is APB2 frequency / 4
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;// data is transmitted MSB first
SPI_Init(SPI1, &SPI_InitStruct);
SPI_Cmd(SPI1, ENABLE); // enable SPI1
}
I've noticed that program goes in infinite loop inside assert_failed function when it reaches SPI_Init() line:
SPI_Init(SPI1, &SPI_InitStruct);
The assert_failed function ( Default in Firmware Library) is below:
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
}
I don't know what does it supposed to mean that it loops in assert_failed function. Is it a problem to do with the SPI configuration. I need guidance to understand the problem and generate a solution. Any help will be greately appreciated. Thanks in advance.
EDIT: I've checked inside of the SPI_Init function in stm32f4xx_spi.c
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct)
{
uint16_t tmpreg = 0;
/* check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
/* Check the SPI parameters */
assert_param(IS_SPI_DIRECTION_MODE(SPI_InitStruct->SPI_Direction));
assert_param(IS_SPI_MODE(SPI_InitStruct->SPI_Mode));
assert_param(IS_SPI_DATASIZE(SPI_InitStruct->SPI_DataSize));
assert_param(IS_SPI_CPOL(SPI_InitStruct->SPI_CPOL));
assert_param(IS_SPI_CPHA(SPI_InitStruct->SPI_CPHA));
assert_param(IS_SPI_NSS(SPI_InitStruct->SPI_NSS));
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_InitStruct->SPI_BaudRatePrescaler));
assert_param(IS_SPI_FIRST_BIT(SPI_InitStruct->SPI_FirstBit));
assert_param(IS_SPI_CRC_POLYNOMIAL(SPI_InitStruct->SPI_CRCPolynomial));
Since the library is locked, I cant get to type anything inside to debug in Live Watch. (I'm using IAR EWARM)
It loops in assert() because the assert failed, so the loop is there to stop further execution.
Just step up on your stack so that you can see which assert in the peripheral library it was that failed. The library does pretty extensive validation of its parameters, so probably something is wrong in one of your calls.
UPDATE It seems you never initialize the CRCPolynomial field, but it's asserted upon. I suggest adding a call to SPI_StructInit() to make sure your init struct is sanely initialized, before you start setting it up according to your application's wishes.