先说一下我的硬件情况:一块ATMEGA128实验板;一个带编码器的80:1的变速电机,编码器的输出端连接到单片机的PD4和PD5引脚;一块电机驱动电路,该电路的输入为:24v电源、两路pwm信号输入,输出即为电机的正负极,要用该电路来驱动电机,则必须让两路pwm输入信号的一路占空比为0,另一路不为0,相当于让电机的一极接地,另一极接pwm,通过控制两路pwm的占空比来控制电机的转速和转动方向。pwm信号的输入端连接到单片机的PD6和PD7引脚。

下面是我的程序的设计思路: 这个程序用了两个定时器:timer0和timer1。

timer0用来产生pwm。timer0产生pwm信号是这样实现的:程序中有一个timer0的溢出事件计数器,和两个保存两路pwm信号占空比的变量,当timer0溢出事件计数器计数超过100时,如果某个pwm信号占空比不为0,则把相应pwm引脚置高电平,同时清零此计数器,当此计数器等于某个占空比时,则把相应引脚置低电平,从而实现timer0溢出事件计数器从0计数到100时输出一个周期的pwm信号。通过调节timer0的溢出频率,即可调节pwm信号输出的频率。

timer1用来对编码器的输出进行计数,同时调整pwm的占空比,实现对电机的控制。对编码器的输出计数是利用了timer1的输入捕捉功能,由于电机可以正转,也可以反转,导致编码器的CHA和CHB的输出也不同,所以可以在程序中可以判断电机是正转还是反转,再对编码器的输出脉冲进行计数,当电机正转的时候计数增加,电机反转的时候计数减少,所以编码器的计数值是有正负的。从而可以知道什么时候该通过调整pwm来控制电机。

下面是我的调试过程,也算是一点经验吧:以开始的思路是只要编码器的计数值不为0,我就要让电机反方向转动,以保持电机抱死,发送给电机的pwm是固定的数值,但是这样反而是抱不死,它在前后地抖动,而且pwm的占空比越大,电机抖动得越厉害,这样显然不行;后来想了一个办法,就是如果编码器的计数值在一定的范围内,我就不用让电机反方向转动。因为这个电机是变速电机,如果电机里面只转动一点点,在外面看来就相当于不动,这样的话就给电机预留了一部分转动的空间,用来消除抖动就是说在这个空间内是不发送pwm给电机的,或者说电机两极的pwm占空比都为0。这样一来,当pwm占空比比较低时,是可以消除抖动,但是力气不大,就是说还是可以用钳子拧得动,调了很久都无法在抖动和电机力气之间取得平衡。后来又想了一个办法,在这个基础上再改进,因为之前的pwm占空比都是不变的,所以很难达到令人满意的效果,现在的方法是,根据电机被拧动的角度,或者说编码器的计数值大小来调整pwm的占空比,编码器的计数值偏离0越多(正或负得越大),pwm的占空比就越大,电机的力气也就越大,从而不会出现电机一旦被拧动就马上以最大速度转回去的情况,抖动也就消除了,而且电机力气很大。

  编译环境是AVR Studio 5.0,下面是程序代码:#include <avr/io.h>
