Skip to content

Commit 4969711

Browse files
committed
UNOR4 - update timer period
Instead of fixed 100us timer, it now changes the timer period on the fly to get better granularity. And like most other Servo implementations, It also only starts one servo at a time, to help minimize current surge to try to start all of the servos at the same time. I have done some testing with one and two servos with the sweep, where the 2nd servo is 180-pos...
1 parent 4def8a2 commit 4969711

File tree

1 file changed

+97
-21
lines changed

1 file changed

+97
-21
lines changed

src/renesas/Servo.cpp

Lines changed: 97 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,18 @@
2828
#include "math.h"
2929
#include "FspTimer.h"
3030

31+
// uncomment to print servo Debug information
32+
//#define SERVO_PRINT_DEBUG_INFO
33+
3134
#define SERVO_MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER)
3235
#define SERVO_INVALID_INDEX (255)
3336
// Lower the timer ticks for finer resolution.
3437
#define SERVO_TIMER_TICK_US (100)
3538
#define SERVO_US_PER_CYCLE (20000)
3639
#define SERVO_IO_PORT_ADDR(pn) &((R_PORT0 + ((uint32_t) (R_PORT1 - R_PORT0) * (pn)))->PCNTR3)
3740

41+
#define MIN_CYCLE_OFF_US 50
42+
3843
// Internal Servo sturct to keep track of RA configuration.
3944
typedef struct {
4045
// Servo period in microseconds.
@@ -43,8 +48,8 @@ typedef struct {
4348
// Servo class are not wide enough for the pulse width.
4449
uint32_t period_min;
4550
uint32_t period_max;
46-
// Period period_count in microseconds.
47-
uint32_t period_count;
51+
// Period period_count in timer ticks.
52+
uint32_t period_ticks;
4853
// Internal FSP GPIO port/pin control bits.
4954
volatile uint32_t *io_port;
5055
uint32_t io_mask;
@@ -58,25 +63,66 @@ static FspTimer servo_timer;
5863
static bool servo_timer_started = false;
5964
void servo_timer_callback(timer_callback_args_t *args);
6065

66+
// GPT pointer.
67+
static R_GPT0_Type *s_pgpt0 = nullptr;
68+
static uint32_t servo_ticks_per_cycle = 0;
69+
static uint32_t min_servo_cycle_low = 0;
70+
static uint32_t active_servos_mask = 0;
71+
static uint32_t active_servos_mask_refresh = 0;
72+
73+
74+
static uint32_t usToticks(uint32_t time_us) {
75+
return (float(servo_ticks_per_cycle) / float(SERVO_US_PER_CYCLE)) * time_us;
76+
}
77+
78+
79+
80+
6181
static int servo_timer_config(uint32_t period_us)
6282
{
6383
static bool configured = false;
6484
if (configured == false) {
65-
// Configure and enable the servo timer.
85+
// Configure and enable the servo timer, for full 20ms
6686
uint8_t type = 0;
6787
int8_t channel = FspTimer::get_available_timer(type);
6888
if (channel != -1) {
89+
// lets initially configure the servo to 50ms
6990
servo_timer.begin(TIMER_MODE_PERIODIC, type, channel,
7091
1000000.0f/period_us, 50.0f, servo_timer_callback, nullptr);
92+
93+
// First pass assume GPT timer
94+
s_pgpt0 = (R_GPT0_Type *)((uint32_t)R_GPT0 + ((uint32_t)R_GPT1 - (uint32_t)R_GPT0) * channel);
95+
// turn off GTPR Buffer
96+
s_pgpt0->GTBER_b.PR = 0;
97+
s_pgpt0->GTBER_b.BD1 = 1;
98+
7199
servo_timer.setup_overflow_irq();
72100
servo_timer.open();
73101
servo_timer.stop();
102+
103+
// Now lets see what the period;
104+
servo_ticks_per_cycle = servo_timer.get_period_raw();
105+
min_servo_cycle_low = usToticks(MIN_CYCLE_OFF_US);
106+
#ifdef SERVO_PRINT_DEBUG_INFO
107+
Serial.print("Period:");
108+
Serial.println(servo_ticks_per_cycle, DEC);
109+
uint32_t ticks_544 = usToticks(544);
110+
uint32_t ticks_2400 = usToticks(2400);
111+
Serial.print("Min 544(ticks): ");
112+
Serial.print(ticks_544);
113+
Serial.print(" Max 2400: ");
114+
Serial.print(ticks_2400);
115+
Serial.print(" per degree: ");
116+
Serial.println((float)(ticks_2400 - ticks_544) / 180.0f, 2);
117+
#endif
118+
74119
configured = true;
75120
}
76121
}
77122
return configured ? 0 : -1;
78123
}
79124

125+
80126
static int servo_timer_start()
81127
{
82128
// Start the timer if it's not started
@@ -99,22 +145,43 @@ static int servo_timer_stop()
99145
return 0;
100146
}
101147

148+
inline static void updateClockPeriod(uint32_t period) {
149+
if (s_pgpt0) s_pgpt0->GTPR = period;
150+
}
151+
152+
102153
void servo_timer_callback(timer_callback_args_t *args)
103154
{
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-
}
155+
(void)args; // remove warning
156+
static uint8_t channel=SERVO_MAX_SERVOS;
157+
static uint8_t channel_pin_set_high=0xff;
158+
static uint32_t ticks_accum=0;
159+
160+
// See if we need to set a servo back low
161+
if (channel_pin_set_high != 0xff) {
162+
*ra_servos[channel_pin_set_high].io_port = (uint32_t)(ra_servos[channel_pin_set_high].io_mask << 16);
163+
}
164+
165+
// Find the next servo to set high
166+
while (active_servos_mask_refresh) {
167+
channel = __builtin_ctz(active_servos_mask_refresh);
168+
active_servos_mask_refresh &= ~(1 << channel);
169+
if (ra_servos[channel].period_us) {
170+
*ra_servos[channel].io_port = (uint32_t)ra_servos[channel].io_mask;
171+
updateClockPeriod(ra_servos[channel].period_ticks);
172+
channel_pin_set_high = channel;
173+
ticks_accum += ra_servos[channel_pin_set_high].period_ticks;
174+
return;
116175
}
117176
}
177+
178+
// Got to hear we finished processing all servos, now delay to start of next pass.
179+
ticks_accum += min_servo_cycle_low;
180+
uint32_t time_to_next_cycle = (servo_ticks_per_cycle > ticks_accum)? servo_ticks_per_cycle - ticks_accum : min_servo_cycle_low;
181+
ticks_accum = 0;
182+
updateClockPeriod(time_to_next_cycle);
183+
channel_pin_set_high = 0xff;
184+
active_servos_mask_refresh = active_servos_mask;
118185
}
119186

120187
Servo::Servo()
@@ -139,6 +206,11 @@ uint8_t Servo::attach(int pin, int min, int max)
139206
return 0;
140207
}
141208

