STM32 嵌入式 PWM信号

麻烦各位看一下代码指出问题,本人刚开始了解嵌入式,在使用TIM2 CH4 输出PWM波形的时候一直不成功,只有从0-1的一个跳变。
但是整个程序都可以编译和烧录,检查了很久,不知道是不是哪里的配置除了问题?

#include "stm32f10x.h"                  // Device header
#include 
#include "Steer.h"
#include "Kinematics.h"
#include "PID.h"

extern    float    Given_Angle;

void TIM_PWM_Init(void)//PWM初始化
{
    GPIO_InitTypeDef GPIO_Initstructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;//初始化TIMx的时间基数单位
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);    
    
    GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); 
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE);
    
    GPIO_Initstructure.GPIO_Pin=GPIO_Pin_3 ;
    GPIO_Initstructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
    GPIO_Initstructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_Initstructure);
    
    TIM_TimeBaseInitStructure.TIM_ClockDivision =TIM_CKD_DIV1;//设置时钟分频系数:不分频
    TIM_TimeBaseInitStructure.TIM_CounterMode =TIM_CounterMode_Up;//向上计数模式
    TIM_TimeBaseInitStructure.TIM_Period = 20000;  //0-19999 计数20000次一个定时周期 20ms
    TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//72*20 -1; //设置预分频 1M Hz 1us计数一次
    TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//每次PWM 溢出中断
    TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);//初始化TIM5的时基
    
    //输出模式配置
    TIM_OCStructInit(&TIM_OCInitStructure);
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;;//PWM模式1
    TIM_OCInitStructure.TIM_Pulse = 0;    //占空比=配置占空比的值/TIM_TImeBaseStructure.TIM_Period
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//当定时器计数值小于CCR1_Val时为高电平
    TIM_OC4Init(TIM2,&TIM_OCInitStructure);//初始化tim2通道4配置
    
    TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
    TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
    TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
    TIM_OC4PreloadConfig(TIM2, TIM_OCPreload_Enable);//使能预装功能
    
    TIM_CtrlPWMOutputs(TIM2,ENABLE); 
    
    TIM_ARRPreloadConfig(TIM2, ENABLE); // 使能TIM2重载寄存器ARR
    TIM_Cmd(TIM2,ENABLE);//使能TIM2
    

}

void PWM_SetCompare4(uint16_t Compare)//角度转换为比较寄存器的值
{
    TIM_SetCompare4(TIM2,Compare);
}

void Steering_position(float angle)//angle为转向角度
{
    float PWM_Output = 0;

    angle = Given_Angle; //用于上传前轮的转向角度
    
    angle = angle * P_STEER_WHEEL;// 舵机转角和前轮相对转角的比率,本质为将给定的前轮转角转化为舵机的转角
    
    angle = LIMIT_MIN_MAX_FUN(angle, -STEERING_MAX_ANGLE, STEERING_MAX_ANGLE);    
    //左右角度限制  -STEERING_MAX_ANGLE 
    
    PWM_Output =  18500 + 100 / 9 * angle; //925 + 5 / 9 * angle;
    
    TIM_SetCompare4(TIM2,PWM_Output);
        
    //(angle>0)?(TIM_SetCompare3(TIM5,Num)):(TIM_SetCompare3(TIM5,1500 + PWM_Output));
}


TIM_TimeBaseInitStructure.TIM_Period调一下看看周期是否会变化

引用chatGPT作答,在您的代码中,我注意到以下几个问题:

1.TIM_TimeBaseInit()中的参数应该是TIM2,而不是TIM5,因为您正在使用TIM2通道4进行PWM输出。所以应该将下面这行代码:

TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);//初始化TIM5的时基

改为:

TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化TIM2的时基

2.在PWM_SetCompare4()函数中,您使用的是定时器通道4的比较寄存器。但是,在TIM_OCInitStructure中,您将模式设置为了PWM模式1,这意味着您应该使用定时器通道4的CCR4寄存器来设置占空比。因此,您需要将PWM_SetCompare4()函数中的以下代码:

TIM_SetCompare4(TIM2,Compare);

改为:

TIM_SetCompare4(TIM2, Compare);

3.在Steering_position()函数中,您没有将角度值限制在 -STEERING_MAX_ANGLE 和 STEERING_MAX_ANGLE 之间,这可能会导致计算得到的PWM输出超出预期范围。您可以使用以下代码将角度值限制在所需范围内:

angle = LIMIT_MIN_MAX_FUN(angle, -STEERING_MAX_ANGLE, STEERING_MAX_ANGLE);

其中,LIMIT_MIN_MAX_FUN()是一个宏定义,可以根据需要自定义。

以下内容引用CHATGPT、有用望采纳:

