# Robotic arm simulation in MikroC

I am supposed to control a robotic arm with a servo motor. The angle of rotation of the servo motor depends on the voltage of a potentiometer.

Here is my setup: And here is my code:

void main()
{
float x;

TRISA = 0xFF;
TRISC = 0x00;

PWM2_Init(3000);

while(1)
{

x = 500 + (sensorReading * 1500) / 1024;

PWM2_Set_Duty(x/3000);
PWM2_Start();
}
}


However my robotic arm (servo motor) doesn't move at all

I am using the MikroC IDE and PIC18F452 as my microchip.

What am I doing wrong?

:EDIT:

Here's (I think) the solution. For servo control, typically you would want a 50 Hz frequency. For that, you'd want

PWM2_Init(50);


Then you'd want a range of values to use for the servo. Again, typically, it'd be 1000 to 2000 microseconds, but in your code it looks like maybe 500 to 2000 microseconds.

When I look at the manual, it's wanting a value from 0 to 255 for the duty cycle. The problem is that the duty cycle the manual refers to is for the full frame, but your application needs just a small subset of the frame.

Doing the math, if the carrier frequency is 50 Hz, then you have a frame width of 20 ms, or 20000 microseconds. Your band of operation is from 500 to 2000 microseconds, which means you want:

$$\begin{array}{l} \text{min_val} &=& (500/20000)*255 \\ \text{min_val} &=& 0.025*255 \\ \text{min_val} &=& 6.375 \\ \end{array}$$ $$\begin{array}{l} \text{max_val} &=& (2000/20000)*255 \\ \text{max_val} &=& 0.1*255 \\ \text{max_val} &=& 25.5 \\ \end{array}$$

You are expecting a value from 0 to 1023 on your sensor, and you need to map that range to 6.375 to 25.5, so you get:

$$\begin{array}{l} \text{pwm_command} &=& 6.375 + \left(\frac{25.5-6.375}{1023-0}\right)*\text{sensorReading} \\ \text{pwm_command} &=& 6.375 + 0.0187 * \text{sensorReading} \\ \end{array}$$

Checking the math here:

• Sensor reading of 0 gives 6.375. 6.375 divided by 255 gives 0.025, and multiplied by the frame width of 20 ms gives 500 microseconds
• Sensor reading of 512 gives 15.95. 15.95 divided by 255 gives 0.06255, and multiplied by the frame width of 20 ms gives 1251 microseconds (halfway between min/max values)
• Sensor reading of 1023 gives 25.5. 25.5 divided by 255 gives 0.1, and multiplied by the frame width of 20 ms gives 2000 microseconds.

So that should all work, except you are using a float for the command and it takes an unsigned short. The documentation at least makes no reference to allowing floats as inputs.

Does this work? I don't know. It depends on the servo you're using and how it handles out-of-bounds values. Some may vibrate/oscillate at values just beyond the acceptable ranges, some might bind, others could fail to respond.

Your safest bet would be to operate within the allowable limits there, meaning instead of trying to go to 6.375 as the minimum value, you go to the "safe" value of 7, because it's the first integer inside the [6.375, 25.5] range. Similarly, pick the maximum value to be 25, again because it's the last integer inside the range of values.

This also means that your neutral position of 15.95 isn't achievable, and you'll need to go for 16 instead. You're lucky that this is very very close to the calculated neutral position!

If I re-work the numbers with these values, I get:

$$\begin{array}{l} \text{pwm_command} &=& 7 + \left(\frac{25-7}{1023-0}\right)*\text{sensorReading} \\ \text{pwm_command} &=& 7 + 0.0176 * \text{sensorReading} \\ \end{array}$$

Without writing out everything again, sanity check passes - A reading of 0 gives 7, 512 gives 16, and 1023 gives 25.

Putting this all together:

void main()
{
int x; // <--- New, was float, now it is int

TRISA = 0xFF;
TRISC = 0x00;

PWM2_Init(50); // <--- New, was 3000 Hz, now 50 Hz

while(1)
{