#include <avr/interrupt.h> int forward = 0, reverse = 0;//存储电机正转和反转pwm占空比的变量
int timer0_count = 0;//timer0溢出事件计数器
int capt_count = 0;//输入捕捉事件计数器void port_init(void)
{
PORTA = 0x00;
DDRA  = 0x00;
PORTB = 0x00;
DDRB  = 0x00;
PORTC = 0x00; //m103 output only
DDRC  = 0x00;
PORTD = 0x00;
DDRD  = 0xC0;
PORTE = 0x00;
DDRE  = 0x00;
PORTF = 0x00;
DDRF  = 0x00;
PORTG = 0x00;
DDRG  = 0x00;
}void timer0_init(void)
{
TCCR0 |= 5;//256分频,普通模式
TIMSK |= 0x01;//timer0溢出中断
TCNT0 = 0xFE;//TCNT0赋初值
}void timer1_init(void)
{
TCCR1B = 0x00;//停止
TCCR1A = 0x00;//普通模式
TCCR1C = 0x00;
TCNT1 = 0;//计数初值
TCCR1B = 0xC4;//启动定时器,256分频,使能输入捕捉噪声抑制器,输入捕捉触发沿选择:上升沿
TIMSK = 0x24;//输入捕捉中断使能,T/C1溢出中断使能
}/************************************************************************/
/*  timer0溢出中断函数,产生提供给电机的pwm                             */
/************************************************************************/
ISR(TIMER0_OVF_vect)//200kHz
{
TCNT0 = 0xFE;//TCNT0重新赋值
//当timer0_count等于100时,如果正转或反转的占空比不为0,则相应引脚输出高电平
if(++timer0_count >= 100)//对timer0溢出事件计数100次,相当于100分频,最后输出到电机的pwm频率是2kHz
{
timer0_count = 0;
if(forward != 0)//forward, reverse存储电机正转和反转pwm占空比的变量
{PORTD |= (1<<6);}
if(reverse != 0)
{PORTD |=(1<<7);}
}
//当timer0_count等于正转或反转的占空比时,相应引脚输出低电平,实现输出pwm信号
if(timer0_count == forward)
{PORTD &= ~(1 << 6);}
if(timer0_count == reverse)
{PORTD &= ~(1 << 7);}
}/************************************************************************/
/*  timer1输入捕捉中断函数,对编码器输出的上升沿进行计数              */
/************************************************************************/
ISR(TIMER1_CAPT_vect)
{if(PIND & (1 << 5))//电机反转{capt_count--;}//输入捕捉计数器减1else               //电机正转{capt_count++;}//输入捕捉计数器加1
}/************************************************************************/
/*  timer1溢出中断函数,100Hz,用于调整电机转速和转动的方向,实现电机抱死*/
/************************************************************************/
ISR(TIMER1_OVF_vect)
{
TCNT1 = 64910;                           //重新给TCNT1赋值
static unsigned char motor_state = 0;    //电机的状态,标志电机是正转还是反转,0:正转,1:反转
switch(motor_state)
{
case 0://电机正转时
if(capt_count > 40)              //如果编码器正转计数超过40,则电机需要反转,以保持电机不动
{reverse = capt_count - 40;} //直接把编码器计数值减去40,作为反转的占空比
else if(capt_count < 0)          //如果编码器计数值小于0
{motor_state = 1;}           //进入状态1
else                             //如果编码器计数值在0~40内,为了不发生抖动,不需要反转
{reverse = 0;}               //反转的占空比为0,相当于负极接地
forward = 0;                     //正转的占空比为0,相当于正极接地
break;case 1:
if(capt_count < -40)             //如果编码器反转计数超过40,则电机需要正转,以保持电机不动
{forward = (-capt_count) - 40;}//直接把编码器计数值减去40,作为正转的占空比
else if(capt_count > 0)          //如果编码器计数值大于0
{motor_state = 0;}           //返回状态0
else                             //如果编码器计数值在-40~0内,为了不发生抖动,不需要正转
{forward = 0;}               //正转的占空比为0,相当于正极接地
reverse = 0;                     //反转的占空比为0 ,相当于负极接地
break;default:
break;
}
}void Init_Devices(void)
{
cli();//关闭全局中断
port_init();//I/O口初始化
timer1_init();//定时/计数器1初始化
timer0_init();//计时/计数器0初始化
sei();//打开全局中断
}int main(void)
{Init_Devices();while(1){}return 0;
}

转载于:https://www.cnblogs.com/wangh0802PositiveANDupward/archive/2012/09/04/2671062.html

