2014-06-23 07:22 AM
I'd like to use all channels of Timer 3 to generate different clocks and timers.
Channel 1- 10ms timer interrupt Channel 2- 250us timer interrupt Channel 3- 50kHz clock (50%) on PB0 Channel 4- 256kHz clock (50%) on PB1 The problems I'm having are:rate)
void Timer3_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* TIM3 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
/* GPIOB clock enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
/* GPIOB Configuration: TIM3 CH3 (PB0) and TIM3 CH4 (PB1) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN ;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Connect TIM Channels to AF1 */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource0, GPIO_AF_1);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource1, GPIO_AF_1);
/* Enable the TIM3 global Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* ---------------------------------------------------------------------------
TIM3 Configuration: Output Compare Toggle Mode:
In this example TIM3 input clock (TIM3CLK) is set to APB1 clock (PCLK1).
=> TIM3CLK = PCLK1 = 48 MHz
CC3 update rate = TIM3 counter clock / CCR3_Val = 75000 Hz
==> So the TIM3 Channel 3 generates a periodic signal with a
frequency equal to 37500 Hz.
CC4 update rate = TIM3 counter clock / CCR4_Val = 9375 Hz
==> So the TIM3 Channel 4 generates a periodic signal with a
frequency equal to 465 Hz.
--------------------------------------------------------------------------- */
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 65535;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* Output Compare Toggle Mode configuration: Generic */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
/* Output Compare Toggle Mode configuration: Channel 3 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
TIM_OC3Init(TIM3, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Disable);
/* Output Compare Toggle Mode configuration: Channel 4 */
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR4_Val;
TIM_OC4Init(TIM3, &TIM_OCInitStructure);
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Disable);
/* TIM enable counter */
TIM_Cmd(TIM3, ENABLE);
/* TIM IT enable */
TIM_ITConfig(TIM3, TIM_IT_CC3 | TIM_IT_CC4, ENABLE);
}
#assert.h
2014-06-23 07:29 AM
Understand that each timer only has a SINGLE counting element.
To generate different periodicity from different channels, you'd need to keep advancing the CCRx comparator setting at EACH interrupt. So you'd want to use the prescaler to set the time base of the counter, say 1 us (1 MHz), and set the period maximal, ie 0xFFFF for 16-bit and 0xFFFFFFFF for 32-bit timers. For a 250us interrupt you'd need to do TIMx->CCRx += 250; at each interrupt to advance the comparator.2014-06-23 08:11 AM
The problem is that when I push the update rate too high the clock starts to break down. For example I have CCR3_Val = 8 and CCR4_Val = On channel 4 I get a 5kHz clock as expected for a 125kHz update rate but on channel 3 I have a 28Hz clock. It seems that when I push the GPIO toggle rate above around 80kHz it starts to break down. Is there something else I need to configure to avoid this or is this beyond the capability of the micro?
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 0xFFFFFFFF;
TIM_TimeBaseStructure.TIM_Prescaler = 12; //4MHz
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
void TIM3_IRQHandler(void)
{
/* TIM3_CH3 toggling with frequency = 37500 Hz */
if (TIM_GetITStatus(TIM3, TIM_IT_CC3) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC3);
capture = TIM_GetCapture3(TIM3);
TIM_SetCompare3(TIM3, capture + CCR3_Val);
}
/* TIM3_CH4 toggling with frequency = 37500 Hz */
if (TIM_GetITStatus(TIM3, TIM_IT_CC4) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC4);
capture = TIM_GetCapture4(TIM3);
TIM_SetCompare4(TIM3, capture + CCR4_Val);
}
}
2014-06-23 08:24 AM
Yes, because at some point you saturate the processor with interrupts. You don't specify a processor or the speed it's running at, but 100 KHz interrupts would have 720 machine cycles on a 72 MHz STM32F1, and flash accesses (execution) would eat 3 cycles.
The way you solve this is to use different timers to generate different frequencies, and let the hardware toggle the pins. For servo type applications where all the channels are the same frequency (50 Hz, 20 ms), you can use all four channels, and alter the duty with the CCRx registers. The STM32 timers, integer dividers and clocking scheme are pretty weak. For specific frequencies you might have to run the processor at sub optimal speeds, or use magic crystal values. If you have exotic requirements consider an PLD/FPGA solution.2014-06-23 09:26 AM
Sorry, part number is STM32F030 running at 48MHz off HSI.
2014-06-23 03:23 PM
''The problem is that when I push the update rate too high the clock starts to break down.''
That is what happens when the code is based on false assumptions. You can use such line in an IRQ to verify if assumptions hold:assert(TIM3->CNT <= IRQ_ACCEPTABLE_DELAY);