在代码中发现了一个问题,TIM5应该改成TIM2,因为我们需要初始化的是TIM2通道4的PWM输出。另外,需要检查一下时钟配置是否正确,以及TIM2通道4对应的GPIO口是否配置正确。如果以上问题都没有解决,可以尝试使用示波器检测PWM输出是否正常。

结合最新版的ChatGPT4.0的回答:
您的代码中存在一些不一致的地方,可能导致PWM输出不成功。我已经找到了一些问题,您可以尝试修复它们,看是否能解决问题。

1.在PWM初始化函数TIM_PWM_Init()中,您使用了TIM5进行时基初始化,但实际上应该使用TIM2。请将以下代码:

TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);//初始化TIM5的时基

修改为:

TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化TIM2的时基

2.在函数Steering_position()中,将angle = Given_Angle;移动到函数的参数传递后。这样,angle才会接收到外部传入的角度值。将以下代码:

void Steering_position(float angle)//angle为转向角度
{
    float PWM_Output = 0;

    angle = Given_Angle; //用于上传前轮的转向角度

修改为:

void Steering_position(float angle)//angle为转向角度
{
    float PWM_Output = 0;

    angle = angle; //用于上传前轮的转向角度

3.您可能需要在main()函数中调用TIM_PWM_Init()来初始化PWM,并使用Steering_position()设置一个初始的PWM占空比。

int main(void)
{
    TIM_PWM_Init();
    Steering_position(0); // 设置一个初始的PWM占空比,例如0度
    
    while(1)
    {
        // 其他代码
    }
}

在进行这些更改后,尝试重新编译和烧录代码,看看问题是否得到解决。如果问题仍然存在,请继续检查硬件连接和配置。

但是很奇怪啊,我在更改上述的配置之后,输出的PWM波形周期是13ms,脉冲宽度是1ms,我期望的期望的是20ms和1.5ms啊,想请教下怎么改呢?

img

TIM_TimeBaseInitStructure.TIM_Period = 20000;
TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
  • 这篇博客: 基于STM32之PWM控制舵机以及PWM控制呼吸灯中的 二.在32单片机的定时器(TIMX)分类 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 分别有1.高级定时器(TIM1~TIM8)2.通用定时器(TIM2~TIM5)3.基本定时器(TIM6~TIM7)

    在我们常用地定时器一般是通用定时器,并且通用定时器在APB1外设总线上面(最大为36MHZ)

    可以实现16位的向上,向下,向上/向下同时的计数,还可以进行输出比较,输入捕获。PWM单脉冲。

1.TIM_PWM_Init函数中初始化的是TIM2,而在后面的代码中却使用了TIM5,这可能是因为拼写错误或者复制粘贴时出错导致的。

2.在TIM_TimeBaseInitStructure中设置了TIM5的时钟分频系数、计数模式、预分频和重复计数器,但是应该设置的是TIM2的。这意味着TIM2的时钟配置并没有被正确设置,可能导致PWM输出不正确。

3.在PWM_SetCompare4函数中,将浮点型的PWM_Output转换为uint16_t类型后直接传入了TIM_SetCompare4函数,这可能导致精度丢失。建议使用四舍五入函数将浮点型转换为uint16_t类型,例如:


```c

uint16_t Compare = (uint16_t)(PWM_Output + 0.5);
TIM_SetCompare4(TIM2, Compare);

4.在Steering_position函数中,angle是通过传参方式获取的,但在函数内部却被直接赋值为了Given_Angle,这可能导致传参无效。如果想要使用传参的方式,应该将函数声明修改为:

void Steering_position(float angle)

同时,在函数内部不要再次赋值给angle

1.TIM_PWM_Init函数中初始化的是TIM2,而在后面的代码中却使用了TIM5,这可能是因为拼写错误或者复制粘贴时出错导致的。

2.在TIM_TimeBaseInitStructure中设置了TIM5的时钟分频系数、计数模式、预分频和重复计数器,但是应该设置的是TIM2的。这意味着TIM2的时钟配置并没有被正确设置,可能导致PWM输出不正确。

3.在PWM_SetCompare4函数中,将浮点型的PWM_Output转换为uint16_t类型后直接传入了TIM_SetCompare4函数,这可能导致精度丢失。建议使用四舍五入函数将浮点型转换为uint16_t类型,例如:


```c

uint16_t Compare = (uint16_t)(PWM_Output + 0.5);
TIM_SetCompare4(TIM2, Compare);

4.在Steering_position函数中,angle是通过传参方式获取的,但在函数内部却被直接赋值为了Given_Angle,这可能导致传参无效。如果想要使用传参的方式,应该将函数声明修改为:

void Steering_position(float angle)

同时,在函数内部不要再次赋值给angle