dsp28335直流电机闭环控制 直流有刷电机转速闭环控制,用普中科技的开发板做的,电机是500线的ab增量编码器,用到了蜂鸣器模块,led,lcd1602,dht11温湿度传感器,l298n电机驱动模块,矩阵键盘。 采用pid控制,进行电机正反调速,超速报警并自动降速。 实时显示温度和湿度在lcd1602上。 有详细的说明,照着接线并把程序下载进去,就可以复现视频功能。
刚拿到普中DSP28335开发板那会儿,手痒想搞点硬核的。正好手头有个500线编码器的直流电机,干脆做个带温湿度显示的PID调速系统。这玩意儿做起来比想象中带感,特别是看着电机随着键盘输入乖乖变速的时候。
先说硬件组合拳:L298N驱动板接电机AB相,编码器信号线直接怼到28335的QEP接口。LCD1602用4位模式省IO口,DHT11随便找个GPIO接,矩阵键盘接法最骚——本来8个IO能搞定的矩阵,我偏用跳线帽做了个4x3布局,毕竟要给蜂鸣器和LED留足戏份。
先看编码器处理这块硬骨头。500线的AB相每转能出2000个脉冲,定时器捕获必须得够快:
#pragma CODE_SECTION(eqep1Isr,"RAMfuncs");
interrupt void eqep1Isr(void){
static int32 old_pos=0;
int32 new_pos = EQep1Regs.QPOSCNT;
motor_rpm = (new_pos - old_pos)*60/(4*500)*1000; //每转2000脉冲,采样周期1ms
old_pos = new_pos;
EQep1Regs.ICCLR.bit.INT=1; //清中断
}
这段中断服务程序1ms执行一次,把编码器计数转成转速。注意EQep寄存器的操作——这货对时序敏感得很,写错顺序直接摆烂。

PID控制才是重头戏。调参那几天差点把示波器看吐。核心算法长这样:
float pid_update(PID* pid, float actual){
pid->error = pid->target - actual;
float p_out = pid->kp * pid->error;
pid->integral += pid->ki * pid->error * dt;
if(pid->integral > pid->max_i) pid->integral = pid->max_i;
else if(pid->integral < -pid->max_i) pid->integral = -pid->max_i;
float d_out = pid->kd * (pid->error - pid->prev_error)/dt;
pid->prev_error = pid->error;
float output = p_out + pid->integral + d_out;
return output > pid->max_out ? pid->max_out : (output < -pid->max_out ? -pid->max_out : output);
}
这里埋了个坑——dt必须准确,我直接用定时器中断里的时间戳。积分限幅很重要,不然电机启动时积分项能飙到姥姥家。
温湿度显示看着简单,DHT11的时序却是个磨人精。代码里这个延时函数是关键:
void dht11_delay_us(int us){
volatile int i = us * 8; //实测主频150MHz时这个系数刚好
while(i--);
}
用volatile防止编译器优化,循环次数得靠示波器抓波形硬调。读数据时遇到校验错误就重试,最多试三次,防止LCD显示抽风。
键盘处理用了状态机套路,长按加速功能贼实用:
uint16 key_scan(){
static uint8 last_state[12] = {0};
static uint16 hold_cnt[12] = {0};
for(int i=0; i<12; i++){
if(key_pressed(i)){
if(last_state[i] == 0){ //首次按下
last_state[i] = 1;
return i;
}
if(++hold_cnt[i] > 1000){ //长按1秒
hold_cnt[i] = 0;
return i | 0x8000; //高位标记长按
}
}else{
last_state[i] = 0;
hold_cnt[i] = 0;
}
}
return 0xFFFF;
}
这样既处理了抖动又实现了长按加速,按着不放转速蹭蹭涨,松手自动锁定目标值。
报警模块最欢乐——超速时蜂鸣器唱《野蜂飞舞》,LED玩呼吸灯:
if(abs(motor_rpm) > MAX_SPEED){
static uint16 pwm_val = 0;
static int dir = 1;
pwm_val += dir*50;
if(pwm_val >= 3000) dir = -1;
else if(pwm_val <= 0) dir = 1;
PWM_set(AUX_PWM, pwm_val); //呼吸灯效果
BUZZER_ON();
DELAY_MS(50);
BUZZER_OFF();
}
其实应该用PWM驱动蜂鸣器,但手头模块只能高低电平控制,就做了个断续报警声。后来发现这样反而比持续响更抓耳。

调通那天,LCD上温度湿度跟着转速一起跳,键盘按正负切转向,超速就闪灯尖叫,有种赛博朋克配蒸汽机的魔幻感。源码里留了几个彩蛋:长按*和#键能切PID参数,不过没写在文档里——留给后来者自己折腾的惊喜。
148

被折叠的 条评论
为什么被折叠?



