STM32 F429 TIM1 Capture Compare always zero - c

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.

Related

Does STM32 timer frequency equal the PWM output frequency?

I have easy understanding Question. Whet I do initialize my STM32 timer for 1 sek counts (TIM8, Prescaller = 16800-1, Period = 10000-1) and want to debug it (measure a pin output frequency) - it a timer frequency the same frequency like I can observe it on oscilloscope?
And it is a right configuration for TIM8 timer Interrupt?
void Second_timer_Init() {
GPIO_InitTypeDef GPIO_InitStructure; //GPIO Init structure definition
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //Enables AHB1 peripheral clock for GPIOC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE); //Enables AHB1 peripheral clock for TIM2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //Specifies the GPIO pins to be configured
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //Specifies the operating output type for the selected pins
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //GPIO Alternate function Mode
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //Specifies the operating Pull-up/Pull down for the selected pins
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //Specifies the speed for the selected pins
GPIO_Init(GPIOC, &GPIO_InitStructure); //Initializes the GPIOA peripheral
TIM_TimeBaseInitTypeDef Timer_InitStructure; //TIM8 Time Base Init structure definition
TIM_OCInitTypeDef Output_ChannelInit; //TIM8 Output Compare Init structure definition
GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_TIM8); // PC6 -> Connect TIM8 pins to AF3
Timer_InitStructure.TIM_Period = 10000 - 1; //Specifies the period value (Orig 1 Sek: 10000-1)
Timer_InitStructure.TIM_Prescaler = 16800-1; //Specifies the prescaler value used to divide the TIM clock (Orig 1 Sek: 16800-1)
Timer_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //Specifies the clock division (0)
Timer_InitStructure.TIM_CounterMode = TIM_CounterMode_Up; //Specifies the counter mode
TIM_TimeBaseInit(TIM8, &Timer_InitStructure); //Initializes the TIM8 Time Base Unit peripheral
// TIM_OCStructInit(&Output_ChannelInit);
Output_ChannelInit.TIM_OCMode = TIM_OCMode_PWM1; //Specifies the TIM8 PWM mode
Output_ChannelInit.TIM_OutputState = TIM_OutputState_Enable;//Specifies the TIM8 Output Compare state
Output_ChannelInit.TIM_Pulse = 0; //Specifies the pulse value to be loaded into the Capture Compare Register
Output_ChannelInit.TIM_OCPolarity = TIM_OCPolarity_Low; //Specifies the output polarity
TIM_OC1Init(TIM8, &Output_ChannelInit); //Initializes the TIM8 Channel1
TIM_OC1PreloadConfig(TIM8, TIM_OCPreload_Enable); //Enables the TIM2 peripheral Preload register on CCR1
TIM_ARRPreloadConfig(TIM8, ENABLE); //Enables TIM2 peripheral Preload register on ARR
TIM_Cmd(TIM8, ENABLE); //Enables the specified TIM8 peripheral
TIM_CtrlPWMOutputs(TIM8, ENABLE); //Enables the TIM peripheral Main Outputs ?
TIM8->CCR1 = 5000; //Set duty cycle to 50%
TIM_ClearITPendingBit(TIM8, TIM_IT_Update); //Clears the TIM8 interrupt pending bits
TIM_ITConfig(TIM8, TIM_IT_Update, ENABLE); //Enables the specified TIM8 interrupts
NVIC_InitTypeDef NVIC_InitStructure; //NVIC Init Structure definition
NVIC_InitStructure.NVIC_IRQChannel = TIM8_CC_IRQn; //Specifies the IRQ channel to be enabled
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;//Specifies the pre-emption priority for the IRQ channel specified in NVIC_IRQChannel (0-15)
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; //Specifies the subpriority level for the IRQ channel specified in NVIC_IRQChannel (0-15)
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //Specifies whether the IRQ channel defined in NVIC_IRQChannel will be enabled
NVIC_Init(&NVIC_InitStructure); //Initializes the NVIC peripheral
}
IRQ Handler
void TIM8_CC_IRQHandler(){
if (TIM_GetITStatus(TIM8, TIM_IT_Update) != RESET) //Checks whether the TIM8 interrupt has occurred
{
TIM_ClearITPendingBit(TIM8, TIM_IT_Update); //Clears the TIM8 interrupt pending bits
TIM3->CCR1 = 500; //Debug
}
else{
TIM3->CCR1 = 0; //Debug
}
}
As I see you change the TIM3 registers in the TIM8 interrupt.
Secondly you check for the wrong event in your interrupt routine (this routine is invoked then the CC event occurs not the UG event. Same when you set the interrupts. You enable the UG interrupt but serve the CC one. Some of the uC models have those interrupt vectors shared some not.
BTW What is the point of using HAL library if you change the registers?

Reading quadrature encoder output using NUCLEO-F072RB

I am using the NUCLEO-F072RB board in conjunction with X2C to read the output of a DC motor encoder, in order to measure its speed. According to the datasheet, using timers TIM2 and TIM3 it is possible to perform this read. For that purpose, I followed this and this sources to write the following code:
/** - configure A6 for encoder input (T1) */
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
GPIO_Init (GPIOA, &GPIO_InitStruct);
GPIO_PinAFConfig(GPIOA, GPIO_InitStruct.GPIO_Pin, GPIO_AF_1);
/** - configure A7 for encoder input (T2) */
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
GPIO_Init (GPIOA, &GPIO_InitStruct);
GPIO_PinAFConfig(GPIOA, GPIO_InitStruct.GPIO_Pin, GPIO_AF_1);
/**********************************************/
/* Encoder input setup */
/* TIM3 clock enabled */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
/* TIM3 DeInit */
TIM_DeInit(TIM3);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_OCStructInit(&TIM_OCInitStructure);
/* Configure the timer TIM3 for encoder input */
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
TIM_SetAutoreload (TIM3, 0xffff);
/* TIM3 counter enable */
TIM_Cmd(TIM3, ENABLE);
Here I use the alternate function 1 for both inputs, which, according to the datasheet, corresponds to TIM3. As far as I understand, with that setup, every time there is a rising edge on T1 (GPIO A6 in this case), the TIM3 counter increases by 1, and when there is a rising edge on T2 (A7), the counter decreases by one.
For debugging purposes, I set a value to a variable I can monitor. If the counter is 1 or greater the value is INT16_MAX is set, otherwise 0 is set.
if (TIM_GetCounter(TIM3) > 0x0) {
Inports.Encoder_Input = INT16_MAX;
} else {
Inports.Encoder_Input = 0;
}
Before using the actual encoder, I have tried using a switch to manually generate rising edges and a PWM output from another GPIO to feed the inputs (GPIO's A6 and A7). This way, I expect the TIM3 counter starts counting up/down, but nothing happens. I have noticed that when I connect a signal to A6 or A7, it gets degradated, which may explain why no rising edges are detected. Measuring with a DC multimeter the resistance on these inputs I get approximately 40 Ohm, which is very low for an input port.
How can I properly setup the timer and the GPIOs to be able to read the encoder signal and get the TIM3 counter start counting?

Square wave generation with STM32 and timer

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.

STM32F429 External Interrupt Edge

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.

STM32F4 SPI Configuration SPI_Init causes assert_failed loop

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.

Resources