The laser tube power supply I have allows two methods of control, 0-5VDC and PWM. The PWM frequency is 20kHz - 50kHz. This suggests there is some RC low pass filter on the input to convert the PWM to 0-5VDC.
Mach allows you to setup a pin as a PWM output.
A decent PWM requires a very fast kernal frequency. If you want a 20,000Hz PWM frequency (minimum recommended by laser power supply) and you want to control the duty cycle in 1% increments. The kernal needs to run at 20,000 * 100 or 2,000,000Hz. That is way beyond what Mach3 can do. What we can get out of a basic computer is 25,000Hz. That means in order to get our 1% adjustment resolution be need to set the frequency at 25,000/ 100 = 250Hz. That is below the required frequency, so we need to convert to a voltage via a RC filter. So how well will this work....See below.
This is under Config >> Spindle Pullies and will equate 0-100 RPM with 0-100% power.
To convert the PWM signal to a voltage, you need to run it through a RC low pass filter. This will smooth out the signal, filling in the gaps durring the off periods of the PWM. The filter needs a time constant that is long enough to reduce the voltage ripple, yet short enough to have decent transitions in power.
Here are some scope plots from Mach3 running PWM out at 50%, 10% and 90% duty cycle.
Here are some plots with two different RC filters. The blue line is the voltage. The first one is pretty smooth, but the response time is pretty slow. It takes about 100ms to reach the desired voltage. The second plot shows a faster rise, but the voltage has too much ripple.
On thing to note on the plots is my 'on' voltage is not 5V. This will lower the power range you work in. In order to fix this, you can run through a transistor tied to a strong 5V before going into the RC filter. The transistor will invert the signal, so you need to deal with that too. The second plot also shows evidence of the filter further loading down the 5V.
I thought one way to fix the problems revealed above would be to use a microcontroller to sense the PWM duty cycle and use it to set the duty cycle of a much faster PWM frequency. It could be done with a PIC chip and very few additional parts. It could easily set the duty cycle within a period or two. The PIC16F88 has a PWM generator an internal oscillator and all the interrupts required.
Below is my first crack at such a program. Below it is a simple circuit I may use to test it. It adds optional pot control.
/*
The program sets the duty cycle of a 20,000Hz PWM signal based on either an A/D
value or and external PWM signal
Modes
Pot Control
Every 262ms it samples the A/D value and set the duty percentage based on the full
scale percentage of the A/D value
PWM
It monitors the duty cycle of a relatively slow PWM signal of 200Hz.
to a resolution of at least 100 and sets a higher 20,000Hz PWM with the same duty
cycle. It should set the duty cycle correctly at the start of the second full
period of the monitored PWM.
Author: B. Dring
Date: 8/19/2009
Revision: 0.3
Status: Basic Testing Complete
Hardware: PIC16F877A
Clock: 8,00,000 MHz (20,000,000 would be better...need a crystal)
Compiler: CCS PCWH 4.042
URL: www.eng-serve.com www.buildlog.net
===============================================================================
Using: #int_TIMER2
The watched PWM signal has a period of .005 seconds (1/200Hz). We want a
resolution of at least 100 so that requires a sample rate of 50us.
We will use Timer2 to do this. Each Timer2 interupt will increment
pwmPeriodCounter and, if the pin is high, pwmOnTimeCounter .
================================================================================
Using: #int_EXT
An external interrupt triggers on the rising edge of the PWM. Each interrupt will
determine the duty cycle of the external PWM signal, set the internal PWM
duty cycle and reset the counters.
A flag will also be set to show we got a pulse and the timer 0 will be reset
ext_int_edge( L_TO_H );
================================================================================
#int_TIMER0
If the PWM goes away we need to set the duty cycle to 0.
Reset the two counters and set the duty cycle to 0
===============================================================================
*/
#include "C:\prouser\laser_misumi\PIC_PWM\laser_pwm3.h"
#include // LCD Driver for EasyPIC4 board
#define MAX_PWM_DUTY 100
#define MAX_ADV_VALUE 255
#define CONTROL_MODE_OFF 0
#define CONTROL_MODE_POT 1
#define CONTROL_MODE_EXTERNAL_PWM 2
#define CONTROL_MODE_SW_PIN PIN_B7
#define EXTERNAL_INTERRUPT_PIN PIN_B0
unsigned int32 pwmOnTimeCounter;
unsigned int32 pwmPeriodCounter;
unsigned int8 dutyCycle;
short bGotPwmPulse;
int8 controlMode;
unsigned int16 adValue;
// sets PWM to zero duty
void zero_pwm()
{
pwmOnTimeCounter = 0;
pwmPeriodCounter = 0;
dutyCycle = 0;
set_pwm1_duty(dutyCycle);
}
// this occurs on the rising edge of eash PWM
#int_EXT
void EXT_isr(void)
{
if (controlMode == CONTROL_MODE_EXTERNAL_PWM)
{
// this tells the interrupt watching PWM to stop that it is still going
bGotPwmPulse = true;
set_timer0(0);
// determine the duty cycle
if (pwmPeriodCounter !=0) // prevent divide by zero
{
dutyCycle = pwmOnTimeCounter * MAX_PWM_DUTY / pwmPeriodCounter;
}
set_pwm1_duty(dutyCycle);
// reset the counters
pwmOnTimeCounter = 0;
pwmPeriodCounter = 0;
}
}
#int_TIMER0 // used to determine if PWM has stopped
void TIMER0_isr(void)
{
if (controlMode == CONTROL_MODE_EXTERNAL_PWM)
{
if (!bGotPwmPulse)
{
zero_pwm();
}
bGotPwmPulse = false;
}
}
#int_TIMER1 // for periodic update of POT value, LCD and LED heartbeat
void TIMER1_isr(void)
{
output_toggle(PIN_B1);
if (controlMode == CONTROL_MODE_POT)
{
adValue = read_adc();
dutyCycle = adValue * MAX_PWM_DUTY / MAX_ADV_VALUE;
set_pwm1_duty(dutyCycle);
}
printf(lcd_putc,"\fDuty: %u",dutyCycle);
}
#int_TIMER2
void TIMER2_isr(void)
{
// this timer fires every 50us. It is used to determine the duty cycle
if (controlMode == CONTROL_MODE_EXTERNAL_PWM)
{
if (input(EXTERNAL_INTERRUPT_PIN))
{
pwmOnTimeCounter++;
}
pwmPeriodCounter++;
}
}
void main()
{
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_INTERNAL);
setup_psp(PSP_DISABLED);
setup_spi(SPI_SS_DISABLED);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_64); // 8ms
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8); // 262ms
setup_timer_2(T2_DIV_BY_1,99,1); //50uS
setup_vref(FALSE);
setup_ccp1(CCP_PWM);
ext_int_edge( L_TO_H );
enable_interrupts(INT_EXT); // B0 on PIC16F877A
enable_interrupts(INT_TIMER0);
enable_interrupts(INT_TIMER1);
enable_interrupts(INT_TIMER2);
dutyCycle = 0;
set_pwm1_duty(dutyCycle);
//setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
// TODO: USER CODE!!
set_adc_channel(0);
lcd_init();
lcd_putc("\fReady...\n");
// manually select the mode...TO DO add switched control
controlMode = CONTROL_MODE_EXTERNAL_PWM;
//controlMode = CONTROL_MODE_POT;
switch(controlMode)
{
case CONTROL_MODE_POT:
lcd_putc("Pot Mode");
break;
case CONTROL_MODE_EXTERNAL_PWM:
lcd_putc("Ext PWM Mode");
break;
}
delay_ms(1500);
enable_interrupts(GLOBAL);
while(true)
{
// nothing to do here...
// to do: turn on the watchdog and reset it in the main loop
}
}
Test Results:
Yea! it worked. I used my MikroElektronic EasyPIC4 developement board to test it using a PIC16F877A. I only had a 8MHz crystal, so I scaled the speed down a little. The response was good. It set the PWM within the 2nd-3rd pulse. It does not show up well on my digital scope because the 2 PWM frequencies are so different. The faster frequency looks like a long pulse when looking at the slower one. But, you can see the turn on speed.
Mach3 can do step and direction pulses for the spindle just like it does the axis motors. You could set the PWM duty proportional to the step rate. It would be the same hardware setup as above.
Send comments, suggestions, corrections to bdring@buildlog.net