Archive for the 'Programming' Category

ESP32: Step Pulse Experiments with Timers

(Edit: Also check out my better “RMT” way to send the pulse)

I have been playing with the ESP32 microcontroller to see how well it would perform as a small scale CNC controller. The low cost and high performance as well as the built in Wifi and Bluetooth make it very attractive.

One of the challenges is step pulse timing. Most stepper drivers work with a direction signal and step signal. The step signals are a short pulse for each step. If they are too short, the driver will not detect them. If they are too long, it limits the rate at which you can send them.

You first set the direction signal high or low depending on the direction you want the motors to spin. You then send the step pulse. The direction signal has to be stable for a short period of time before the step signal is sent. The process is…

  1. Set direction
  2. Wait a bit (if it changed)
  3. Turn on the step pulse signal.
  4. Wait a bit
  5. Turn off the step pulse signal.

The timing is critical and varies by motor driver. Here is a typical spec.

Here are the specs for a few of the stepper drivers I regularly use.

 Allegro A4988TI DRV8825Toshiba TB6600
Direction Delay200ns650ns?
Step Pulse Delay1us1.9us2.2us

Test Firmware

Typically the firmware motion planner determines when to take a step, then sets an interrupt to occur at that time in the future. This allows the firmware to do other things like interacting with the user and planning future moves while it is waiting for the interrupt.

To simulate a stream of pulses, I created a timer interrupt that would case steps to occur at a constant 5kHz rate. That is onStepperDriverTimer() in the code.

In that interrupt service routine I first set the direction pin. Normally you only need to set it when the direction changes, but it will be easier to see this on a logic analyzer if I change it every time for this test. I then need to wait a little time before setting the step pulse pin. I could use another interrupt to do this, but the time is so short at about 750ns, that it is better to just waste a few cycles. In the CNC software I will only need to do this when the direction changes. That will be at the few beginning of the acceleration when the step rate is the slowest. I do this delay with a few NOP()s. The are “no operations”.

I then setup the the interrupt to end the pulse. That is onStepPulseOffTimer() in the code. I set the step pin after this because those instructions take clock cycles too. I can use those as part of my delay.

When that interrupt occurs, I turn off the step pulse signal. I also turn off the direction in this example. I am only doing it here so I can see that change on the logic analyzer. Normal CNC frmware would just leave it alone because there are typically thousands of steps before the direction is likely to change.

I wrote a program to simulate some CNC firmware so I could play with step pulse timing.

// create the hardware timers */
hw_timer_t * stepperDriverTimer = NULL;  // The main stepper driver timer
hw_timer_t * stepPulseOffTimer = NULL;  // This turns the step pulse off after xx uSeconds

// define the gpio pins
#define STEP_PIN 17
#define DIR_PIN 16

// the step pulse interrupt service routine. 
void IRAM_ATTR onStepperDriverTimer()
{
  // if ... the direction changed from last time (not in this demo)
  digitalWrite(DIR_PIN, HIGH);  // in actual CNC firmware this will go high or low
  for(uint8_t i=0; i<10; i++)
  {
    NOP();  // do nothing for one cycle
  }
  // end if

  // setup the pulse off timer
  timerWrite(stepPulseOffTimer, 0);
  timerAlarmWrite(stepPulseOffTimer, 22, false);  // the alarm point is found by looking at logic analyzer
  timerAlarmEnable(stepPulseOffTimer);  
  
  digitalWrite(STEP_PIN, HIGH); // put it after the timer setup to include the timeto do that
}


// 
void IRAM_ATTR onStepPulseOffTimer()
{
  digitalWrite(STEP_PIN, LOW); // end step pulse 
  digitalWrite(DIR_PIN, LOW); // only here for dem program CNC firmware would leave this until direction change
}


void setup() {  

 pinMode(DIR_PIN, OUTPUT);
 pinMode(STEP_PIN, OUTPUT);

    
 stepperDriverTimer = timerBegin(0, 4, true); // 80Mhz / 4  = 20Mhz// setup stepper timer interrupt ... this will simulate a flow of steps
 stepPulseOffTimer = timerBegin(1, 1, true); // 

 // attach the interrupts
 timerAttachInterrupt(stepperDriverTimer, &onStepperDriverTimer, true);// attach the interrupttimerAttachInterrupt(directionDelayTimer, &onDirectionDelayTimer, true);// attach the interrupt
 timerAttachInterrupt(stepPulseOffTimer, &onStepPulseOffTimer, true);// attach the interrupt
 
// setup the time for the 
 timerAlarmWrite(stepperDriverTimer, 4000, true);  // 20Mhz / 4000 = 5kHz rate ... this is the only one that auto repeats  
 timerAlarmEnable(stepperDriverTimer); 
 
}

