Input Capture

Hey everyone. First post here. I’ve been reading around and I can’t find anyone who seems to have done this yet, or not on a level I’m looking at.

I could use my Wixel to relay commands to a Baby-O which has more “friendly” libraries but there’s no fun in that and I’m working with really tight spacial constraints.

Here’s where I’m at, I have a robot, with a motor, encoder, and a servo. The motor and servo are driven by Timer 1 on P1_0 and P1_1 with a free running counter making my PWMs. (Thank you to everyone who’s posted in regards to PWM, I’ve probably read it if you posted it.)

I’m looking to measure the rpm of my motor using Timer 1 Channel 0 on P1_2 if possible. Does anyone understand the syntax for creating an interrupt? I can’t use ISR(T1,0) because I am already using Timer 1 for PWMs.

Any help here is appreciated. Thanks in advance.

-Peter

Hello, Peter.

To define an ISR, you should use the ISR macro, which is defined in cc2511_map.h and documented here:

pololu.github.io/wixel-sdk/cc251 … 0b276acd11

It looks like Timer 1 just has one interrupt vector, so maybe you will have to check the interrupt flags in your ISR to see which event happened and then do the appropriate response to that event.

–David

Thanks for the quick response, David.

For now, here’s where I’m at:

void main()
{
systemInit();
usbInit();

T1CTL = 0b00000001;		//Enable T1 Free Running
T1CCTL0 = 0b01000011;	// Enable Ch0 interrupt and set capture
IEN1 |= 0b00000010;  // Enable T1 Interrupts
P1SEL |= 0b00000100; // Set P1_2 as peripheral function (non-general I/O)
    while(1)
    {
        boardService();
        updateLeds();
        usbComService();
        processBytesFromUsb();
    }
}

and my ISR:

ISR(T1,0){
	if (T1CTL & 0b00010000){

	}
	else{
		LED_RED(1);
	}
}

I’m just trying to turn on the LED as my debug method for now. If I’m correct in my methods, the if statement above does nothing when the Overflow flag trips, but should turn on the LED if any other T1 flag is tripped. As of yet, I haven’t found a configuration that works correctly. Anyone out there had success or see my flaws?

Before I carefully check your register settings, could you post your entire program here? I noticed you did not post the source code of updateLeds, and if that contains a line for turning off the red LED then that would explain your problem.

–David

I haven’t posted the entire code here, because it’s somewhat lengthy, a lot of stuff I have commented out right now though, so here’s is what’s currently in the program, and not commented out.

/* bob_receiver app:
 *
 * based on wireless_serial app
 *
 * This app allows bob to operate via PD control
 */


/** Dependencies **************************************************************/
#include <wixel.h>

#include <usb.h>
#include <usb_com.h>
#include <stdio.h>
#include <radio_com.h>
#include <radio_link.h>

/**************DEBUG STUFF***********************/
/* VARIABLES ******************************************************************/

/** True if a serial protocol error occurred recently. */
BIT serialProtocolError = 0;

/** The binary command byte received from the computer. */
uint8 commandByte;

/** The binary data bytes received from the computer. */
uint8 dataBytes[32];

/** The number of data bytes we are waiting for to complete the current command.
 * If this is zero, it means we are not currently in the middle of processing
 * a binary command. */
uint8 dataBytesLeft = 0;

/** The number of data bytes received so far for the current binary command.
 * If dataBytesLeft==0, this is undefined. */
uint8 dataBytesReceived;

/** A temporary buffer used for composing responses to the computer before
 * they are sent.  Must be bigger than the longest possible response to any
 * command.
 */
uint8 XDATA response[32];
/***********END DEBUG STUFF***********************/

/** Parameters ****************************************************************/

int32 CODE param_baud_rate = 9600;

/** Functions *****************************************************************/

void updateLeds()
{
    static BIT dimYellowLed = 0;
    static uint16 lastRadioActivityTime;
    uint16 now;

    usbShowStatusWithGreenLed();

    now = (uint16)getMs();

    if (!radioLinkConnected())
    {
        // We have not connected to another device wirelessly yet, so do a
        // 50% blink with a period of 1024 ms.
        LED_YELLOW(now & 0x200 ? 1 : 0);
    }
    else
    {
        // We have connected.

        if ((now & 0x3FF) <= 20)
        {
            // Do a heartbeat every 1024ms for 21ms.
            LED_YELLOW(1);
        }
        else if (dimYellowLed)
        {
            static uint8 DATA count;
            count++;
            LED_YELLOW((count & 0x7)==0);
        }
        else
        {
            LED_YELLOW(0);
        }
    }

    if (radioLinkActivityOccurred)
    {
        radioLinkActivityOccurred = 0;
        dimYellowLed ^= 1;
        //dimYellowLed = 1;
        lastRadioActivityTime = now;
    }

    if ((uint16)(now - lastRadioActivityTime) > 32)
    {
        dimYellowLed = 0;
    }
}

void RadioService()
{
    uint8 signals;
	uint8 status;
    // Data

    while(radioComRxAvailable())
    {
        status = radioComRxReceiveByte();

		switch(status){
			case 'w':
				LED_RED(1);
				break;
			case 's':
				LED_RED(0);
				break;
		}
    }

    // Control Signals

	radioComTxControlSignals(usbComRxControlSignals() & 3);

	// Need to switch bits 0 and 1 so that DTR pairs up with DSR.
	signals = radioComRxControlSignals();
	usbComTxControlSignals( ((signals & 1) ? 2 : 0) | ((signals & 2) ? 1 : 0));
}

