2013-07-10 08:17 AM
I'm not sure how to remap pins that do not line up correctly with the alternate functions specified in the datasheet. For example, the picture below shows the pin out of my microcontroller where OCx-LOW are pwm out and ICx are pwm in. Comparing it to the datasheet's alternate function listing, only some pins line up to timer channels. For example, OC1-LOW and OC2-LOW pin's alternate functions match up to timer 9 channel 2 and 1, but OC3-LOW and OC4-LOW don't match up to any timers on their alternate functions so I'm not sure how to use these pins with the timers for pwm out/in.
When they match, you can do something like the following, but not sure what to do when they don't line up.GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
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(GPIOI, &GPIO_InitStructure);
/* Connect pins to TIM8 AF2 */
GPIO_PinAFConfig(GPIOI, GPIO_PinSource2, GPIO_AF_TIM8);
Sorry if it's a simple solution, I've never programmed any microcontroller before.
2013-07-10 08:31 AM
When they match, you can do something like the following, but not sure what to do when they don't line up.
I guess you ask the board designer and review team what they were thinking? The AF muxing fabric has limited connectivity, it does not offer a crossbar-switch type functionality. So if it's not listed, it's not escaping on those pins. See if you have other free pins in your design where you can escape the signals you need, and add some trace wires to your board. TIM9 only has TWO channels. The other alternative is to program them as GPIO pins and drive them via software, providing the speed and placement are less critical.
2013-07-10 08:40 AM
What do you mean drive them as GPIO pins/how would you do that? Someone suggested just having a timer run and poll it every let's say 1ms and then forward the polled signal/value to the pin, is that what you mean? I've briefly tried doing this, but couldn't get it to work properly.
2013-07-10 08:49 AM
Set them as GPIO outputs, and drive them high and low as required
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOE, &GPIO_InitStructure); GPIO_SetBits(GPIOE, GPIO_Pin_5 | GPIO_Pin_6); // Both High Polling seems a bit sloppy, if you have a TIM unit generating CC1/2/3/4 interrupts when the events occur, you'd be micro second level accurate.2013-07-10 08:53 AM
hmmm okay that makes sense, I'll give it a try and see what happens and post back with results. Thanks for the help
2013-07-10 11:13 AM
So I tried this but always seems to yield a 50% duty cycle when I check the OC3-LOW pin with a scope and changing the timer channel 1 pulse does not affect it. Any ideas why this isn't working?
static TIM_OCInitTypeDef TIM3_OC;
void TIM_Init_Out(void) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
// Randomly chose timer 3 because it's not used elsewhere
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// micro picture in first post, trying to get pwm-out on OC3-LOW, pin PE2
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
// OC3-LOW on pin PE3
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_SetBits(GPIOE, ~GPIO_Pin_2); //start bit low
PrescalerValue = (uint16_t) ((SystemCoreClock) / MHZ_20) - 1;
// setup timer
TIM_TimeBaseStructure.TIM_Period = 665;
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// should give ~25% duty cycle
TIM3_OC.TIM_OCMode = TIM_OCMode_PWM1;
TIM3_OC.TIM_OutputState = TIM_OutputState_Enable;
TIM3_OC.TIM_Pulse = 166;
TIM3_OC.TIM_OCPolarity = TIM_OCPolarity_High; // interrupt on high signal? not sure
TIM_OC1Init(TIM3, &TIM3_OC);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3, ENABLE);
// set up timer 3 interrupt
NVIC_Setup(TIM3_IRQn, 0, ENABLE);
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);
/* TIM3 enable counter */
TIM_Cmd(TIM3, ENABLE);
}
static int reverse=1;
void TIM3_IRQHandler(void) {
if (TIM_IT_CC1) {
TIM_ClearFlag(TIM3, TIM_FLAG_CC1 | TIM_FLAG_CC2 | TIM_FLAG_CC3 | TIM_FLAG_CC4);
if (reverse) {
// first time through signal will be low, and interrupt was triggered from high edge so set
// gpio pin high, and switch interrupt to low triggered
TIM3_OC.TIM_OCPolarity = TIM_OCPolarity_Low;
GPIO_ToggleBits(GPIOE, GPIO_Pin_2);
reverse=0;
} else {
TIM3_OC.TIM_OCPolarity = TIM_OCPolarity_High;
GPIO_ToggleBits(GPIOE, GPIO_Pin_2);
reverse=1;
}
TIM_OC1Init(TIM3, &TIM3_OC);
}
}
2013-07-10 12:36 PM
Any ideas why this isn't working?
GPIO_SetBits(GPIOE, ~GPIO_Pin_2); // Don't think this works GPIO_ResetBits(GPIOE, GPIO_Pin_2); // Try this As for banging things up/down and getting the duty you want, I'd suggest you have the pin set high in the Update interrupt, and low when the CC1 hits. Honestly not sure you can catch the alternate edges in the manner you're attempting.
2013-07-11 06:05 AM
So if I understand that right, I should start the signal high. Have the CC1 interrupt set to low triggered and when triggered, set gpio bit low, and then at the next update event, set the gpio bit high - repeat?
Is this setting the CC1 interrupt low triggered?TIM3_OC.TIM_OCPolarity = TIM_OCPolarity_Low;
Also, how would I manage setting a frequency and duty cycle with this setup? I thought the timer update event was set every timer the counter hit overflow, so I'm not seeing how to trigger the update event and set the bits high at the appropriate frequency.
EDIT: Okay I see that the CC1 interrupt is triggered when the counter matches the CCR1 register value. So the CC1 interrupt should trigger on the correct timing for the falling edge (assuming timer is reset to 0 for a starting point), but then the timer update and getting the right frequency is still throwing me off.
2013-07-22 09:12 AM
Still having trouble getting this to work. Trying another approach with no luck. This time around I initialize the gpio bit high and then on the CC1 interrupt, set the bit low, change the CCR1 value for the remaining cycles of a period and reset the timer. Then on the next interrupt, set the gpio bit high to mark one period.
static int flip=1;
void TIM3_IRQHandler(void){
if ((TIM3->SR & TIM_IT_CC1) == TIM_IT_CC1) {
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
if (flip == 1) {
GPIO_ResetBits(GPIOE, GPIO_Pin_2);
TIM_OCInitStructure.TIM_Pulse = (TIM3->ARR)-(TIM3->CCR1);
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_SetCounter(TIM3, 0);
flip = 2;
} else {
GPIO_SetBits(GPIOE, GPIO_Pin_2);
TIM_OCInitStructure.TIM_Pulse = 499; // static value for testing 499/665 period ~75% duty
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_SetCounter(TIM3, 0);
flip = 1;
}
}
}
The output I'm getting is strange when watching it with an oscilloscope. The duty cycle and frequency do not seem to have a direct correlation with how the pulse values are set within the TIM3_IRQHandler. Any suggestions?
2013-07-22 12:27 PM
I can't get my head around what you're doing there.
Here's how I'd generate a software PWM using TIM3 on an F4, the F2 should be directly portable.// STM32 PWM 30 KHz (TIM3 CH1 via PE2 by software) STM32F4 Discovery - sourcer32@gmail.com
#include ''stm32f4_discovery.h''
/**************************************************************************************/
void RCC_Configuration(void)
{
/* --------------------------- System Clocks Configuration -----------------*/
/* TIM3 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
/* GPIOE clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
}
/**************************************************************************************/
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*-------------------------- GPIO Configuration ----------------------------*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_ResetBits(GPIOE, GPIO_Pin_2);
}
/**************************************************************************************/
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**************************************************************************************/
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
GPIO_SetBits(GPIOE, GPIO_Pin_2); // Set high at beginning of period
}
if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
GPIO_ResetBits(GPIOE, GPIO_Pin_2); // Set low when width count hit
}
}
/**************************************************************************************/
void TIM3_Configuration(void)
{
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
uint16_t Period, Prescaler;
#if 1
Prescaler = ((SystemCoreClock / 2) / 20000000); // Timebase ticks at 20 MHz
Period = 666; // ~30030 Hz, from 20000000/30030
#else
Prescaler = 0;
Period = ((SystemCoreClock / 2) / 30000); // Period for 30 KHz frequency
#endif
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Prescaler = Prescaler - 1;
TIM_TimeBaseStructure.TIM_Period = Period - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* Enable TIM3 Preload register on ARR */
TIM_ARRPreloadConfig(TIM3, ENABLE);
/* TIM PWM1 Mode configuration */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable; // Don't need pin output, just interrupt
TIM_OCInitStructure.TIM_Pulse = Period / 4; // 25%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // Unrelated to interrupt
/* Output Compare PWM1 Mode configuration: Channel 1 */
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
/* TIM3 enable counter */
TIM_Cmd(TIM3, ENABLE);
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);
}
/**************************************************************************************/
int main(void)
{
RCC_Configuration();
NVIC_Configuration();
GPIO_Configuration();
TIM3_Configuration();
while(1); // Don't want to exit
}