oPossum 1,083 Posted January 31, 2012 Share Posted January 31, 2012 This is derived from 16 channel PWM with a single timer. The ISR has be extended to handle PWM and async serial reception. The first timer capture/compare is used for timing the serial bits and the second is used for PWM. This is the ISR... __interrupt void Timer0_A1(void) { static unsigned bit_mask = 0; // Serial rx data bitmask static unsigned rx_data; // Serial rx data // const unsigned x = TAIV; // Clear interrupt flag, get vector // if(x == 4) { // --- CCR2 - PWM P1OUT &= ~pa->port_off; // Port pins off P2OUT &= ~pa->port_off >> 8; // P1OUT |= pa->port_on; // Port pins on P2OUT |= pa->port_on >> 8; // pa = pa->next; // Next node in list TACCR2 = pa->time; // Update timer compare time // } else if(x == 2) { // --- CCR1 - Serial rx if(TACCTL1 & CAP) { // Start bit (capture mode) rx_data = 0; // Clear rx data bit_mask = 1; // Begin with bit 0 TACCTL1 &= ~CAP; // Switch from capture to compare mode TACCR1 += 2500; // Set timer for middle of first data bit } else { // Data/stop (compare mode) if(bit_mask & 0x0100) { // Stop bit? *head++ = rx_data; // Add rx data to ring buffer if(head >= &rx_buf[sizeof(rx_buf)/sizeof(rx_buf[0])]) head = rx_buf; TACCTL1 |= CAP; // Switch from compare to capture mode } else { // Data bit if(TACCTL1 & SCCI) rx_data |= bit_mask; // Update rx data bit_mask <<= 1; // Next data bit TACCR1 += 1667; // Set timer for middle of next bit } } } } The PWM code is unchanged, but now is only executed when the interrupt vector indicates capture/compare 2 match. The serial code begins in capture mode. When a falling edge is detected the mode is changed to compare to sample in the middle of each data bit and the stop bit. A ring buffer is used to prevent loss of received data. Two test cases are provided. The first allows the brightness of the Lauchpads LED to be set using 0 to 9 and a to j. The second test case implements a simple serial protocol to set the PWM value of all 15 outputs. The packet format is... 0x55 0xAA PWM0 PWM1 ...... PWM13 PWM14 Checksum Addressing could easily be added to allow multiple MSP430 on the same serial line. Since this code uses only a single TimerA module, it can run on any MSP430 with a TimerA. #include "msp430g2553.h" #include "string.h" typedef struct { // PWM ISR linked list node struct unsigned time; // Time for on/off action to occur unsigned port_off; // Port pins to turn off unsigned port_on; // Port pins to turn on void *next; // Next node in linked list } TPWM; // TPWM pw[16], *pi; // Array and inactive list head volatile TPWM *pa; // Active node unsigned char rx_buf[32]; unsigned char *head, *tail; void pwm_set(const unsigned pin, const unsigned time) { const unsigned mask = 1 << pin; // const unsigned tm = time & 0xFF00; // Limit closeness of entries TPWM *b, *p, *n; // // // -- Try to find existing active node for this pin for(b = &pw[0], p = b->next; p != &pw[0]; b = p, p = b->next) { if(p->port_off & mask) { // Found it if(p->time == tm) return; // - Time is the same, nothing to do, return... while(pa != p); // Wait for this node to be active while(pa == p); // Wait for this node to become inactive // Safe to remove now if(p->port_off == mask) { // - Node is only used for this pin, remove it b->next = p->next; // Remove link p->next = pi; // Add to inactive list pi = p; // } else { // - Node is used for multiple pins p->port_off &= ~mask; // Remove this pin from the node } // break; // Found the pin, so stop searching } // } // // - Update first node in list if(tm == 0) { // If time is 0, turn off and return pw[0].port_on &= ~mask; // pw[0].port_off |= mask; // return; // } else { // If time is non-zero, turn on pw[0].port_on |= mask; // pw[0].port_off &= ~mask; // if(time == 0xFFFF) return; // If max, no need to turn off, so return } // // // Find where the new turn off node will go for(b = &pw[0], p = b->next; p != &pw[0]; b = p, p = b->next) if(p->time >= tm) break; // Stop when an entry of >= time is found // if(p->time == tm) { // If same time, use existing node p->port_off |= mask; // Add this pin return; // All done... } // // n = pi; // Get a node from the inactive list pi = n->next; // // n->port_off = mask; // Setup new node n->port_on = 0; // n->time = tm; // // n->next = p; // Insert node in to active list b->next = n; // } void pwm_init(void) { unsigned n; memset(pw, 0, sizeof(pw)); // Clear entire array pa = &pw[0]; // Active list always begins with first array element pa->next = &pw[0]; // Begin with only 1 node in list pi = &pw[1]; // First inactive node is second array element for(n = 1; n < sizeof(pw)/sizeof(TPWM) - 1; ++n) // Link the inactive nodes pw[n].next = &pw[n + 1]; // // TACTL = TASSEL_2 + MC_2 + ID_0; // Setup timer, continuous mode, SMCLK/1 TACCTL2 = CCIE; // Enable timer interrupt _EINT(); // Enable interrupts } #pragma vector = TIMER0_A1_VECTOR __interrupt void Timer0_A1(void) { static unsigned bit_mask = 0; // Serial rx data bitmask static unsigned rx_data; // Serial rx data // const unsigned x = TAIV; // Clear interrupt flag, get vector // if(x == 4) { // --- CCR2 - PWM P1OUT &= ~pa->port_off; // Port pins off P2OUT &= ~pa->port_off >> 8; // P1OUT |= pa->port_on; // Port pins on P2OUT |= pa->port_on >> 8; // pa = pa->next; // Next node in list TACCR2 = pa->time; // Update timer compare time // } else if(x == 2) { // --- CCR1 - Serial rx if(TACCTL1 & CAP) { // Start bit (capture mode) rx_data = 0; // Clear rx data bit_mask = 1; // Begin with bit 0 TACCTL1 &= ~CAP; // Switch from capture to compare mode TACCR1 += 2500; // Set timer for middle of first data bit } else { // Data/stop (compare mode) if(bit_mask & 0x0100) { // Stop bit? *head++ = rx_data; // Add rx data to ring buffer if(head >= &rx_buf[sizeof(rx_buf)/sizeof(rx_buf[0])]) head = rx_buf; TACCTL1 |= CAP; // Switch from compare to capture mode } else { // Data bit if(TACCTL1 & SCCI) rx_data |= bit_mask; // Update rx data bit_mask <<= 1; // Next data bit TACCR1 += 1667; // Set timer for middle of next bit } } } } unsigned char get_rx(void) { const unsigned char c = *tail++; if(tail >= &rx_buf[sizeof(rx_buf)/sizeof(rx_buf[0])]) tail = rx_buf; return c; } void protocol(void) { static unsigned state = 0; unsigned n; unsigned char pwm[15]; unsigned char checksum; unsigned char c; for(; { c = get_rx(); switch(state) { case 0: // First byte of packet must be 0x55 if(c == 0x55) ++state; break; case 1: // Second byte of packet must be 0xAA if(c == 0xAA) { n = 0; checksum = 0; ++state; } else { state = 0; } break; case 2: // Get 15 PWM bytes pwm[n++] = c; checksum += c; if(n >= 15) ++state; break; case 3: // Get checksum if(c == checksum) { // Update PWM if checksum is valid for(n = 0; n < 15; ++n) { pwm_set((n < 2) ? n : n + 1, (unsigned)pwm[n] << 8); } } state = 0; // Next packet break; } } } void main(void) { unsigned char c; WDTCTL = WDTPW | WDTHOLD; // Disable watchdog DCOCTL = 0; // Run at 16 MHz BCSCTL1 = CALBC1_16MHZ; // DCOCTL = CALDCO_16MHZ; // P1OUT = P2OUT = 0; // P1DIR = 0xFF ^ BIT2; // P1.2 is serial in, all other output P1SEL = BIT2; // Enable capture interrupt P2SEL = 0; // Allow P2.6 and P2.7 to be used as GPIO P2DIR = 0xFF; // Enable all P2 pins as output pwm_init(); // Initialize software PWM TACCTL1 = CM_2 | CAP | CCIE; // Enable capture on falling edge head = tail = rx_buf; // Init rx ring buffer pointers #if 0 protocol(); // Serial control of all 15 PWM outputs #else for(; { // Control LP LEDs with 0-9 and a-j if(head != tail) { c = get_rx(); if(c >= '0' && c <= '9') { pwm_set(0, (c - '0') * 7000); } else if(c >= 'a' && c <= 'j') { pwm_set(6, (c - 'a') * 7000); } } } #endif } Rickta59, xpg, bluehash and 3 others 6 Quote Link to post Share on other sites
acolytesumatra 0 Posted August 21, 2012 Share Posted August 21, 2012 hi, can you tell how it really works and how to control it? i think this project can help me to control 15 servos in one MSP430G2553. do you have any circuit if theirs any. thank you. Quote Link to post Share on other sites
oPossum 1,083 Posted August 21, 2012 Author Share Posted August 21, 2012 This code really isn't suitable for use with RC servos. They typically want a ~20 ms frame rate to work properly, and are usually addressed sequentially rather than concurrently. See this thread for some more appropriate code. GeekDoc 1 Quote Link to post Share on other sites
bcarroll 2 Posted August 16, 2013 Share Posted August 16, 2013 The code doesn't work.. There is HTML in 2 of the for loops. for(;<img src='http://forum.43oh.com/public/style_emoticons/<#EMO_DIR#>/icon_e_wink.gif' class='bbc_emoticon' alt=';)' /> { Quote Link to post Share on other sites
abecedarian 330 Posted August 17, 2013 Share Posted August 17, 2013 The code doesn't work.. There is HTML in 2 of the for loops. for(;<img src='http://forum.43oh.com/public/style_emoticons/<#EMO_DIR#>/icon_e_wink.gif' class='bbc_emoticon' alt=' ;-)' /> { I think the code should be- for(; { GeekDoc 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.