Skip to content

Commit 962ead5

Browse files
KurtEiabdalkader
andauthored
UNO R4: fix PWM timer period updates (#116)
Co-authored-by: Ibrahim Abdelkader <[email protected]>
1 parent f36eb39 commit 962ead5

File tree

1 file changed

+68
-21
lines changed

1 file changed

+68
-21
lines changed

src/renesas/Servo.cpp

+68-21
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@
3131
#define SERVO_MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER)
3232
#define SERVO_INVALID_INDEX (255)
3333
// Lower the timer ticks for finer resolution.
34-
#define SERVO_TIMER_TICK_US (100)
3534
#define SERVO_US_PER_CYCLE (20000)
3635
#define SERVO_IO_PORT_ADDR(pn) &((R_PORT0 + ((uint32_t) (R_PORT1 - R_PORT0) * (pn)))->PCNTR3)
36+
#define SERVO_MIN_CYCLE_OFF_US 50
3737

3838
// Internal Servo struct to keep track of RA configuration.
3939
typedef struct {
@@ -43,8 +43,8 @@ typedef struct {
4343
// Servo class are not wide enough for the pulse width.
4444
uint32_t period_min;
4545
uint32_t period_max;
46-
// Period period_count in microseconds.
47-
uint32_t period_count;
46+
// Period period_count in timer ticks.
47+
uint32_t period_ticks;
4848
// Internal FSP GPIO port/pin control bits.
4949
volatile uint32_t *io_port;
5050
uint32_t io_mask;
@@ -58,6 +58,16 @@ static FspTimer servo_timer;
5858
static bool servo_timer_started = false;
5959
void servo_timer_callback(timer_callback_args_t *args);
6060

61+
static uint32_t servo_ticks_per_cycle = 0;
62+
static uint32_t min_servo_cycle_low = 0;
63+
static uint32_t active_servos_mask = 0;
64+
static uint32_t active_servos_mask_refresh = 0;
65+
66+
67+
static uint32_t us_to_ticks(uint32_t time_us) {
68+
return ((float) servo_ticks_per_cycle / (float) SERVO_US_PER_CYCLE) * time_us;
69+
}
70+
6171
static int servo_timer_config(uint32_t period_us)
6272
{
6373
static bool configured = false;
@@ -68,9 +78,14 @@ static int servo_timer_config(uint32_t period_us)
6878
if (channel != -1) {
6979
servo_timer.begin(TIMER_MODE_PERIODIC, type, channel,
7080
1000000.0f/period_us, 50.0f, servo_timer_callback, nullptr);
71-
servo_timer.setup_overflow_irq();
81+
servo_timer.set_period_buffer(false); // disable period buffering
82+
servo_timer.setup_overflow_irq(10);
7283
servo_timer.open();
7384
servo_timer.stop();
85+
// Read the timer's period count.
86+
servo_ticks_per_cycle = servo_timer.get_period_raw();
87+
min_servo_cycle_low = us_to_ticks(SERVO_MIN_CYCLE_OFF_US);
88+
7489
configured = true;
7590
}
7691
}
@@ -99,22 +114,47 @@ static int servo_timer_stop()
99114
return 0;
100115
}
101116

117+
inline static void servo_timer_set_period(uint32_t period) {
118+
servo_timer.set_period(period);
119+
}
120+
102121
void servo_timer_callback(timer_callback_args_t *args)
103122
{
104-
for (size_t i=0; i<SERVO_MAX_SERVOS; i++) {
105-
ra_servo_t *servo = &ra_servos[i];
106-
if (servo->period_us) {
107-
servo->period_count += SERVO_TIMER_TICK_US;
108-
if (servo->period_count <= servo->period_us) {
109-
*servo->io_port = (uint32_t) servo->io_mask;
110-
} else {
111-
*servo->io_port = (uint32_t) (servo->io_mask << 16);
112-
}
113-
if (servo->period_count == SERVO_US_PER_CYCLE) {
114-
servo->period_count = 0;
115-
}
123+
(void)args; // remove warning
124+
static uint8_t channel = SERVO_MAX_SERVOS;
125+
static uint8_t channel_pin_set_high = 0xff;
126+
static uint32_t ticks_accum = 0;
127+
128+
// See if we need to set a servo back low
129+
if (channel_pin_set_high != 0xff) {
130+
*ra_servos[channel_pin_set_high].io_port = ra_servos[channel_pin_set_high].io_mask << 16;
131+
}
132+
133+
// Find the next servo to set high
134+
while (active_servos_mask_refresh) {
135+
channel = __builtin_ctz(active_servos_mask_refresh);
136+
if (ra_servos[channel].period_us) {
137+
*ra_servos[channel].io_port = ra_servos[channel].io_mask;
138+
servo_timer_set_period(ra_servos[channel].period_ticks);
139+
channel_pin_set_high = channel;
140+
ticks_accum += ra_servos[channel].period_ticks;
141+
active_servos_mask_refresh &= ~(1 << channel);
142+
return;
116143
}
144+
active_servos_mask_refresh &= ~(1 << channel);
145+
}
146+
// Finished processing all servos, now delay to start of next pass.
147+
ticks_accum += min_servo_cycle_low;
148+
uint32_t time_to_next_cycle;
149+
if (servo_ticks_per_cycle > ticks_accum) {
150+
time_to_next_cycle = servo_ticks_per_cycle - ticks_accum;
151+
} else {
152+
time_to_next_cycle = min_servo_cycle_low;
117153
}
154+
ticks_accum = 0;
155+
servo_timer_set_period(time_to_next_cycle);
156+
channel_pin_set_high = 0xff;
157+
active_servos_mask_refresh = active_servos_mask;
118158
}
119159

120160
Servo::Servo()
@@ -139,6 +179,11 @@ uint8_t Servo::attach(int pin, int min, int max)
139179
return 0;
140180
}
141181

182+
// Configure the servo timer.
183+
if (servo_timer_config(SERVO_US_PER_CYCLE) != 0) {
184+
return 0;
185+
}
186+
142187
// Try to find a free servo slot.
143188
ra_servo_t *servo = NULL;
144189
bsp_io_port_pin_t io_pin = g_pin_cfg[pin].pin;
@@ -151,6 +196,7 @@ uint8_t Servo::attach(int pin, int min, int max)
151196
servo->period_max = max;
152197
servo->io_mask = (1U << (io_pin & 0xFF));
153198
servo->io_port = SERVO_IO_PORT_ADDR(((io_pin >> 8U) & 0xFF));
199+
active_servos_mask |= (1 << i); // update mask of servos that are active.
154200
writeMicroseconds(DEFAULT_PULSE_WIDTH);
155201
break;
156202
}
@@ -164,9 +210,8 @@ uint8_t Servo::attach(int pin, int min, int max)
164210
R_IOPORT_PinCfg(&g_ioport_ctrl, io_pin,
165211
IOPORT_CFG_PORT_DIRECTION_OUTPUT | IOPORT_CFG_PORT_OUTPUT_HIGH);
166212

167-
// Configure and start the timer if it's not started.
168-
if (servo_timer_config(SERVO_TIMER_TICK_US) != 0 ||
169-
servo_timer_start() != 0) {
213+
// Start the timer if it's not started.
214+
if (servo_timer_start() != 0) {
170215
return 0;
171216
}
172217
return 1;
@@ -178,10 +223,12 @@ void Servo::detach()
178223
ra_servo_t *servo = &ra_servos[servoIndex];
179224
servo_timer_stop();
180225
servo->period_us = 0;
226+
active_servos_mask &= ~(1 << servoIndex); // update mask of servos that are active.
227+
servoIndex = SERVO_INVALID_INDEX;
181228
if (--n_servos) {
182229
servo_timer_start();
183230
}
184-
servoIndex = SERVO_INVALID_INDEX;
231+
185232
}
186233
}
187234

@@ -207,8 +254,8 @@ void Servo::writeMicroseconds(int us)
207254
{
208255
if (servoIndex != SERVO_INVALID_INDEX) {
209256
ra_servo_t *servo = &ra_servos[servoIndex];
210-
servo->period_count = 0;
211257
servo->period_us = constrain(us, servo->period_min, servo->period_max);
258+
servo->period_ticks = us_to_ticks(servo->period_us);
212259
}
213260
}
214261

0 commit comments

Comments
 (0)