Micro-FOBO Fall 2019

Micro-FOBO: ServoShield Firmware

Author: James Spane

Table of Contents

Introduction

Micro-FOBO’s electrical design is based around using a CD4017 decade counter to drive all eight servos using only two pins. Luckily for us there is a shield designed by Renbotics, the same shield ProjectBiped bases their design off of, that has a library already programmed for it that we can utilize. Unfortanutely the original ServoShield code is intended to work with the Arduino Uno, which is based on the ATmega328P microcontroller. This has a 16MHz clock. We are using the 3DoT, which is based on the 32u4 microcontroller. This presents an issue.

How It Works

To understand why it is an issue, we must first understand how the file works with the CD4017 Decade counter. On a higher level, the file uses two pins on the Arduino to output a pulse to the clock pin and the reset pin on the CD4017. The program will start by sending an initial pulse to the clock pin, and then after a calculated amount of time it will send a second pulse to the clock pin. The time between each pulse will be between 1ms and 2ms, and this will direct the servo motor to which angle to position itself to. Using a combination of the clock pin and the reset pin, we are then able to fully control all of our servos using just those two pins. The relevant ServoShield Firmware is split into two files, a header file and a CPP file.

ServoShield Header File

The ServoShield header file which contains all of the assigned pin outs for the hardware the program is intended to work for, along with all of the libraries necessary to make it work. As well as common functions that can be called to execute the code in the arduino files we will use.

We need to review the code assigning the pins for the Arduino Uno:

#define counter1resetpin 7
#define counter2resetpin 9
#define deadperiod 18500
#define step 26
#define counter1cntddr DDRD
#define counter1cntport PORTD
#define counter1cntpin PORTD6
#define counter2cntddr DDRB
#define counter2cntport PORTB
#define counter2cntpin PORTB0

We can see that there are two counters being assigned to a total of four pins. Counter1reset is assigned to digital pin 7 for the reset pin, and counter1cnt is assigned to PORTD6, which is digital pin 6 which outputs to the clock pin of the CD4017. Since the program can be used for up to 16 servos, it uses two counters. For our design we only need 8 servos, so we can actually simplify the code and reduce it down to one counter, eliminating counter 2. From here we need to assign new pins that are compatible with the 3DoT. The reset pin will be MOSI, D16, and for the clock pin we will use SCK, D15. The following result is the code shown below:

#define counter1resetpin 16
#define counter1cntddr DDRB
#define counter1cntport PORTB
#define counter1cntpin 1

ServoShield CPP File

The ServoShield.cpp file is a lot more complicated and very poorly annotated. The file contains two ISRs, interrupt service routines, that are used to send the pulses as discussed before through the reset and clock pins. Both counter one and counter two as discussed in the header file are set up to work with Timer 1 and Timer 2 of the AtMega328P microcontroller. The AtMega 32u4 does not have a timer 2. So initially we converted all of the relevant timer 2 values to timer 3. After many hours of testing, experimenting, and researching though we were determined only one timer was necessary. This lead us to updating the code in three major spots:

uint16_t CalcCounterValue(int Time)
{
return (uint16_t)(65535 - (Time * 16) + 1);
}
ISR(TIMER1_OVF_vect){...}
ISR(TIMER2_OVF_vect){...}

After a bit of educated guessing and experimenting due to the poorly annotated code, we figured out that inside of the CalcCounterValue function, the 16 did indeed represent the frequency of the clock. We changed that to 8. After that, we were able to remove the Timer 2 from the whole algorithim as it was only necessary if you were running 16 servos. This took a small bit of cleaning up beyond the few lines I referrenced above, but it was fairly straight forward. This left us with the following result:

uint16_t CalcCounterValue(int Time)
{ 
return (uint16_t)(65535 - (Time*8) + 1); 
} 

ISR(TIMER1_OVF_vect) {...}

Conclusion

Although the overall solution appears to be simple now, this issue actually delayed Micro-FOBO’s overall progress a significant amount. This proved to be a very time consuming issue that was completely necessary to solve in order to move forward with using the 3DoT as the primary processor unit for Micro-FOBO. Once it was solved we were able to make an enormous amount of progress on the electrical design such as the PCB that was being help up. All of the updated code is linked in the references for future generations of Micro-FOBO to use as well.

Professor Hill also sent a mostly complete version of the ServoShield file that helped us simplify our code a bit. Although a long term goal would be to convert our working code to be as clear and efficient as the code that was sent from the Professor.

References/Resources