void loop() {
  // no loop code required.:
  
}

Results

Here is a picture of my setup.

This is screen shot of what the logic analyzer captured. The upper line is the step signal and the lower line is the direction signal. The direction signal comes on first and then the step pulse signal comes on 0.75us later. The step pulse then lasts for about 2.5us before turning off.

Next Steps

  • I’ll go forward this method to see how well it works in actual CNC firmware.
  • I have been programming in the Arduino-ESP32 environment. This is an easy way to learn about the peripherals and do some quick tests. I may switch to the ESP-IDF  in the future.
  • I would like to investigate the RMT features of the ESP32. It is designed for Remote Controls, but I have heard it is quite flexible and might help with pulse generation.

 


If you want to be notified of future blog posts, please subscribe.

Using the HC-05 Bluetooth Module

In a previous post I showed how to use the HC-06 Bluetooth module. In this post we will be using the HC-05. They are very similar, but the HC-05 has more features. The ability to be a master is the main feature. A master can create a connection with other Bluetooth devices. In this post we will only being using it as a slave.

The basic module looks like this.

It is typically purchased soldered to carrier PCB with some additional electronics. The HC-05 typically has a 6 pin header, rather than the 4 pin HC-06 header. The two extra pins are state and enable (en). State gives the state of the Bluetooth connection and enable can power down the module by turning off the power regulator. I will not be using the enable. I will use the state to allow programming of the Arduino via Bluetooth.

 

Here is a schematic of the carrier board. Not all carrier boards are the same, though.

The parts on the carrier PCB are pretty basic

  • 3.3V Low Dropout Regulator, which allows you to power it from 3.6V to 6V.
  • An LED to show the mode.
    • Fast Blink = Waiting for Bluetooth connection3.6
    • Slow Blink = In AT command mode
    • Double Blink = Connected via Bluetooth
  • A button to enter AT Command Mode
  • A diode, probably for reverse voltage protection.
  • Various Pull Up/Down resistors and bypass capacitors.

Configuring the HC-05

Like the HC-06, the HC-05 has a AT command mode, but the commands are a little different. The HC-05 is put in the AT command mode by holding in the switch while applying power. It will do a slow blink when in AT mode. AT Mode accepts commands at 38400 baud , N,8,1 using the Rx and Tx pins. You should level shift the Tx out of your Arduino to 3.3V using a resistor divider. Commands are sent with line feed and carriage return appended to the end. Most serial monitors can do this for you including the Arduino Serial Monitor.

Any command that sets a parameter can also get it.

  • Set commands are in this format “AT+CMD=PARAM” like … AT+NAME=FRED to set the name to FRED. Some commands have multiple parameters that are separated by commas.
  • Get commands are in this format AT+CMD?” like AT+PSWD? to get the password. Weirdly, they all seem to work except AT+NAME?.

