resource 发表于 2008-5-27 15:42:21

基与ATMEL ATmega8 的多路舵机控制的实现, 玩多路舵机的朋友可看看

http://www.arm10.net/wenzhang.asp?q=AVR%B5%A5%C6%AC%BB%FA&b=7&id=275
基与ATMEL ATmega8 的多路舵机控制的实现

一般单片机的pwm数量都有限,要作多个舵机的控制就变得麻烦了,
不是采用专用模块,就是专用芯片,本人曾想作个机器人,考虑到
舵机要采用16个,所以就自己用m8模拟一个了。成本低,协议还可以自己定
主要是对舵机的控制起到了学习的目的,
该方案,实现了20路的pwm输出,由于舵机的控制频率不用高,所以mega8完全可以胜任



原理图:



上位机:

PWM上位机V1.rar 点此下载附件


代码如下:

/******************************************
创建时间 2005-5-13
芯片型号 : M8
采用内部RC: 8.0000Mhz
******************************************/

#include <iom8v.h>   
#include <macros.h>   
#include "me.h"                     //自定义的通用io简化位*作   

void timer0_init(void);   
void port_init(void);   
void init(void);   
void UART_init(void);                //串口初始化程序   
void UART_rx(void);                  //串口接收中断函数   
void send_text(unsigned char *s);    //字符串发送函数   
void sendchar(unsigned char c);      //字符发送函数   
void dog_init(void);               //初始化看门狗   

unsigned char RX_data={0};      //串口接收的数据   
unsigned char RX_counter=0;          //串口接收到的字节数计数器   

unsigned char pwm1,pwm2,pwm3,pwm4,pwm5,pwm6,pwm7,pwm8,pwm9,pwm10,pwm11,pwm12,pwm13,pwm14,pwm15,pwm16,pwm17,pwm18,pwm19,pwm20; //分别为20个pwm的值   
unsigned char count;               //pwm定位变量   
void main(void)   
{   
OSCCAL=0xAA;                         //系统时钟校准,不同的芯片和不同的频率   
init();   

   while(1)   
   {   
    WDR();                           //拼命喂狗   
       if(RX_counter==4)             //收到一个完整的命令信息   
       {   
         RX_counter=0;             //清除串口接收到的字节数计数器   

            if((RX_data==''''''''S'''''''')&&(RX_data==''''''''E''''''''))//判断头尾是不是符合   
            {   
                  
               CLI();                //关闭中断,开始判断数据   
               switch(RX_data)   
               {   
                  case 0x01:   
                  pwm1=RX_data;   
                  break;   
                  case 0x02:   
                  pwm2=RX_data;   
                  break;   
                  case 0x03:   
                  pwm3=RX_data;   
                  break;   
                  case 0x04:   
                  pwm4=RX_data;   
                  break;   
                  case 0x05:   
                  pwm5=RX_data;   
                  break;   
                  case 0x06:   
                  pwm6=RX_data;   
                  break;   
                  case 0x07:   
                  pwm7=RX_data;   
                  break;   
                  case 0x08:   
                  pwm8=RX_data;   
                  break;   
                  case 0x09:   
                  pwm9=RX_data;                  
                  break;   
                  case 0x0a:   
                  pwm10=RX_data;   
                  break;   
                  case 0x0b:   
                  pwm11=RX_data;   
                  break;   
                  case 0x0c:   
                  pwm12=RX_data;   
                  break;   
                  case 0x0d:   
                  pwm13=RX_data;   
                  break;   
                  case 0x0e:   
                  pwm14=RX_data;   
                  break;   
                  case 0x0f:   
                  pwm15=RX_data;   
                  break;   
                  case 0x10:   
                  pwm16=RX_data;   
                  break;   
                  case 0x11:   
                  pwm17=RX_data;   
                  break;   
                  case 0x12:   
                  pwm18=RX_data;   
                  break;   
                  case 0x13:   
                  pwm19=RX_data;   
                  break;   
                  case 0x14:   
                  pwm20=RX_data;   
                  break;   
                  default:   
                  SEI();               //错误时打开中断,以便发送错误信息   
                  send_text("ER");   //范围超出20个pwm,就发出大写字母"ER"   
                  break;   
                  
                  
               }      
               SEI();                  //恢复中断允许   
               send_text("OK");      //判断处理完毕返回ok;   
            }   
       }   
   }   
}   


