/* 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 } }