This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Using CCR0 & CCR1 for Timer A0 using MSP430G2553

I have been trying to get my code to run two functions at different intervals.  I want my UART update to happen at about every 200ms and my SPI commands to about every 5ms.  CCR0 works for me, but CCR1 does not.  Both functions execute but CCR1 seems to execute as quickly as possible while CCR0 executes about every 200ms.  Can anyone see what I am missing here?

#include

<msp430.h>

#define

MYTRUE 1

#define

MYFALSE 0

#define

numcycles 10

#define

update_delay 2620//1310 //0

#define

page_write 3000 //5

#define

recharge_cycles 3000000

extern void CSL_init(void);

void adc2asciidec(unsigned long anum);

void

adc2asciihex(unsigned long anum);

unsigned

char MST_Data, SLV_Data;

volatile

unsigned int i;

float

curr_V;

unsigned

long device_id;

unsigned

long charge_cnt;

unsigned

long bytes_wr;

int

first_run;

unsigned

char digit[16];

unsigned

long lnum;

char

lnum2;

void

main(int argc, char *argv[])

{

CSL_init();

//confirmation copy

WDTCTL = WDTPW + WDTHOLD;

UCA0CTL1 |= UCSWRST;

UCB0CTL1 |= UCSWRST;

ADC10CTL0 &= ~ENC;

UCB0CTL0 =

/*UCCKPL +*/ UCCKPH + UCMSB + UCMST + UCMODE_0; //+ UCSYNC;

ADC10CTL0 = ADC10IE + ADC10ON +

/*REFON +*/ ADC10SHT_3 + SREF_0;

UCA0CTL1 = UCSSEL_2 + UCSWRST;

UCB0CTL1 = UCSSEL_2 + UCSWRST;

ADC10CTL1 = CONSEQ_0 + ADC10SSEL_0 + ADC10DIV_3 + SHS_0 + INCH_0;

UCA0MCTL = UCBRF_0 + UCBRS_1;

UCA0BR0 = 104;

//64;

UCA0BR1 = 0;

//3;

UCB0BR0 |= 0x1;

UCB0BR1 = 0;

ADC10CTL0 |= ENC;

UCA0CTL1 &= ~UCSWRST;

UCB0CTL1 &= ~UCSWRST;

P2OUT = BIT0;

P1SEL = BIT1 + BIT2 + BIT5 + BIT6 + BIT7;

P1SEL2 = BIT1 + BIT2 + BIT5 + BIT6 + BIT7;

P1DIR = BIT3 + BIT4;

P2DIR = BIT0 + BIT1 + BIT2 + BIT3 + BIT4 + BIT5 + BIT6 + BIT7;

P1IES = 0;

P1IFG = 0;

P2IES = 0;

P2IFG = 0;

IFG2 &= ~(UCA0RXIFG + UCB0RXIFG);

IE2 |= UCA0RXIE;

IE2 |= UCB0TXIE;

IE2 |= UCB0RXIE;

TA0CCTL0 = CCIE;

TA0CCTL1 = CCIE;

TA0CCR0 = update_delay;

TA0CCR1 = page_write;

TA0CTL = TASSEL_1 + MC_1 + TAIE;

// ACLCK, 1/8 DIVIDER, upmode to TCCR0 value

charge_cnt = 0;

bytes_wr = 0;

lnum2 = 0x01;

first_run = MYFALSE;

P2OUT |= BIT1;

// Vdd

__delay_cycles(recharge_cycles);

// Wait for slave to initialize

P2OUT &= ~BIT1;

// Vdd

for(;;){

__bis_SR_register(LPM0_bits + GIE); // CPU off, enable interrupt

}

}

void

USCIA0RX_ISR(void)