/** For Debugging Only ******************************************************/

void processByte(uint8 byteReceived)
{

        uint8 responseLength;
        uint32 time;
        switch(byteReceived)
        {
        case 't':
            time = getMs();
            // SDCC's default sprintf doesn't seem to support 32-bit ints, so we will
            // split getMs into two parts and print it in hex.
            responseLength = sprintf(response, "time=0x%04x%04x\r\n", (uint16)(time >> 16), (uint16)time);
            usbComTxSend(response, responseLength);
            break;

        case 'y':
        	LED_RED(0);
            break;
    }
}

/** Checks for new bytes available on the USB virtual COM port
 * and processes all that are available. */
void processBytesFromUsb()
{
    uint8 bytesLeft = usbComRxAvailable();
    while(bytesLeft && usbComTxAvailable() >= sizeof(response))
    {
        processByte(usbComRxReceiveByte());
        bytesLeft--;
    }
}

/****************************************************************************/

ISR(T1,0){

	if (T1CTL & 0b00010000){

	}
	else{
		LED_RED(1);
	}


}

void main()
{
	systemInit();

    usbInit();

	radioComRxEnforceOrdering = 1;
	radioComInit();

	// Set up P1_5 to be the radio's TX debug signal.
    P1DIR |= (1<<5);
    IOCFG0 = 0b011011; // P1_5 = PA_PD (TX mode)

	T1CTL = 0b00000001;		//Enable T1
	T1CCTL0 = 0b01000011;	// Enable interrupt and set capture on both edhes

	IEN1 |= 0b00000010;
	P1SEL |= 0b00000100;

    while(1)
    {
        boardService();
        updateLeds();
        usbComService();
        processBytesFromUsb();
    }
}

I think there are two problems with your code:

  • Your settings of T1CTL indicate that the timer should be running in up/down mode, where the value in the 16-bit T1CC0 register determines the frequency of the timer. However, you are also trying to use Timer 1 Channel 0 as a capture channel, which means you are asking T1CC0 to serve two purposes. I suspect that kind of configuration is not allowed but I could not find any definite statement about it in the datasheet.
  • In your ISR, you are checking the Timer 1 overflow flag OVFIF, but you are never clearing it. The first time the timer overflows, that flag will remain set forever and the red LED will never be able to turn on.

–David

Thanks again for the help with this David. After some revision, I have made a lot of progress and can trip the interrupt correctly but I need to play with my hysteresis in my circuitry. Still a few small issues I’m working with though.

My understanding if I read this right was that 01 was free-running while 11 is up/down. in T1CTL. In up/down you would be correct in regards to frequency but I am not trying to use it that way, unless absolutely necessary.

Here’s my problem: I receive a negative value for T1CC0. Any ideas what this means?

Here is the revised functions:

ISR(T1,0){
	uint16 responseLength;
   if (T1CTL & 0b00010000){
	   // Note Overflow for counting somewhere
	   T1CTL &= 0b11101111;
   }
   else if (T1CTL & 0b00100000){
	  LED_RED_TOGGLE();
	  T1CTL &= 0b11011111;
	  responseLength = sprintf(response, "time=%d\r\n", T1CC0);
	  usbComTxSend(response, responseLength);
   }
}

void main()
{
	systemInit();

    usbInit();

	radioComRxEnforceOrdering = 1;
	radioComInit();

	// Set up P1_5 to be the radio's TX debug signal.
    P1DIR |= (1<<5);
    IOCFG0 = 0b011011; // P1_5 = PA_PD (TX mode)

	T1CTL = 0b00000001;		//Enable T1
	T1CCTL0 |= 0b01000011;	// Enable interrupt and set capture on both edges
	T1CCTL0 &= 0b11111011;
	PERCFG |= 0x40;			// Timer 1 to Alt2 location
	IEN1 |= 0b00000010;		// Enable Timer 1 interrupts
	P1SEL &= 0b11111011;	// P1_2 as peripheral function
	P1DIR &= 0b11111011;	// P1_2 as input
	P1INP &= 0b11111011;	// P1_2 to pull-up/down mode
	P2INP &= 0b10111111;	// P1_2 set to pull-up

	while(1){
        boardService();
        updateLeds();
        usbComService();
        processBytesFromUsb();
    }
}

Use %u (unsigned value) instead of %d (signed value) in the sprintf statement.

Just an update:

I think I’ve got everything working as I needed. I now have P1_0 and P1_1 creating PWMs to drive a motor and servo from Timer 1. I’m also using P1_2 as an interrupt based encoder reader. While I have it set as a capture interrupt, I am using an alternative method of timing my ticks and not using the Timer 1 register anymore. However, rather than creating a new interrupt when this was already set up, I simply removed the timing portion.

For anyone interested, I have posted the code below that works for me.
Thanks again for the help!

void captureInit(){
	IEN1 |= 0b00000010;
	PERCFG |= 0b01000000;	// Set Timer 1 to Alt 2 location
	T1CTL |= 0b00000001;	// Set Timer 1 to Tick/1 and Free-Running
	T1CCTL0 |= 0b01000001;	// Set T1 Ch0 to capture on rising edge
	T1CCTL0 &= 0b01111011;	// Set to regular capture mode
	P1SEL &= 0b11111011;	// Set P1_2 to general purpose I/O
	P1DIR &= 0b11111011;	// Set P1_2 set as input
	P1INP &= 0b11111011;	// Set P1_2 to pull-up/down mode
	P2INP &= 0b10111111;	// Set Port 1 to pull-up configured pins
}