使用 PWM 电路来控制直流电机的正转、反转、加速和减速,并将转向和转速显示出来。


#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit k1=P3^4; //启动/停止按键
sbit k2=P3^5; //减速按键
sbit k3=P3^6; //加速按键
sbit k4=P3^7; //正反转按键
sbit k5=P3^2; //模式控制
//LCD1602控制引脚,其中P2为数据口
sbit RS=P0^5;
sbit RW=P0^6;
sbit E=P0^7;
sbit ENA=P1^2; //IN1,IN2使能信号
sbit IN1=P1^0; //电机输入线
sbit IN2=P1^1;
bit model=1; //手动/自动,默认启动时显示自动
bit direc=1; //转向标志,默认启动时为正转 L
int rate=1; //高电平时间常数
int num=0; //1ms中断记录 统计脉冲个数
int count=0; //1ms中断记录,自动模式加速时间常数
uchar code tab1[]=" Welcome ";
uchar code tab2[]=" 2020/12/9";
uchar code tab3[]=" Motor Control ";
uchar code tab4[]="Team:ZHL YYQ ZYY";
uchar code tab5[]="Dire: Mode: ";
uchar code tab6[]="Rate: T:29 ";/********************** LCD显示 ****************************/
void w_com(uint com) //写指令
{
RS=RW=E=0;
P2=com;
E=1; //写指令的操作时序:RS=0,RW=0,EN=高电平
delay_xus(1);
E=0;
}
void w_data(uint dat) //写数据
{
RS=1;RW=E=0;
P2=dat;
E=1; //写数据的操作时序:RS=0,RW=0,EN=高电平
delay_xus(1);
E=0;
}
void w_shuzu(int addr,uchar code *p) //显示字符串
{
int i;
w_com(0x80+addr);
delay_xus(1);
for(i=0;p[i]!='\0';i++) //串口发送字符串时,可以通过'\0'判断字符串是否结束
{
w_data(p[i]);
delay_xus(1);
}
}
void disp() //显示速度、转向、模式
{
//显示数值 转成ASCII码表
if(rate<10) //小于10的时候 只有个位
{
w_com(0x80+0x45);
delay_xus(1);
w_data(' ');
delay_xus(1);
w_com(0x80+0x46);
delay_xus(1);
w_data(rate%10+0x30); //查ASCII码表 这一位的数字+0x30 数字转换成ASCII码才能显示
delay_xus(1);
}
else
{
w_com(0x80+0x45);
delay_xus(1);
w_data(rate/10+0x30); //十位
delay_xus(1);
w_com(0x80+0x46);
delay_xus(1);
w_data(rate%10+0x30); //个位
delay_xus(1);
}
if(direc==1) //正转
{
w_shuzu(0x06,"L");
}
else //反转
{
w_shuzu(0x06,"R");
}
if(model==0) //手动按键控制
{
w_shuzu(0x0f,"H");
}
else //自动
{
w_shuzu(0x0f,"A");
}
}/************* 主程序初始化 ************************/
void init()
{
//LCD显示初始化
w_com(0x38); //设置16*2显示
delay_xus(1);
w_com(0x0c); //开显示 显示光标,光标闪烁
delay_xus(1);
w_shuzu(0x00,tab1); //显示字符串
w_shuzu(0x40,tab2);
delay(500);
w_com(0x01); //清屏
delay_xus(1);
delay(2); //忙碌
w_shuzu(0x00,tab3);
w_shuzu(0x40,tab4);
delay(1000);
w_com(0x01); //清屏
delay_xus(1);
delay(2);
w_shuzu(0x00,tab5);
w_shuzu(0x40,tab6);
disp();
ENA=0;
TMOD=0X01; //定时器0工作在方式1
TH0=0XFC; //(65536-1000)=FC18(12MHz定时的1ms)
TL0=0X18;
ET0=1; //定时器0中断允许
TR0=0;
EX1=1; //外部中断INT1
IP=0X02; //设置定时器0为高优先级中断 T0的中断级别为高,因为PWM的脉冲宽度就是在中断程序里设定,如果此时被其他更高级别的中断打断,那么脉冲宽度将不准确,从而导致控制转速失败。
EA=1; //开总中断
}/*********************** 主程序 ***************************/
void main()
{
init();
while(1) ;
}/***************** 延时子程序 **************************/
void delay_xus(uint x) //小延时 us
{
while(x--);
}
void delay(uint x) //为12MHz晶振提供延时函数 ms级
{
uint y;
for(;x>0;x--)
for(y=110;y>0;y--) ;
}/******************* 按键处理程序 ************************/
void keyscan()
{
if(k1==0) //启停
{
delay(120);
if(k1==0)
{
TR0=~TR0; //开启定时 输出PWM波
ENA=~ENA; //使能 电机转
rate=1;
}
}
if(k2==0&&model==0) //减速 减速键按下
{
delay(120);
if(k2==0&&model==0)
{
rate-=2; //为之后改变输出PWM波的占空比
if(rate<=1) rate=1;
}
}
if(k3==0&&model==0) //加速 加速键按下
{
delay(120); //软件延时
if(k3==0&&model==0)
{
rate+=2; //为之后改变输出PWM波的占空比
if(rate>=29) rate=29;
}
}
if(k4==0&&model==0) //转向
{
delay(120);
if(k4==0&&model==0)
{
direc=~direc; //取反
}
}
if(k5==0&&ENA==0) //模式选
{
delay(120);
if(k5==0&&ENA==0)
{
model=~model;
}
}
disp(); //状态改变,更新显示数据
}/******************* 中断服务程序 *************************/
void extern1() interrupt 2 //外部中断INT1 按键扫描
{
keyscan();
}
void timer0() interrupt 1 //定时器0中断
{
num++; //计数
if(num>=29) num=0; //超过范围 num=0
if(direc==1) //通过direc判断 为正转
{
if(num<=rate) //跟rate比较 高电平持续时间
{
IN1=1; //正转
IN2=0;
}
else IN1=IN2=0; //低电平持续时间
}
else //否则 反转
{
if(num<=rate)
{
IN1=0; //反转
IN2=1;
}
else IN1=IN2=0;
}
if(model==1) //自动模式下,执行,加速时间常数
{
count++;
if(count==1500) //1.5s后占空常数增加,加速
{
rate+=2;
if(rate>=29) rate=29;
count=0;
disp();
}
}
TH0=0XFC; //重装初值 (65536-1000)=FC18(12MHz的定时1ms)
TL0=0X18;
}
刚启动时,电机转速较慢,转向显示L,逆时针在转动,加速到转速较大时,再改变电机的转向,转向显示R,顺时针转动,实现控制电机正转、反转。
控制电机加速,输出 PWM 矩形波高电平持续时间变长

控制电机减速,输出 PWM 矩形波高电平持续时间变短

控制电机正转、反转

转向显示 L 时,P1.0口输出 PWM 波,正转;转向显示 R 时,P1.1口输出 PWM 波,反转
设计符合要求,仿真效果也挺好。
作者:叶庭云 CSDN:https://yetingyun.blog.csdn.net/