PWM控制SG90舵机

// main.c

#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "PWM.h"
#include "Delay.h"

uint16_t angle = 18, direction_flag = 0, one_sec_IT = 0, IT_flag = 0;

int main(void)
{
    OLED_Init();
    PWM_Init();
    One_Sec_Interrupt_Init();
    
    OLED_ShowString(1, 1, "TIM2 IT:");
    OLED_ShowString(2, 1, "1Sec IT = ");
    OLED_ShowString(3, 1, "TIM2 CH2 PWM:");
    OLED_ShowString(4, 1, "CCR val = ");
    
    while(1)
    {
        OLED_ShowNum(2, 11, one_sec_IT, 6);
        OLED_ShowNum(4, 11, angle, 2);
        TIM_SetCompare1(TIM2, angle); // 更新【CCR】值,调整PWM的占空比。
        
        if(!direction_flag)
        {
            angle++;
            Delay_ms(100);
        }
        if(direction_flag)
        {
            angle--;
            Delay_ms(100);
        }
        if(angle == 36) direction_flag = 1;
        if(angle == 18) direction_flag = 0;
        
    }
}

void TIM2_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
    {
        if (IT_flag < 50)
        {
            IT_flag++;
        }
        else
        {
            IT_flag = 0;
            one_sec_IT++;
        }
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}
// PWM.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void) // 采用TIM2 OC1(CH1)
{
    // 时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    TIM_InternalClockConfig(TIM2); // 可以不写这一句,上电默认内部时钟源
    
    // 时基单元
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrucure;
    TIM_TimeBaseInitStrucure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStrucure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStrucure.TIM_Period = 359; // 【ARR】
    TIM_TimeBaseInitStrucure.TIM_Prescaler = 3999; // 【PSC】
    TIM_TimeBaseInitStrucure.TIM_RepetitionCounter = 0; //通用定时器没有重复计数器
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStrucure);
    
    // 初始化输出比较OC(1)
    TIM_OCInitTypeDef TIM_OCInitStruture;
    TIM_OCStructInit(&TIM_OCInitStruture); // 给结构体赋初始值,因为下面用不到结构体的全部成员
    TIM_OCInitStruture.TIM_OCMode = TIM_OCMode_PWM1; // 模式PWM1
    TIM_OCInitStruture.TIM_OCPolarity = TIM_OCPolarity_High; // REF有效时输出高电平(有效电平为高电平)
    TIM_OCInitStruture.TIM_OutputState = TIM_OutputState_Enable; // 输出使能
    TIM_OCInitStruture.TIM_Pulse = 18; // 【CCR】
    TIM_OC1Init(TIM2, &TIM_OCInitStruture);
    // 现在OC1就初始化好了,下面还需要初始化TIM2的OC1对应的GPIO口
    // 不同定时器不同OC对应的GPIO是不同的,是固定的,需要查表,得是PA0口
    
    // 配置GPIOA PIN0
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 要设置为复用推挽输出,原因看GPIO结构
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // 启动定时器
    TIM_Cmd(TIM2, ENABLE);
    
    /*
    【计算公式】:
    Freq = CK_PSC / (PSC+1)*(ARR+1)  其中CK_PSC是72Mhz
    Duty = CCR / (ARR+1)
    Reso = 1 / (ARR+1)
    注意:PSC,ARR,CCR都是16位的,在0-FFFF(0-65535)之间。
    
    要输出频率50Hz,占空比5%,分辨率360的PWM波形:
    PSC = 3999,ARR = 359,CCR = 18
    保持上述频率和分辨率不变,输出占空比10%的PWM波形,只需修改CCR = 36。
    */
}

void One_Sec_Interrupt_Init(void) // 采用TIM2 OC2(CH2),其触发中断的时间由上面的ARR和PSC决定了,是20毫秒,在中断中里计50次即每隔一秒钟“触发”。
{
    // 下面这一句防止复位就执行一次中断,对应1秒+1的情况就是复位值为0. 否则刚复位显示就是1.
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    
    // IT中断使能
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //更新中断
    
    // NVIC
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitTypeDef NVIC_InitStrucure;
    NVIC_InitStrucure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStrucure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStrucure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStrucure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStrucure);
}

/* 此处仅作模板用,建议把中断函数写在要用的地方
void TIM2_IRQHandler(void)
{
    if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
    {
        // xxxxx 中断函数写在这里 
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}
*/