
1 总线舵机的介绍# A1 F) L0 n+ O! d Q0 f8 }7 g. a 总线伺服舵机即串行总线智能舵机,实际上可以理解为数字舵机的衍生品,数字舵机与模拟舵机相比而言是控制系统设计上的颠覆,而总线伺服舵机对于舵机而言则是在功能和运用上的颠覆。舵机的运用方式实际上只能发挥出总线伺服舵机非常小的一部分功能。这款TS-315是通过单线的串口通信对旋转进行控制的,控制字符串协议如下:) }' l/ s2 l& ~, [5 n 2 O; k% ?. L7 s0 { " B+ b2 x+ U0 B# \& q, y 7 d* p9 w) Q7 M/ M) L 字符串协议: W9 [" G* q: _+ G$ } #1P1500T100 控制舵机旋转,无返回* }- u# _& ~2 J6 H! m (1P是ID为1的舵机,中间的1500是参数,范围是500-2500,控制舵机范围,后面的参数100,表示舵机旋转的时间参数,也就是速度,意思是舵机从当前角度达到命令中的1500的位置,所需要的时间为100ms,时间的范围是1-50000,时间越大速度越慢,但是舵机的最快速度以实际舵机的参数而定)8 _) f a1 A* N #1PRAD 读取角度,返回格式 #001P1467 ' k) }- A1 O, v/ h #1PULK 释放扭力,无返回 #1PID002 改变ID (将1改成2,后面的参数必须为3位数,不够补0),返回格式 #002P #1PBD1 返回 #OK!- M) U/ P! n7 | c6 O: ~5 | 改变波特率(9600,19200,38400,115200,128000) (舵机重启之后才会生效) //1:9600 , 2:19200 , 3:38400 , 4:57600 , 5:115200 , 6:128000 G; \- N, l+ A #1PCSD 将当前位置设置舵机的初始位置,返回 #OK! (设置之后舵机上电之后会旋转到设置的位置) #1PCLE (恢复出厂设置)(舵机重启之后才会生效),返回 #OK! #1PMOD1 ,返回 #OK! (最后的参数1表示工作模式,0:270度逆向,1:270度顺向,2:180度逆向,3:180度顺向,4:连续旋转)8 ]+ ]- Z! |$ _1 k 逆向和顺向表示舵机的旋转方向。逆向:脉冲信号从500到2500,舵机逆时针旋转。顺向:脉冲信号从500到2500,舵机顺时针旋转。 连续旋转模式就是普通的360度舵机,连续旋转的,无法控制角度,这个模式下控制舵机的命令#1P1500T100这样的命令的作用就改变了,这个命令在这个模式下面的作用是: 中间的参数1500,范围为500-2500,其中500-1500控制舵机的正转,1500-2500控制舵机的反转。越接近1500速度越慢,越远离1500速度越快(即500和2500速度都是最快的,1500是停止) 最后的参数100,表示舵机旋转的圈数,100就是100圈(存在一两圈的误差,正常!),如果这个参数改成50000,那就是无限旋转。如果参数是49999,那就只会转49999圈 上面的1P 表示编号为1的舵机,上面所有的命令最后都要带回车换行符(0x0d 和 0x0a)& R2 }* n# D6 M. g 舵机的ID默认是1,ID为255是广播,广播命令对所有舵机都有效。默认波特率1280002 [0 g. i* h4 \( S `1 m, e 2 STM32单线串口通信7 y+ ^+ @. f" f 参考手册里面是这样描述的,非常简单单线半双方模式通过设置USART_CR3寄存器的HDSEL位选择。在这个模式里,下面的位必须保持清零状态:3 m! u: C Q+ Y9 a ● USART_CR2寄存器的LINEN和CLKEN位* N1 U. Z7 K3 v! y ● USART_CR3寄存器的SCEN和IREN位; @9 l' `7 z( U1 L1 f0 C USART可以配置成遵循单线半双工协议。在单线半双工模式下,TX和RX引脚在芯片内部互连。使用控制位”HALF DUPLEX SEL”(USART_CR3中的HDSEL位)选择半双工和全双工通信。) t3 p* h1 U5 H4 E4 m 当HDSEL为’1’时 ● RX不再被使用; N: g$ Q4 a2 P' S ● 当没有数据传输时,TX总是被释放。因此,它在空闲状态的或接收状态时表现为一个标准I/O口。这就意味该I/O在不被USART驱动时,必须配置成悬空输入(或开漏的输出高)。 除此以外,通信与正常USART模式类似。由软件来管理线上的冲突(例如通过使用一个中央仲裁器)。特别的是,发送从不会被硬件所阻碍。当TE位被设置时,只要数据一写到数据寄存器上,发送就继续。 参考初始化源码如下 //初始化IO 串口2, s6 R, g9 T8 ]' n3 z" W //pclk2CLK2时钟频率(Mhz) //bound:波特率 void uart2_init(u32 pclk2,u32 bound) { # C( {/ u% p2 z3 j; V& f6 t $ w# a, u1 F1 H# m8 p9 B7 g u32 temp; 9 ^4 i1 X( ?0 i& w( Y temp=(pclk2*1000000+bound/2)/bound; //得到USARTDIV@OVER8=0,采用四舍五入计算7 O5 ^6 y* W; m 0 V$ W9 e: j( T- J4 { 1 @4 N) e t, v# y RCC->AHB1ENR|=1<<0; //使能PORTA口时钟 $ z8 G# p+ ~7 N# _ RCC->APB1ENR|=1<<17; //使能串口2时钟 GPIO_Set(GPIOA,PIN2|PIN3,GPIO_MODE_AF,GPIO_OTYPE_OD,GPIO_SPEED_50M,GPIO_PUPD_PU);//PA2,PA3,复用功能,上拉输出& |% ]6 E- c5 N- ]) P3 S+ t GPIO_AF_Set(GPIOA,2,7); //PA2,AF7 GPIO_AF_Set(GPIOA,3,7);//PA3,AF7 USART2->CR1=0; //清零CR1寄存器2 |! R6 y+ G/ L' i' O2 n. i: P& P USART2->BRR=temp/2; //波特率设置@OVER8=0 //下面5行代码是根据参考手册设置寄存器,注意使能HDSEL放到最后 USART2->CR3&=0<<5; //清SCEN USART2->CR3&=0<<1; //清IREN' f+ w2 ]3 s2 T# o USART2->CR2&=0<<11; //清CLKEN USART2->CR2&=0<<14; //清LINEN USART2->CR3|=1<<3; //使能HDSEL4 k B: l7 `6 H7 D- G* X$ ] ' R" I: F+ I( ~& n USART2->CR1|=0<<28; //设置M1=0 USART2->CR1|=0<<12; //设置M0=0&M1=0,选择8位字长* z l3 R3 x, m: b/ j! \# D# X USART2->CR1|=0<<15; //设置OVER8=0,16倍过采样 USART2->CR1|=1<<3; //串口发送使能 #if EN_USART2_RX //如果使能了接收/ S, B3 d. O" F" i; C" m! L //使能接收中断 USART2->CR1|=1<<2; //串口接收使能 USART2->CR1|=1<<5; //接收缓冲区非空中断使能 MY_NVIC_Init(0,0,USART2_IRQn,2);//组2,最低优先级 #endif. d1 g. x$ q5 k6 t/ O5 X+ @& E3 X USART2->CR1|=1<<0; //串口使能; t( N3 ^% p0 S" t ! `3 {1 {" p% v } 3数据读取时的注意点, Y! X4 x6 ]! c: m5 D) ~7 ]2 q B) d1 b0 y# p 通过逻辑分析仪分析,命令传送至舵机后,舵机给出了正确的反馈,但是由于舵机回馈数据的速度非常快,造成了丢包问题,数据总是无**确传送至单片机,由于在单线模式,RXD一直接收TXD发送的数据,致使真正需要的数据丢包,因而只需要修改程序即可修正,更改的程序只是判断下是否是自己发送的命令即可,若是,则不在接收,最后验证,已经可以正确读取舵机反馈值 - h) q' W$ W8 B8 [# @6 U u16 USART2_RX_STA=0; //接收状态标记 ) {0 I/ ~* g0 E$ I. n void USART2_IRQHandler(void)$ z2 _0 R6 u9 P D { u8 res; $ n0 Q& R- }1 i; G1 E# m #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS. OSIntEnter(); & ~4 k' s( g9 v* u: S #endif if(USART2->ISR&(1<<5))//接收到数据' u- z9 g l0 N8 T0 ^9 t! Y; x6 M { , f- M; |- x) N6 a" r res=USART2->RDR;, p& A- ]$ o2 c+ k" @0 a8 s6 ]: _ //if(USART2->RDR!=USART2->TDR) {7 Q' `, |- X; _6 W ; j& Z3 d8 f Q: s if((USART2_RX_STA&0x8000)==0)//接收未完成) z( I- \" t) y- V' F {3 A8 E: Z- P- q, {; C6 X if(USART2_RX_STA&0x4000)//接收到了0x0d% Z" t5 ^+ b% K8 X {0 e, ^: u6 I( t* [3 W( L I7 y; Y if(res!=0x0a)USART2_RX_STA=0;//接收错误,重新开始 else if (USART2_RX_BUF[5]==0x44)USART2_RX_STA=0; //防止丢包,禁止接收自己发送出去的命名 5 g# v F$ i X( j* b else {, G( W* L: o8 z8 {. H, Q9 B USART2_RX_STA|=0x8000; //接收完成了" b# `: S5 u6 V$ L }1 G9 g* r7 ^; U& P* O. N; c } else //还没收到0X0D% R, \7 o' q( q* T4 N; k6 b5 e0 r { X6 ^) N* P: a7 l E if(res==0x0d)USART2_RX_STA|=0x4000;3 [9 t2 i) l' p9 [+ H else* q% F1 n* T' Q( C4 C9 E { USART2_RX_BUF[USART2_RX_STA&0X3FFF]=res;. J! N. q/ ~& {2 J# J1 D USART2_RX_STA++;% }$ ^% _4 M0 e9 | if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0;//接收数据错误,重新开始接收 } 0 a$ o' Q3 i6 }3 T } } 5 Y0 E0 m& x/ _4 v: R; m } }3 ?- J+ w4 _+ Z 2 q0 [4 x& v8 S% V2 l |
中断接收