209+
// Configure and the timer
210+
if (servo_timer_config(SERVO_US_PER_CYCLE) != 0) {
211+
return 0;
212+
}
213+
142214
// Try to find a free servo slot.
143215
ra_servo_t *servo = NULL;
144216
bsp_io_port_pin_t io_pin = g_pin_cfg[pin].pin;
@@ -151,6 +223,7 @@ uint8_t Servo::attach(int pin, int min, int max)
151223
servo->period_max = max;
152224
servo->io_mask = (1U << (io_pin & 0xFF));
153225
servo->io_port = SERVO_IO_PORT_ADDR(((io_pin >> 8U) & 0xFF));
226+
active_servos_mask |= (1 << i); // update mask of servos that are active.
154227
writeMicroseconds(DEFAULT_PULSE_WIDTH);
155228
break;
156229
}
@@ -164,11 +237,11 @@ uint8_t Servo::attach(int pin, int min, int max)
164237
R_IOPORT_PinCfg(&g_ioport_ctrl, io_pin,
165238
IOPORT_CFG_PORT_DIRECTION_OUTPUT | IOPORT_CFG_PORT_OUTPUT_HIGH);
166239

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) {
240+
// start the timer if it's not started.
241+
if (servo_timer_start() != 0) {
170242
return 0;
171243
}
244+
172245
return 1;
173246
}
174247

@@ -178,10 +251,12 @@ void Servo::detach()
178251
ra_servo_t *servo = &ra_servos[servoIndex];
179252
servo_timer_stop();
180253
servo->period_us = 0;
254+
active_servos_mask &= ~(1 << servoIndex); // update mask of servos that are active.
255+
servoIndex = SERVO_INVALID_INDEX;
181256
if (--n_servos) {
182257
servo_timer_start();
183258
}
184-
servoIndex = SERVO_INVALID_INDEX;
259+
185260
}
186261
}
187262

@@ -207,8 +282,9 @@ void Servo::writeMicroseconds(int us)
207282
{
208283
if (servoIndex != SERVO_INVALID_INDEX) {
209284
ra_servo_t *servo = &ra_servos[servoIndex];
210-
servo->period_count = 0;
211-
servo->period_us = constrain(us, servo->period_min, servo->period_max);
285+
//servo->period_count = 0;
286+
servo->period_us = constrain(us, (int)servo->period_min, (int)servo->period_max);
287+
servo->period_ticks = usToticks(servo->period_us);
212288
}
213289
}
214290

0 commit comments

Comments
 (0)