Measure duration of the signal - c

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
}
}

Related

I'm having trouble with buttons and IRQ handler in keil uVision(STM32F103RBT6)

Sorry for asking such a basic question, I am a beginner to ARM programming. I'm currently struggling with a lab exercise.
I was supposed to: Use the on-board button (PC13) to switch on and off the on-board LED (PA5). When the button is pressed, the LED is on. When the button is released, the LED is off.
The code below can be built, but it doesn't produce the result I wanted. In fact, nothing happens when I push the button.
#include "stm32f10x.h" // Device header
#include "stdbool.h" // For Boolean data type
#define BUTTON_RCC_GPIO RCC_APB2Periph_GPIOC
#define BUTTON_GPIO GPIOC
#define BUTTON_GPIO_PIN GPIO_Pin_13
int main(void) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(BUTTON_RCC_GPIO, ENABLE);
//GPIO set up for PA5 (on board LED)
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//Timer 2 set up
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseInitTypeDef timerInitStructure;
timerInitStructure.TIM_Prescaler = 0;
timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
timerInitStructure.TIM_Period = 0;
timerInitStructure.TIM_ClockDivision = 0;
timerInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &timerInitStructure);
TIM_Cmd(TIM2, ENABLE);
TIM_TIxExternalClockConfig(TIM2, TIM_TIxExternalCLK1Source_TI2, TIM_ICPolarity_Rising, 0);
//Enable update event for Timer2
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
NVIC_EnableIRQ(TIM2_IRQn);
while(1);
static char state = 0;
}
static char state = 0;
void TIM2_IRQHandler(void) {
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
state = 1;
} else {
GPIO_SetBits(GPIOA, GPIO_Pin_5);
state = 0;
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
Edit: I have modified my code heavily, I decided not to use timers at all.
#include "stm32f10x.h" // Device header
#include "stdbool.h"
//TI2 PA1 Tim2 Ch2
#define BUTTON_RCC_GPIO RCC_APB2Periph_GPIOA
#define BUTTON_GPIO GPIOA
#define BUTTON_GPIO_PIN GPIO_Pin_13
#define L3_RCC_GPIO RCC_APB2Periph_GPIOA
#define L3_GPIO GPIOA
#define L3_PIN GPIO_Pin_5
static bool wait = true;
int main(void) {
GPIO_InitTypeDef GPIO_InitStruct;
// GPIO clock for I/O
RCC_APB2PeriphClockCmd(BUTTON_RCC_GPIO, ENABLE);
RCC_APB2PeriphClockCmd(L3_RCC_GPIO, ENABLE);
// Configure I/O for L3
GPIO_InitStruct.GPIO_Pin = L3_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(L3_GPIO, &GPIO_InitStruct);
while(1) {
if(BUTTON_GPIO_PIN==true)
{
GPIO_SetBits(L3_GPIO, L3_PIN);
}else
{
GPIO_ResetBits(L3_GPIO, L3_PIN);
}
}
}
You didn't write this explicitly, but I assume your lab exercise is using the
NUCLEO-F103RB, where you find the
STM32F103RBTx controller equipped with a user push button at PC13 and a user LED at PA5 - I hardly think another brand creats a PCB with the very same properties... :-)
As KIIV already pointed out in the comment, the reason you aren't seeing the LED change when the button is pressed, is that the controller input pin the button is connected to (PC13) isn't even checked.
Instead, the output state (PA5) for the LED is toggled in an ISR triggered by the interrupt of a timer (TIM2).
The timer configuration (which may be incomplete, I can't tell from reading your code snippet alone) is triggered by rising edges at an external clock source at event TI2, which is tied to channel TIM2_CH2 (available for pins PA1 and PB3, for which I don't see any init code, however).
Some lines are redundant (the invocations to RCC_APB2PeriphClockCmd() and the definitions of state, which will shadow each other...).
I have the impression that this code is not yet near to completion, but it may be helpful for you to first study what you want to achieve with the program, and what you are going to achieve by which section of your code. Then, finding the right solution for each detail is more a matter of studiousness and looking up the right datasheets (see links above).

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 interrupt handler for capture its not called

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
}
}

