tech_juggernaut 5 Posted April 18, 2012 Share Posted April 18, 2012 Thanks Rob, I set up some conditions to try to get closer to full range. When the adc value is very close to 0 or 1023 then the servo position is set to either 113 or 263 respectively, and otherwise it calculates for any values in between. #pragma vector=ADC10_VECTOR __interrupt void ADC10_ISR(void) { while (ADC10CTL1 & BUSY) ; i = 0; // update servo positions while(i < 6) { // CCR from ADC is calculated if ( adcValues[i] >= 0x3FC ){ // adcValue >= 1020 servoPosition[5 - i] = 0x107; // 2100us / 8us = ~263 = 0x107 } else if ( adcValues[i] <= 0x03 ){ // 0 <= adcValue <= 3 servoPosition[5 - i] = 0x71; // 900us / 8us = ~113 = 0x71 } else { servoPosition[5 - i] = (adcValues[i] >> 3) + 0x7F; // servoPosition = adcValue / 8 + 127 } i++; } } I don't know if this is a very good solution; as you can imagine I get a pretty good jolt when the position jumps between the calculated values and the values that are set. I'm looking into fine-tuning it by generating and flashing a look-up table to find the servo times from the ADC values. I found an example of one at the site below but I'm not familiar with LUT's or how I'd implement one... Thanks for any advice, I really appreciate your help! :thumbup: http://gushh.net/blog/?cat=361 Quote Link to post Share on other sites
oPossum 1,083 Posted April 18, 2012 Share Posted April 18, 2012 You can easily map any ADC range to any servo range with this formula: // Specify the ranges const int adc_min = xxx; // Minimum ADC value const int adc_max = xxx; // Maximum ADC value const int servo_min = xxx; // Minimum servo value const int servo_max = xxx; // Maximum servo value unsigned servo = (unsigned)((long)(adc - adc_min) * (servo_max - servo_min) / (adc_max - adc_min) + servo_min); tech_juggernaut 1 Quote Link to post Share on other sites
RobG 1,892 Posted April 18, 2012 Author Share Posted April 18, 2012 I found an example of one at the site below but I'm not familiar with LUT's or how I'd implement one... LUT is just an array of values. You can use oPossum's formula and calculate required CCR value for each ADC/4 const unsigned int ccr[256] = { 113, 113, 113, 114, 114, 115, ......263, 263 }; tech_juggernaut 1 Quote Link to post Share on other sites
tech_juggernaut 5 Posted April 19, 2012 Share Posted April 19, 2012 Thanks to you both, worked like a charm!! I replaced the original code in the while loop in the ADC ISR with oPossum's formula to set the servo positions: const int adc_min = 0; // Minimum ADC value const int adc_max = 1023; // Maximum ADC value const int servo_min = 75; // Minimum CCR value const int servo_max = 320; // Maximum CCR value // ADC10 interrupt service routine #pragma vector=ADC10_VECTOR __interrupt void ADC10_ISR(void) { while (ADC10CTL1 & BUSY) ; i = 0; while(i < 6) { // update servo positions servoPosition[5 - i] = (unsigned)((long)(adcValue[i] - adc_min) *(servo_max - servo_min) / (adc_max - adc_min) + servo_min); i++; } } It made it a lot easier to work with the max and mins to get the most out of the servos, I just tweaked the assigned values in their declarations a few times and voila! Here's a picture of the arm if anyone is interested: http://flic.kr/p/bNH7Br The final code: #include #define SERVO_OUTPUTS BIT0 + BIT1 + BIT2 + BIT3 + BIT4 + BIT5 // P2 unsigned int counter = 0; // Servo counter unsigned int servoPosition[6] = {187, 187, 187, 187, 187, 187}; // Default servo positions unsigned int servoOn[6] = {BIT0, BIT1, BIT2, BIT3, BIT4, BIT5}; unsigned char i = 0; unsigned int adcValue[6] = {0,0,0,0,0,0}; // ADC buffer (ADC values are stored in this array) const int adc_min = 0; // Minimum ADC value const int adc_max = 1023; // Maximum ADC value const int servo_min = 75; // Minimum CCR value const int servo_max = 320; // Maximum CCR value void main(void) { WDTCTL = WDTPW + WDTHOLD; // Disable WDT BCSCTL1 = CALBC1_1MHZ; // Set Digitally Controlled Oscillator at 1MHz DCOCTL = CALDCO_1MHZ; P2OUT &= ~(SERVO_OUTPUTS); P2DIR |= (SERVO_OUTPUTS); // Port P2.0 - P2.5 servo outs P2SEL &= ~(SERVO_OUTPUTS); ADC10CTL1 = INCH_5 + CONSEQ_1; // Sample A5/A4/A3/A2/A1/A0, sequence of channels sampled ADC10CTL0 = ADC10SHT_2 + MSC + ADC10ON + ADC10IE; ADC10DTC1 = 0x06; // 6 conversions ADC10AE0 |= 0x3F; // Enable A5/A4/A3/A2/A1/A0 CCTL0 = CCIE; // CCR0 interrupt enabled CCR0 = 180; // ~1.5ms TACTL = TASSEL_2 + MC_1 + ID_3; // SMCLK / 8, upmode _bis_SR_register(LPM0_bits + GIE); // Enter LPM0 w/ interrupt } // Timer A0 interrupt service routine #pragma vector = TIMER0_A0_VECTOR __interrupt void Timer_A0(void) { counter++; // Increase counter if(counter == 0x07) counter = 0; P2OUT &= ~(SERVO_OUTPUTS); // Clear ports if(counter == 0x06) { // Counter range is 0-6, the last count is for the 20ms difference delay CCR0 = 2500 - (servoPosition[0] + servoPosition[1] + servoPosition[2] + servoPosition[3] + servoPosition[4] + servoPosition[5]); // [20ms/8us = 2500 ticks] <=> [2500 - total position values => 20ms - total synchronized on-time] ADC10CTL0 &= ~ENC; // Stop sampling; Set ENC bit to 0 while (ADC10CTL1 & BUSY) ; ADC10SA = (unsigned int)&adcValue[0]; // Data transfer location ADC10CTL0 |= ENC + ADC10SC; // Start conversions } else { P2OUT |= servoOn[counter]; // Set port of the current servo CCR0 = servoPosition[counter]; // Set time for the current servo } } // ADC10 interrupt service routine #pragma vector=ADC10_VECTOR __interrupt void ADC10_ISR(void) { while (ADC10CTL1 & BUSY) ; i = 0; while(i < 6) { // update servo positions servoPosition[5 - i] = (unsigned)((long)(adcValue[i] - adc_min) * (servo_max - servo_min) / (adc_max - adc_min) + servo_min); i++; } } Rob commented on almost everything, that was a huge help. I tried to green it up as well (bad memory), not sure if I made any mistakes there... RobG, oPossum and zeke 3 Quote Link to post Share on other sites
RobG 1,892 Posted April 19, 2012 Author Share Posted April 19, 2012 Nice arm! Just FYI, look up table is a lot faster than division. The problem is, because 2553 has only 512 bytes of RAM, you would have to calculate it in advance and declare it constant. tech_juggernaut 1 Quote Link to post Share on other sites
zeke 693 Posted April 19, 2012 Share Posted April 19, 2012 I love the warning sticker! :thumbup: Where can I get one? tech_juggernaut 1 Quote Link to post Share on other sites
tech_juggernaut 5 Posted April 19, 2012 Share Posted April 19, 2012 Hey Rob, thanks! It's been a lot of fun working on it, I'll post a video of it in action in a couple of days. Thanks for the input, you're right about the LUT, ideally that's what I'd like to use. However, it seems like a daunting task, mostly because I don't have any experience with them, i.e. I wasn't sure if I should just use oPossum's formula to populate the LUT or not?... or how to use it once I did. :problem: I have a tendancy to overcomplicate things... zeke, thanks, I couldn't resist, I've been looking for a reason to use it! :thumbup: I actually just got it along with a gift last winter but I found a place online that sells them (no affiliation): http://www.etgiftstore.com/items/all-pr ... detail.htm RobG and oPossum 2 Quote Link to post Share on other sites
oPossum 1,083 Posted April 19, 2012 Share Posted April 19, 2012 I found a place online that sells them (no affiliation): http://www.etgiftstore.com/items/all-pr ... detail.htm Argg, I want to order one of each! Quote Link to post Share on other sites
GeekDoc 226 Posted April 19, 2012 Share Posted April 19, 2012 I found a place online that sells them (no affiliation): http://www.etgiftstore.com/items/all-pr ... detail.htm Argg, I want to order one of each! Me too! But, I'm not paying $4 shipping for $3 worth of stickers. Maybe I'll just get some label stock and make up my own pity warnings. :think: tech_juggernaut 1 Quote Link to post Share on other sites
tech_juggernaut 5 Posted April 20, 2012 Share Posted April 20, 2012 Me too! But, I'm not paying $4 shipping for $3 worth of stickers. Maybe I'll just get some label stock and make up my own pity warnings. :think: Ha, I don't blame you, good idea. You could get free shipping for $25 worth, but I have no idea what I'd do with 34 stickers. Just enough to drive me mad. :!!!: Gotta love this one though : http://www.etgiftstore.com/items/all-pr ... detail.htm Quote Link to post Share on other sites
GeekDoc 226 Posted April 20, 2012 Share Posted April 20, 2012 That one is awesome! BTW: That was supposed to be *pithy* warnings (not pity). tech_juggernaut 1 Quote Link to post Share on other sites
tech_juggernaut 5 Posted April 20, 2012 Share Posted April 20, 2012 BTW: That was supposed to be *pithy* warnings (not pity). Ha, I guess it depends on the observer... the warnings could either be pithy or induce pity... or both! 8-) I'd say the probability of a good laugh is likely either way. :-P Quote Link to post Share on other sites
RobG 1,892 Posted April 20, 2012 Author Share Posted April 20, 2012 Going back to servos... Here's a little utility page that will help you generate look up table. Just enter min and max, copy result to your code, and you are all done! const unsigned int ccr[256] = { 90, 91, 92, 93, 94, 95, 96, 97, 98, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 326, 327, 328, 329 }; This is the only change you have to make to the code // update servo positions servoPosition[5 - i] = ccr[adcValue[i] >>2]; servoLutGenerator.html.zip tech_juggernaut and GeekDoc 2 Quote Link to post Share on other sites
tech_juggernaut 5 Posted April 24, 2012 Share Posted April 24, 2012 Man you've outdone yourself, thanks! :clap: That utility page is a handy tool to have, I'll try it out tonight. I was wondering if you could give me a quick step by step about how and when the interrupts actually occur, I'm a little fuzzy when it comes to ISR's. Thanks for all of your help! Quote Link to post Share on other sites
oPossum 1,083 Posted April 24, 2012 Share Posted April 24, 2012 There are two interrupt vectors for each TimerA unit. The first interrupt vector (TIMERx_A0_VECTOR) will be called when the count matches TAxCCR0. The second interrupt vector (TIMERx_A1_VECTOR) will be called when the counter is reset to 0, or the count matches any other TAxCCRn register. The timer interrupt vector register TAxIV indicates what caused the interrupt. This register must be read even if you assume what caused the interrupt. The overflow interrupt is enabled by the TAIE bit of TAxCTL. The TAxCCRn interrupts are enabled by the CCIE bit of the TAxCCTLn register. Global interrupts must also be enabled. All the details of timer operation are in chapter 12 of [tipdf]slau144[/tipdf] tech_juggernaut 1 Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.