diff --git a/main.c b/main.c index 57df6af..fc5e913 100644 --- a/main.c +++ b/main.c @@ -6,123 +6,132 @@ #pragma config FOSC = INTRCIO // Internal oscillator #pragma config WDTE = OFF // Watchdog timer off #pragma config PWRTE = ON // Power-up timer enabled +#pragma config MCLRE = OFF #define _XTAL_FREQ 4000000 +volatile uint8_t current_button_status = 1; +volatile uint8_t current_mode = 0; +volatile uint8_t servo_pos = 127; // 0=1ms, 255=2ms +volatile uint8_t pulse_state = 0; // 0=start pulse, 1=end pulse +volatile uint8_t isincreasing = 0; + +void __interrupt() ISR(void) { + INTCONbits.GIE = 0; + if (PIR1bits.TMR1IF) { + PIR1bits.TMR1IF = 0; // Clear interrupt flag + static uint16_t off_ticks; // Retains value between interrupts + static uint16_t timer; + if (pulse_state == 0) { + // Calculate pulse parameters + uint16_t on_ticks = 75 + ((uint16_t)servo_pos * 237) / 255; // Map to 0.5ms-2.5ms + off_ticks = 2500 - on_ticks; // 2500 ticks = 20ms + RC5 = 1; // Start pulse + timer = 65535 - on_ticks; + TMR1L = (unsigned char)(timer & 0xFF); + TMR1H = (unsigned char)(timer >> 8); + pulse_state = 1; + } else { + RC5 = 0; // End pulse + timer = 65535 - off_ticks; + TMR1L = (unsigned char)(timer & 0xFF); + TMR1H = (unsigned char)(timer >> 8); + pulse_state = 0; + } + } + INTCONbits.GIE = 1; +} + void setup() { - // Set RA0 as analog input, rest of PORTA as outputs - TRISA = 0x01; - // Set PORTC as outputs except RC5 which will toggle PWM - TRISC = 0x00; + // Set RA1 as digital input + ANSEL = 0; + CMCON = 0x07; + TRISC = 0x00; + TRISAbits.TRISA1 = 1; + WPUAbits.WPUA1 = 1; + OPTION_REGbits.nRAPU = 0; - // Enable analog input on RA0 - ANSEL = 0x01; + // Configure Timer1 + T1CON = 0b00110000; // Timer1: prescaler 1:8, internal clock, OFF + TMR1H = (65536 - 188) >> 8; // Initial 1.5ms pulse (high byte) + TMR1L = (65536 - 188) & 0xFF; // Initial 1.5ms pulse (low byte) + PIR1bits.TMR1IF = 0; // Clear interrupt flag + PIE1bits.TMR1IE = 1; // Enable Timer1 interrupt + INTCONbits.PEIE = 1; // Enable peripheral interrupts + INTCONbits.GIE = 1; // Enable global interrupts + T1CONbits.TMR1ON = 1; // Configure ADC - ADCON0 = 0b00000101; // Channel 0, ADC enabled - ADCON1 = 0b10001110; // Right justified, Vdd/Vss reference - - // Timer0 initialization - OPTION_REG = 0b11110111; // Prescale = 256, Internal clock source - TMR0 = 0; // Clear timer register -} - -#define SERVO_MIN_PULSE_US 750 // Minimum pulse width (approximately 0°) -#define SERVO_MAX_PULSE_US 2250 // Maximum pulse width (approximately 180°) -void delay_us(uint16_t us) { - uint16_t i; - for(i = 0; i < us; i++) { - _nop(); // Insert assembly-level no-operation instruction - } -} - -void generate_pwm(uint16_t pulse_width_us) { - RC5 = 1; // Start high - delay_us(pulse_width_us); // Keep high for desired pulse width - RC5 = 0; // Go low until next pulse - delay_us(20 - (pulse_width_us / 1000)); // Wait remaining 20ms period + TRISAbits.TRISA2 = 0; + ANSELbits.ANS2 = 1; + ADCON0 = 0b00001001; + ADCON1 = 0; } #define bool uint8_t #define false 0 #define true 1 -volatile bool button_pressed = false; -uint8_t current_mode = 0; + +void UpdateLeds() +{ + RC0 = current_mode == 0; + RC1 = current_mode == 1; + RC2 = current_mode == 2; +} void check_button() { - if (!RA1 && !button_pressed) { // Detect falling edge of button press - button_pressed = true; - __delay_ms(50); // Basic debounce delay - current_mode++; - if (current_mode > 2) current_mode = 0; // Cycle through modes - - // Update LEDs based on mode - switch(current_mode) { - case 0: - RC0 = 1; RC1 = 0; RC2 = 0; break; // Manual mode LED - case 1: - RC0 = 0; RC1 = 1; RC2 = 0; break; // Center mode LED - case 2: - RC0 = 0; RC1 = 0; RC2 = 1; break; // Oscillator mode LED + if (!RA1 && current_button_status) { + __delay_ms(20); + if(!RA1) { + current_mode = (current_mode + 1) % 3; // Cycle through modes + UpdateLeds(); } } - if (RA1) { - button_pressed = false; // Release button + current_button_status = RA1; +} + +void smooth_servo(uint8_t target_pos) { + // Move servo_pos towards target_pos gradually + if (servo_pos < target_pos) { + servo_pos++; + } else if (servo_pos > target_pos) { + servo_pos--; } } -void manual_mode() { - uint16_t adc_value = (ADRESH << 8) | ADRESL; // Get ADC value - uint16_t pulse_width = (SERVO_MIN_PULSE_US + (adc_value * (SERVO_MAX_PULSE_US - SERVO_MIN_PULSE_US) / 1023)); - generate_pwm(pulse_width); -} - -void center_mode() { - generate_pwm((SERVO_MIN_PULSE_US + SERVO_MAX_PULSE_US) / 2); -} - -int8_t direction = 1; // Direction of movement -uint8_t osc_duty = 75; // Start at min duty cycle - -uint16_t osc_pulse_width = SERVO_MIN_PULSE_US; -bool increasing = true; - -void oscillator_mode() { - generate_pwm(osc_pulse_width); - - if (increasing) { - osc_pulse_width += 10; // Gradual increment - if (osc_pulse_width >= SERVO_MAX_PULSE_US) { - increasing = false; - } - } else { - osc_pulse_width -= 10; // Gradual decrement - if (osc_pulse_width <= SERVO_MIN_PULSE_US) { - increasing = true; - } - } +inline uint8_t read_adc() { + ADCON0bits.GO = 1; + while(ADCON0bits.GO_DONE); + return ADRESH; } void main() { setup(); // Initialize peripherals - + UpdateLeds(); while(1) { - check_button(); // Check for button presses - + check_button(); // Check for button presses switch(current_mode) { - case 0: - manual_mode(); + case 0: + smooth_servo(read_adc()); + __delay_us(100); break; case 1: - center_mode(); + //target_pos = 511; + smooth_servo(127); + __delay_us(100); break; case 2: - oscillator_mode(); + if(isincreasing) { + smooth_servo(servo_pos + 1); + __delay_ms(10); + if(servo_pos + 1 >= 254) isincreasing = 0; + } else + { + smooth_servo(servo_pos - 1); + __delay_ms(10); + if(servo_pos - 1 <= 0) isincreasing = 1; + } break; } - - // Perform ADC conversion - GO_nDONE = 1; // Start ADC conversion - while(GO_nDONE); // Wait for completion } } \ No newline at end of file