Programming the Infrared board

Hi -

I wrote a quick sample program that reads the infrared board connected to 4 digital inputs. The problem is that the pulses are 100ms from the board so it’s easy to miss one if your program is in a Wait.

The quick solution was to register a repeating timer - so every 30 milliseconds the code checks the four ports (4, 5, 6, and 7 in this case) and if the value changed from off to on, it sets a corresponding bit in a char variable. This all happens in the interrupt service routine for the timer. Then you can read the values in your code that may not be checking as often.

This particular program is build for Vex, but everything but the header files is the same.


#include "BuiltIns.h"
#include "ifi_picdefs.h"
#include "Vex_ifi_aliases.h"

volatile static unsigned char irCommands = 0;
volatile static unsigned char previous = 0;

// Timer interrupt service routine
// Poll the remote receiver input ports every 30ms and check if a recognized code
// came in. This will work even if the main program is in a Wait statement or doing
// other things. The IR receiver pulses for 100ms when a valid code is received.

// This will potentially show multiple hits
void CheckIR(void)
{
	unsigned char current = 0;
	unsigned char changed;
	if (rc_dig_in05) current |= 1;
	if (rc_dig_in06) current |= 2;
	if (rc_dig_in07) current |= 4;
	if (rc_dig_in08) current |= 8;
	
	changed = previous ^ current;
	irCommands |= (~previous) & current;
}

// Sample test program to 
void main(void)
{
	RegisterRepeatingTimer(30, CheckIR);
	while (1)
	{
		Wait(4000);
		printf("_____________________\r\r");
		if (irCommands & 1) 
		{
			irCommands &= ~1;
			printf("Command 1\r");
		}
		if (irCommands & 2)
		{
			irCommands &= ~2;
			printf("Command 2\r");
		}
		if (irCommands & 4)
		{
			irCommands &= ~4;
			printf("Command 3\r");
		}
		if (irCommands & 8)
		{
			irCommands &= ~8;
			printf("Command 4\r");
		}
	}
}

Looks good Brad I’ll have to try it out. :smiley:

Hi Brad -

Thanks for doing this. This is also a good example of showing that you can use the IFI headers in a WPILib program, something that’s not so apparent on first glance.

Some questions:

  • I think I understand what RegisterRepeatingTimer() does, although it is not really documented. The header file says that timer interrupt handlers are “called with interrupts disabled”. However, do you not also need to disable interrupts when you access the shared globals in the main( ) routine?

  • The variables “changed” and “previous” in your ISR don’t seem to do anything? Again, I think I understand what you are trying to do (e.g. collect all the commands in between times that the program actually checks for them), but “previous” never gets set to anything other than zero, and “changed” is calculated, but then not used. Would it not be simpler to just say:

 irCommands |= current;

and then the main() routine can turn off each bit as it gets processed? I think that’s what the code is essentially doing now, since “previous” is always zero.

You’re right… not documented, I’ll try to fix that. It is called as an interrupt service routine so you want the code to be fast. As far as the shared globals in the main routine… good question. I don’t think it’s a problem because the interrupt service routine never turns them off and there is only a single value. If you wanted to count the number of times the IR code was received in the ISR and decrement the count in the main program, then it would have to be protected. As it is, I don’t think you lose anything, but I could be wrong.

You are so right about the previous variable not being set… I must have been sleeping. And I showed it to a few people and nobody else noticed. What I was trying to do was only set the bit on a change from off to on so. I didn’t want two polls to look like two events. If you just OR the new value into the result, then if in one ISR call the bit is set, then before the next 30ms period, the main program clears the bit, then the next timer interrupt notices that the bit is still set, it would like to two button presses.

Thanks for noticing the problem!

Brad

When using WPILib with the IFI headers, which headers should I include? Also, do I need to specify whether a digital I/O is used for INPUT or OUTPUT? And can the code in the first post be used as a standalone program? which headers should be included for it?
EDIT: This is for use with an FRC controller (not Vex).
Thank you

Could you please put up the header files that you used and what header file do we need to use instead of the vex one.

I understand your program Brad, however our IR Board sends a value of 0 to digital inputs 1, 2, 4 and a 1 to digital input 3. When the button for the corresponding inputs are pressed the values stay and do not change.

