How to Program a Servo and Joystick in C Language ATMEGA328p
Updated: Nov 9, 2020
Welcome back to the most incredible website on Earth that shows you the secrets of programming electrical components and robots in the C language, designed for anyone to understand.
Moving a servo is the first robotic dream you had. You imagined a lethal looking fighting robot that sends out a cross jab hook combo just before ripping it's opponent's head from it's circuits. Maybe you imagined an Earth gripping remote control tank that can shoot lasers into space.
I'm going to explain the complex workings of the servo along with how the chip interacts with the joystick to control it. Keep in mind you don't have to use a joystick to move a servo. You could use simple push buttons or program a routine for the chip to tell the servo when and how much to move clockwise or counter clockwise.
The code is complex and very involved. It requires you to be familiar with the datasheet in order to flip the correct bits. For the sake of getting you to move your servo quickly I will not go too into all the available bits and functions. Let's focus on getting this done in the simplest way possible.
Here is the schematic showing how to hook up the whole thing.
Programming the Servo in C
There are several types of servos. Let's focus on the most common and cheapest used in Arduino and Raspberry pi circles. Ladies and gentlemen I introduce to you the TowerPro SG90 micro hobby servo.
I have a complete explanation of how servos work here on my website. If you want to dive into that here is the link.
Let's review the basics of the servo. Inside there is a small DC motor connected to a gear box that translates it's little power into higher torque. The motors are connected to a tiny circuit board that moves the servo clockwise or counterclockwise based upon the PWM electrical pulse coming in on it's signal line.
Basic servos have 3 lines: Power, Ground, and Signal.
When connecting your servo it is vital that you connect it's ground wire to the ground wire of your microcontroller chip. I'm using the ATMEGA328p. This allows the servo and chip to share a common ground for reference. If you don't do this, your servo will not work and you will end up throwing everything at the wall. I've been there.
The servo needs a PWM coming in at 20 milliseconds (20ms).
We need to program our chip to send out pulses at that same frequency, 20ms, in order for the servo to work. We will program these electrical pulses to shoot out of a PWM capable pin PB1.
Here is a snapshot of the C code to move a servo with a joystick:
No copy pasty.
You will learn it.
Let's take a closer look.
We only need to included the avr/io and util/delay header files for this build.
Next we can set up the 16bit timer for PWM sending pulses out on pin PB1.
PB1 is on the bottom right of the chip and is one of our 6 PWM enabled pins available.
We give this a name timer1Servo, you can use any name you want.
This code divides our chip's clock by 8 making an easy 1mHz to refer to instead of having to calculate for an 8mHz clock. That's why we use the prescaler here.
We are turning on with: |= (1 << whateverbitisinthedatasheetthatweneed)
I left notes in neon pink so read them to understand everything.
ICR1 is a thing in the chip that we can use to set a TOP maximum value to be reached cleared and counted up to over and over again. 20,000 is the maximum value.
It stands for 20,000 microseconds.
20,000 microseconds = 20 milliseconds.
Our servo frequency is 20 milliseconds(ms).
Like I said, the pulses are sent out on DDRB's pin PB1.
So to recap, the chip is counting up to 20000 microseconds which is the same as 20 milliseconds and setting pin PB1 to be ready to output whatever length of pulse we set it to.
We will cover how to set how long each pulse goes out at this frequency in a minute. Let's go over how the joystick works first.
Need some servos? Get em here:
Programming the Joystick in C
The next part is setting up your PS/2 (not Playstation 2) style joystick.
You can use whatever you want. I'm going to show you how this one works.
The joystick uses a minimum of 3 pins as well but 5 total. We will be using: Power(VCC), Ground(GND), and X axis. The pins we won't be using are the Y axis and the button press.
The joystick is so simple it will blow your mind.
The joystick needs a flow of electricity running through it.
The gimbals that you push up and down and left and right are a pair of potentiometers. That's right. If you don't know what a potentiometer is, lucky for you I have a tutorial right here for you.
A potentiometer is a blade that sweeps across a resistive circular surface. The resistance increases as more of the surface touches the blade. The electricity is then sent out on the x-axis and y-axis pins.
The microcontroller has an Analog to Digital Converter that is very complex which translates the electrical force coming in to digital values of 0-1024. Pretty cool but a little difficult to wrap your head around. It will take a few re-reads to get it, don't worry, you will.
We will keep this simple and just use the x-axis and hook it up to PC0 which is the pin attached to ADC pin 0 (ADC0).
We use the ADC VCC and GND on the right side of the ATMEGA328p.
Setting up the ADC is just as critical as setting up the PWM correctly for the servo. Don't worry, it will all be worth it.
Once you can do PWM and know how to use the ADC you can do pretty much anything. The whole world of electronics and robotics will be wide open to you.
Here is the code to set up the ADC:
If you read the pink notes you will see how simple it is to set the ADC up and turn it on. The bits flipped on in the chip can be found in the datasheet
The hardest part is making the code to actually read and work with the ADC.
Here is the next part of code we need to do that:
We set up a 16bit variable we can call anything. I call it readADC and we need it to refer to an 8 bit variable we can call channel.
What this is going to do is allow us to read the 10 bit value from the ADC which is why we don't use an 8 bit variable but a 16 bit instead.
When we run through this code we can include a number which we will call channel.
This is going to let us flip through whatever ADC pin we want.
channel 0 = pin ADC0 which is pin PC0
channel 1 = pin ADC1 which is pin PC1
If you are confused, look at the chip's pinout and it will make sense.
This took me a long time to understand so I hope this helps you learn much faster than I did. Have patience with yourself, this is a new language and culture.
The pink notes tell the rest of the story.
In our MAIN section of the code all we have to do is call the 16bit Timer1 code and our ADC setup into play.
Now onto the LOOP:
We create a 16 bit variable we can call anything, I call it ' x ' so I can remember it's for the x axis labeled pin on the joystick. 8 bits is too small for our 10 bit ADC value so we use 16 bits here.
This x is going to be attached to our 10 bit ADC value.
Next, we have x become whatever value the readADC function which does the actual ADC conversion on whatever channel we want. Here we set it to channel 0 which is pin ADC0 which is pin PC0.
See? It's that simple, it just takes a little brain power.
It's all just labels and flipping switches on and off and it's fun to get good at it!
So, we have our ADC values ready now we just need to translate them to output pulses on our PB1 pin.
We do this by referring to the OCR1A bit which is assigned to PB1, again just look at the pinout diagram and you will see.
OCR1A is going to be the pulse width, the length of time the pulse of electricity is sent out each 20 milliseconds.
The pulse is going to be that 16 bit value we labeled ' x '.
When we move our joystick on its x axis the ADC will translate the electricity coming in on pin PC0 to values ranging from 0 - 1024.
We need pulses of 1millisecond to 2 milliseconds.
Already, you can see our ADC value will fall short.
This is why I multiplied our ADC value by 3.5.
This is just a number I came across after trial and error to help scale the ADC values.
So, instead of pulses of 0-1.24 milliseconds now we have 0-3.58 milliseconds which is too much I agree but it works pretty well.
Now YOU can play around with this multiplier value and find a BETTER way to do it!
Damn I'm good at teaching.
Need some joysticks?
Get em here:
Congratulations, you just learned how to use the 2 most critical skills in robotics and electronics.
It may take a few tries to get your wiring right but just stick with it.
Electronics and robotics is an exercise in patience.
Make sure to subscribe to my videos so you don't miss out on the best looking holographic face ever conceived.
See the best servo joystick video in the world below!