128 lines
3.6 KiB
C
128 lines
3.6 KiB
C
#include <xc.h>
|
|
#include <stdint.h>
|
|
#include <pic16f676.h>
|
|
|
|
// CONFIGURATION
|
|
#pragma config FOSC = INTRCIO // Internal oscillator
|
|
#pragma config WDTE = OFF // Watchdog timer off
|
|
#pragma config PWRTE = ON // Power-up timer enabled
|
|
#define _XTAL_FREQ 4000000
|
|
|
|
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;
|
|
|
|
// Enable analog input on RA0
|
|
ANSEL = 0x01;
|
|
|
|
// 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
|
|
}
|
|
|
|
#define bool uint8_t
|
|
#define false 0
|
|
#define true 1
|
|
volatile bool button_pressed = false;
|
|
uint8_t current_mode = 0;
|
|
|
|
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) {
|
|
button_pressed = false; // Release button
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
setup(); // Initialize peripherals
|
|
|
|
while(1) {
|
|
check_button(); // Check for button presses
|
|
|
|
switch(current_mode) {
|
|
case 0:
|
|
manual_mode();
|
|
break;
|
|
case 1:
|
|
center_mode();
|
|
break;
|
|
case 2:
|
|
oscillator_mode();
|
|
break;
|
|
}
|
|
|
|
// Perform ADC conversion
|
|
GO_nDONE = 1; // Start ADC conversion
|
|
while(GO_nDONE); // Wait for completion
|
|
}
|
|
} |