The circuit consists largely of the usual minimum requirements for a PIC (PIC18F248 dealt with here) that is, power supply, oscillator (external crystal oscillator - 24MHz) and in-circuit serial programming (ICSP). A voltage reference is formed by the TL431 programmable shunt regulator diode.
The majority of the circuit is based upon the DIY PIC Development Board.
Circuit Operation
A "wall wart" power supply was chosen rather than constructing a dedicated DC power supply dropping/converting from an AC wall socket. Surplus chargers from laptops are readily available (in this case supplying 16-24V with 65W max) which provide not only a safer option (compared to construction from a suitable transformer, rectifier, connection to AC etc) but also a much more economical option (generally zero cost for a surplus charger, compared to ten's of dollars for a suitable transformer, let alone cost of ancillary circuitry, PCB etc).
The surplus laptop charger requires a suitable socket connection and a voltage regulator, in this case a LM317T, to provide the regulated 5V generally required by PIC microcontrollers. The power supply circuit is given in the Schematics Section. The LM317T circuit is the standard design direct from the datasheet, with input and output capacitors to provide smoothing and the resistor/potentiometer to provide selection of output voltage.
A TL431 programmable shunt regulator diode is used to provide a 5V reference for the PIC ADC module.
A MAX232 is used to enable RS-232 communication between the PIC microcontroller and an attached PC, in order to provide a means of starting/stop the servomotor, varying the pulse-width and similar functionality (rather than using buttons or similar).
Light Sensing/Tracking
Two light dependant resistors (LDR), in conjunction with 1K resistors forming voltage dividers, are used to sense light levels via voltage sensing using the PIC analog digital convertor (ADC) peripheral. The LDR's are mounted (see the Construction Notes/Trouble Shooting Section) in such a manner that an incident light beam cannot fall directly upon both LDR's simultaneously unless the LDR's are both perpendicular to the light beam.
The differential signal from the LDR's is therefore used to control the servomotor to move a platform holding the LDR's in order to reduce the LRD voltage differential below the desired threshold (and thus track the brightest incident light source). The firmware for reading the voltage from the LDR's is the same as described and detailed in the PIC analog to digital converter (ADC) page.
Software/Firmware
The firmware enables connection of the PIC18F248 with a PC via RS-232 and therefore control of the servomotor via keyboard commands. The firmware provides the necessary control of the PWM signal and reading of the light-dependant resistors (LDR's) etc. The firmware in terms of the reading the voltage across the LDR's is the same as described and detailed in the PIC analog to digital converter (ADC) page.
Pulse Width Modulated Signal
As briefly discussed in the Project Background Section, the SG-90 servomotor requires a PWM signal of 50Hz frequency with a variable duty cycle (nominally 1ms to 2ms). There are various methods of producing such a PWM signal, but since using a PIC18F248 for testing the SG-90, the microcontroller onboard peripherals were used.
The PIC18F248 has a PWM module which notionally would make using the SG-90 straight-forward. However, with a PIC oscillator frequency of 24MHz, the slowest possible PWM frequency using the PIC18F248 PWM peripheral is ~1.4kHz.
Using a slower oscillator speed is just "wasting" processing power of the PIC18F248, so in this case one of the PIC18F248 16-bit onboard timers is used to "manually" generate a PWM signal via an interrupt routine. This enables the PIC hardware to predominately handle the PWM signal, maximising the number of instruction cycles for other firmware use.
In order to produce the PWM signal, the PIC timer interrupt routine consists of initially driving the desired output pin high for the desired amount of time (nominally 1ms as a minimum for the -90o servomotor position, to a maximum of 2ms for the +90o position), and then holding the output pin low for the necessary remaining time to complete the PWM period (for a total of 20ms, i.e., 50Hz) - see Diagram 1 in the Project Background Section.
The following equation calculates the milliseconds for each "tick" of the PIC Timer1:
each PIC Timer1 tick (milliseconds) = 4 * prescaler / Tosc * 1000 where:
- 4 is the MIPS for the PIC
- Prescaler = allowable values 1,2,4,8
- Tosc = PIC oscillator in Hz (24Mhz = 24000000)
- 1000 = 1000ms/s (converts the calc to give clock tick in millisecs)
PIC microcontroller timers generate an interrupt on overflow, so the calculated number of clock ticks (equation above) need to be subtracted from the timer overflow value (65535 for 16-bit Timer1) to give the “starting point” for the PIC timer counting (which is the parameter used in the set_timer1() CSS compiler function).
Code Snippet 1:
#INT_TIMER1
void servoPulse(void) {
disable_interrupts(INT_TIMER1);
if (input_state(OP_PIN)) { //high .: set for low part of pulse
set_timer1(pulseLow);
} else { //low .: set for high part of pulse
set_timer1(pulseHigh);
}
output_toggle(OP_PIN);
enable_interrupts(INT_TIMER1);
}
The values of "pulseLow" and "pulseHigh" in code snippet 1 are calculated by the following function.
Code Snippet 2:
void calcPWMparameters() {
/******************************************************************************
* Overview: calculates set_timer1 values for PWM high portion (duty cycle), and
* low portion (the PWM period minus the duty cycle)
* Dependency: assumes timer1 is being used (16 bit) with prescaler value = 2, 24Mhz system clock
* inputDuty is 8-bit integer 0-254, but represents 0 to 2.54 ms for duty cycle
* Notes: 1. because doesn't check/change values before existing PWM cycle has finished
* could get some funny jitter or similar on the servomotor
*****************************************************************************/
float timerTickTime;
float interruptPeriod;
timerTickTime = (float)(4 * 2)/(24 * 1000000) * 1000; //gives ms/Timer1 tick where 4=PIC MIPS, 2 = prescaler, 24= PIC clock Mhz, 1000=ms/s conversion
interruptPeriod = (float)inputDuty/100; //convert the integer 8bit representation into the desired milliseconds 0-2.54
pulseHigh = 65535 - (interruptPeriod/timerTickTime);
interruptPeriod = 20 - interruptPeriod; //20ms is the fixed length of the PWM period required by the SG90
pulseLow = 65535 - (interruptPeriod/timerTickTime);
interruptPeriod = (float)inputDuty/100; //JUST FOR PRINTF
}
Only Logged-In Members can add comments