void init(void)   
{   
CLI(); //disable all interrupts   
port_init();   
timer0_init();   
TIMSK = 0x01; //定时器中断源   
UART_init();   
SEI(); //re-enable interrupts   
}   


void port_init(void)   
{   
PORTB = 0x00;   
DDRB= 0xFF;   
PORTC = 0x00;   
DDRC= 0x7F;   
PORTD = 0x00;   
DDRD= 0xFF;   
}   


void send_char(unsigned char c)      //发送单字符函数   
{   
   while (!(UCSRA&(1 << UDRE)));   //判断上次发送有没有完成   
   UDR = c;                        //发送数据   
}   

#pragma interrupt_handler UART_rx: iv_USART_RX //将串口接收中断,指给UART_rx   
/********************************************************
通讯协议:S+PWM?+Volue+E
标志说明:    S : 头标志   16进制:0x53
标志说明: PWM? : PWM标号,?范围:0x01~0x14 指20个pwm输出
标志说明: Volue:PWM占空比,范围0x00~0xff关闭pwm则为0x00   
标志说明:    E : 结束标志 16进制:0x45
其他说明: 完整一个数据占4个byte,头尾必须分别为S、E方为有效
如果pwm位超出20个,返回字母ER;
********************************************************/
void UART_rx(void)                              //串口接收中断函数   
{   
   RX_data = UDR;   
      
   if (RX_data==''''''''S'''''''')            //纠正错位用,和RX_counter溢出。   
       {   
          RX_data=RX_data;   
          RX_counter=0;   
       }   
      
   RX_counter++;                              //接收的字节数计数   
}   


void send_text(unsigned char *s)                //字符串发送函数   
{   
   while (*s)   
       {   
          send_char(*s);   
          s++;   
       }   
}   
   

void UART_init(void)                           //串口初始化程序   
{   
   UCSRB = BIT(RXCIE)| BIT(RXEN) |BIT(TXEN);   //允许串口发送和接收,并响应接收完成中断   
   UBRR = 51;                                  //时钟8Mhz,波特率9600   
   UCSRC = BIT(URSEL)|BIT(UCSZ1)|BIT(UCSZ0);   //8位数据+1位stop位   
}   


/********************************************
设计思路:舵机典型需要20mS的频率,即50Hz的频率
为了实现8位的pwm精度,需将20mS的时间再平均分正
256份,即78.125u秒,在一次中断的时候与目标定义
需要的pwm占空比的值(pwm1~20)比较,判断io是否该
输出0电平,如果不是则输出高电平   
目标中断时间: 78.125uSec (加上0.2%误差)
实际中断时间: 78.000uSec
如果想提高频率,只需要修改定时器的溢出时间
如果想提高pwm的分辨率,则修改count的值
********************************************/   
void timer0_init(void)            //定时器初始化程序   
{   
TCCR0 = 0x00;                  //停止定时器   
TCNT0 = 0xB4;                  //设置初始值   
TCCR0 = 0x02;                  //开动定时器   
}   

