STM32F105RBT6 使用定时器TIM3输出PWM波

1. TIM3的GPIO口,查阅STM32F105RBT6 数据手册,TIM3的4通道用的是PB1

STM32F105RBT6 使用定时器TIM3输出PWM波_第1张图片

2. 初始化GPIO口和定时器TIM3

2.1 相关函数

RCC_APB1PeriphClockCmd、GPIO_Init、TIM_TimeBaseInit、TIM_OC4Init、TIM_OC4PreloadConfig、NVIC_Init、TIM_ITConfig、TIM_Cmd、

void TIM3_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    TIM_TimeBaseInitTypeDef TIM_InitStruct;
    TIM_OCInitTypeDef TIM_OCInitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;

    // Enable clock for TIM3 and GPIOB
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3 | RCC_APB2Periph_GPIOB, ENABLE);

    // Initialize GPIOB to output PWM signal
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStruct);

    // Initialize TIM3 for PWM generation with interrupt on update
    TIM_InitStruct.TIM_Period = 999; // Set PWM frequency to 70kHz (72 MHz / 1000 / 7)
    TIM_InitStruct.TIM_Prescaler = 0;
    TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_InitStruct);

    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStruct.TIM_Pulse = 500; // Duty cycle = 50%
    TIM_OC4Init(TIM3, &TIM_OCInitStruct);
    TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);

    NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 4;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM3, ENABLE);
}

3. 中断入口 TIM3_IRQHandler

3.1 在启动文件里面找到TIM3 对应的中断入口函数,也就是中断服务函数 TIM3_IRQHandler

STM32F105RBT6 使用定时器TIM3输出PWM波_第2张图片

4. 编写中断服务函数

void TIM3_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) // overflow interrupt
    {
        printf("龙傲天说,我三岁拳打南山不老院,四岁脚踢北海幼儿园\r\n");
    }
    TIM_ClearITPendingBit(TIM3,TIM_IT_Update); // clear interrupt flag

    // Handle interrupt by updating PWM duty cycle value
//    static uint16_t duty_cycle = 500; // Initial value of 50%
//    duty_cycle = duty_cycle < 950 ? duty_cycle + 50 : 0; // Increase duty cycle by 5% every period
//    TIM_SetCompare4(TIM3, duty_cycle);
}

4.1 中断服务函数需要快速地执行完毕。中断服务函数应该避免执行太多的计算复杂度较高的操作,否则可能会导致中断响应时间过长,甚至因为延迟而导致系统不稳定。

4.2 如果你需要在中断服务函数中访问全局变量,需要将这些变量定义为volatile类型。这是因为中断服务函数可能会在任何时间被外部中断所打断,如果没有使用volatile类型,就有可能导致变量值不准确。

4.3 在中断服务函数的结尾处,需要调用NVIC_ClearPendingIRQ()函数来清除中断挂起位。

4.4 中断函数最好别用printf 函数等耗时、有可能阻塞的一些函数,printf函数本身就比较耗时,在中断服务函数中调用的话,可能会导致中断响应时间过长,使系统不稳定。如果在中断服务函数中使用了printf函数,可能会导致printf函数与被打断的低优先级代码发生冲突,造成数据异常。我这里用 printf 只是为了装13,我龙傲天谁都不服

4.4 中断服务函数需要快速、简洁、有效地处理中断,并且需要小心地处理共享资源和全局变量。

5. 主函数调用一下初始化函数就可以了

int main(void)
{
    TIM3_Init();
    while (1)
    {
    	printf("剑圣来了,快跑");
	}
}

6. 串口数据

STM32F105RBT6 使用定时器TIM3输出PWM波_第3张图片

7. 拿示波器或者逻分仪去量PB1 引脚,看波形,有毛刺,我没滤波的,可以处理掉

STM32F105RBT6 使用定时器TIM3输出PWM波_第4张图片

STM32F105RBT6 使用定时器TIM3输出PWM波_第5张图片

8. PWM 波频率、周期计算

8.1 频率 F = SYSCLK / ((TIM_Prescaler + 1) / (TIM_Period + 1))

8.2 周期 T = 1 / F

8.3 制造一个频率是1 Hz的PWM 波,周期 1s, 占空比50%,改下面三个参数值就行了

	TIM_InitStruct.TIM_Prescaler = 7199;
   	TIM_InitStruct.TIM_Period = 10000;
    TIM_OCInitStruct.TIM_Pulse = 5000;  // 占空比50% = 5000 / (TIM_Period + 1)

STM32F105RBT6 使用定时器TIM3输出PWM波_第6张图片

你可能感兴趣的:(stm32,单片机,嵌入式硬件,mcu,物联网)