Skip to content

Commit 76430b3

Browse files
Ensure Servo detach()es only on idle state
Avoid any short pulses which could cause servo twitches or damage by adding a shutdown command to the PIO program and checking it's in that safe part of the loop before detaching the servo.
1 parent ba0777e commit 76430b3

File tree

4 files changed

+83
-66
lines changed

4 files changed

+83
-66
lines changed

libraries/Servo/src/Servo.cpp

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,8 @@
2424
#include <hardware/clocks.h>
2525
#include <hardware/pio.h>
2626

27-
// The PWM example from theSDK is general purpose enough to use here
28-
// with no PIO code changes whatsoever. RP2040 is awesome!
29-
#include "pwm.pio.h"
30-
static PIOProgram _servoPgm(&pwm_program);
27+
#include "servo.pio.h"
28+
static PIOProgram _servoPgm(&servo_program);
3129

3230
// similiar to map but will have increased accuracy that provides a more
3331
// symmetrical api (call it and use result to reverse will provide the original value)
@@ -80,21 +78,23 @@ int Servo::attach(pin_size_t pin, int minUs, int maxUs, int value)
8078
_minUs = max(200, min(_maxUs, minUs));
8179

8280
if (!_attached) {
83-
int off;
8481
digitalWrite(pin, LOW);
8582
pinMode(pin, OUTPUT);
86-
if (!_servoPgm.prepare(&_pio, &_smIdx, &off)) {
83+
_pin = pin;
84+
if (!_servoPgm.prepare(&_pio, &_smIdx, &_pgmOffset)) {
8785
// ERROR, no free slots
8886
return -1;
8987
}
90-
pwm_program_init(_pio, _smIdx, off, pin);
88+
_attached = true;
89+
servo_program_init(_pio, _smIdx, _pgmOffset, pin);
9190
pio_sm_set_enabled(_pio, _smIdx, false);
9291
pio_sm_put_blocking(_pio, _smIdx, RP2040::usToPIOCycles(REFRESH_INTERVAL) / 3);
9392
pio_sm_exec(_pio, _smIdx, pio_encode_pull(false, false));
9493
pio_sm_exec(_pio, _smIdx, pio_encode_out(pio_isr, 32));
94+
write(value);
95+
pio_sm_exec(_pio, _smIdx, pio_encode_pull(false, false));
96+
pio_sm_exec(_pio, _smIdx, pio_encode_mov(pio_x, pio_osr));
9597
pio_sm_set_enabled(_pio, _smIdx, true);
96-
_pin = pin;
97-
_attached = true;
9898
}
9999

100100
write(value);
@@ -105,6 +105,12 @@ int Servo::attach(pin_size_t pin, int minUs, int maxUs, int value)
105105
void Servo::detach()
106106
{
107107
if (_attached) {
108+
// Set a 0 for the width and then wait for the halt loop
109+
pio_sm_put_blocking(_pio, _smIdx, 0);
110+
delayMicroseconds(5); // Avoid race condition
111+
do {
112+
// Do nothing until we are stuck in the halt loop (avoid short pulses
113+
} while (pio_sm_get_pc(_pio, _smIdx) != servo_offset_halt + _pgmOffset);
108114
pio_sm_set_enabled(_pio, _smIdx, false);
109115
pio_sm_unclaim(_pio, _smIdx);
110116
_attached = false;

libraries/Servo/src/pwm.pio.h

Lines changed: 0 additions & 51 deletions
This file was deleted.

libraries/Servo/src/pwm.pio renamed to libraries/Servo/src/servo.pio

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,38 @@
1+
; Servo.PIO - Generate Servo pulses
12
;
2-
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3+
; Based on PWM.PIO Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
34
;
45
; SPDX-License-Identifier: BSD-3-Clause
56
;
67

78
; Side-set pin 0 is used for PWM output
89

9-
.program pwm
10+
; A servo count of 0 will cause an infinite loop allowing for
11+
; the RP2040 to determine when it is safe to shut down this
12+
; PIO program without causing any short pulses.
13+
; RP2040 code can check the PIO PC and determine when to stop it.
14+
15+
.program servo
1016
.side_set 1 opt
1117

1218
pull noblock side 0 ; Pull from FIFO to OSR if available, else copy X to OSR.
1319
mov x, osr ; Copy most-recently-pulled value back to scratch X
14-
mov y, isr ; ISR contains PWM period. Y used as counter.
20+
public halt:
21+
jmp !x halt ; If x == 0 then a halt was requested
22+
mov y, isr ; ISR contains servo period. Y used as counter.
1523
countloop:
1624
jmp x!=y noset ; Set pin high if X == Y, keep the two paths length matched
1725
jmp skip side 1
1826
noset:
1927
nop ; Single dummy cycle to keep the two paths the same length
2028
skip:
21-
jmp y-- countloop ; Loop until Y hits 0, then pull a fresh PWM value from FIFO
29+
jmp y-- countloop ; Loop until Y hits 0, then pull a fresh servo value from FIFO
2230

2331
% c-sdk {
24-
static inline void pwm_program_init(PIO pio, uint sm, uint offset, uint pin) {
32+
static inline void servo_program_init(PIO pio, uint sm, uint offset, uint pin) {
2533
pio_gpio_init(pio, pin);
2634
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
27-
pio_sm_config c = pwm_program_get_default_config(offset);
35+
pio_sm_config c = servo_program_get_default_config(offset);
2836
sm_config_set_sideset_pins(&c, pin);
2937
pio_sm_init(pio, sm, offset, &c);
3038
}

libraries/Servo/src/servo.pio.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// -------------------------------------------------- //
2+
// This file is autogenerated by pioasm; do not edit! //
3+
// -------------------------------------------------- //
4+
5+
#if !PICO_NO_HARDWARE
6+
#include "hardware/pio.h"
7+
#endif
8+
9+
// ----- //
10+
// servo //
11+
// ----- //
12+
13+
#define servo_wrap_target 0
14+
#define servo_wrap 7
15+
16+
#define servo_offset_halt 2u
17+
18+
static const uint16_t servo_program_instructions[] = {
19+
// .wrap_target
20+
0x9080, // 0: pull noblock side 0
21+
0xa027, // 1: mov x, osr
22+
0x0022, // 2: jmp !x, 2
23+
0xa046, // 3: mov y, isr
24+
0x00a6, // 4: jmp x != y, 6
25+
0x1807, // 5: jmp 7 side 1
26+
0xa042, // 6: nop
27+
0x0084, // 7: jmp y--, 4
28+
// .wrap
29+
};
30+
31+
#if !PICO_NO_HARDWARE
32+
static const struct pio_program servo_program = {
33+
.instructions = servo_program_instructions,
34+
.length = 8,
35+
.origin = -1,
36+
};
37+
38+
static inline pio_sm_config servo_program_get_default_config(uint offset) {
39+
pio_sm_config c = pio_get_default_sm_config();
40+
sm_config_set_wrap(&c, offset + servo_wrap_target, offset + servo_wrap);
41+
sm_config_set_sideset(&c, 2, true, false);
42+
return c;
43+
}
44+
45+
static inline void servo_program_init(PIO pio, uint sm, uint offset, uint pin) {
46+
pio_gpio_init(pio, pin);
47+
pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);
48+
pio_sm_config c = servo_program_get_default_config(offset);
49+
sm_config_set_sideset_pins(&c, pin);
50+
pio_sm_init(pio, sm, offset, &c);
51+
}
52+
53+
#endif
54+

0 commit comments

Comments
 (0)