#pragma interrupt_handler timer0_ovf_isr:10 //将定时器溢出中断指到timer0_ovf_isr中,好比汇编中的ORG   
void timer0_ovf_isr(void)      //定时器溢出中断程序   
{   
TCNT0 = 0xB4;                   //从新调入初始值   
count++;                        //每中断一次加1   
if (count<pwm1)               //判断pwm1是不是改输出高电平   
{   
portc5_1;   
}else{                        //不是则输出0   
portc5_0;   
}   
   
if (count<pwm2)   
{   
portc4_1;   
}else{   
portc4_0;   
}   
   
if (count<pwm3)   
{   
portc3_1;   
}else{   
portc3_0;   
}   
   
if (count<pwm4)   
{   
portc2_1;   
}else{   
portc2_0;   
}   
   
if (count<pwm5)   
{   
portc1_1;   
}else{   
portc1_0;   
}   
   
if (count<pwm6)   
{   
portc0_1;   
}else{   
portc0_0;   
}   
   
if (count<pwm7)   
{   
portb5_1;   
}else{   
portb5_0;   
}   
   
if (count<pwm8)   
{   
portb4_1;   
}else{   
portb4_0;   
}   
   
if (count<pwm9)   
{   
portb3_1;   
}else{   
portb3_0;   
}   
   
if (count<pwm10)   
{   
portb2_1;   
}else{   
portb2_0;   
}   
   
if (count<pwm11)   
{   
portb1_1;   
}else{   
portb1_0;   
}   
   
if (count<pwm12)   
{   
portd2_1;   
}else{   
portd2_0;   
}   
   
if (count<pwm13)   
{   
portd3_1;   
}else{   
portd3_0;   
}   
   
if (count<pwm14)   
{   
portd4_1;   
}else{   
portd4_0;   
}   
   
if (count<pwm15)   
{   
portb6_1;   
}else{   
portb6_0;   
}   
   
if (count<pwm16)   
{   
portb7_1;   
}else{   
portb7_0;   
}   
   
if (count<pwm17)   
{   
portd5_1;   
}else{   
portd5_0;   
}   
   
if (count<pwm18)   
{   
portd6_1;   
}else{   
portd6_0;   
}   
   
if (count<pwm19)   
{   
portd7_1;   
}else{   
portd7_0;   
}   
   
if (count<pwm20)   
{   
portb0_1;   
}else{   
portb0_0;   
}   
}   


void dog_init(void)    //看门狗初始化   
{   
WDR();       //看门狗计数清零   
WDTCR=0x0F;//使能看门狗,并且,采用2048分频,溢出时间5V时2.1S   
}

zhou_1989 发表于 2008-5-27 15:56:03

帮楼主把照片和软件转过来


http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_294296.jpg
(原文件名:1_2007782358.jpg)

http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_294297.gif
(原文件名:1_2007784037.gif)

点击此处下载 ourdev_294298.rar(文件大小:10K) (原文件名:1_2007784190.rar)
http://cache.amobbs.com/bbs_upload782111/files_10/ourdev_294299.JPG
(原文件名:QQ截图未命名.JPG)

zhxl 发表于 2008-6-12 14:01:02

舵机的工作原理是每20ms输出一个0.5ms-2.5ms的脉冲,楼上的程序好像是每20ms输出一个小于20毫秒的脉冲,如控制信号是120,输出的脉冲宽度为78us*120=9360us,不符合舵机的要求,所以,传输pwm值应小于2.5ms/78us=31,不知道我理解的对不对,请指正。

kpvrzip 发表于 2008-6-13 15:27:45

如果你给普通模拟舵机的信号小于50赫兹每秒,舵机的扭力和反应都回变慢的。

cqr1988215 发表于 2008-7-22 11:15:02

强!!   我也要用M8 控制至少 18路舵机,原来还可以这样啊。。。。

cqr1988215 发表于 2008-7-22 11:36:25

板子上 还有一个 小一点的芯片是干什么用的??   我是新手。。

zcdyyu 发表于 2008-7-22 11:39:45

MAX232之类的电平转换芯片...充当MCU与PC的桥梁...

elecwolf 发表于 2008-8-24 21:36:00

顶一下!

microyao 发表于 2008-8-24 22:19:52

我在控制多路电调的时候是用的汉库的那个双足机器人的控制手法实现的.

tiger2008 发表于 2008-9-1 13:47:08

正在找这方面的资料,狂顶,嘿嘿

fireyes7 发表于 2008-9-7 12:12:22

那个上位机下了用不了啊,说无法注册

XIE2099 发表于 2008-9-8 21:05:14

怎么下不了上位机?
谢谢!

cxq200300 发表于 2008-10-12 10:14:55

上位机不能用

fgw1949 发表于 2009-3-11 10:52:09

谢谢楼主了