一个基于ATMEGA128的直流电机抱死程序相关推荐

  1. 一个基于codeigniter的淘宝客程序源码,适合学习参考,京东拼多多淘宝联盟api

    一个基于codeigniter的淘宝客程序源码,直接调用淘宝联盟api,效果图如下: 该源码需要的环境是php5.6,适合初学者开发学习之用. 下载资源地址分享, https://download.c ...

  2. 一个基于stream的EPICS IOC应用程序

    本文将介绍如何开发一个基于stream的EPICS IOC应用程序,其将作为一个简单的基于消息的设备(用于EPICS stream练习的设备模拟程序_yuyuyuliang00的博客-CSDN博客中最 ...

  3. JAVA小程序:一个基于MVC框架的贪吃蛇程序

    学习JAVA也有一段时间了,之前看了翁恺老师的视频,跟着做了一个细胞自动机,粗浅地了解了一点MVC框架的知识,感觉获益匪浅.但是细胞自动机毕竟是跟着视频完成的,有很大程度上都是参考了视频里的代码,没有 ...

  4. Java使用IntelliJ IDEA创建一个基于Swing的GUI图形化程序,打包发布为jar

    1.创建GUI窗体 首先,直接新建一个默认的空的Java模块即可,随便取个名字 之后再src目录下右键,新建,创建一个Swing的GUI窗体,随便取个名字 给主窗体改个名字 到java代码中生成一个窗 ...

  5. java在线问答系统_求一个基于JAVA的在线答疑系统程序

    /** * 计算 * * @author 3306 2017年5月9日 * @see * @since 1.0 */public class NumPicker { public static voi ...

  6. 基于XMPP的IOS聊天客户端程序(XMPP服务器架构)

    最近看了关于XMPP的框架,以文本聊天为例,需要发送的消息为: <message type="chat" from="kang@server.com" t ...

  7. 基于VTK与Qt的体绘制程序

    基于VTK与Qt的体绘制程序 分类: VTK Qt Volume Rendering C&C++ 2013-06-10 09:17 3434人阅读 评论(13) 收藏 举报 目录(?)[+] ...

  8. [iPhone高级] 基于XMPP的IOS聊天客户端程序(XMPP服务器架构)

    最近看了关于XMPP的框架,以文本聊天为例,需要发送的消息为: [html] view plaincopy <message type="chat" from="k ...

  9. 树莓派开发笔记(九):基于CSI口的摄像头拍照程序(同样适用USB摄像头)

    若该文为原创文章,未经允许不得转载 原博主博客地址:https://blog.csdn.net/qq21497936 原博主博客导航:https://blog.csdn.net/qq21497936/ ...

  10. 树莓派android摄像头驱动开发,树莓派开发笔记(九):基于CSI口的摄像头拍照程序(同样适用USB摄像头)...

    前话 前面开发完GPIO口的功能,还剩下2个接口,一个是摄像头,一个是显示屏.本篇我们开发一个基于CSI接口的摄像头拍照程序. Demo:摄像头CSI的拍照程序 树莓派摄像头 笔者买的不是USB网络摄 ...

最新文章

  1. 3D机器人视觉在仓储物流和工业自动化领域的应用 | AI ProCon 2019
  2. 解决HierarchyViewer不能连接真机的问题
  3. Kubernetes存储之Secret
  4. SSH-CLIENT : gSTM
  5. Android角落 不妨再看LinearLayout
  6. 深入了解Java 8中的可选类API
  7. linux水滴怎么抓包,tcpdump 抓包
  8. vim php 配置文件,spacevimsetting
  9. 条形码和二维码的原理简介
  10. 软件测试体系学习及构建(20)测试专项丨易用性测试
  11. 考勤系统的软件上位机设计
  12. 全球酷站中心 html5网站,除了Behance,还有哪些优质设计资源站
  13. 江苏计算机对口高考分数线,单招好还是高考好|江苏省对口单招 历年分数线是多少?...
  14. .join()用法 | python学习
  15. 桌面虚拟化中VDI和IDV的五大区别
  16. 聚焦网络攻击|知道创宇云安全2018年度网络安全态势报告
  17. PDF如何翻译?看完这个方法就学会了
  18. 主板后置音频接口图解_不用再看说明书 机箱主板接线全攻略
  19. 超全!Tkinter 使用教程!4000字!
  20. 从今起,我蹲马步,苦练基本功

热门文章

  1. Fences v3.0.9 中文破解版,总结了一些注意事项,亲测可用
  2. spring cloud微服务(二)euraka服务消费者
  3. mysql 查看环境变量_MySQL的环境变量
  4. 新版itunes不显示图书_告别 iTunes!苹果新版mac操作系统正式上线,12大升级亮点解读...
  5. 前端H5面试题Js: Javascript 创建对象的几种方式? (必会)
  6. oracle sql列转行_SQL列转行及行转列
  7. Oracle数据库日志存放位置
  8. 使用calibre制作kindle电子书
  9. java实现ssdp协议_SSDP协议最简单的Reactor / Netty实现是什么?
  10. 电脑打开应用程序提示配置系统未能初始化--解决方案