Here are the commands you needs for slave mode. Remember, each is followed by a line feed and carriage return.

  • AT (This is just a way to test the connection. It will respond “OK”)
  • AT+VERSION? (This returns firmware version info)
  • AT+ROLE=x (for x use 0 =Slave role, 1 = Master role, 2 = Slave-Loop role default = 0)
  • AT+NAME=xxxxx (to change name to xxxxx default=HC-05″)
  • AT+PSWD=nnnn (to change password to 4 digit nnnn default = 1234″)
  • AT+UART=nnnn,s,p (nnnn=Baud, s=stop bits (0=1, 1=2), p=parity (0=None, 1=Odd, 2=Even) Example AT+UART=115200,0,0
  • AT+POLAR=a,b (a=PIO8 (LED), b=PIO9 for both 0=low turn on, 1 = high turn on. (see below for how we use this)
  • AT+ORGL (reset all parameters to defaults)
  • AT+RESET (restarts the HC-05. Will not be in AT mode afterward unless button held”)

Using an Arduino to program the HC-05

We need some hardware to talk to the HC-05. An Arduino will easily do that. Here is a diagram and sketch to do this using an Arduino UNO.

This is the hardware diagram. I show an UNO, but virtually any hardware (Nano, Mega, etc) will work. The HC-05 is a 3.3V device so we need to level shift the Arduino 5V Tx signal down to 3.3V.  The diagram uses a resistor divider to do this. The Arduino should have no trouble reading the 3.3V Tx signal from the HC-05, so we don’t need to shift that.

The State connection through the capacitor is optional. This will force a reboot of the Arduino when a Bluetooth connection is made. More on that later.

BTW: A lot of people don’t bother to level shift and it appears to work fine, at least in the short term 🙂

The Arduino Sketch

Here is the sketch I use. We will be setting up 2 serial links. One link will be from the PC to the Arduino to send the commands from the keyboard over USB.  We also need a serial connection from the Arduino the HC-05. We will use a software serial port for this and can use any remaining pins to do this. HC-05 uses 38400 baud for AT commands, regardless of the what you set it to for Bluetooth operation.  I used 115200 for the PC to Arduino connection. Set the Serial monitor like this.

You can then type AT commands in the Sereial Monitor.

Here is the sketch…

#include <SoftwareSerial.h>

#define SOFT_RX 11
#define SOFT_TX 12

SoftwareSerial hcSerial(SOFT_RX, SOFT_TX); // RX, TX

String fromPC = "";

void setup() { 
  Serial.begin(115200); // hardware serial for the USB-PC
  hcSerial.begin(38400);  // software serial Arduino to HC-06 (38400 is default)

  // print instructions
  Serial.println("HC-05 AT Command Programmer V1.2");

  Serial.print("For Arduino Rx use pin ");
  Serial.println(SOFT_RX);
  
  Serial.print("For Arduino Tx use pin ");
  Serial.println(SOFT_TX);  
  
  Serial.println(" -- Command Reference ---");
  Serial.println("To Read use '?', Like AT+PSWD?");
  Serial.println("AT (simply checks connection)");
  Serial.println("AT+VERSION (requests the firmware verison)");
  Serial.println("AT+ROLE=x (0 =Slave role, 1 =  Master role, 2 = Slave-Loop role  default = 0)");
  Serial.println("AT+NAME=xxxxx (to change name to xxxxx default=HC-05");
  Serial.println("AT+PSWD=nnnn (to change password to 4 digit nnnn default = 1234");
  Serial.println("AT+UART=nnnn,s,p (nnnn=Baud, s=stop bits (0=1, 1=2), p=parity (0=None, 1=Odd, 2=Even)");
  Serial.println("AT+POLAR=a,b (a=PIO8 (LED), b=PIO9 for both 0=low turn on, 1 = high turn on.");  
  Serial.println("AT+ORGL (reset all parameters to defaults)");
  Serial.println("AT+RESET (restarts the HC-05. Will not be in AT mode afterward unless button held");
  
  
}

void loop() {
  // Read from HC-05
  if (hcSerial.available()) {
    while(hcSerial.available()) { // While there is more to be read, keep reading.
      Serial.print((char)hcSerial.read()); // send it to the PC
      }   
  }
  
  // Read from PC
  if (Serial.available()){
    delay(10); //     
    fromPC = (char)Serial.read();    
 
    
      hcSerial.print(fromPC); // show the HC-05 responce
      Serial.print(fromPC); // echo it back to the PC
    
  }
}

Arduino Programming over Bluetooth.

Arduinos are programmed over serial via a bootloader. A bootloader is program that runs for a few seconds whenever the Arduino is started. It looks for someone trying to program it. It runs in one part of the Arduino’s memory. If it does not detect an attempt to program the Arduino it switches to the part of memory where the last program (sketch) resides. If it does detect an attempt to program the Arduino, it reads the incoming program instructions over the serial port and writes them to that other part of memory where normal programs (sketches) reside. Once the upload is complete it switches to that program and runs it.

Therefore, in order to program the Arduino over a serial connection, you need to trigger a reboot. The Arduino USB creates a full RS232 connection. In addition to Rx and Tx is has other control lines like DTR (Data Terminal Ready). The Arduino uses the DTR signal to force a reset. To reset an Arduino you pull the reset line to ground. The DTR signal out of the USB chip is high when there is no connection and low (ground) when there is a connection.

If we directly connect DTR to the reset pin, the Arduino will be stuck in permanent reset mode whenever a serial connection is open. To correct that, a capacitor is inserted in the circuit. Capacitors block a continuous signal, but pass a quick transition. Therefore the the change from high to low will look like a quick pulse to ground at the reset pin. That pulse is exactly what is needed to reboot run the bootloader.

Here is what that circuit looks likes on an Arduino Nano schematic. The length of the pulse depend on the value of the capacitor and characteristics of the high to low transition.

The HC-05 state pin will work for this. In its normal mode it is high during a connection. We need that to be low (ground). Fortunately the HC-05 has the Polar command. That allows you to flip that logic. AT+POLAR=1,0 will do the trick. The first parameter is for the LED. We leave that at 1. The second parameter is the state and we switch that from the default of 1 to 0.

I found that the typical 0.1uF capacitor would not generate an adequate pulse to ground, so I bumped it up to 1.0uF. It occasionally does not work when uploading. I think a little less capacitance might be better. The Arduino uses the hardware serial connections for programming, so you use those pins. When programming the Arduino use the virtual serial port you got when pairing the Bluetooth. Do not use Bluetooth and the USB serial port at the same time. Both would be connected to the hardware Rx and Tx and conflict with each other and possibly cause damage.

 

Other Reset Features

You may not care about uploading code over Bluetooth, but some of your applications may expect that reboot on connect behavior. I have found this with some GCode senders. They open the serial port and expect to see the text Grbl spits out at startup. Without seeing this text, the program wrongly assumes there is a problem and closes the connection.

Video

Useful Links

 


If you want to be notified of future blog posts, please subscribe.

The Polar Coaster – A Drink Coaster Drawing Machine

I designed this machine to draw custom, round drink coasters. I already have a laser cutter for square coasters and I wanted to try something unique for round coaster.

The Base

The base of the machine has two stacked 5mm bearings in the center for the bed to rotate on. There are (3) 3mm bearings on the bed perimeter that provide support and keep it level. They have little shafts that snap into the base.

The Bed

The bed is  a 156 tooth GT2 pulley. It has little springy fingers that grip the coaster when it is on the bed. The bed connects to the motor pulley with a closed loop belt.

The Radial Arm.

This is a belt driven, cantilevered arm that uses 6mm shafts and linear bearings. The belt is a cut pieces with the ends clamped at the carriage. It has a slotted mounting hole that lets the arm rotate. The pen must be adjustable to get to the exact center of the coaster or the drawing will be distorted. There is a limit switch on the top.  This is the only axis that needs to be homed. To setup the machine you home it and jog the pen until it is exactly over the center of the bed. You then set the work zero for X (Gcode: “G10 L20 P0 X0”). This only needs to be done once. If you use different types of pens, the center should be rechecked.

The Z Axis

The Z axis uses a micro servo and a cam to control the height of the pen. The firmware is setup to only have (2) Z positions, pen up and pen down. It uses 3mm rods and tiny little 3mm linear bearings.  There is a compression spring on one of the rods that applies a little pressure to the pen, and allows the pen to float a little on uneven coasters.

The Controller

I used my Grbl HAT controller. It is a bit overkill for this project but works perfectly.  It is attached to a Raspberry Pi in this photo, but I have not been using the Pi in this project yet. I just connect directly via USB.

Kinematics and Pre-Processin

See this blog post on how it was done. The pre-processor is written in C#, but it is rather simple and you could probably read the source file and convert if you cannot deal with C# on Windows.

Firmware

I use a modified version of Grbl 1.1f.  Grbl does not support servos, so I needed to hack that in.  I used the PWM that is normally used for the spindle speed to control the servo. I turned off the variable speed spindle option and streamlined the spindle functions to the bare minimum I thought Grbl needed.  I adjusted the PWM parameters for use with a servo and added pen_up() and pen_down() functions. I tried to put as much of the custom code into one file spindle_control.c. I had to add a few lines in stepper.c to look at the current machine Z height and apply the correct pen up/down function.

CAM

You can use anything to generate the gcode that works with Grbl. The pen will go up when the Z is above zero and down when it is below zero. Therefore, you want the Z movement as short as possible to speed up the drawing and not have the pen dwell on the material and bleed.  I make the depth of cut 1mm and the z clearance 3mm.

CAD Files.

The design was done using PTC CREO 3.0.  A STEP version of the design is linked at the end of the post.

Performance

It does a great job. Here a recent coaster. This was done from a rasterized bitmap image found online (searched: circular Celtic braid).

Here is a Fat Tire beer themed coaster.

Coasters are made to be super absorbent, so larger tipped felt pens tend to bleed a little too much. I like to sketch with Micron pens and the thinner ones really work well on this machine.

Build You Own?

The build is not difficult, but covers a lot of areas. You should know how to work with STEP files and compile firmware.

The design is open source with no commercial restrictions, so feel free to use any part of my work. I found most of the parts on Amazon and eBay. I bought the belt from Stock Drive Products. The polar motor pulley is 36 tooth and the arm pulley is 20 tooth.  Cutting the shafts requires an abrasive cutoff wheel.

Please post any questions in the comments section and I will try to address them.

Links

 

I sell on Tindie

 

 

 

PSoC 5 Daughter Card for XCC

I finished a PSoC 5 daughter card design for X-Controller-Controller project.  10 boards should arrive in about a week. This will clean up all the wiring from the breadboard testing I have been doing. My goal is to have a clean development platform for me and possibly others to work with.

The design has the following features.

  • Mounts CY8CKIT-059 dev board directly
  • Mates directly to the X-controller-Controller
  • Independent control of 4 axes.
  • Connector for X-Controller button panel
  • Connector for  X-Controller power supply PCB
  • Connector for a Serial LCD panel (Itead Studio Nextion style)
  • PSoC controlled stepper motor current.
  • PCoC controlled idle current.

Here is an image of the CYC8CKIT-059 development board.  The CPU is a PSoC 5LP.  The price is only about $10.  It comes with a built in programmer, debugger, and USB/UART. This can be snapped off.  To fit into the X-Controller, I snap off the programmer and mount it in another location.  The connections are made on the PCB.  I plan to use stackable headers so all of the pins are still easily accessible.

Links

Please subscribe to me on YouTube or follow me on Twitter.

 

Step Pulse Extender – PSoC Style

The TB6600 stepper drivers I have in the X-Controller have a “Torque” feature.  You set the motor current with a reference voltage and the torque feature allows you to easily switch between that current and 1/3 third of it. This is typically used for an idle current reduction feature at the system level.

tq_schm

Why Idle Reduction?

Stepper motors have a lot of hold torque, but that torque quickly falls off with speed.   Therefore you typically size a stepper motor and set the current for your maximum cut or rapid speed.  This means your motors will have excess torque when idle and will tend to run hottest at idle.  You basically the the current as high as possible until the motors get too hot.  If you could reduce the current at idle, you would reduce the temperature and could set the current higher than normal when spinning.

This is great, but the machine will never be in idle during a long job.  At least one of the motors should always be running. If you could figure out when each individual motor was idle, you handle each motor independently.  That is not easy in firmware, but there are tricks to do it in hardware.  You could tie the feature to the step pulse.  Whenever the step pulse is active, the full torque could be active.  That has two problems. The step pulse is extremely short, in the range of a few microseconds.  The other is you might want the current high for a a short bit after the motor goes idle just to make sure the machine is stable in the new position.

The trick is to use the step pulse, but extend it to the desired duration.  It should stay on through all the step pulses and extend the last pulse.

Discrete Hardware Solution

The X-Controller uses a discrete logic chip to do this. It uses a retriggerable monostable vibrator (74HC123D).  The R/C circuit on the right of the schematic snippet sets the duration. It works great, but this adds a lot of parts and things are locked down and not easily adjustable. If you needed to override this function, you have to break out the soldering iron.

PSoC Solution

With PSoC, when you hear “discrete logic” you should know there is probably a good way to do it on the chip. In this case I designed a custom component using verilog.

The verilog code is quite simple.  The best part is none of this is done on the CPU, so there is no impact on the motion control performance. What the video to see the details.

 

Yet Another Way to do the Kinematics

image1

Paul Kaplan, originator of the Easel project, came up with another way to do the kinematics for the Line-us Clone. My method used intersecting circles. His method uses the Law of Cosines.

The Law of Cosines relates the lengths of the sides of a triangle to the cosine of one of its angles.

Triangle_with_notations_2.svg

lawofcosines

 

This can be used to find the angles of the servo arms.

(Click on the images if you want a larger view)

The Goal

goal

The goal is to find the two angles, A1 and A2, of the servo arms

Known Values

  • Px is the desired X location of the pen
  • Py is the desired Y location of the pen
  • L1 is the length of the upper servo arm (50mm)
  • L2 is the length of the end of the Pen Arm (50mm)

Step 1

Find the distance “D” of the pen to hub using the Pythagorean Theroem and the angle T1 using arctangent.

Px2 + Py2 = D2

rewritten … D = Sqrt(Px2 + Py2)

step1

 

T1 can be found using the arctangent or inverse tangent formula. Note: When programming use the atan2(x,y) function to preserve the quadtrant.

T1 = atan2(Py,Px)

Step 2

step2

 

Find T2 using the Law of Cosines

L12 + D2 – L22 = 2 * L1 * D *cos(T2)

rewritten …  T2 = acos( (L12 + D2 – L22) / (2 * L1 * D))

Step 3

step3

 

Find T3 using the Law of Cosines. We want the left one of the two T3 angles, but since the linkages form a parallelogram that same angle shows occurs in several places.  We will use the right one and the dimensions associated with it.

L12 + L22 – D2 = 2 * L1 * L3 * cos(T3)

rewritten … T3 = acos( (L12 + L22 – D2) / (2 * L1 * L2))

Step 4

step4

 

Determine A1 and A2 from the angles we figured out.

A1 = T1 + T2

A2 = A1 + T3

Conclusion

I think I will switch the code to use this method. I think I can optimize it better in C code. The speed of the code is important.  The faster it runs, the most times per second we can run it.  The more often we run it, the smoother it will run.

 

Hobby/RC Servo Control in PSoC

psoc_setup

 

The PSoC family is my go to line of processors for prototyping.  It is like having a breadboard full of digital and analog circuits that you can wire up on the fly. I have been doing some stuff with hobby servos lately so I needed to figure out how to do it on the PSoC.

Hobby Servos

Wikipedia

From Wikipedia

servo_timing

Image from Adafruit

 

Hobby servos set their rotation based on the length or a repeating pulse. The pulse should be 1ms to 2ms long and repeat every 20ms.  One end of the rotation is at 1ms and the other is at 2ms.

The PSoC PWM  Component

PWM_Comp

The PWM component is perfect for this job.  The PWM component can be setup to have a period and an on time.  The period should be 20ms and the on time would be between 1ms and 2ms.  The component uses a clock and two counter values.  The component will count on every clock pulse.  It resets the counters after the period count has been reached and the CMP value determines how long the pulse is logic high.

The PWM output goes to the servo control line.  Here is the configuration dialog box for the PWM component. The graph at the top is a good reference for what the output will look like.

pwm_setup

The goal is to have a pretty decent resolution to set the 1ms to 2ms pulse.  I chose a 2MHz clock.  I picked the fastest clock that would still fit within the 16bit (65535) limit of the control.  PSoC clocks are derived from system clocks, so you need to pick values easily divided down from them.  The IDE helps with creation of these clocks.  At 2Mhz the period (repeat rate) should be set to 40,000.  The equation is the clock * period(in second) = period counts (2,000,000 counts/sec * 0.02 secs = 40,000 counts).

The CMP Value is how many counts the high pulse should last.  The equation is the same. For 1ms the count would be (2,000,000 cnts/sec * 0.001secs =  2,000 counts) and for 2ms the counts would be 4,000.  The range is 2,000 to 4,000 (2,000 count resolution).  This is better than most hobby servos can do.

The Code

The IDE will generate a bunch of functions, a custom API, for each component used when the application is built. There are two PWM Component functions we need to use for this application .

  • PWM_Servo_Start() This will initialize the component and get it running. This is called once at the beginning of the program.
  • PWM_Servo_WriteCompare(val) This sets the CMP Value that will be used to set the pulse length.

I also wrote a function the can set the value by degrees.

void setServo(float degrees)
{
unsigned int val;
// convert degrees to compare value
// 2000 to 4000 = 0 to 180
// value is
val = (degrees / 180.0 * 2000.0) + 2000;

PWM_Servo_WriteCompare(val);
}

The Results

Here is a screen shot of my logic analyzer. The output was set for 1/2 rotation. The pulse is 1.51ms and the period is 20.14ms.  That is close enough for me.  It is likely the clock speed is different between the PSoC and  and the analyzer.

capture1

 

Typically you will have to tune the to the actual servos used.  Just tweak the endpoint values until you get the rotation you want.