Details | Last modification | View Log | SVN | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 207 | eqlazer | 1 | /* |
| 2 | * PWM output to control a RC servo |
||
| 3 | * |
||
| 209 | eqlazer | 4 | * It is possible to use both OCR1A and OCR1B, but they uses the same value. |
| 5 | * If it's desired to control A and B individually some rewriting is necessary. |
||
| 6 | * |
||
| 207 | eqlazer | 7 | * Author: Erik Larsson |
| 8 | * Date: 2007-01-04 |
||
| 9 | * |
||
| 209 | eqlazer | 10 | * TODO anpassa för fler hastigheter än 8 MHz |
| 11 | * testa libbet, det fungerar ännu bara i eqlazers huvud |
||
| 207 | eqlazer | 12 | */ |
| 13 | |||
| 14 | /*----------------------------------------------- |
||
| 15 | * Includes |
||
| 16 | * ---------------------------------------------*/ |
||
| 17 | #include <avr/io.h> |
||
| 18 | #include <stdio.h> |
||
| 209 | eqlazer | 19 | #include <stdlib.h> |
| 207 | eqlazer | 20 | #include <rc_servo.h> |
| 21 | |||
| 22 | |||
| 23 | /*---------------------------------------------- |
||
| 24 | * Functions |
||
| 25 | * --------------------------------------------*/ |
||
| 26 | |||
| 27 | /* |
||
| 28 | * Initiate PWM output. |
||
| 29 | */ |
||
| 30 | void rcServoInit(){ |
||
| 209 | eqlazer | 31 | // set up counter 1 (16 bit) to act as a dual channel PWM generator |
| 32 | // we want OC1A and B to be set on reaching BOTTOM, clear on reaching |
||
| 33 | // compare match, use ICR1 as TOP and have a prescale of 8. |
||
| 34 | #if DOUBLE_PWM |
||
| 35 | TCCR1A |= (1<<COM1A1) // set OC1A/B at TOP |
||
| 36 | | (1<<COM1B1) // clear OC1A/B when match |
||
| 37 | | (1<<WGM11); // mode 14 (fast PWM, clear TCNT1 on match ICR1) |
||
| 38 | TCCR1B |= (1<<WGM13) |
||
| 39 | | (1<<WGM12) |
||
| 40 | | (1<<CS11); // timer uses main system clock with 1/8 prescale |
||
| 41 | ICR1 = 20000; // used for TOP, makes for 50 hz PWM |
||
| 42 | OCR1A = 1500; // servo at center |
||
| 43 | OCR1B = 1500; // servo at center |
||
| 44 | DDRB |= (1<<PB1) |
||
| 45 | | (1<<PB2); // have to set up pins as outputs |
||
| 46 | #else |
||
| 47 | TCCR1A |= (1<<COM1A1) // set OC1A/B at TOP |
||
| 48 | // clear OC1A/B when match |
||
| 49 | | (1<<WGM11); // mode 14 (fast PWM, clear TCNT1 on match ICR1) |
||
| 50 | TCCR1B |= (1<<WGM13) |
||
| 51 | | (1<<WGM12) |
||
| 52 | | (1<<CS11); // timer uses main system clock with 1/8 prescale |
||
| 53 | ICR1 = 20000; // used for TOP, makes for 50 hz PWM |
||
| 54 | OCR1A = 1500; // servo at center |
||
| 55 | DDRB |= (1<<PB1); // have to set up pins as outputs |
||
| 56 | #endif |
||
| 207 | eqlazer | 57 | } |
| 58 | |||
| 209 | eqlazer | 59 | /* |
| 60 | * Set the servo to an absolute position. |
||
| 61 | * Interval -128 to 127. |
||
| 62 | */ |
||
| 207 | eqlazer | 63 | void setPosition(int8_t abs_pos){ |
| 209 | eqlazer | 64 | uint16_t temp_pos; |
| 65 | temp_pos = (uint16_t)PWM_CENTER_PULSE + abs_pos*STEP; |
||
| 66 | #if DOUBLE_PWM |
||
| 67 | OCR1A = temp_pos; |
||
| 68 | OCR1B = temp_pos; |
||
| 69 | #else |
||
| 70 | OCR1A = temp_pos; |
||
| 71 | #endif |
||
| 207 | eqlazer | 72 | } |
| 73 | |||
| 209 | eqlazer | 74 | /* |
| 75 | * Alter the position rel_pos steps. |
||
| 76 | * Interval -128 to 127. |
||
| 77 | */ |
||
| 207 | eqlazer | 78 | void alterPosition(int8_t rel_pos){ |
| 209 | eqlazer | 79 | uint16_t temp_pos; |
| 207 | eqlazer | 80 | |
| 209 | eqlazer | 81 | temp_pos = OCR1A; |
| 82 | temp_pos = temp_pos + rel_pos*STEP; |
||
| 83 | #if DOUBLE_PWM |
||
| 84 | if(temp_pos>=PWM_MAX_PULSE){ |
||
| 85 | OCR1A = PWM_MAX_PULSE; |
||
| 86 | OCR1B = PWM_MAX_PULSE; |
||
| 87 | }else if(temp_pos<=PWM_MIN_PULSE){ |
||
| 88 | OCR1A = PWM_MIN_PULSE; |
||
| 89 | OCR1B = PWM_MIN_PULSE; |
||
| 90 | }else{ |
||
| 91 | OCR1A = temp_pos; |
||
| 92 | OCR1B = temp_pos; |
||
| 93 | } |
||
| 94 | #else |
||
| 95 | if(temp_pos>=PWM_MAX_PULSE){ |
||
| 96 | OCR1A = PWM_MAX_PULSE; |
||
| 97 | }else if(temp_pos<=PWM_MIN_PULSE){ |
||
| 98 | OCR1A = PWM_MIN_PULSE; |
||
| 99 | }else{ |
||
| 100 | OCR1A = temp_pos; |
||
| 101 | } |
||
| 102 | #endif |
||
| 207 | eqlazer | 103 | } |
| 104 | |||
| 209 | eqlazer | 105 | /* |
| 106 | * Get current position of the servo. |
||
| 107 | */ |
||
| 207 | eqlazer | 108 | int8_t getPosition(){ |
| 109 | |||
| 209 | eqlazer | 110 | return (OCR1A - PWM_CENTER_PULSE)/STEP; |
| 207 | eqlazer | 111 | } |
| 112 | |||
| 209 | eqlazer | 113 | /* |
| 114 | http://members.shaw.ca/climber/avrtimers.html |
||
| 115 | |||
| 116 | To change the servo position all you have to do is assign a value to OCR1A or B. |
||
| 117 | With an 8 MHz clock the normal range you use is 1000 to 2000. |
||
| 118 | I especially like it at 8 MHz because the timing for normal servo control |
||
| 119 | is pulse widths of (usually) 1.0 to 2.0 milliseconds. Just multiply that |
||
| 120 | by 1000 and you get the value to assign to OCR1A or B. |
||
| 121 | |||
| 122 | If you want to use a different system clock then all you have to do is |
||
| 123 | adjust the value you assign ICR1 and the useable range to assign OCR1A |
||
| 124 | and B to control the servos is 5% to 10% of the value you use for ICR1. |
||
| 125 | For example, for a 1 MHz clock (like the default RC clock from the factory) |
||
| 126 | use 2500 for ICR1 and 125 - 250 for the range of values to give OCR1A and B. |
||
| 127 | */ |