2020
2121typedef struct _pyb_servo_obj_t {
2222 mp_obj_base_t base ;
23- uint16_t servo_id ;
24- uint16_t time_left ;
23+ uint8_t servo_id ;
24+ uint8_t pulse_min ; // units of 10us
25+ uint8_t pulse_max ; // units of 10us
26+ uint8_t pulse_centre ; // units of 10us
27+ uint8_t pulse_angle_90 ; // units of 10us; pulse at 90 degrees, minus pulse_centre
28+ uint8_t pulse_speed_100 ; // units of 10us; pulse at 100% forward speed, minus pulse_centre
29+ uint16_t pulse_cur ; // units of 10us
30+ uint16_t pulse_dest ; // units of 10us
2531 int16_t pulse_accum ;
26- uint16_t pulse_cur ;
27- uint16_t pulse_dest ;
32+ uint16_t time_left ;
2833} pyb_servo_obj_t ;
2934
3035STATIC pyb_servo_obj_t pyb_servo_obj [PYB_SERVO_NUM ];
@@ -36,9 +41,14 @@ void servo_init(void) {
3641 for (int i = 0 ; i < PYB_SERVO_NUM ; i ++ ) {
3742 pyb_servo_obj [i ].base .type = & pyb_servo_type ;
3843 pyb_servo_obj [i ].servo_id = i + 1 ;
39- pyb_servo_obj [i ].time_left = 0 ;
40- pyb_servo_obj [i ].pulse_cur = 150 ; // units of 10us
44+ pyb_servo_obj [i ].pulse_min = 64 ;
45+ pyb_servo_obj [i ].pulse_max = 242 ;
46+ pyb_servo_obj [i ].pulse_centre = 150 ;
47+ pyb_servo_obj [i ].pulse_angle_90 = 97 ;
48+ pyb_servo_obj [i ].pulse_speed_100 = 70 ;
49+ pyb_servo_obj [i ].pulse_cur = 150 ;
4150 pyb_servo_obj [i ].pulse_dest = 0 ;
51+ pyb_servo_obj [i ].time_left = 0 ;
4252 }
4353}
4454
@@ -47,6 +57,13 @@ void servo_timer_irq_callback(void) {
4757 for (int i = 0 ; i < PYB_SERVO_NUM ; i ++ ) {
4858 pyb_servo_obj_t * s = & pyb_servo_obj [i ];
4959 if (s -> pulse_cur != s -> pulse_dest ) {
60+ // clamp pulse to within min/max
61+ if (s -> pulse_dest < s -> pulse_min ) {
62+ s -> pulse_dest = s -> pulse_min ;
63+ } else if (s -> pulse_dest > s -> pulse_max ) {
64+ s -> pulse_dest = s -> pulse_max ;
65+ }
66+ // adjust cur to get closer to dest
5067 if (s -> time_left <= 1 ) {
5168 s -> pulse_cur = s -> pulse_dest ;
5269 s -> time_left = 0 ;
@@ -57,6 +74,7 @@ void servo_timer_irq_callback(void) {
5774 s -> time_left -- ;
5875 need_it = true;
5976 }
77+ // set the pulse width
6078 switch (s -> servo_id ) {
6179 case 1 : TIM5 -> CCR1 = s -> pulse_cur ; break ;
6280 case 2 : TIM5 -> CCR2 = s -> pulse_cur ; break ;
@@ -135,7 +153,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(pyb_pwm_set_obj, pyb_pwm_set);
135153
136154STATIC void pyb_servo_print (void (* print )(void * env , const char * fmt , ...), void * env , mp_obj_t self_in , mp_print_kind_t kind ) {
137155 pyb_servo_obj_t * self = self_in ;
138- print (env , "<Servo %lu at %lu >" , self -> servo_id , self -> pulse_cur );
156+ print (env , "<Servo %lu at %luus >" , self -> servo_id , 10 * self -> pulse_cur );
139157}
140158
141159STATIC mp_obj_t pyb_servo_make_new (mp_obj_t type_in , uint n_args , uint n_kw , const mp_obj_t * args ) {
@@ -159,20 +177,64 @@ STATIC mp_obj_t pyb_servo_make_new(mp_obj_t type_in, uint n_args, uint n_kw, con
159177 return s ;
160178}
161179
180+ STATIC mp_obj_t pyb_servo_pulse_width (uint n_args , const mp_obj_t * args ) {
181+ pyb_servo_obj_t * self = args [0 ];
182+ if (n_args == 1 ) {
183+ // get pulse width, in us
184+ return mp_obj_new_int (10 * self -> pulse_cur );
185+ } else {
186+ // set pulse width, in us
187+ self -> pulse_dest = mp_obj_get_int (args [1 ]) / 10 ;
188+ self -> time_left = 0 ;
189+ servo_timer_irq_callback ();
190+ return mp_const_none ;
191+ }
192+ }
193+
194+ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (pyb_servo_pulse_width_obj , 1 , 2 , pyb_servo_pulse_width );
195+
196+ STATIC mp_obj_t pyb_servo_calibrate (uint n_args , const mp_obj_t * args ) {
197+ pyb_servo_obj_t * self = args [0 ];
198+ if (n_args == 1 ) {
199+ // get calibration values
200+ mp_obj_t tuple [5 ];
201+ tuple [0 ] = mp_obj_new_int (10 * self -> pulse_min );
202+ tuple [1 ] = mp_obj_new_int (10 * self -> pulse_max );
203+ tuple [2 ] = mp_obj_new_int (10 * self -> pulse_centre );
204+ tuple [3 ] = mp_obj_new_int (10 * (self -> pulse_angle_90 + self -> pulse_centre ));
205+ tuple [4 ] = mp_obj_new_int (10 * (self -> pulse_speed_100 + self -> pulse_centre ));
206+ return mp_obj_new_tuple (5 , tuple );
207+ } else if (n_args >= 4 ) {
208+ // set min, max, centre
209+ self -> pulse_min = mp_obj_get_int (args [1 ]) / 10 ;
210+ self -> pulse_max = mp_obj_get_int (args [2 ]) / 10 ;
211+ self -> pulse_centre = mp_obj_get_int (args [3 ]) / 10 ;
212+ if (n_args == 4 ) {
213+ return mp_const_none ;
214+ } else if (n_args == 6 ) {
215+ self -> pulse_angle_90 = mp_obj_get_int (args [4 ]) / 10 - self -> pulse_centre ;
216+ self -> pulse_speed_100 = mp_obj_get_int (args [5 ]) / 10 - self -> pulse_centre ;
217+ return mp_const_none ;
218+ }
219+ }
220+
221+ // bad number of arguments
222+ nlr_raise (mp_obj_new_exception_msg_varg (& mp_type_TypeError , "calibrate expecting 1, 4 or 6 arguments, got %d" , n_args ));
223+ }
224+
225+ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (pyb_servo_calibrate_obj , 1 , 6 , pyb_servo_calibrate );
226+
162227STATIC mp_obj_t pyb_servo_angle (uint n_args , const mp_obj_t * args ) {
163228 pyb_servo_obj_t * self = args [0 ];
164229 if (n_args == 1 ) {
165230 // get angle
166- return mp_obj_new_int ((self -> pulse_cur - 152 ) * 90 / 85 );
231+ return mp_obj_new_int ((self -> pulse_cur - self -> pulse_centre ) * 90 / self -> pulse_angle_90 );
167232 } else {
168233#if MICROPY_ENABLE_FLOAT
169- machine_int_t v = 152 + 85.0 * mp_obj_get_float (args [1 ]) / 90.0 ;
234+ self -> pulse_dest = self -> pulse_centre + self -> pulse_angle_90 * mp_obj_get_float (args [1 ]) / 90.0 ;
170235#else
171- machine_int_t v = 152 + 85 * mp_obj_get_int (args [1 ]) / 90 ;
236+ self -> pulse_dest = self -> pulse_centre + self -> pulse_angle_90 * mp_obj_get_int (args [1 ]) / 90 ;
172237#endif
173- if (v < 65 ) { v = 65 ; }
174- if (v > 210 ) { v = 210 ; }
175- self -> pulse_dest = v ;
176238 if (n_args == 2 ) {
177239 // set angle immediately
178240 self -> time_left = 0 ;
@@ -188,8 +250,37 @@ STATIC mp_obj_t pyb_servo_angle(uint n_args, const mp_obj_t *args) {
188250
189251STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (pyb_servo_angle_obj , 1 , 3 , pyb_servo_angle );
190252
253+ STATIC mp_obj_t pyb_servo_speed (uint n_args , const mp_obj_t * args ) {
254+ pyb_servo_obj_t * self = args [0 ];
255+ if (n_args == 1 ) {
256+ // get speed
257+ return mp_obj_new_int ((self -> pulse_cur - self -> pulse_centre ) * 100 / self -> pulse_speed_100 );
258+ } else {
259+ #if MICROPY_ENABLE_FLOAT
260+ self -> pulse_dest = self -> pulse_centre + self -> pulse_speed_100 * mp_obj_get_float (args [1 ]) / 100.0 ;
261+ #else
262+ self -> pulse_dest = self -> pulse_centre + self -> pulse_speed_100 * mp_obj_get_int (args [1 ]) / 100 ;
263+ #endif
264+ if (n_args == 2 ) {
265+ // set speed immediately
266+ self -> time_left = 0 ;
267+ } else {
268+ // set speed over a given time (given in milli seconds)
269+ self -> time_left = mp_obj_get_int (args [2 ]) / 20 ;
270+ self -> pulse_accum = 0 ;
271+ }
272+ servo_timer_irq_callback ();
273+ return mp_const_none ;
274+ }
275+ }
276+
277+ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (pyb_servo_speed_obj , 1 , 3 , pyb_servo_speed );
278+
191279STATIC const mp_map_elem_t pyb_servo_locals_dict_table [] = {
280+ { MP_OBJ_NEW_QSTR (MP_QSTR_pulse_width ), (mp_obj_t )& pyb_servo_pulse_width_obj },
281+ { MP_OBJ_NEW_QSTR (MP_QSTR_calibrate ), (mp_obj_t )& pyb_servo_calibrate_obj },
192282 { MP_OBJ_NEW_QSTR (MP_QSTR_angle ), (mp_obj_t )& pyb_servo_angle_obj },
283+ { MP_OBJ_NEW_QSTR (MP_QSTR_speed ), (mp_obj_t )& pyb_servo_speed_obj },
193284};
194285
195286STATIC MP_DEFINE_CONST_DICT (pyb_servo_locals_dict , pyb_servo_locals_dict_table );
0 commit comments