STM32 小白实战 04:PWM 驱动舵机与直流电机,函数声明和大小写错误排查

.

摘要

今天完成了 PWM 驱动舵机的参数理解与按键控制,并开始编写直流电机驱动模块。重点不是新增了多少代码,而是确认了几类非常典型的 C 语言工程错误:函数原型拼写不一致、函数被隐式声明、变量大小写不一致、标准库函数名大小写写错、PWM 通道接口未统一,以及函数名中误插入空格。舵机已验证可以按照设定角度转动;电机模块的编译问题已解决,实物正反转与调速效果待确认。

开发环境

  • MCU:STM32F103 系列
  • IDE:Keil μVision 5
  • 编译器:ARMCC V5.06 update 7
  • 固件库:STM32F10x 标准外设库
  • 调试器:ST-LINK
  • 本次外设:舵机、按键、OLED、直流电机驱动接口
  • PWM 定时器:TIM2
  • 舵机 PWM 输出:TIM2_CH2,对应 PA1
  • 按键输入:PB1、PB11

一、舵机 PWM 中 ARR、PSC、CCR 到底怎样对应

现象:定时器配置为 ARR=20000-1、PSC=72-1,已经知道 ARR+1=20000,但不理解为什么 CCR=500 被课程当作舵机 0°。

TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;   // ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;   // PSC
TIM_OCInitStructure.TIM_Pulse = 0;                   // CCR

原因:按 72 MHz 定时器时钟计算,经过 72 分频后 CNT 计数频率为 1 MHz,即每个计数为 1 μs。ARR+1=20000,所以 PWM 周期为 20 ms,频率为 50 Hz。CCR 在当前参数下可直接看作高电平持续的微秒数。

CCR = 500  → 高电平 0.5 ms
CCR = 1500 → 高电平 1.5 ms
CCR = 2500 → 高电平 2.5 ms

CCR=500 并不是天生代表 0°,而是当前程序采用“0.5 ms→0°、1.5 ms→90°、2.5 ms→180°”的舵机映射。

解决方法:把角度 0~180°线性映射到 CCR 500~2500。

void Servo_SetAngle(float Angle)
{
    PWM_SetCompare2(Angle / 180.0f * 2000.0f + 500.0f);
}

角度

CCR

高电平时间

占空比

500

0.5 ms

2.5%

90°

1500

1.5 ms

7.5%

180°

2500

2.5 ms

12.5%

验证结果:舵机在修正后可以按照设定角度转动。

1 课程中的 PWM 频率、占空比和分辨率计算示意。

二、OLED 角度变化,但舵机不跟随转动

现象:按键读取正常,OLED 上的角度值可以递增,但舵机不随角度变化。Keil 提示 Servo_SetAngle 被隐式声明。

warning: #223-D: function "Servo_SetAngle" declared implicitly

原因:Servo.h 中把函数名写成 Servo_SetAngel,实际定义和调用是 Servo_SetAngle。函数原型没有被正确包含,ARMCC 虽继续编译,但浮点参数传递可能与实际函数定义不一致。

// 错误
void Servo_SetAngel(float Angle);

// 正确
void Servo_SetAngle(float Angle);

解决方法:统一 Servo.h、Servo.c 和 main.c 中的函数名与参数类型;Servo.c 同时包含 Servo.h;保存后执行 Rebuild all target files。

#ifndef __SERVO_H
#define __SERVO_H

void Servo_Init(void);
void Servo_SetAngle(float Angle);

#endif

验证结果:隐式声明警告消失,舵机可以按照程序设定角度正常转动。

2 Servo_SetAngle() 缺少正确函数原型时的隐式声明警告。

三、Motor.c 中标准库函数和变量名写错

现象:编写电机正反转和速度控制函数时,Keil 报告 GPIO_ReSetBits 被隐式声明,speed 未定义。

原因:STM32 标准库正确函数名是 GPIO_ResetBits;函数参数定义为 Speed,但代码使用 speed。C 语言严格区分大小写。

解决方法:统一标准库函数名和变量名。

void Motor_SetSpeed(int8_t Speed)
{
    if (Speed >= 0)
    {
        GPIO_SetBits(GPIOA, GPIO_Pin_4);
        GPIO_ResetBits(GPIOA, GPIO_Pin_5);
        PWM_SetCompare3(Speed);
    }
    else
    {
        GPIO_ResetBits(GPIOA, GPIO_Pin_4);
        GPIO_SetBits(GPIOA, GPIO_Pin_5);
        PWM_SetCompare3(-Speed);
    }
}

验证结果:GPIO_ReSetBits 和 speed 对应的编译错误消失。

3 Motor.c 中函数名和变量名大小写错误。

四、链接阶段提示 Undefined symbol PWM_SetCompare1

现象:语法编译通过,但链接阶段报告 Undefined symbol PWM_SetCompare1,并指出由 main.o 引用。

Undefined symbol PWM_SetCompare1 (referred from main.o)

原因:main.c 调用了 PWM_SetCompare1(),但当前 PWM 模块实际提供的是通道 3 接口 PWM_SetCompare3()。声明、定义和调用的 PWM 通道没有统一。

解决方法:主函数通过 Motor_SetSpeed() 使用电机模块,不再调用不存在的 PWM_SetCompare1();PWM.h、PWM.c 和 Motor.c 统一使用 PWM_SetCompare3。

void PWM_SetCompare3(uint16_t Compare)
{
    TIM_SetCompare3(TIM2, Compare);
}

验证结果:PWM_SetCompare1 未定义的链接错误消失,工程进入后续编译检查。

五、Motor.h 中函数名被空格拆开

现象:Motor.h 报 incomplete type、expected a ";",main.c 同时提示 Motor_SetSpeed 被隐式声明。

void Motor _SetSpeed(int8_t Speed);

原因:Motor 和 _SetSpeed 之间多了一个空格,编译器无法把它识别为函数名 Motor_SetSpeed,导致头文件声明无效。

解决方法:删除多余空格,并在头文件中包含能够定义 int8_t 的头文件。

#ifndef _MOTOR_H
#define _MOTOR_H

#include "stm32f10x.h"

void Motor_Init(void);
void Motor_SetSpeed(int8_t Speed);

#endif

验证结果:该问题已经解决,相关编译错误和隐式声明警告消失。直流电机实物正反转与调速效果待确认。

固定排查清单

  1. 出现 declared implicitly 时,先检查对应 .h 是否有正确函数原型。
  2. 函数声明、函数定义和函数调用的名字及参数类型必须完全一致。
  3. C 语言区分大小写,重点检查 Speed/speed、Reset/ReSet。
  4. 错误来自 main.o 时,优先在 main.c 搜索对应调用。
  5. Undefined symbol 属于链接错误,要检查函数是否真正定义、源文件是否加入工程、接口名是否统一。
  6. PWM 通道必须一致:初始化 CH3,就使用 TIM_SetCompare3() 和 PWM_SetCompare3()。
  7. 头文件中的函数名不能包含意外空格。
  8. 每个 .c 文件尽量包含自己的 .h,让接口不一致尽早暴露。
  9. 修改头文件或接口后执行 Rebuild all target files。
  10. 软件编译通过不等于硬件验证完成,电机方向、转速和供电仍需实物测试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值