实现硬件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 = ; PWM3T1 = 0x0001;
PWM3T2 = ; PWM4T1 = 0x0001;
PWM4T2 = ; PWM5T1 = 0x0001;
PWM5T2 = ; PWMCR |= 0x80; //使能PWM模块
P_SW2 &=~0x80;
}
void IN_1( unsigned int DUTY) //PWM2
{
if(DUTY==) //通过DUTY来控制占空比,进而控制PWM输出电压,最终实现转速的变化
{
PWMCR &=~0x01;
PWM2=;
}
else if(DUTY==)
{
PWMCR &=~0x01;
PWM2=;
}
else
{
P_SW2 |= 0x80; //使能访问PWM在扩展RAM区的特殊功能寄存器XSFR
PWM2T1 = 0x0001; //设置PWM2第1次反转的PWM计数
PWM2T2 = CYCLE * DUTY / ; //设置PWM2第2次反转的PWM计数
P_SW2 &=~0x80; //占空比为(PWM2T2-PWM2T1)/PWMC
PWMCR |= 0x01; //使能PWM信号输出 } }
void IN_2(unsigned int DUTY) //PWM3
{
if(DUTY==)
{
PWMCR &=~0x02;
PWM3=;
}
else if(DUTY==)
{
PWMCR &=~0x02;
PWM3=;
}
else
{
P_SW2 |= 0x80;
PWM3T1 = 0x0001;
PWM3T2 = CYCLE * DUTY / ;
P_SW2 &=~0x80;
PWMCR |= 0x02; }
}
void IN_3(unsigned int DUTY) //PWM4
{
if(DUTY==)
{
PWMCR &=~0x04;
PWM4=;
}
else if (DUTY==)
{
PWMCR &=~0x04;
PWM4=;
}
else
{
P_SW2 |= 0x80;
PWM4T1 = 0x0001;
PWM4T2 = CYCLE * DUTY / ;
P_SW2 &=~0x80;
PWMCR |= 0x04; }
} void IN_4(unsigned int DUTY) //PWM5
{
if(DUTY==)
{
PWMCR &=~0x08;
PWM5=;
}
else if (DUTY==)
{
PWMCR &=~0x08;
PWM5=;
}
else
{
P_SW2 |= 0x80;
PWM5T1 = 0x0001;
PWM5T2 = CYCLE * DUTY / ;
P_SW2 &=~0x80;
PWMCR |= 0x08; }
} //功能:电机驱动模块的输入端口控制函数
void IN_SetPwm(int wide_1,int wide_2,int wide_3,int wide_4,int uDir)
{
if(uDir==)
{
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^;
sbit PWM3=P2^;
sbit PWM4=P2^;
sbit PWM5=P2^; 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=;
unsigned char Curr_io=; //编码器结构体的初始化
void Encoder_Init(Encoder_HandleTypeDef * encoder)
{
encoder-> zheng_count =;
encoder-> fan_count =;
encoder-> end_count =; } void Delay50us() //@28MHz
{
unsigned char i, j; i = ;
j = ;
do
{
while (--j);
} while (--i);
} void Initial_INT0(void) //用外部中断来实现A波的触发
{ IT0=; // 设置成上升沿和下降沿均触发
EX0=; //使能INT0中断
EA=; } int exint0() interrupt //外部中断入口 {
Delay50us();
if(PIN_A==) //上升沿触发
{ Curr_io=PIN_B; //记录PIN_B的触发信号 }
else if(PIN_A==) //下降沿触发
{ Last_io=PIN_B; //记录PIN_B的触发信号 } } //扫描编码器的计数
void scan_encoder(Encoder_HandleTypeDef *encoder)
{ if((Curr_io==)&&(Last_io==)) //编码器逆时针旋转时,A波上升沿时,B波为1,A波下降沿时,B波为0;
{ encoder->zheng_count++; //每有12个判断信号,编码器转一圈 SendString(" 证 转 \n"); Curr_io=; // 判断信号置0,如果不置0会有误差
Last_io=; }
if((Curr_io==)&&(Last_io==)) //编码器顺时针旋转时,A波上升沿时,B波为0,A波下降沿时,B波为1;
{ encoder->fan_count++; //每有12个判断信号,编码器转一圈 SendString(" 反 转 \n"); Curr_io=; // 判断信号置0
Last_io=; } if(encoder->zheng_count==*) //当编码器所转圈数到达一定数量时,电机的输出轴转一圈,该数字为估量值
{ // SendString("输出轴 证 转 了 一 圈\n"); encoder->zheng_count=; encoder->end_count++; }
if(encoder->fan_count==*)
{ // SendString("输出轴 反 转 了 一 圈\n"); encoder->fan_count=; encoder->end_count++; } }
编码器的encoder.h程序:
#ifndef _ENCODER_H_
#define _ENCODER_H_ #include "STC15W.H" sbit PIN_B=P4^; //B相接P41
sbit PIN_A=P3^; //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 =;
motor->L_PWM =;
motor->number =;
motor->flag =;
} void Motor_Start(Motor_HandleTypeDef *motor,int tack) //启动电机
{
motor->H_PWM=; //占空比恒为20%
if(tack==)
{ IN_SetPwm(motor->H_PWM, motor->L_PWM, motor->L_PWM, motor->H_PWM,); //MOC管显示为 (1 0 0 1) 电机正转 }
if(tack==)
{ IN_SetPwm(motor->L_PWM, motor->H_PWM, motor->H_PWM, motor->L_PWM,); //MOC管显示为 (0 1 1 0) 电机反转 } } void Motor_Stop(Motor_HandleTypeDef *motor,int tack) //关闭电机
{
motor->H_PWM=;
if(tack==)
{ IN_SetPwm(motor->H_PWM,motor->H_PWM,motor->L_PWM,motor->L_PWM,); //MOC管显示为 (0 0 0 0) 电机刹车
}
if(tack==)
{ IN_SetPwm(motor->L_PWM,motor->L_PWM,motor->H_PWM,motor->H_PWM,); //MOC管显示为 (0 0 0 0) 电机刹车
}
} //电机开关函数
void Motor_key(Motor_HandleTypeDef *motor) {
if( motor->flag==)
{
motor->flag=;
} } //tack:电机方向 count:目的圈数
void Motor_control(Motor_HandleTypeDef *motor,Encoder_HandleTypeDef * encoder,int tack,int count)
{ if(motor->flag==) //flag=1时,电机才能运行
{
Motor_Start(motor,tack); //启动电机 motor->number=encoder->end_count ; if(motor->number >= count) //当输出轴转的圈数到达目的圈数时,停止旋转
{
Motor_Stop(motor,tack); //关闭电机 encoder->end_count=; //编码器圈数置0 motor->flag=; //flag=0 motor->number=; //电机圈数置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()
{
Motor_control(&motor,&encoder,1,5);
scan_encoder(&encoder); } }
结语
只不过一个简简单单的控制电机和计算圈数的程序,就前前后后花了我两个多星期的业余时间。现在看来,单片机有很多硬件功能是我了解不足的,差不多是从零开始写的。学不以致用不可取以。期望对后来者有参考帮助。
看官们觉得好,有用就给个推荐,如果何处不足,有错请大方留言指出。
谢谢浏览。
实现硬件PWM控制电机旋转和通过编码器计算所转圈数的简单例程的更多相关文章
- 【单片机入门】(四)应用层软件开发的单片机学习之路-----ESP32开发板PWM控制电机以及中断的使用
引言 各位大佬,晚上好啊,在上一篇博客中,我们讲了什么是UART串口通讯,以及使用USB转TTL使得单片机可以和c#上位机做一个串口通讯,接下来,为大家带来PWM的概念原理,以及实际案例,使用PWM对 ...
- 8_陀螺仪MPU6050和PWM控制在STM32F4-Discovery开发板上的实现
非常早曾经就把圆点博士的程序从STM32F103移植到STM32F4_Discovery (STM32F407), battery.陀螺仪和PWM电机控制的程序都已经測试完成,执行有一段时间,正常.以 ...
- STM32 精确输出PWM脉冲数控制电机(转)
STM32 精确输出PWM脉冲数控制电机 发脉冲两种目的1)速度控制2)位置控制 速度控制目的和模拟量一样,没有什么需要关注的地方发送脉冲方式为PWM,速率稳定而且资源占用少 stm32位置控制需要获 ...
- 树莓派 温度监控 PWM 控制风扇 shell python c 语言
Mine: 图中圈出来的是三极管 和滤波电容 依赖库: wiringPi sudo apt-get install wiringpi Shell脚本 本文介绍使用Shell脚本在树莓派上启用软件PWM ...
- 用STM32定时器中断产生PWM控制步进电机
控制步进电机可以使用PWM.定时器中断.延时,这里用的就是定时器中断来让它转动. 一.硬件部分1.使用的硬件板子用的是正点原子的STM32F103 mini板,驱动器是DM420(DM420驱动器资料 ...
- stm32控制电机
一.总体思路 使用端口GPIOA来连接电机,所以给GPIOA编程就可以控制电机.使用系统时钟SysTick来周期性的给电机发送脉冲.用四个按钮来控制需要发送脉冲的个数,每个按钮被按下就设置 ...
- PWM控制led渐变
PWM,中文释义:脉冲宽度调制.它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术. PWM 是一种对模拟信号电平进行数字编码的方法.通过高分辨率计数器的使用,方波的占空比被调制用来对 ...
- OpenGl 导入读取多个3D模型 并且添加鼠标控制移动旋转
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11627508.html 前言: 因为接下来的项目需求是要读取多个3D模型,并且移动拼接,那么我 ...
- 在DirectX9中使用DXUT定制按钮来控制模型旋转的问题
使用DXUT中的按钮控件类实现 控制模型旋转的过程如下: 1.创建一个CDXUTDialog对话框,并绑定至CDXUTDialogResourceManager对话框资源管理器. 2.绑定回调函数GU ...
随机推荐
- jquery 一键复制文本到剪切板
<a id="copy" data-clipboard-text="123456">复制文本</a> $(function(){ var ...
- 安装owncloud出现:Error while trying to create admin user: An exception occurred while executing
安装owncloud出现:Error while trying to create admin user: An exception occurred while executing 1.安装ownc ...
- WinForm外包公司 WInform外包项目监控案例展示
北京动点飞扬软件开发团队 C# WInform监控项目案例展示 长年承接WInForm C#项目开发,商业案例欢迎联系我们索取 有相关项目外包定制开发 欢迎联系我们 qq372900288 Tel 1 ...
- 记一个在移动端调试 web 页面的方法
1. 工具:Weinre 2. 安装:npm -g install weinre | npm install weinre -g --registry=https://registry.npm.tao ...
- IO队列深度max_queue_depth对系统性能的影响
前段时间,发生了一个问题引起了我对IO队列深度的研究. 存储服务器中linux kernel的mpt2sas驱动模块,将max_queue_depth设置为1024时,引起系统加载驱动时卡死,而调整为 ...
- Vue开发环境搭建
Nodejs与包管理工具安装: Windows安装: https://nodejs.org/en/ 下载安装包安装就可以. Ubuntu安装: //安装nodejs sudo apt-get inst ...
- leetcode 921. 使括号有效的最少添加(Python)
class Solution: def minAddToMakeValid(self, S): """ :type S: str :rtype: int "&q ...
- 利用MYSQL的函数实现用户登录功能,进出都是JSON(第二版)
利用MYSQL的函数实现用户登录功能,进出都是JSON(第二版) CREATE DEFINER=`root`@`%` FUNCTION `uc_session_login`( `reqjson` JS ...
- Ubuntu16.04主题美化
系统更新 sudo apt-get update sudo apt-get upgrade # 卸载libreOffice libreoffice是ubuntu自带的开源office软件,体验效果不如 ...
- html的换行代码<br/>介绍和写法
在网页中,我们要对文字进行换行,就需要使用到<br/>标签,写法如下 换行:<br/> <br/>属于一个单独标签,仅需要将需要换行的文字后方加入此标签即可实现换行 ...