{

int x = 0;

int y = 0;

lnum2 = 0x01;

ADC10CTL0 &= ~ENC;

while (ADC10CTL1 & BUSY); // Wait if ADC10 core is active

ADC10CTL0 |= ENC + ADC10SC;

lnum = ADC10MEM;

if (lnum < 559)//512

{

P2OUT |= BIT1;

__delay_cycles(recharge_cycles);

P2OUT &= ~BIT1;

__delay_cycles(200);

charge_cnt++;

}

P2OUT &= ~BIT0;

// /CE low

__delay_cycles(numcycles);

// Add time between transmissions to

UCB0TXBUF = 0x06;

// WREN

__delay_cycles(numcycles);

// Add time between transmissions to

P2OUT |= BIT0;

// /CE high

P2OUT &= ~BIT0;

// /CE low

__delay_cycles(numcycles);

// Add time between transmissions to

while (!(IFG2 & UCB0TXIFG));

UCB0TXBUF = 0x02;

// write op code

while (!(IFG2 & UCB0TXIFG));

UCB0TXBUF = 0x01;

// write MSB address

while (!(IFG2 & UCB0TXIFG));

UCB0TXBUF = MST_Data;

// write LSB address

for (x = 0; x < 32; x++)

{

while (!(IFG2 & UCB0TXIFG));

UCB0TXBUF = 0xAA;

// write 1byte

}

__delay_cycles(numcycles);

// Add time between transmissions to

P2OUT |= BIT0;

// /CE high

while

((lnum2 % 2) > 0)

{

P2OUT &= ~BIT0;

// /CE low

__delay_cycles(numcycles);

// Add time between transmissions to

while (!(IFG2 & UCB0TXIFG));

UCB0TXBUF = 0x05;

// read op code

while (!(IFG2 & UCB0TXIFG));

UCB0TXBUF = 0xFF;

// dummy for clock

while (!(IFG2 & UCB0TXIFG));

UCB0TXBUF = 0xFF;

// dummy for clock

while (!(IFG2 & UCB0RXIFG));

lnum2 = UCB0RXBUF;

// write 1byte

__delay_cycles(numcycles);

// Add time between transmissions to

P2OUT |= BIT0;

// /CE high

y++;

}

if

(y > 1)

{

device_id = 1;

}

else

{

device_id = 0;

}

lnum2 = 0x01;

bytes_wr = bytes_wr + 32;

MST_Data++;

__delay_cycles(numcycles);

// Add time between transmissions to

TA0CCR1 = page_write;

}

 

void

TIMER_ISR(void)

{

adc2asciihex(device_id);

adc2asciihex(lnum);

adc2asciihex(charge_cnt);

adc2asciihex(bytes_wr);

while (!(IFG2 & UCA0TXIFG)); // USCI_A0 TX buffer ready?

UCA0TXBUF =

'\r'; // Send next value

while (!(IFG2 & UCA0TXIFG)); // USCI_A0 TX buffer ready?

UCA0TXBUF =

'\n'; // Send next value

TA0CCR0 = update_delay;

}

void

adc2asciidec(unsigned long anum)

{

int i=0;

while(anum >= 10)

{

digit[i] = anum % 0x0a;

anum = anum / 0x0a;

i++;

}

digit[i] = anum;

while(i>=0)

{

while (!(IFG2 & UCA0TXIFG)); // USCI_A0 TX buffer ready?

UCA0TXBUF = digit[i] + 48;

//uart_data[x];//ADC10MEM; // Send next value

digit[i] = 0;

i--;

}

//delay_ms(500);

while (!(IFG2 & UCA0TXIFG)); // USCI_A0 TX buffer ready?

UCA0TXBUF =

' '; // Send next value

while (!(IFG2 & UCA0TXIFG)); // USCI_A0 TX buffer ready?

UCA0TXBUF =

' '; // Send next value

}

void

adc2asciihex(unsigned long anum)

{

int i=0;

while(anum >= 0x10)

{

digit[i] = anum % 0x10;

anum = anum / 0x10;

i++;

}

digit[i] = anum;

while(i>=0)

{

while (!(IFG2 & UCA0TXIFG)); // USCI_A0 TX buffer ready?

if (digit[i] < 10)

{

UCA0TXBUF = digit[i] + 48;

//uart_data[x];//ADC10MEM; // Send next value

}

else

{

UCA0TXBUF = digit[i] - 10 + 65;

//uart_data[x];//ADC10MEM; // Send next value

}

digit[i] = 0;

i--;

}

//delay_ms(500);

while (!(IFG2 & UCA0TXIFG)); // USCI_A0 TX buffer ready?

UCA0TXBUF =

' '; // Send next value

while (!(IFG2 & UCA0TXIFG)); // USCI_A0 TX buffer ready?

UCA0TXBUF =

' '; // Send next value

}

 

 

#pragma vector = TIMER0_A1_VECTOR

__interrupt

void TIMER_A1(void)

