
五线舵机改串口舵机
简介
市面上五线舵机很便宜,和普通舵机不同,我做的这个舵机控制板是串口485通讯的(不是为航模设计的哦) 这种舵机控制板的优点:接线简单,所有舵机均连接在由4根线构成的总线上(+5V\GND\A\B)
简介:市面上五线舵机很便宜,和普通舵机不同,我做的这个舵机控制板是串口485通讯的(不是为航模设计的哦) 这种舵机控制板的优点:接线简单,所有舵机均连接在由4根线构成的总线上(+5V\GND\A\B)开源协议
:GPL 3.0
描述
设计目的
大家应该发现市场上有好多五线舵机出售,个头大,力气大,价格也相当便宜,我是1.8元一个买的。如下图所示。
拆掉好的舵机控制板或买一块控制板总感觉不合算,成本比这款舵机都贵了。于是我决定自制一款。用航模遥控器控制舵机也是一种方法,但我不喜欢,因为我最终的目的是用这些舵机去做机械手之类的玩具,我想直接通过电脑去控制这些舵机会更好一点。因为动作我随时可以在电脑上编程改变。
于是便有了这次的设计目的以简单方法(新手也能轻松上手)较低成本(不超过5元,不计人工情况下哦,实际我的连舵机在内都没超过5元)《自制一款通过485串口通讯的舵机控制板》(我去看了一下,这种好像叫总线舵机)

主要元器件的选择
五线舵机里面有一个电机(两根线),一个电位器(三根线)所以它才会是5根线。因为舵机内部的空间较小,容纳不下太大太多的元件,所以本次设计均采用贴片元件,一些保护电路也能省则省。
主要元件如下:
1、STC8G1K08A-36I-SOP8 单片机 1片 (主控芯片)

2、MAX485-SOP8 1片 (用于通讯)

3、RZ7889-SOP8 1片 (电机驱动)

其他非必要元件:
4、1k电阻 0603 1个(led限流电阻,阻值不是非要1k,5.1k,10k都可以,led也会亮)
5、led 0603 1个(用于指示通讯状态)
6、电容 100nf 0603 1个 (俩电容主要用于滤波,减小电压波动对单片机的干扰)
7、电容 10uf 0603 1个
总体设计思路:单片机比较给定信号和电位器反馈信号的大小,通过RZ7889控制电机转动,从而使电位器信号发生变化,直到电位器信号和给定信号相等。

主要元件引脚图
1、STC8G1K08A-36I-SOP8 引脚图

2、MAX485-SOP8 引脚图

3、RZ7889-SOP8 引脚图

PCB说明
pcb上的3个插座是不需要购买的,那里是直接焊线的位置。485口,电机,和电位器我没有明确标明哪接哪,是因为这些地方我也分不清,最容易接反。焊接第一块pcb时注意(如果你有好多舵机需要改造),3个主要芯片焊接好之后,原则上先焊接485,然后用电脑与板子通讯,看是否能正常通讯。如果通讯不上,给485口的两根线调换一下顺序(在485上调换,不是在pcb上调换线序,之后你所有的板子都按照这个线序焊就行了)。通讯正常后焊接电位器,用手搬动舵机臂,看电脑上的反馈数据是否正常,正常之后焊接电机。这里注意,因为电机线序可能会焊反,所以要小心。试验的时候随时断电,试验的时候不要给太大的舵量,如果正常,舵机到达正常位置停下来,如果反了,就会达到极限位置卡在那里,所以要及时断电,调整电机线序。经过以上就确定了控制板接线的线序,之后的控制板按照刚才试验的线序焊就好了。
另外在焊接单片机之前,最好先把固件下载进去。不然焊接上再想下载有点困难,尤其是对于新手来说。提示:固件在附件里,当然源程序也在。

