当前位置:网站首页>一个基于ATMEGA128的直流电机抱死程序
一个基于ATMEGA128的直流电机抱死程序
2022-07-21 05:21:00 【help_wei】
最近用ATMEGA128写了一个让直流电机抱死的程序,效果还不错,分享给大家。
先说一下我的硬件情况:一块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)
{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--;}//输入捕捉计数器减1
else //电机正转
{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;
}
边栏推荐
- 正则表达式
- Interview question 02.05 List summation
- Peoplecode objects and classes
- Let interns set up a redis cluster, which almost got me in~~~
- Peoplecode understands the structure and content of component cache
- Analysis of cross apply and outer apply query of SQL Server - Part II
- ensp配置DHCP
- Oracle subquery
- Geographic scene visualization system based on GeoServer
- 2022-07-20 study notes of group 5 self-cultivation class (every day)
猜你喜欢
Connections and differences between three-level dispatching
The shortcomings of LVS and the fourth forwarding type fullnat
Use Mysql to query logs slowly
Zabbx6.0(生产实战)
Blocking and wakeup of processes
Synchronization and mutual exclusion of processes
Deploy tidb in multiple data centers in the same city
Practical exercise | export MySQL tables to CSV
LVS load balancing + high availability
Deployment of three centers in two places
随机推荐
Piracy leads to device paralysis | official solemn statement: do not buy or download Navicat through unofficial channels
Let interns set up a redis cluster, which almost got me in~~~
JDBC connection / operation database of JMeter
Lamp Architecture - MySQL high availability switching (MHA high availability)
DNS domain name resolution service
Encapsulation des machines virtuelles
JMeter's BeanShell generates random Chinese characters
The danger of testing SQL | don't take it lightly
(C language) flexible array
Lamp Architecture - MySQL installation and deployment, MySQL Cluster and master-slave structure (1)
Part 04: guava retry component
Name reference defined by peoplecode
Does Navicat 16 support native Apple silicon M1 chips| Apple user must read
JMeter aggregation Report
Peoplecode variable
Follow the guidelines and improve yourself quickly: how to quickly turn functional testing to automated testing
vim编辑器
Practical exercise | export MySQL tables to CSV
LNMP ------php7 installation
Frequently asked interview questions in software testing [2]