实现硬件PWM控制电机旋转和通过编码器计算所转圈数的简单例程
Posted feiniaoliangtiangao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现硬件PWM控制电机旋转和通过编码器计算所转圈数的简单例程相关的知识,希望对你有一定的参考价值。
该例程所用的硬件设备:
直流电机驱动模块YYH-LWZ: H桥 大功率 正反转 刹车 PWM 调速 5/12/24V
12V直流减速电机JGB37-520B:ASLONG JGB37-520B编码器减速电机直流减速马达A/B相码盘信号测速 带编码器 A/B相输出 噪音小
芯片:IAP15w4k58s4
电机控制:
因该电机驱动模块无法直接通过单片机的IO口位的拉高,拉低来控制,故用PWM来控制。软件模拟PWM不够稳定快速,故采用硬件PWM,然而硬件PWM只可使用IAP15w4k58s4芯片固定的PWM输出IO口,来输出PWM波形:
P0.6/P0.7/P1.6/P1.7/P2.1/P2.2
P2.3/P2.7/P3.7/P4.2/P4.4/P4.5
芯片资料:http://www.stcmcudata.com/datasheet/stc/STC-AD-PDF/STC15.pdf(要用自取)
PWM波形输入到电机驱动模块的IO口后,被MOC管放大,在输出到电机电源线
硬件PWM的PWM.C程序如下:
#include "STC15W.H" //单片机头文件 #include "Uart.h" #include "PWM.h" /*系统晶振频率为28Mhz ,PWM输出信号频率为20khz以内*/ //参考stc15系列单片机指南1056页, void PWM_Init(void) { P_SW2 |= 0x80; PWMCFG = 0x00; //PWM的输出初始电平为低电平 PWMCKS = 0x0f; //PWM的时钟为Fosc/(0+1) PWMC = CYCLE; //PWM周期,定义PWM周期(最大值为32767) PWM2CR = 0x00; //PWM2波形输出到P37,不使能PWM2中断 PWM3CR = 0x00; //PWM3波形输出到P21,不使能PWM3中断 PWM4CR = 0x00; //PWM4波形输出到P22,不使能PWM4中断 PWM5CR = 0x00; //PWM5波形输出到P23,不使能PWM5中断 PWM2T1 = 0x0001; PWM2T2 = 0; PWM3T1 = 0x0001; PWM3T2 = 0; PWM4T1 = 0x0001; PWM4T2 = 0; PWM5T1 = 0x0001; PWM5T2 = 0; PWMCR |= 0x80; //使能PWM模块 P_SW2 &=~0x80; } void IN_1( unsigned int DUTY) //PWM2 { if(DUTY==0) //通过DUTY来控制占空比,进而控制PWM输出电压,最终实现转速的变化 { PWMCR &=~0x01; PWM2=0; } else if(DUTY==100) { PWMCR &=~0x01; PWM2=1; } else { P_SW2 |= 0x80; //使能访问PWM在扩展RAM区的特殊功能寄存器XSFR PWM2T1 = 0x0001; //设置PWM2第1次反转的PWM计数 PWM2T2 = CYCLE * DUTY / 100; //设置PWM2第2次反转的PWM计数 P_SW2 &=~0x80; //占空比为(PWM2T2-PWM2T1)/PWMC PWMCR |= 0x01; //使能PWM信号输出 } } void IN_2(unsigned int DUTY) //PWM3 { if(DUTY==0) { PWMCR &=~0x02; PWM3=0; } else if(DUTY==100) { PWMCR &=~0x02; PWM3=1; } else { P_SW2 |= 0x80; PWM3T1 = 0x0001; PWM3T2 = CYCLE * DUTY / 100; P_SW2 &=~0x80; PWMCR |= 0x02; } } void IN_3(unsigned int DUTY) //PWM4 { if(DUTY==0) { PWMCR &=~0x04; PWM4=0; } else if (DUTY==100) { PWMCR &=~0x04; PWM4=1; } else { P_SW2 |= 0x80; PWM4T1 = 0x0001; PWM4T2 = CYCLE * DUTY / 100; P_SW2 &=~0x80; PWMCR |= 0x04; } } void IN_4(unsigned int DUTY) //PWM5 { if(DUTY==0) { PWMCR &=~0x08; PWM5=0; } else if (DUTY==100) { PWMCR &=~0x08; PWM5=1; } else { P_SW2 |= 0x80; PWM5T1 = 0x0001; PWM5T2 = CYCLE * DUTY / 100; P_SW2 &=~0x80; PWMCR |= 0x08; } } //功能:电机驱动模块的输入端口控制函数 void IN_SetPwm(int wide_1,int wide_2,int wide_3,int wide_4,int uDir) { if(uDir==1) { IN_1(wide_1); IN_2(wide_2); IN_3(wide_3); IN_4(wide_4); } }
硬件PWM的PWM.h程序如下:
#ifndef _PWM_H_ #define _PWM_H_ #include "STC15W.H" //芯片晶振频率设置为28mhz #define CYCLE 0x6500L //定义PWM周期(最大值为32767) sbit PWM2=P3^7; sbit PWM3=P2^1; sbit PWM4=P2^2; sbit PWM5=P2^3; extern void PWM_Init(void);extern void IN_SetPwm(int wide_1,int wide_2,int wide_3,int wide_4,int uDir); #endif
编码器计数:
该电机自带的编码器为A/B相霍尔计数编码器,根据编码器的旋转产生A,B相的不同方波,每有A相的4个方波,编码器转了90度,转一圈故有12个方波信号。根据旋转方向的不同,A波产生的上升下降沿时,B波同时刻处于不同的电平。电机输出轴转一圈的时间内,根据电机的转速,减速比和PWM的频率不同,编码器所转的圈数是不固定的,要精确计数要使用算法,该例程只是前提量不变的估量值。
编码器的encoder.c程序:
#include "STC15W.H" //单片机头文件 #include "Uart.h" #include "encoder.h" #include "center.h" unsigned char Last_io=0; unsigned char Curr_io=0; //编码器结构体的初始化 void Encoder_Init(Encoder_HandleTypeDef * encoder) { encoder-> zheng_count =0; encoder-> fan_count =0; encoder-> end_count =0; } void Delay50us() //@28MHz { unsigned char i, j; i = 2; j = 89; do { while (--j); } while (--i); } void Initial_INT0(void) //用外部中断来实现A波的触发 { IT0=0; // 设置成上升沿和下降沿均触发 EX0=1; //使能INT0中断 EA=1; } int exint0() interrupt 0 //外部中断入口 { Delay50us(); if(PIN_A==1) //上升沿触发 { Curr_io=PIN_B; //记录PIN_B的触发信号 } else if(PIN_A==0) //下降沿触发 { Last_io=PIN_B; //记录PIN_B的触发信号 } } //扫描编码器的计数 void scan_encoder(Encoder_HandleTypeDef *encoder) { if((Curr_io==1)&&(Last_io==0)) //编码器逆时针旋转时,A波上升沿时,B波为1,A波下降沿时,B波为0; { encoder->zheng_count++; //每有12个判断信号,编码器转一圈 SendString(" 证 转 "); Curr_io=0; // 判断信号置0,如果不置0会有误差 Last_io=0; } if((Curr_io==0)&&(Last_io==1)) //编码器顺时针旋转时,A波上升沿时,B波为0,A波下降沿时,B波为1; { encoder->fan_count++; //每有12个判断信号,编码器转一圈 SendString(" 反 转 "); Curr_io=0; // 判断信号置0 Last_io=0; } if(encoder->zheng_count==360*15) //当编码器所转圈数到达一定数量时,电机的输出轴转一圈,该数字为估量值 { // SendString("输出轴 证 转 了 一 圈 "); encoder->zheng_count=0; encoder->end_count++; } if(encoder->fan_count==360*15) { // SendString("输出轴 反 转 了 一 圈 "); encoder->fan_count=0; encoder->end_count++; } }
编码器的encoder.h程序:
#ifndef _ENCODER_H_ #define _ENCODER_H_ #include "STC15W.H" sbit PIN_B=P4^1; //B相接P41 sbit PIN_A=P3^2; //A相接外部中断使能端口P41 typedef struct Encoder //编码器结构体 { unsigned int zheng_count; //编码器正转圈数 unsigned int fan_count; //编码器反转圈数 unsigned int end_count; //输出轴已转圈数 }Encoder_HandleTypeDef; extern void scan_encoder(Encoder_HandleTypeDef *motor); extern void Encoder_Init(Encoder_HandleTypeDef * encoder); extern void Initial_INT0(void); #endif
电机控制:
控制电机正转或反转,并旋转指定圈数
控制电机的motor.c程序
#include "STC15W.H" //单片机头文件 #include "Uart.h" #include "encoder.h" #include "PWM.h" #include"motoc.h" void Motor_Init(Motor_HandleTypeDef *motor) { motor->H_PWM =0; motor->L_PWM =0; motor->number =0; motor->flag =1; } void Motor_Start(Motor_HandleTypeDef *motor,int tack) //启动电机 { motor->H_PWM=20; //占空比恒为20% if(tack==1) { IN_SetPwm(motor->H_PWM, motor->L_PWM, motor->L_PWM, motor->H_PWM,1); //MOC管显示为 (1 0 0 1) 电机正转 } if(tack==2) { IN_SetPwm(motor->L_PWM, motor->H_PWM, motor->H_PWM, motor->L_PWM,1); //MOC管显示为 (0 1 1 0) 电机反转 } } void Motor_Stop(Motor_HandleTypeDef *motor,int tack) //关闭电机 { motor->H_PWM=0; if(tack==1) { IN_SetPwm(motor->H_PWM,motor->H_PWM,motor->L_PWM,motor->L_PWM,1); //MOC管显示为 (0 0 0 0) 电机刹车 } if(tack==2) { IN_SetPwm(motor->L_PWM,motor->L_PWM,motor->H_PWM,motor->H_PWM,1); //MOC管显示为 (0 0 0 0) 电机刹车 } } //电机开关函数 void Motor_key(Motor_HandleTypeDef *motor) { if( motor->flag==0) { motor->flag=1; } } //tack:电机方向 count:目的圈数 void Motor_control(Motor_HandleTypeDef *motor,Encoder_HandleTypeDef * encoder,int tack,int count) { if(motor->flag==1) //flag=1时,电机才能运行 { Motor_Start(motor,tack); //启动电机 motor->number=encoder->end_count ; if(motor->number >= count) //当输出轴转的圈数到达目的圈数时,停止旋转 { Motor_Stop(motor,tack); //关闭电机 encoder->end_count=0; //编码器圈数置0 motor->flag=0; //flag=0 motor->number=0; //电机圈数置0 } } }
控制电机的motor.h程序
#ifndef _MOTOR_H_ #define _MOTOR_H_ #include "STC15W.H" typedef struct Motor //电机结构体 { int L_PWM; //PWM低电位 int H_PWM; //PWM高电位 int number; //电机已转圈数 int flag; //电机开关 }Motor_HandleTypeDef; extern void Motor_Init(Motor_HandleTypeDef *motor); extern void Motor_key(Motor_HandleTypeDef *motor); extern void Motor_Start(Motor_HandleTypeDef *motor,int tack); extern void Motor_Stop(Motor_HandleTypeDef *motor,int tack); extern void Motor_control(Motor_HandleTypeDef *motor,Encoder_HandleTypeDef * encoder,int tack,int count);
主函void main()
{ Motor_HandleTypeDef motor; Encoder_HandleTypeDef encoder; STC15W_IOinit(); //单片机初始化 Core_Init_Uart(); //串口初始化 PWM_Init(); //PWM初始化 Encoder_Init(&encoder); //编码器初始化 Motor_Init(&motor); //电机初始化 Initial_INT0(); //外部中断初始化 while(1) { Motor_control(&motor,&encoder,1,5); scan_encoder(&encoder); } }
结语
只不过一个简简单单的控制电机和计算圈数的程序,就前前后后花了我两个多星期的业余时间。现在看来,单片机有很多硬件功能是我了解不足的,差不多是从零开始写的。学不以致用不可取以。期望对后来者有参考帮助。
看官们觉得好,有用就给个推荐,如果何处不足,有错请大方留言指出。
谢谢浏览。
void main(){ Motor_HandleTypeDef motor; Encoder_HandleTypeDef encoder; uchar Light_Data=0x43;
STC15W_IOinit(); //单片机初始化Core_Init_Uart(); //串口初始化 PWM_Init(); //PWM初始化Encoder_Init(&encoder); //编码器初始化Motor_Init(&motor); //电机初始化 Initial_INT0(); //外部中断初始化 BH1750_Init(); //GY-30初始化
while(1){ // Motor_Start(&motor,1); // Motor_control(&motor,&encoder,1,5); //scan_encoder(&encoder); //BH1750_Init();
IIC_write_Commend(BH1750FVI_POWER_ON); //上电 IIC_write_Commend(BH1750FVI_CONTINUOUSLY_H_RESOLUTION_MODE); //模式为连续高分辨模式1
Delay180ms(); Light_Data=IIC_read_Commend(); //
conversion(Light_Data); //
}
}
以上是关于实现硬件PWM控制电机旋转和通过编码器计算所转圈数的简单例程的主要内容,如果未能解决你的问题,请参考以下文章
请教三菱plc中怎么用旋转编码器的脉冲信号来控制步进电机的启停,编码转电机也转,梯形图怎么写?