|
本帖最后由 XTXB 于 2018-2-20 19:02 编辑
大伙见过类似的东东吧?最早是老外用arduino做的开源项目,用的是avr芯片,春节闲来无事,移植到STC,供大家一乐。
本人业余电子爱好者,多年前从AVR入坑,得到坛友们的无私奉献,收获许多快乐,多谢了!到现在还是菜鸟一个,代码都是东拼西凑的,多多包涵。
在此特别感谢: 傻孩子,马潮,鸿哥,有名称的**,heicad,还有众多网友,当然更要感谢阿莫,感谢amoBBS!
arduino代码:https://github.com/9a/plotclock/blob/master/plotclock.ino
网友作品视频地址:http://v.youku.com/v_show/id_XODIxNTk4MzA4.html。
左图是STC完工成品,数码管显示时,分,星期,写字时数码管熄灭,右图是网友arduino作品。
STC芯片下载线只需四根,串口无需晶振,又不用复位电路,单面板DIY排版超级简洁方便,这也是我不用AVR芯片DIY的主要原因了。
三个舵机的电源端合在一起,电容躺着不占地方,数码管是单排的,把管脚弯了90°,装在TOP层,哦!数码管装倒了。
题外话,硅胶线(用30#细线)做下载线就是爽:软,耐高温,胶皮多次焊接不熔,拆普通杜邦线的插头焊上,爽得不要不要的。
用SolidWorks画的外壳,部分设计参照网友的外壳,在此感谢了。个人经验,PAL材料壳体厚度1.5mm比较好,强度够用,打印也迅速。
笔座XY坐标与舵机角度转换的数学模型,当初我看arduino程序时只敢照抄,后来画图仔细研究了一下,其实就是初中几何及三角函数而已。
C语言程序实现:------从xy坐标到舵机角度转换函数,有了以上的推导,C实现起来好像也不是那么复杂了。
- ***********************************************************/
- float return_angle(float a, float b, float c) //备用函数,已知ABC三边求AC夹角
- {
- // cosine rule for angle between c and a
- return acos((a * a + c * c - b * b) / (2 * a * c));
- }
- //固定尺寸OL=L1,OT=L2,TH=L3 ∠TMO=120°,∠HTO=46.7°(实际测量角度)
- void set_XY(double Tx, double Ty) 从xy坐标到舵机角度转换函数,
- {
- float dx;float dy;float c;float a1;float a2;float Hx;float Hy;
- delay_ms(1);//写字速度调节
- // calculate triangle between pen, servoLeft and arm joint
- // cartesian dx/dy
- //左舵机臂长度L1,OT=L2
- dx = Tx - O1X;
- dy = Ty - O1Y;
- // polar lemgth (c) and angle (a1)
- c = sqrt(dx * dx + dy * dy); // 笔(Tx,Ty)到左舵机中心点L的距离TL
- a1 = atan2(dy, dx); //a1=(Tx,Ty)到左舵机中心点与X轴夹角a1=∠TLC,
- a2 = return_angle(L1, L2, c);//a2=LO与LT的夹角∠TLO
- write_Ms(1,floor(((a2 + a1 - M_PI) * SERVOFAKTORLEFT) + SERVOLEFTNULL));//驱动左舵机
- //左舵机角度∠OLC=∠TLO+∠TLC=a1+a2
- //舵机总角度180°(弧度3.14)对应总脉宽2000us,
- //角度与脉宽转换比例SERVOFAKTORLEFT=总脉宽2000us/总角度180°=2000/3.14=637
- //舵机臂安装起始角度=SERVOLEFTNULL
- a2 = return_angle(L2, L1, c);//a2=L2与C的夹角∠OTL
- //三叉支点H的坐标=(Hx,Hy)
- //L3与Y轴的夹角=a1-a2+46.7°
- //46.7°=0.815(OT与HT夹角,机械结构决定的)
- //∠MTH=∠0TG+∠OTH+180°=(∠LTH-∠LTO)+∠OTH+180°
- Hx = Tx + L3 * cos((a1 - a2 + 0.815) + M_PI);
- Hy = Ty + L3 * sin((a1 - a2 + 0.815) + M_PI);
- // calculate triangle between pen joint, servoRight and arm joint
- dx = Hx - O2X;
- dy = Hy - O2Y;
- c = sqrt(dx * dx + dy * dy);//C=HR
- a1 = atan2(dy, dx);//a1=∠HRC,
- a2 = return_angle(L1, 45, c);//a1=∠HRP,HP=45
- write_Ms(2,floor(((a1 - a2) * SERVOFAKTORRIGHT) + SERVORIGHTNULL));//驱动右舵机
- //右舵机角度∠PRC=∠MRC-∠MRP=a1-a2
- }
- //
复制代码
C语言程序实现:------舵机角度与高电平脉宽的转换
舵机脉冲周期20ms 0°-180°对应高电平0.5ms-2.5ms 相差2000us,角度与脉宽转换比例 SERVOFAKTORLEFT=总脉宽/总角度=2000/3.14=637。
左舵机的驱动函数可以写成write_Ms(1,floor(((a1 + a2 - M_PI) * SERVOFAKTORLEFT) + SERVOLEFTNULL)); (floor是取整函数)
1为左舵机编号,由于舵机安装起始角度无法绝对准确,所以先减M_PI=180°,后加180°=SERVOLEFTNULL=2000作为可调补偿量,当初为这个一减一加困惑了很久。
这是arduino写的驱动函数:servo2.writeMicroseconds(floor(((a1 + a2 - M_PI) * SERVOFAKTORLEFT) + SERVOLEFTNULL));
右舵机初始跟左舵机垂直,可调补偿量SERVORIGHTNULL=1000,就是直接加90°。
由于设定字体的中心纵坐标为0,设定01Y=02Y=-25 笔尖位于蓝色中心线最左到最右,旋转角度为90°,舵机摇臂初始安装时与极限位置(橙色虚线)的夹角45°时最佳。
C语言程序实现:------3个舵机驱动脉冲的产生
整体思路:参照开源程序,占用一个16位定时器,连续输出3个舵机所需的高电平,加上剩下的低电平时间凑够舵机驱动周期20ms。
- ***********************************************************/
- void write_Ms(unsigned char Channel,unsigned int value)//舵机驱动函数,channel=通道,value=高电平脉宽(550-2500)us
- {
- TR0 = 1;//启动定时器0
- PWM_Value[Channel]=(value*2);//时钟24MHz 定时器12分频 计数2次1us
- if(PWM_Value[Channel]>5000)PWM_Value[Channel]=5000;//脉冲宽度不大于2.5ms
- if(PWM_Value[Channel]<1200)PWM_Value[Channel]=1200;//脉冲宽度不小于0.6ms
- }
- void Timer0_Init(void) //servo定时器初始化@24MHz
- {
- AUXR &= 0x7F; //定时器时钟12T模式24MHz
- TMOD &= 0xF0; //设置定时器模式
- TL0=(65536-5000)%256;//虽然后面都要重装,但初始化时还是不要太大为好
- TH0=(65536-5000)/256;
- TR0 = 1;//启动定时器0
- //ET0 = 1;//仅在写字时打开定时器0中断,数码管扫描及读时钟时要关闭中断
- }
- void timer0(void) interrupt 1 //连续输出3路高电平脉冲,剩下的时间为低电平补足20ms
- {
- TR0 = 0;//关闭定时器0
- switch(order)
- {
- case 1:
- PWM_OUT0=1; //开第0路高电平,
- TL0=(-PWM_Value[0])%256; //(-PWM_Value[0])=(65536-PWM_Value[0]) 定时时间会更精准
- TH0=(-PWM_Value[0])/256; //赋值第0路舵机高电平时间
- break;
- case 2:
- PWM_OUT0=0;//关第0路高电平,开第1路高电平,
- PWM_OUT1=1;
- TL0=(-PWM_Value[1])%256;
- TH0=(-PWM_Value[1])/256; //赋值第1路舵机高电平时间
- break;
- case 3:
- PWM_OUT1=0; //关第1路高电平,开第2路高电平
- PWM_OUT2=1;
- TL0=(-PWM_Value[2])%256;
- TH0=(-PWM_Value[2])/256; //赋值第2路舵机高电平时间
- break;
- case 4:
- PWM_OUT2=0; //3路舵机低电平时间=20ms-3路高电平时间
- TL0=(25536+PWM_Value[0]+PWM_Value[1]+PWM_Value[2])%256;
- TH0=(25536+PWM_Value[0]+PWM_Value[1]+PWM_Value[2])/256;
- order=0;
- break;
- }
- TR0 = 1;//启动定时器0
- order++;
- }
复制代码
C语言程序实现:------直线运动插补(插入许多点逼近直线),实现从A(lastX,lastY)走直线到B(pX,pY),
- /************************************************/
- void drawTo(float pX, float pY) {
- float dx; float dy; float c;
- int i;
- // dx dy of new point
- dx = pX - lastX;//以上次点为坐标原点
- dy = pY - lastY;
- //path lenght in mm, times 4 equals 4 steps per mm
- c = floor(4 * sqrt(dx * dx + dy * dy));
- //计算两点之间直线距离并取整 CxC=AxA+BxB
- if (c < 1) c = 1;
- for (i = 0; i <= c; i++) {
- // 每次走i/c ,逐点走完距离C
- set_XY(lastX + (i * dx / c), lastY + (i * dy / c));
- }
- lastX = pX;//存储此次位置坐标
- lastY = pY;
- }
- //
复制代码
C语言程序实现:------圆弧运动插补(插入许多点逼近圆弧),弧的圆心(bx,by),半径radius,起始角度start,结束角度ende
- /************************************************/
- void bogenGZS(float bx, float by, float radius, int start, int ende, float sqee)
- {//圆弧圆心(bx,by),圆弧半径radius,起始角度start,结束角度ende,X轴向比例sqee
- float inkr = 0.05;//圆弧插补,逆时针每次+0.05°顺时针是-0.05°
- float count = 0;
- do {
- drawTo(sqee * radius * cos(start + count) + bx,radius * sin(start + count) + by);
- //三角函数 X=斜边*cosα, Y=斜边*sinα,
- count += inkr;//下一点旋转角度inkr
- }
- while ((start + count) <= ende);//到达角度ende,结束
- }
- //
复制代码
C语言程序实现:------有了以上函数,如何写数字?
- /************************************************/
- case 5: //在中心坐标(bx,by)处写数字5
- drawTo(bx + 2 * scale, by + 5 * scale);//走到起始位置
- lift(0);//落笔
- bogenGZS(bx + 5 * scale, by + 6 * scale, 6 * scale, -2.5, 2, 1);//从5的最下端逆时针画圆弧
- drawTo(bx + 5 * scale, by + 20 * scale);//画5的脖子
- drawTo(bx + 12 * scale, by + 20 * scale);//画5的顶上一横
- lift(1);//抬笔
- break;
复制代码
C语言程序实现:------main流程
- ******************************************/
- void main(void)
- {
- unsigned char Last_ucTemp4=0;//分钟个位记录
- delay_ms(5);
- Init_devices();//初始化
- //ds3231_write_time();//时钟初始数据写入
- uiVoiceCnt=40;//开机短响
- delay1s();
- while (1)
- {
- key_service_1_win_4_flash();//按键服务的应用程序
- display_service_1_win_6_flash(); //显示的窗口菜单服务程序
- delay1s();
- if(ucTemp4!=Last_ucTemp4){//如果分有变化,开始写字程序,每分钟写一次
- P1|=0x3F;////0B xx11 1111;熄灭数码管
- Last_ucTemp4=ucTemp4;//保存当前分钟值
- IE2 &=~(1<<2);//关定时器2中断
- ET0 = 1;//开定时器0中断
- /*lift(0); //落笔 没用白板笔
- drawTo(rubberx, rubbery); //停留在笔擦位置,小心撞笔,损坏舵机
- delay1s();
- lift(2); //抬笔 */
- /*lift(1); //落笔
- drawTo(0, 25); //装配测试,笔从写字板左边走到右边
- delay1s();
- drawTo(60, 25);
- delay1s(); */
- Test_DrawTime();//写时间函数
- IE2 |=(1<<2);//开定时器2中断,准备按键扫描,数码管显示,
- ET0 = 0;//关定时器0中断,防止干扰
- }
- }
- }
- //
复制代码
C语言程序实现:------编译结果,code=13800 ,好家伙,有十几K!
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
阿莫论坛20周年了!感谢大家的支持与爱护!!
曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……
|