软件说明(能力有限,本人只会编写这样初级水平的程序,如果有喜欢此项目的小伙伴,本人不介意程序被复制篡改,程序中的协议是我拍脑门拍出来的,并未做过多的思考,毕竟只是为了玩)
``` #include "reg51.h" #include "intrins.h" #include "stdlib.h" #define FOSC 11059200UL #define BRT (65536 - FOSC / 9600 / 4)sfr AUXR = 0x8e;
sfr P5 = 0xC8;
sfr P0M1 = 0x93;
sfr P0M0 = 0x94;
sfr P1M1 = 0x91;
sfr P1M0 = 0x92;
sfr P2M1 = 0x95;
sfr P2M0 = 0x96;
sfr P3M1 = 0xb1;
sfr P3M0 = 0xb2;
sfr P4M1 = 0xb3;
sfr P4M0 = 0xb4;
sfr P5M1 = 0xc9;
sfr P5M0 = 0xca;
sfr IAP_DATA = 0xC2;//EEPROM 相关寄存器
sfr IAP_ADDRH = 0xC3;
sfr IAP_ADDRL = 0xC4;
sfr IAP_CMD = 0xC5;
sfr IAP_TRIG = 0xC6;
sfr IAP_CONTR = 0xC7;
sfr IAP_TPS = 0xF5;
sfr ADC_CONTR = 0xbc;//ADC相关寄存器
sfr ADC_RES = 0xbd;
sfr ADC_RESL = 0xbe;
sfr ADCCFG = 0xde;
sfr P_SW2 = 0xba;
#define ADCTIM (*(unsigned char volatile xdata *)0xfea8)
sbit d1= P5^4;
sbit d2= P5^5;
sbit led=P3^1;
sbit RS485_DIR = P3^2;
bit flagFrame = 0; //帧接收完成标志,即接收到一帧新数据
bit flagTxd = 0; //单字节发送完成标志,用来替代TXD中断标志位
bit ctrl;
unsigned char m=0,n=0,mn=0,nm=0;
unsigned char cntRxd = 0; //接收字节计数器
unsigned char pdata bufRxd[64]; //接收字节缓冲区
unsigned char add,setangry,myangry,minangry,maxangry;
extern void UartAction(unsigned char *buf, unsigned char len);
void Delay20ms() //@11.0592MHz
{
unsigned char i, j;
i = 216;
j = 37;
do
{
while (--j);
} while (--i);
}
//==============EEPROM=======================================================
void IapIdle()
{
IAP_CONTR = 0; //关闭IAP功能
IAP_CMD = 0; //清除命令寄存器
IAP_TRIG = 0; //清除触发寄存器
IAP_ADDRH = 0x80; //将地址设置到非IAP区域
IAP_ADDRL = 0;
}
char IapRead(int addr)
{
char dat;
IAP_CONTR = 0x80; //使能IAP
IAP_TPS = 12; //设置等待参数12MHz
IAP_CMD = 1; //设置IAP读命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_();
dat = IAP_DATA; //读IAP数据
IapIdle(); //关闭IAP功能
return dat;
}
void IapProgram(int addr, char dat)
{
IAP_CONTR = 0x80; //使能IAP
IAP_TPS = 12; //设置等待参数12MHz
IAP_CMD = 2; //设置IAP写命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_DATA = dat; //写IAP数据
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
nop();
IapIdle(); //关闭IAP功能
}
void IapErase(int addr)
{
IAP_CONTR = 0x80; //使能IAP
IAP_TPS = 12; //设置等待参数12MHz
IAP_CMD = 3; //设置IAP擦除命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
nop(); //
IapIdle(); //关闭IAP功能
}
//==============================================================================
void Delay100us() //@11.0592MHz
{
unsigned char i, j;
i = 2;
j = 109;
do
{
while (--j);
} while (--i);
}
/* 串口数据写入,即串口发送函数,buf-待发送数据的指针,len-指定的发送长度 */
void UartWrite(unsigned char *buf, unsigned char len)
{
RS485_DIR = 1; //RS485设置为发送
while (len--) //循环发送所有字节
{
flagTxd = 0; //清零发送标志
SBUF = buf++; //发送一个字节数据
while (!flagTxd); //等待该字节发送完成
}
Delay100us(); //等待最后的停止位完成,延时时间由波特率决定
RS485_DIR = 0; //RS485设置为接收
}
/ 串口数据读取函数,buf-接收指针,len-指定的读取长度,返回值-实际读到的长度 */
unsigned char UartRead(unsigned char *buf, unsigned char len)
{
unsigned char i;
if (len > cntRxd) //指定读取长度大于实际接收到的数据长度时,
{ //读取长度设置为实际接收到的数据长度
len = cntRxd;
}
for (i=0; i 0) //接收计数器大于零时,监控总线空闲时间
{
if (cntbkp != cntRxd) //接收计数器改变,即刚接收到数据时,清零空闲计时
{
cntbkp = cntRxd;
idletmr = 0;
}
else //接收计数器未改变,即总线空闲时,累积空闲时间
{
if (idletmr < 30) //空闲计时小于30ms时,持续累加
{
idletmr += ms;
if (idletmr >= 30) //空闲时间达到30ms时,即判定为一帧接收完毕
{
flagFrame = 1; //设置帧接收完成标志
}
}
}
}
else
{
cntbkp = 0;
}
}
/* 串口驱动函数,监测数据帧的接收,调度功能函数,需在主循环中调用 */
void UartDriver()
{
unsigned char len;
unsigned char pdata buf[40];
if (flagFrame) //有命令到达时,读取处理该命令
{
flagFrame = 0;
len = UartRead(buf, sizeof(buf)-2); //将接收到的命令读取到缓冲区中
UartAction(buf, len); //传递数据帧,调用动作执行函数
}
}
/* 串口中断服务函数 */
void InterruptUART() interrupt 4
{
if (RI) //接收到新字节
{
RI = 0; //清零接收中断标志位
TR0= 0;
if (cntRxd < sizeof(bufRxd)) //接收缓冲区尚未用完时,
{ //保存接收字节,并递增计数器
bufRxd[cntRxd++] = SBUF;
}
}
if (TI) //字节发送完毕
{
TI = 0; //清零发送中断标志位
flagTxd = 1; //设置字节发送完成标志
}
}
void UartInit()//串口初始化
{
SCON = 0x50;
TMOD = 0x00;
TL1 = BRT;
TH1 = BRT >> 8;
TR1 = 1;
AUXR = 0x40;
}
void Timer0Init(void) //5毫秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x00; //设置定时初始值
TH0 = 0x28; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 =1;
}
void main()
{
P0M0 = 0x00;
P0M1 = 0x00;
P1M0 = 0x00;
P1M1 = 0x00;
P2M0 = 0x00;
P2M1 = 0x00;
P3M0 = 0x00;
P3M1 = 0x08;//设置P3.3为ADC口
P4M0 = 0x00;
P4M1 = 0x00;
P5M0 = 0xff;
P5M1 = 0x00;
ADCTIM = 0x3f; //设置ADC内部时序
ADCCFG = 0x0f; //设置ADC时钟为系统时钟/2/16
ADC_CONTR = 0x80; //使能ADC模块
ADC_CONTR |= 0x43; //启动AD转换
ctrl=0;
d1=1;d2=1;
UartInit();
Timer0Init();
ES = 1;
EA = 1;
add = IapRead(0x400);
minangry = IapRead(0x402);
maxangry = IapRead(0x404);
mn = IapRead(0x406);
nm = IapRead(0x408);
RS485_DIR = 0;
while (1)
{
while (!(ADC_CONTR & 0x20)); //查询ADC完成标志
ADC_CONTR &= ~0x20; //清完成标志
myangry = ADC_RES; //读取ADC结果
ADC_CONTR |= 0x43;
if ((ctrl)&&(setangry>=minangry)&&(setangry<=maxangry))
{
if (myangry>(setangry+mn)){{d1=1;d2=0;}}
if (myangry<(setangry-nm)){{d1=0;d2=1;}}
if((myangry<=(setangry+mn))&&(myangry>=(setangry-nm))) {d1=1;d2=1;}
}
UartDriver(); //调用串口驱动
UartRxMonitor(1); //串口接收监?
}
}
/*============================通讯协议解释========================================================
上位机指令由5字节组成 buf[0],buf[1],buf[2],buf[3],buf[4]
buf[0]通讯地址
buf[1]指令码 1=设置地址 2=设置角度 3=查询 4=停机 5=瞬时旋转小角度 6=逆势旋转小角度 7=设置最小角度和最大角度
buf[2],buf[3]数据 如果数据只用1个请用buf[3] 地址 设置角度 都是1位数据,只有指令7需要2字节,buf[2]是最小角度buf[3]是最大角度
buf[4]效验码为,buf[0]+buf[1]+buf[2]+buf[3]之和除256的余数
如果数据位无用也可以用无意义数代替,但校验码也需要计算无意义数
指令56用于人工测试舵机最大和最小角度7用于设置最大最小角度,当用指令2设置角度不在区间内,舵机不动作
指令3返回数据,第一位现行地址第二位eeprom内存的地址第三位设置角度第四位现在实际角度第五位最小角度第六位最大角度第七位校验码
通用地址为FF,在不知道某个舵机地址的情况下可用FF对其进行操作,但最好此时只接这一个舵机。
地址和最大最小角度将存在eeprom里
指令8为惯性提前量
*/
void UartAction(unsigned char *buf, unsigned char len)
{
//判断接收数据有效性
if (((buf[0]==add)||(buf[0]==0xff))&&buf[4]==(buf[0]+buf[1]+buf[2]+buf[3])%256&&len==5)
{
if(buf[1]==1){add=buf[3];IapErase(0x400);IapProgram(0x400,add);IapProgram(0x402,minangry);IapProgram(0x404,maxangry);IapProgram(0x406,mn);IapProgram(0x408,nm);}//设置地址
if(buf[1]==2){setangry=buf[3];ctrl=1;m=1;} //定时器0开始计时//设置角度
if(buf[1]==3){buf[0]=add;buf[1]=IapRead(0x400);buf[2]=setangry;buf[3]=myangry;buf[4]=minangry;buf[5]=maxangry;buf[6]=mn;buf[7]=nm;buf[8]=buf[0]+buf[1]+buf[2]+buf[3]+buf[4]+buf[5]+buf[6]+buf[7];UartWrite(buf, 9);}//返回地址号和角度
if(buf[1]==4){ctrl=0;d1=1;d2=1;} //强行停机
if(buf[1]==5){ctrl=0;d1=1;d2=0;Delay20ms();d1=1;d2=1;}//人工转,轴向人,逆时针,电位器变小
if(buf[1]==6){ctrl=0;d1=0;d2=1;Delay20ms();d1=1;d2=1;}//人工转,轴向人,顺时针,电位器变大
if(buf[1]==7){minangry=buf[2];maxangry=buf[3];IapErase(0x400);IapProgram(0x400,add);IapProgram(0x402,minangry);IapProgram(0x404,maxangry);IapProgram(0x406,mn);IapProgram(0x408,nm);}//设置角极限值
if(buf[1]==8){mn=buf[2];nm=buf[3];IapErase(0x400);IapProgram(0x400,add);IapProgram(0x402,minangry);IapProgram(0x404,maxangry);IapProgram(0x406,mn);IapProgram(0x408,nm);}//设置惯性变量
}
}
void TM0_Isr() interrupt 1
{
if(++m>=200) {m=0;ctrl=0;}
}
实物展示

我买的舵机,它的电位器是用一个带大平垫的螺丝固定的,为了避免线路板接触螺丝短路,我用3d打印机打印了一个隔层。这个你也可以用其他绝缘材料代替。我的绝缘垫安装进去如下图所示。再拧颗螺丝进行固定就可以了。焊工很渣凑合看吧,又舍不得SMT费用。毕竟我的设计低成本是永恒的主题。

我做这个舵机最终就是为了完成下面这个东西,我想就叫他机械手吧,当然这只是初级的,和人家成品机械手没法比。就是个玩,为的就是掌握某种知识。正常机械手爪子不是这样,网上13元一个,没舍得,自己设计了一个。你看它像啥,其实是根据卡尺设计的。



注意事项
首次焊接时因不知道舵机旋转方向和电位器反馈信号方向是否一致,所以在实验时舵机电源上最好有能快速分断的开关。如果真的是方向错误,把电机上的两根线对调一下就可以了。

关于演示视频(就让我的机械手动弹两下吧,实际并未开发出像样的上位机软件呢,仅用STC的串口助手发了4条指令,只是证明一下舵机程序是好用的)
注意,本文不是教大家制作机械手,我用机械手演示是告诉你们我做的这个舵机能干什么,同时证明舵机程序及电路是有效的,能用的。我的机械手还没有完善,动作还很不协调,最主要的是头重脚轻,各种尺寸还需要修改,因此不赞成大家模仿。
源程序及固件都在附件里
欢迎有疑问的小伙伴留言探讨问题,一同玩耍吧!做PCB一定要按照自己的舵机尺寸来,修改一下,我这电路简单就几根线,注意电源线粗一点就行,别急着下单,我用别人的文件直接下单没有一次是能用的,都浪费了。我买了10个舵机做了6个还有4个留着做别的DIY。
项目完成后的思考
现在看貌似舵机能正常工作了,但是它有一个弊端,就是所有舵机不能同时运动,舵机速度太快了(机械手运动太快了,感觉害怕,尤其是大臂动起来),之后该给他的程序里加上斜坡加减速算法了。感觉将来做一个示教器指导他工作还是有必要的。下一步我准备1做个示教器2改协议让所有舵机能同时运动3增加斜坡算法控制运动速度。当然这些是下一个项目要解决的问题
设计图
未生成预览图,请在编辑器重新保存一次BOM
暂无BOM
克隆工程工程成员
知识产权声明&复刻说明
本项目为开源硬件项目,其相关的知识产权归创作者所有。创作者在本平台上传该硬件项目仅供平台用户用于学习交流及研究,不包括任何商业性使用,请勿用于商业售卖或其他盈利性的用途;如您认为本项目涉嫌侵犯了您的相关权益,请点击上方“侵权投诉”按钮,我们将按照嘉立创《侵权投诉与申诉规则》进行处理。
请在进行项目复刻时自行验证电路的可行性,并自行辨别该项目是否对您适用。您对复刻项目的任何后果负责,无论何种情况,本平台将不对您在复刻项目时,遇到的任何因开源项目电路设计问题所导致的直接、间接等损害负责。










