- // Servo.c
- // RabidCicada
- // RabidCicada@gmail.com
- #define F_CPU 14745600
- #include <stdio.h>
- #include <math.h>
- #include <avr/io.h>
- #include <avr/interrupt.h>
- #include <avr/pgmspace.h>
- #include <inttypes.h>
- #include "../libnerdkits/delay.h"
- #include "../libnerdkits/lcd.h"
- uint8_t timer0_clkslct;
- uint8_t timer1_clkslct;
- uint16_t step;
- uint16_t servo_pos_time_period;
- uint16_t ADC_val;
- //INitialize the Analog to Digital Converter
- void adc_init(){
- //DIDR0=0;
- //Configure ADC to use external reference voltage (5v) and to use ADC0
- ADMUX = 0;
- //ADCSRA |= (1<<ADEN);
- //Enable ADC and configure the prescale to be 1/128
- //--witha clock of 14745600 this means it will be 115.2khz
- ADCSRB = 0;
- ADCSRA =(1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0) | (1<<ADATE);
- //Kick off the ADC Initializing conversion
- //This causes the hardware logic initialization
- //which takes longer then normal conversions.
- ADCSRA |= (1<<ADSC);
- }
- //Perform a read of the Analog to Digital Converter
- //Assuming a conversion has already been initiated
- //Reads 10 bits worth of unique values(1024)
- uint16_t adc_read(){
- //Spin in circles waiting for a conversion to finish (ADCSC=0)
- //while( ADCSRA & (1<<ADSC) ){
- //}
- //This actually reads from the result registers of the ADC
- uint16_t result = ADCL;
- uint16_t temp = ADCH;
- result = result + (temp<<8);
- //Kickoff the next conversion
- //ADCSRA |= (1<<ADSC);
- return result;
- }
- //Timer initialization of Timer 1
- //Used to control length of control pulse for servo
- //Need the smallest timeslice possible(resolution)
- //for the most accuracy
- //Configure Timer/Counter1 to run at prescale(8)(14745600/8) 1843200Hz
- //Did this because we want smallest resolution possible that fits
- //in 2bytes(16bits)
- void timer1_init(){
- //Timer Counter Control Register 1 (B)
- TCCR1B = (1<<WGM12);
- //Parially set mode to Clear Timer on Counter match (WGM13,12)
- //and incidentally stop timer (0->bits CS1, CS2, and CS3)
- //Timer Counter Control Register 1 (A)
- TCCR1A = 0; //Finish setting mode to Clear Timer on Counter match (0-> bits WGM11, WGM10)
- //Sets compare output match mode for both channels(a,b) to not use normal ports
- //as counter outputs.(disconnected)
- //In total, what this means is that the timer will count up to it's
- //limit(OCR1A and OCR1B) then reset to 0.
- timer1_clkslct = ((0<<CS12) | (1<<CS11) | (0<<CS10));
- //Will be used in timer1_start to set prescaler to 8
- }
- //Timer Initialization of Timer 0
- //Sets up the timer used for maintaining the pulse window that the
- //hobby servo likes (10-20 ms window) We have chosen 15ms
- void timer0_init(){
- //Configure Timer/Counter0 to run at prescale(1024)(14745600/256) 14400Hz
- //Arbitrarily chose a prescale that had a large timeslice (large resolution)
- //So that the number of counter cycles in our window would fit
- //in 8 bits of resolution
- //Due to the way the timer operates the timer prescale will be set
- //in start_timer0 where the clock select is set to the 1024 prescale
- //Timer Counter Control Register 0 (B)
- TCCR0B = 0;
- //Parially set mode to Clear Timer on Counter match (WGM01,00)
- //and incidentally stop timer (0->bits CS1, CS2, and CS3)
- //This allows us to make updates without worrying about the timer.
- timer0_clkslct = ((1<<CS02) | (0<<CS01) | (1<<CS00));
- //Will be used in timer0_start to set prescaler to 1024
- //Timer Counter Control Register 0 (A)
- TCCR0A = (1<<WGM01); //Finish setting mode to Clear Timer on Counter match (0-> bits WGM11, WGM10)
- //Sets compare output match mode for both channels(a,b) to not use normal ports
- //as counter outputs.(disconnected)
- //In total, what this means is that the timer will count up to it's
- //limit(OCR1A and OCR1B) then reset to 0.
- //Set the compare register A to the timer fire limit
- //OCR0A = 216; // - number of cycles in 15ms at 1024 prescale
- OCR0A = 250;
- //OCR0A = 288; // - number of cycles in 20ms at 1024 prescale
- //Set the Interrupt enable flag for the compare reg A (OCR0A)
- TIMSK0 = (1<<OCIE0A);
- }
- void timer0_start(){
- //Timer Counter Control Register 0 (B)
- TCCR0B |= timer0_clkslct;
- //Sets prescaler to 1024
- }
- void timer1_start(){
- //Timer Counter Control Register 1 (B)
- TCCR1B |= timer1_clkslct;
- //Sets prescaler to 8
- }
- void timer0_stop(){
- TCCR0B &= ~((1<<CS12) | (1<<CS11) | (1<<CS10));
- }
- void timer1_stop(){
- TCCR1B &= ~((1<<CS12) | (1<<CS11) | (1<<CS10));
- }
- void portio_init(){
- //Enable output on pin 4 of port C
- //Inputs on all others
- DDRC = (1<<DDC4);
- //PORTC = (0<<PORTC0);
- }
- //It is critical that this entire routine finish in under the servo
- //actuator window so that it can does not get clipped.
- void poll_adc_and_actuate(){
- uint16_t pot_value = adc_read();
- //ADC_val += 1;//pot_value;
- ADC_val = pot_value;
- //servo_pos +=1;//
- servo_pos_time_period = 2037 + (uint16_t) (pot_value * 1.45);
- //Set Timer upper bound for length of pulse to actuate servo
- //The potentiometer only measures 10 bits worth of distinct
- //values(1024 exactly). Because of this we must scale the potentiometer value
- //by 1.45 to cover the full range of the number of cycles needed to span
- //.8ms below(1482)
- //Precalculated values based on number of cycles in a given timeframe...
- //Base number of cycles 2037 (1.1 ms) + difference(0-1482 cycles)3519(range of .8ms)
- //This gives actuation signal of 1.1-1.9ms
- OCR1A = servo_pos_time_period;
- TCNT1 = 0;//Clear timer to 0--will count up to limit (OCR1A)
- PORTC|=(1<<PORTC4); //Set control pulse for servo high
- timer1_start();
- //Wait for timer to be over
- //Check the timer output compare pin to see if a match has occured.
- //OCF1A is set when a compare match occured(timer hit our limit)
- //Spin in circles while timer hasn't hit limit.(1.1ms-1.9ms)
- while(!(TIFR1 & (1<<OCF1A))){
- }
- timer1_stop();
- TIFR1|=(1<<OCF1A);//clear compare match flag.
- //Clear output bit
- PORTC&= ~(1<<PORTC4); //Set control pulse for servo low
- }
- //Interrupts are automatically disabled in this
- ISR(TIMER0_COMPA_vect){
- poll_adc_and_actuate();
- }
- //The plan is to use timer based calls for controlling the servo
- //The servo expects a pulse every 15-20 milliseconds
- //Every time poll_adc_and_actuate is called it will
- //kick off the timer for a new poll_adc_and_actuate call,
- //do a read of the adc, actuate the servo, then spin in an
- //infinite loop waiting for the timer to fire again
- int main(){
- int cnt=0;
- step=0;
- ADC_val=0;
- servo_pos_time_period=0;
- // fire up the LCD
- lcd_init();
- FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
- lcd_home();
- // print message to screen
- lcd_write_string(PSTR(" Initializing System "));
- lcd_line_two();
- lcd_write_string(PSTR(" "));
- //Initialize ADC
- adc_init();
- //Initialize ports for use
- portio_init();
- //Initialize the timer that repeatedly executes the
- //poll_adc_and_actuate() function
- //This one is our "window" timer for the servo pulses
- timer0_init();
- //Initialize the timer that controls the length of the pulse
- //for each servo actuation
- timer1_init();
- //Enable Interrupts
- sei();
- //Delay for visual confirmation
- delay_ms(3000);
- // print message to screen
- lcd_clear_and_home();
- lcd_write_string(PSTR(" Entering Actuator Loop "));
- lcd_line_two();
- lcd_write_string(PSTR(" "));
- delay_ms(3000);
- //Start timer0...This starts our 15ms window loop
- timer0_start();
- //Spin in infinite loop letting interrupt for poll_adc_and_actuate trigger
- while(1){
- //ADC_val = adc_read();
- lcd_home();
- lcd_write_int16(cnt);
- lcd_write_string( PSTR(" ADC Value: "));
- lcd_write_int16(ADC_val);
- lcd_write_string(PSTR(" of 1024"));
- lcd_line_two();
- lcd_write_string( PSTR(" Servo position: "));
- lcd_write_int16(servo_pos_time_period);
- cnt=!cnt;
- //delay_ms(1000);
- }
- //poll_adc_and_actuate();
- return 0;
- }
Posted by RabidCicada on Thu 16 Apr 12:05
report abuse | download | new post
Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.