hebin939 发表于 2009-3-11 19:57:04

mark

lynnlase 发表于 2009-5-12 16:51:43

程序不实用,效率比较低 就算是仅仅产生2MS脉冲(20ms以内),余下18MS还是在这里做判断效率低下 不能做其他事情。

echooy 发表于 2009-8-21 15:24:34

顶一下,我要一次控制6个就够了

sonyex678 发表于 2009-12-9 17:23:41

mark~

zhangyu1986517 发表于 2009-12-9 21:04:06

mark

sonyex678 发表于 2009-12-10 11:42:03

LZ,我也是新手再玩MEGA控制多路舵机~

想请教几个问题啊~

如果以0.5ms到2.5ms对应舵机0-180的话,也就是每一度是11.1度/us(或是0.9度/10us);

你20ms除以256就是78.125us,也就是舵机最小转角分辨率是7度,这个一般玩玩还可以,如果做双足机器人就不够了;

如果增加分辨率的话那么定时中断的时间就需要短了,能否在一次中断之间内完成20次的判定操作?

或者请问LZ有没有侧过定时器中断一次执行20次判断的最长周期?

谢谢啊~~呵呵~~


PS:那个COUNT计数怎么没有清零啊~

wangxianbin 发表于 2010-9-18 14:52:45

mark

allenjwb 发表于 2010-9-19 12:58:03

mark

huanter 发表于 2010-10-18 11:46:46

上位机的代码有吗?

huanter 发表于 2010-10-19 17:43:15

程序编译时出现如下错误
如何怎么解决,(我是新手)
code address 0 already contains a value
code address 0x1 already contains a value
code address 0x16 already contains a value
.
.
.

kelos3000 发表于 2010-10-19 18:26:39

dddddddd

ldch 发表于 2010-10-19 19:52:51

mark一下

huanter 发表于 2010-10-23 10:39:09

问个问题,为什么我用Proteus物理元件仿真软件没办法输出所要的PWM信号呢?

andriy 发表于 2010-10-23 11:19:11

这个不错

wcm_e 发表于 2010-10-23 11:38:34

mark

3050311118 发表于 2011-3-4 16:15:04

老大 编译不了

diziaihaozhe 发表于 2011-5-30 17:49:53

mark

lbbmx 发表于 2011-6-5 12:13:43

不错,值得夸奖啊。呵呵呵呵

jssz_hf 发表于 2011-6-5 19:18:29

mark

tomy 发表于 2011-6-13 15:56:43

mark^

diziaihaozhe 发表于 2011-6-30 01:55:43

上位机用不了

fghui 发表于 2011-8-28 21:32:38

做一个输出总线,用373选设备,路数太多干脆用373配164,只要你需求的控制频率不太高,肯定行。

HYLG 发表于 2011-8-28 23:24:35

几分钟前才用MEGA48控制了一个9克的舵机,就看到了这个贴子。小舵机挺好玩的。
三年多了,这么好的贴子竟然顶的人不多。

dfood 发表于 2011-8-30 10:16:51

可以在20us内分。如
do
a1.5ms
b2ms
.
.
第20舵机1.5
loop
这样可以满足每个舵机2ms的分辨律,又可以延时18ms。符合舵机标准。
如果用bascom的专用pluse涵数那就更easy拉

Dalong357 发表于 2011-11-1 22:41:58

楼住的方法确实是使用的,但是在计算方法上确实也存在问题,最重要的事,楼主没有考虑到系统的晶振频率问题,我曾经使用飞思卡尔在72M主频的情况下写过类似的算法,可以实现,现在利用AVR发现,方法可用,但是精度大大降低……

kingboy100 发表于 2011-11-2 07:57:39

mark

8s209 发表于 2011-11-2 09:25:51

mark

szy494468597 发表于 2011-11-5 15:53:56

mark

longwu537 发表于 2012-6-19 21:26:42

又看到了自己原来的账号……苦啊,忘了密码的孩子伤不起啊……
页: [1]
查看完整版本: 基与ATMEL ATmega8 的多路舵机控制的实现, 玩多路舵机的朋友可看看