{

USCIA0RX_ISR();

}

#pragma

vector = TIMER0_A0_VECTOR

__interrupt

void TIMER_A0(void)

{

TIMER_ISR();

}

  • The paste came out weird for my code.  Please let me know if clarification is needed.

  • Hi,

    Jonathan Hunter1 said:

    TA0CCTL0 = CCIE;

    TA0CCTL1 = CCIE;

    TA0CCR0 = update_delay;

    TA0CCR1 = page_write;

    TA0CTL = TASSEL_1 + MC_1 + TAIE;

    // ACLCK, 1/8 DIVIDER, upmode to TCCR0 value

    You are basically using the Up mode which means that the timer counter TA0R will count up to TA0CCR0 then resets back to zero. Which value are using at ACLK?

    Jonathan Hunter1 said:

    #define update_delay 2620//1310 //0

    #define page_write 3000 //5

    However you are setting the value of TA0CCR1 with a value which is greater than the TA0CCR0 itself (means TA0R will never reach the TA0CCR1).

    I would suggest the following:

    #define ACLK_FREQ_INPUT_HZ       (32768UL)  // assume 32 kHz input at ACLK

    #define CCR0_PERIOD_MS           (200UL)  // 200 ms

    #define CCR1_PERIOD_MS           (5UL)  // 5ms

    #define CCR0_VALUE               ((unsigned int) (CCR0_PERIOD_MS*ACLK_FREQ_INPUT/1000UL))

    #define CCR1_VALUE               ((unsigned int) (CCR1_PERIOD_MS*ACLK_FREQ_INPUT/1000UL))

    and use the CCR0_VALUE and CCR1_VALUE above as your input to the TA0CCR0/1 registers.

  • Hello Leo,

    Thanks for replying!

    I agree that I need to be more precise with my CCR0 and CCR1 values, but no matter what value I make CCR1 the timing does not seem to change for executing that section of code.  As I understand it with a CCR1 = 3000, that interrupt code should be occurring even less frequently than my CCR0 interrupt code.  For some reason, my code is triggering constantly and the only time CCR1 is not running is when CCR0 triggers.

    I think it points to a configuration error, but I don't know what.  I also reference ADC10, SPI ports for B0, and UART A0 pins.  I will use all the rest of the pins for GPIOs to control other components.  Is there a conflict with using TA0CCR1 with these configurations?

    Here are my CLK settings that I forgot to add:

        BCSCTL2 = SELM_0 + DIVM_0 + DIVS_0;

    if (CALBC1_1MHZ != 0xFF) {

    /* Follow recommended flow. First, clear all DCOx and MODx bits. Then

             * apply new RSELx values. Finally, apply new DCOx and MODx bit values.

             */

            DCOCTL = 0x00;

            BCSCTL1 = CALBC1_1MHZ;     

    /* Set DCO to 1MHz */

            DCOCTL = CALDCO_1MHZ;

        }

        BCSCTL1 |= XT2OFF + DIVA_0;

        BCSCTL3 = XT2S_0 + LFXT1S_2 + XCAP_1;

  • CCR0 generates an unique interrupt.  Its interrupt flag is automatically cleared when that interrupt is acknowledged.

    CCR1 shares its interrupt with other causes. Thus its interrupt flag is not automatically cleared. The ISR must clear it, otherwise it will cause another interrupt the moment your ISR finishes. And that is what you experienced.  Add a CCTL1 &= ~CCIFG; statement in its ISR.

  • Hi,

    old_cow_yellow said:

    CCR1 shares its interrupt with other causes. Thus its interrupt flag is not automatically cleared. The ISR must clear it, otherwise it will cause another interrupt the moment your ISR finishes. And that is what you experienced.  Add a CCTL1 &= ~CCIFG; statement in its ISR.

    OCY was right. referring to the User's Guide document, the best way to do this is by reading out the TAIV register:

    12.2.6.2 TAIV, Interrupt Vector Generator
    The TACCR1 CCIFG, TACCR2 CCIFG, and TAIFG flags are prioritized and combined to source a single interrupt vector. The interrupt vector register TAIV is used to determine which flag requested an interrupt. The highest priority enabled interrupt generates a number in the TAIV register (see register description).
    This number can be evaluated or added to the program counter to automatically enter the appropriate software routine. Disabled Timer_A interrupts do not affect the TAIV value.


    Any access, read or write, of the TAIV register automatically resets the highest pending interrupt flag. If another interrupt flag is set, another interrupt is immediately generated after servicing the initial interrupt. For example, if the TACCR1 and TACCR2 CCIFG flags are set when the interrupt service routine accesses the TAIV register, TACCR1 CCIFG is reset automatically. After the RETI instruction of the interrupt service routine is executed, the TACCR2 CCIFG flag will generate another interrupt. 

  • Jonathan Hunter1 said:
    As I understand it with a CCR1 = 3000, that interrupt code should be occurring even less frequently than my CCR0 interrupt code.

    No. In cont mode, wher ethe tiemr counts fromm 0 to 65535 and begins with 0, all CCR units trigger once per round, just at different moments.
    In up mode, which you use,the timer counts from 0 to CCR0 and then begins with 0 again. But the interrupts trigger when the timer counts to the stored value. Hence 'compare mode', in opposition to 'capture mode' for the CCRs (Capture-Compare-Registers). SO if the CCR1 value is higher than CCR0 value, the timer never will count that high and the interrupt is never triggered at all.
    If it is less than CCR0, it will trigger exactly as often (once per tiemr cycle), only the moment during the cycle differs with the value.

    BTW: if you want the cycle to be 1000 timer ticks, you'll have to write 999 into CCR0, since '0' is a tick as well.

    The reason why you got this amount of CCR1 interrupts was already explained by the others. And since CCR1 and CCR0 were 0 before you wrote the values that effectively prohibited any CCR1 interrupt, there was already one interrutp pending which you never cleared.

  • Thanks for the replies!

    So I tried clearing the interrupt after it triggered using (because the CCR1 interrupt is not clearing):

    TA0CCTL1 &= ~CCIFG;

    I put this line at the end of my USCIA0RX_ISR() function call.  It had no effect on the how the code operated.

     

    I then tried reading the TA0IV register with:

    lnum2 = TA0IV;

    This caused my code to go back to the main function each time this line was reached.  The code was planned to go through the main function once to initialize variables and then remain in the infinite loop that contained sleep and interrupt enable.  The program would only run when CCR0 and CCR1 interrupts occur.

     

    I see what you are saying Jens about the CCR1 must be smaller than CCR0 otherwise the code will never trigger.  I suppose that is why I am confused because with the timer in up configuration, I should never see my SPI commands rather than always seeing my SPI commands. So I suppose this all points back to I still am not clearing interrupt correctly or my configuration is not allowing me to do so.

    Do you see anything else that could be causing this?

     

     

  • Jonathan Hunter1 said:
    So I tried clearing the interrupt after it triggered using (because the CCR1 interrupt is not clearing):
    TA0CCTL1 &= ~CCIFG;
    I put this line at the end of my USCIA0RX_ISR() function call.  It had no effect on the how the code operated.


    I wouldn't expect it to work :)
    You must clear the IFG bit inside the ISR it was triggering. So inside the timer ISR. If you leave the timer ISR before it is cleared, you'll go into the timer ISR again right away, and never execute any ISR with a lower interrupt priority.

  •  

    I see what you are saying.  I corrected Timer_A1 ISR function call.

    #pragma vector = TIMER0_A1_VECTOR

    __interrupt void TIMER_A1(void)

    {

        TA0CCTL1 &= ~CCIFG;

        USCIA0RX_ISR();

    }

    #pragma vector = TIMER0_A0_VECTOR

    __interrupt void TIMER_A0(void)

    {

    TIMER_ISR();

    }

    This change does not seem to change how the program runs. 

    The only thing that seems to change how the code executes is reading/writing from TA0IV, but that just causes the program to reset and start from the beginning of main again.

    Here is my current configuration:

    In the main() function:

    #define update_delay 3000

    #define page_write 200

     

    TA0CCR0 = update_delay;

    TA0CCR1 = page_write;

    TA0CTL = TASSEL_1 + MC_1 + TAIE;   

    TA0CCTL0 |= CCIE;

    TA0CCTL1 |= CCIE;

     

    At the end of USCIA0RX_ISR():

    TA0CCR1 += page_write;

     

    In my CCR0 interrrupt function:

    void TIMER_ISR(void)

    {

    int x;

       adc2asciihex(device_id);

        adc2asciihex(lnum);

    adc2asciihex(charge_cnt);

        adc2asciihex(bytes_wr);

      

    while (!(IFG2 & UCA0TXIFG));              // USCI_A0 TX buffer ready?

        UCA0TXBUF = '\r';                         // Send next value

       

    while (!(IFG2 & UCA0TXIFG));              // USCI_A0 TX buffer ready?

        UCA0TXBUF = '\n';                         // Send next value

        TA0CCR1 = page_write;

        TA0CCR0 = update_delay;

      }

     

    So my understanding, the code should have timerA0 count up to CCR0 = 3000 and reset. Since CCR1 += 200, then every 200 cycles it would run the SPI commands until CCR0 triggers and the timer is reset to 0.  The Timer_ISR() function that triggers at CCR0 interrupt shoud reset CCR1 value to 200.  I know that CCR0 works because I can increase that value and watch teh updates happen at a slower rate.

    Is there something else that I need to do before 'TA0CCTL1 &= ~CCIFG;' that would allow me to clear the interrupt? Would it help to zip up my project to view?

  • Is there any further advice?  Does anyone know why reading from TA0IV would cause the entire program to reset?

    Could someone point me to code that runs 2 interrupt routines in a similar setup that I am trying to acheive?

  • Jonathan,

    could you share your code? i might try to test it myself. Looking the code above gives a little headache :)

  • Thanks Leo!

    This code is supposed to work with a development board but you can still see the issue with out that board.

    I included 2 images from a logic analyzer to show what I am seeing.

    In MSP430_1.jpg, you will see a lot of activity with periods (every ~200ms) of "no activity" for the SPI lines (this is the UART update which you can see on the terminal as well).  That 200ms period will change if you modify the CCR0 value in the code.

    In MSP430_2.jpg, you see the SPI commands which are 32 bytes of written data and a status register update.

    In the terminal window, you will see the 4 values printed on each line.  The main value to be concerned about is the 4th value (number of bytes written in hex)  which should increment a certain rate depending on CCR1, but it does not.  As you can see from the logic analyzer, the rate is as fast as possible.

    Please let me know if need any other information.

  • I do not use c to program MSP430, especially not CCS or Libraries from TI. They tend to do things “automatically” which I know nothing about. (What I do not know often hurts me.) Hence I cannot help you much.

    I did try to read the code you posted.

    CCIE in both TA0CCTL0 and TA0CCTL1 are enabled. And the corresponding handlers exist. TAIE in TA0CTL is also enabled but not handled. I am not sure how many other interrupts are enabled (automatically) and not handled. Any interrupt without a matching “#pragma vector = …” handler may “automatically” cause a crash.

    TA0CTL is configured to count ACLK in Up-mode. TA0CCR is set to 2620. This means TA0R will count from 0 up to 2620 and back to 0 and up again repeatedly, resulting in a 2621 ACLK cycle repetitive counting pattern.

    CCIE in TA0CCTL0 is enabled. A corresponding interrupt is generated when TA0CCR0 matches the TA0R counter. Thus this happens repetitively at every 2620 of the 0-2620 counting pattern. The period is 2621, very close to what you want.

    TA0CCR1 is set to 100 and CCIE in TA0CCTL1 is also enabled. A corresponding interrupt is generated when TA0CCR1 matches the TA0R counter. Thus this happens repetitively at every 100 of the 0-2620 counting pattern. The period is also 2621. The only difference is that it is 100 counts later than the other one. This is not what you want.

    The interrupt caused by TAIE of TA0CTL shares the same “#pragma vector = …” as the CCIE of TA0CCTL1. Thus the code there gets executed unintentionally. Further more, the TAIFG in TA0CTL is not cleared. And the code there gets executed repeatedly.

  • Good morning Gentlemen,

    So Jonathan, i read your code, and although i didn't test your code directly, here are some thoughts:

    - You are setting the TAIE bit in TA0CTL register. Do you really need this? Referring to the 2xx Family User's Guide, this interrupt is basically "almost" the same as the CCR0  IFG in up mode:

    As you can see above, the TAIFG bit will be generated everytime 1 clock cycle right after the CCR CCIFG interrupt. OCY has also pointed out above that this shares the same interrupt with other CCRx interrupts other than CCR0. In my opinion i don't think you need to set this TAIE bit since it will makes the same interrupt vector as CCR1 CCIFG invoked (maybe this is the root of your problem). Please read the following chapter from the users guide document carefully again:

    - I am not really sure what is the goal of your program. I can see that you do some ADC sampling and send the results, however i don't think this is the right way to do it (e.g. you do the ADC sampling inside the interrupt routine which basically blocks other interrupt). If you are not familiar with the MSP430 programming, would heavily recommend you to read the following short document : MSP430 Software Coding Techniques: http://www.ti.com/lit/an/slaa294a/slaa294a.pdf. This is a very great documentation which i always recommend for starting in the world of MSP430. Just to summarize:  basically you shall do the hard work in the main loop. The ISRs are only used for getting the events, and setting a flag to notify the main loop to do the job.

    -You don't need to reload the TAxCCRy value in the interrupt routine, as long as you don't touch it, it will not be changed.

    - @OCY: actually Jonathan has assigned the interrupt routines. He is using GRACE (http://www.ti.com/tool/grace), and if you search for a file called CSL_init.c in the zip file, you can find the interrupt vectors assigned.

    Let me know if you still have other questions.

  • Thanks all for the information!  This forum has been great.

    After reading the materials, I guess my code may be organized incorrectly.  The goal of the program was to run 2 devices with the same SPI commands.  One device takes longer to complete the write (~5ms) so I wanted to set a timer that would run the code at set intervals that I could configure so both devices would be writing at the same rate.  So if I set the timer up correctly, both devices would be receiving the spi commands every 5 ms (even though one would finish after ~0.7 ms).  The ADC was to measure voltage from a capacitor to determine power differences in usage.

    Also a timer would be implemented to send UART updates every ~200ms.

    I am willing to reorganize the code as needed.  Do you guys have any tips for the correct organization of my code?

    I have tried calling the spi function in an infinite loop in the main function, but also enable interrupts with:

    __bis_SR_register(GIE);

    inside that loop also.  But it seems that the interrupt causes the entire main program to run again which initializes my variables...

    Should the organization of the code similar to the following pseudocode:

    initialize system;

    enable timer;

    for(::)

    {

    if (timer has incremented 200ms)

           run UART_update function;

    if (timer has incremented 5ms)

                   run spi function;

    }

     

    If so, what is the best way to pull that time difference from timer?  Do you see any way to use the LP modes with this setup (I wanted to use it earlier, but in the interest of getting it working, I am willing to skip it for now)?

    Any ideas for restructuring are welcome...

    Thanks

  • Your requirements are more complex than it seems at first.
    It might be necessary to setup a state machine.

    Set up the timer to trigger an interrupt every 1ms.

    One counter (a static local variable) increments to 200 and then is reset to 0. When it has reached 200 adn is reset, you set a global flag indicating 'time for UART update'. And maybe exit LPM, so main can do the UART update and won't hinder the timer interrupts. Don't do the UART update inside the ISR. It will block further itnerrupts until done, maybe interfering with the SPI update.

    Then you have a second counter that counts up from 0 to 5. When it reaches 5 and is reset to 0, you send command to the first SPi device and set a third static variable 'state' to '1' indicating that the firt step has been done. On next interrupt, check for answer,and if properly received, proceed to the next device and increment the state to 2. If the device isn't finished yet, stay on state 1 and check next time.

    This ensures that you never wait inside the ISR for any response. Jsu tcheck next interrutp whether the response has arrived.

    You can go for 0.5ms interrupt speed if you need a finer granularity. Since your ISR won't take many clock cycles, this is fine.

    With a similar approach I check for a newly inserted SD-Card in one of our projects and initialize it in the background. It takes several 100 ms to initialize it, but the main program won't notice, as the ISR won't spend much time due to the state machine.

    If you look at the MSP users guide, you will find lots of state diagrams (those with circles and arrows). The yshow what you do and on which criteria you move form one state to the next. It's a common way to solve complex or even nested job requirements.

  • Thanks Leo, OYC, and Jens!

    After reorganizing the code, I have my project working now!  I really appreciate it.

    As discussed earlier, I set the timer to function as it should be.  I have it clocking at 1ms and sending flags at the time the commands need to be sent.  All of the feedback and referenced reading material made it so that I was able to get this working.

    You all are awesome...this was exemplary forum support!

**Attention** This is a public forum