Analogue input pins PA8, PA11 and PA12 not working on STM32F103RB

Working on a simple ADC project reading voltages from a potential divider on a number of channels. I am using the STM32f103rb on a commercial and well made PCB. I am reading pins on PORTA and the following pins work and return a voltage:
PA0, PA1, PA4, PA5, PA6, PA7.
However the following pins do not work and return a raw value of around 2000-2700 approx:
PA8, PA11 and PA12.
The nature of the project and the fact that the PCB is of a fixed design means that we are stuck with these pin selections. The datasheet is quite specific about these pins being usable as AIN. All set-up and config is as per standard STM32, taken from basic example code and modified for our purposes. The included code is a debugging version we made in an attempt to find the cause but to no avail.
Voltage at the pins has been measured and is correct for the type of voltage divider.
Any help would be greatly appreciated on this.
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stdio.h"
#include "stdlib.h"
// Standard STM peripheral config functions
void RCC_Configuration(void);
void GPIO_Configuration(void);
// ADC config - STM example code
void NEW_ADC_Configuration(void);
// Function to read ADC channel and output value
u16 NEW_readADC1(u8 channel);
// Variables
double voltage_su; // Variable to store supply voltage
double voltage7;
double voltage8;
//*****************************************************************************
// Main program
int main(void)
{
// Initialise peripheral modules
RCC_Configuration();
GPIO_Configuration();
NEW_ADC_Configuration();
// Infinate loop
while (1)
{
// Get value of supply voltage and convert
voltage_su = (((3.3 * NEW_readADC1(ADC_Channel_12)) / 4095));
}
}
//*****************************************************************************
// STM RCC config
void RCC_Configuration(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // Connect PORTC
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // Connect PORTB...
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // Connect PORTA...
}
//*****************************************************************************
// STM GPIO config
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure; // Value for setting up the pins
// Sup
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
//*****************************************************************************
void NEW_ADC_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure; // Varibale used to setup the ADC
RCC_ADCCLKConfig(RCC_PCLK2_Div4); // Set ADC clock to /4
// Enable ADC 1 so we can use it
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// Conenct the port A to peripheral clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// Restore to defaults
ADC_DeInit(ADC1);
/* ADC1 Configuration ----------------------------------------------------*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
// Scan 1 at a time
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
// No continuous conversions - do them on demand
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
// Start conversion on software request - not bu external trigger
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
// Conversions are 12 bit - put them in the lower 12 bits of the result
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
// How many channels are to be sued by the sequencer
ADC_InitStructure.ADC_NbrOfChannel = 1;
// Setup ADC
ADC_Init(ADC1, &ADC_InitStructure);
// Enable ADC 1
ADC_Cmd(ADC1, ENABLE);
// Enable ADC1 reset calibaration register
ADC_ResetCalibration(ADC1);
// Check end of reset calib reg
while(ADC_GetResetCalibrationStatus(ADC1));
//Start ADC1 calib
ADC_StartCalibration(ADC1);
// Check the end of ADC1 calib
while(ADC_GetCalibrationStatus(ADC1));
}
//*****************************************************************************
// Function to return the value of ADC ch
u16 NEW_readADC1(u8 channel)
{
// Config the channel to be sampled
ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_239Cycles5);
// Start the conversion
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
// Wait until conversion complete
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
// Get the conversion value
return ADC_GetConversionValue(ADC1);
}
//*****************************************************************************
I have never worked with this chip, but after looking at the hardware data sheet for 5 minutes I came up with the following naive conclusions:
The chip has 2 ADCs with 8 channels each, a total of 16.
PA0, PA1, PA4, PA5, PA6, PA7 sounds as if they will belong to the first ADC.
I then assume that PA8, PA11 and PA12 belong to the second ADC, called ADC2 by ST.
Your code seems to only ever mention and initialize ADC1. No trace of ADC2.
Maybe these pins don't work because you haven't even initialized or activated the ADC they belong to?
Also, you should check the data sheet for contention due to those pins being used as "Alternate Function".
Typically, they will list what needs to be done/disconnected in order to utilize the pins without contention (e.g. disconnect some resistor)

Resources