Instead of using the rc_dig_in variables that Kevin Watson uses. I use the WPILib function GetDigitalInput().

	
if(GetDigitalInput(IR_IN1))
	printf("IR #1
");
else if(GetDigitalInput(IR_IN2))
	printf("IR #2
");
else if(GetDigitalInput(IR_IN3))
	printf("IR #3
");
else if(GetDigitalInput(IR_IN4))
	printf("IR #4
");

I also understand that for the DigitalInputs to work I have to set whether they are used as inputs are outputs correct? so therefore I use SetDirection() for the used Digital Ports.


SetDirection(IR_IN1, INPUT);
SetDirection(IR_IN2, INPUT);
SetDirection(IR_IN3, INPUT);
SetDirection(IR_IN4, INPUT);

The port parameters all refer to macros, defined like:

#define IR_IN1 2
#define IR_IN2 3
#define IR_IN3 4
#define IR_IN4 5

One last thing, in CheckIR()

void CheckIR(void)
{
	unsigned char current = 0;
	unsigned char changed;
	if (rc_dig_in05) current |= 1;
	if (rc_dig_in06) current |= 2;
	if (rc_dig_in07) current |= 4;
	if (rc_dig_in08) current |= 8;
	
	changed = previous ^ current;
	irCommands |= (~previous) & current;
}

after each if statement could you not just do whatever each button does right after the statement, instead of using three different variables. For example,

void CheckIR(void)
{
	unsigned char current = 0;
	unsigned char changed;
	if (rc_dig_in05) Autonomous1(); //Autonomous1() is a function that does what command one is intended to be used for
	if (rc_dig_in06) current |= 2;
	if (rc_dig_in07) current |= 4;
	if (rc_dig_in08) current |= 8;
	
	changed = previous ^ current;
	irCommands |= (~previous) & current;
}

Thanks for your help ahead of time,
Sravan

Sravan,

The reason that the CheckIR() function is written that way is that it is an Interrupt Service Routine (ISR) and needs to be as fast as possible. It is called every 30ms, and while it is running, no other interrupts can occur.

Using GetDigitalInput() is good to use in your code that runs in the normal service loop, if for no other reason than it aids in program readability.
However, all it really does is access the same rc_dig_inXX variables that Brad’s (and Kevin’s) code uses. The function call has the disadvantage of having to use additional clock cycles to put the parameter, the return value, and the return address on the call stack, something you don’t want to do if you don’t have to when time is of the essence.

The time critical nature of the routine is also why you especially don’t want to call any other functions from within it to do your work. If you do, then the entire system will slow down waiting for your code to finish. Instead, you want to just get in, check the ports of interest, put the found information in a place that’s easy to access later, and get out. That’s also why Brad is using the bitwise operators to save his information, rather than the more normal arithmetic operators - bitwise operations are much quicker!

Karl200 & d235j,

You should only need to include ifi_aliases.h and ifi_defaults.h. They can be found in the IFI default code package. They will also pull in a few other header files from the mcc18/h directory, so it is important to include that directory in your compile line include path. This old thread may be of some interest:
http://www.chiefdelphi.com/forums/showthread.php?t=42407

Brad,

While I’m here - I got rid of the changed variable and modified the last two lines of the ISR to look like this:

irCommands |= (~previous) & current;
previous = irCommands;

Does that do what you originally intended?

I see.

If i do use the rc_dig_inXX variables will they not provide the same input as GetDigitalInput(XX)? The reason I’m asking is that when I press the remote button the light on the IR Sensor lights up but it doesn’t show anything.

By using the if structure:

	
if(GetDigitalInput(IR_IN1))
	printf("IR #1
");
else if(GetDigitalInput(IR_IN2))
	printf("IR #2
");
else if(GetDigitalInput(IR_IN3))
	printf("IR #3
");
else if(GetDigitalInput(IR_IN4))
	printf("IR #4
");

I would be able to see when buttons 1, 2, 3, or 4 are pushed correct.

What I originally had was the code above in a very long loop to just see if any of the above printf() commands would work however they did not.

I will try using the rc_dig_inXX variables and after that implement a quicker Check() function.

Thanks for all the help.

  1. this is my first post
  2. I have a broken wrist ans this post was typed with one hand… sorry
  3. i am a java programmer, not a c programmer
  4. using mplab, which i’ve never used before

I’m using Kevin’s code and I don’t think it has the RegisterRepeatingTimer() …
also there is no wait()
method or some thing b/c I can’t build. I’m not a total noob to programming I’ve just never programmed for first. So if you guys could help me out with why this isn’t working or point me to a general tutorial i’d appreciate it.

do i need to include WPILib or someting?
Error:


MPLINK 4.12, Linker
Copyright (c) 2007 Microchip Technology Inc.
Error - could not find definition of symbol 'RegisterRepeatingTimer' in file ~:\~~\~~~~\~~~~\Robotics\Programs\ifi_frc_sensor_30\autonomous.o'.
Errors    : 1

Link step failed.
BUILD FAILED: Wed Feb 06 15:22:07 2008

You may want to start with easyC to get you started espcially with the ship date less then 10 days away. easyC has 12 tutorials in the help file. You can download it at http://www.intelitekdownloads.com/easyCPRO