chipKIT® Development Platform

Inspired by Arduino™

Using interrupt registers

Created Tue, 06 Dec 2011 02:46:55 +0000 by docwelch


docwelch

Tue, 06 Dec 2011 02:46:55 +0000

I have some Arduino code to read pins attached to an RC reciever. The code uses the low level interrupt on change registers on the Atmel 328. I want to use a similar process on the chipkit Uno. I have found the registers (CNCONx, CNENx, CNSTATx) that I think I need to use. However, when I tried to use the registers in a sketch, I get an error that they are not defined. Any help would be appreciated. Just for reference so it is clear what I am trying to accomplish, the setup code for the 328 is: pinMode(2, INPUT); // throttle pinMode(4, INPUT); // roll pinMode(5, INPUT); // pitch pinMode(6, INPUT); // yaw pinMode(7, INPUT); // mode // interrupt on pin change PCINT PCICR |= (1 << PCIE2);

PCMSK2 = (1 << PCINT18) | // pin2 (1 << PCINT20) | // pin4 (1 << PCINT21) | // pin5 (1 << PCINT22) | // pin6 (1 << PCINT23) ; // pin7

The interrupt for the 328 is: ISR(PCINT2_vect) { pulseTime=micros(); newbyte=PIND; changed=newbyte^oldbyte; if (changed&1<<2) // Throttle if (newbyte&&1<<2) start[0]=pulseTime; else rx.raw[0]=pulseTime-start[0];

if (changed&1<<4) // Roll (aileron) if (newbyte&&1<<4) start[1]=pulseTime; else rx.raw[1]=pulseTime-start[1];

if (changed&1<<5) // Pitch (elevator) if (newbyte&&1<<5) start[2]=pulseTime; else rx.raw[2]=pulseTime-start[2];

if (changed&1<<6) // Yaw (rudder) if (newbyte&&1<<6) start[3]=pulseTime; else rx.raw[3]=pulseTime-start[3];

if (changed&1<<7) // Mode (aux) if (newbyte&&1<<7) start[4]=pulseTime; else rx.raw[4]=pulseTime-start[4];

oldbyte=newbyte; }


Jacob Christ

Tue, 06 Dec 2011 05:14:34 +0000

I have found the registers (CNCONx, CNENx, CNSTATx) that I think I need to use. However, when I tried to use the registers in a sketch, I get an error that they are not defined. Any help would be appreciated.

When you use CNCONx, CNENx, CNSTATx you don't need to add the x.

So use do something like:

CNCON = 0x00008000; //turn "interrupt on change" on CNEN = 0x000000f; //enable cn0-3

Jacob


docwelch

Tue, 06 Dec 2011 17:57:32 +0000

Thanks for the reply. I must be missing something. When I use CNCON, I get an error that CNCON does not name a type. Is there a specific library to include to get this functionality?


Jacob Christ

Wed, 07 Dec 2011 00:11:41 +0000

What board do you have selected? Tools->Boards menu?


docwelch

Wed, 07 Dec 2011 02:22:08 +0000

Okay, I figured it out. It's been a while and I had forgotten the basics that there has to be a void setup() and a void loop() for the compiler to work. Duh.

Thanks for your responses and help. When I get this running, I will post it in the appropriate area for others to use.


Jacob Christ

Wed, 07 Dec 2011 06:11:06 +0000

We actually have code working that does what your trying to do (capture PPM servo inputs)... It looks very similar to what your proposing. We are having internal discussions on whether we should open source the code. I think we probably will, but we are not ready to today.

Keep up the effort and let me know if you have any more questions.

Jacob


KurtE

Sat, 10 Dec 2011 00:56:07 +0000

Any update on this?

I have been playing around some with the SoftwareSerial class that is now part of 1.0 Arduino. I have some output working reasonably well on Uno32 and thought I might take a stab at the Change Notify. Will probably first need to build a table with which pins map to which Change Notify... Not sure what to do about a few pins that change depending on jumpers. Than again I have not looked up what is done with normal calls like digitalWrite to these pins.

Anyone have a program with the set up the interrupt, clearing appropriate bits when it happens? Otherwise I may try to stumble through.

Kurt


avenue33

Mon, 12 Dec 2011 16:22:11 +0000

I raised a ticket about SoftwareSerial (formerly NewSoftSerial) on early August 2011: see #64 on the GitHub issues repository for chipKIT.

Good luck with the implementation :!:


KurtE

Mon, 12 Dec 2011 17:36:20 +0000

Thanks,

As I mentioned in a different thread, I now have output working at least for the few baud rates I defined (9600,38400,115200,125000 and maybe 2400). I also have a test program that maps the arduino pin to change notify pin and a simple interrupt handler that simply shows the state of that pin on the led... Noe integrating it into the serial class. Still slow at this as I am always trying to remember where I read something. Like which macros are defined to disable interrupts and remember the previous state and the second one that restores it.

Also looking over the Arduino 1.0 version of software serial, I am not sure about the listen implementation as it simply clears the one buffer and sets this pointer, but does not update which pin change pins are active, so I believe that any character received on any software serial receive pin will try to be read by the active one at its baud rate. Thinking mine will only have one active pin at a time.

Kurt


Jacob Christ

Mon, 12 Dec 2011 17:42:05 +0000

Kurt,

What would be really slick is if you can support different baud rates on each pin.

Jacob


avenue33

Mon, 12 Dec 2011 18:24:06 +0000

The NewSoftSerial author, Mikal Hart clearly stated that

It occurred to me, though, that multiple instances could still be possible if the library user were willing to make a small concession. NewSoftSerial is written on the principle that you can have as many devices connected as resource constraints allow, as long as you only use one of them at a time. If you can organize your program code around this constraint, then NewSoftSerial may work for you.

The :arrow: beta release 11 has a separate file icrmacros for all the low level stuff.

Maybe Mikal Hart, if asked, could help you :!:


KurtE

Mon, 12 Dec 2011 19:20:44 +0000

Yep, they do support different baud rates on different pins. Just that you can only have one active receive pin at a time. I was thinking of enforcing this by only have the pin associated with the Listen to be the only one whose pin change interrupt is active...

That is to change the code to look something like:

bool SoftwareSerial::listen()
{
  if (active_object != this)
  {
    _buffer_overflow = false;
    unsigned int int_status;
    int_status=INTDisableInterrupts();
    _receive_buffer_head = _receive_buffer_tail = 0;
    active_object = this;
    cnen = (p32_regset *)&amp;CNEN;   //enable the specific bit...
    cnen-&gt;reg = 1 &lt;&lt; (g_abMapArduinoPinToCNPin[_receivePin]);   //enable the specific     
    INTRestoreInterrupts(int_status);
    return true;
  }

  return false;
}

Note: I think I found the macros I mentioned. This was updated on the fly so not tested. Also my table is specific to UNO32. In theory the mapping table should be part of the board specific defines. Can also be used to enable PU resistors.

Kurt


Jacob Christ

Mon, 12 Dec 2011 20:02:01 +0000

I pretty sure it would not be to hard to support receiving from multiple pins simultaneously, just would need a Rx buffer for each port.

Jacob


KurtE

Mon, 12 Dec 2011 21:37:19 +0000

I totally agree that adding an Rx buffer to each instance of the class and having the interrupt function, try to figure out which IO pin(s) changed and then map that to the appropriate SoftwareSerial instance that would do the actual read of the byte. There may be some issues to figure out to do that: a) What IO pin changed. Don't know of any Status register that shows this, I believe I would need to keep the current state of all of the IO ports that have CN pins on it, which include B, C, D, F, G. Then at the interrupt read in the status and compare it with your saved values b) Overlapping. Currently SoftwareSerial is a simple bit-bang set of functions, that chooses to do with interrupts disabled. This helps to make sure that your serial output (or input) is not corrupted, But bad for other things, like maybe missing interrupts for hardware serial ports and losing data... I am still learning about the Pic32 architecture so not sure it things are set up such that these interrupts are at a higher priority and will still happen.

So for now, for my own use, I will be happy to just to get it running for one input, with the ability to switch... But later maybe...

So far my use of the Uno32 is only an experiment. Still spending most of my time and energy on other platforms. But I do like the possibility of speed.

Also short term may also add in the ability to only have output Serial ports or input only serial ports... As may use one for a Serial LCD or maybe a compass stream.

Kurt


docwelch

Wed, 14 Dec 2011 03:45:38 +0000

Fair warning: I have not had the opportunity to test all of this. I am putting it out because there was a question if any progress had been made. This code uses PortB but there should not be a problem using other ports. I picked portB because I needed 5 bits (RC channels) to check and only wanted to have to read a single port to find out which one triggered the interrupt. The other ports only have 4 bits where a CN can be detected.

setup code: //variables for the ISR unsigned long newData, oldData, changed; unsigned long pulseTime; unsigned long start[5]; unsigned long pulseLength[5];

//Set the appropriate pins to input //On the Arduino, you don't have to do anything special to set Analog pins to //digital - I do not know if this is the case with the PIC32. The next line sets the //PIC32 analog pins (RB1-RB5) to digital - I don't know for sure if it has to be //done. AD1PCFGSET= (1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5); pinMode(41, INPUT); // Throttle pinMode(A0, INPUT); // Roll pinMode(A6, INPUT); // Pitch pinMode(A1, INPUT); // Yaw pinMode(A7, INPUT); // Mode

// turn off CPU interrupts (from wiring.h) noInterrupts();

CNCONSET=0x00008000; //turn on change notice

//Enable specific pins for interrupt CNENSET=(1 <<3) | //pin 41 (RB1,CNEN3) (1<<4) | //pin A0 (RB2,CNEN4) (1<<5) | //pin A6 (RB3,CNEN5) (1<<6) | //pin A1 (RB4,CNEN6) (1<<7) ; //pin A7 (RB5,CNEN7)

PORTB; //Read PortB to clear any mismatch

//Set the priority and subpriority of the interrupt //Priority can be 1-7 (7 being highest priority) (bits 18-20) //Subpriority can be 1-3 (bits 16-17) IPC6SET=0x140000 | 0x30000; //arbitrarily set to 5 and 3

IFS1CLR = 0x01; //Clear the interrupt flag bit

IEC1SET=0x01; //Enable the interrupt enable bit

//enable CPU interrupts (from wiring.h) interrupts();

The ISR code:

#ifdef __cplusplus extern "C" { #endif

void __ISR(_CHANGE_NOTICE_VECTOR, single) ChangeNoticeHandler(void) { pulseTime=micros(); newData=PORTB; //read PORTB (clears mismatch condition) changed=newData^oldData;

//determine pulse length if (changed&1<<1) //Throttle if (newData &&1<<1) start[0]=pulseTime; else pulseLength[0]=pulseTime-start[0];

if (changed&1<<2) //Roll if (newData &&1<<2) start[0]=pulseTime; else pulseLength[0]=pulseTime-start[0];

if (changed&1<<3) //Pitch if (newData &&1<<3) start[0]=pulseTime; else pulseLength[0]=pulseTime-start[0];

if (changed&1<<4) //Yaw if (newData &&1<<4) start[0]=pulseTime; else pulseLength[0]=pulseTime-start[0];

if (changed&1<<5) //Mode if (newData &&1<<5) start[0]=pulseTime; else pulseLength[0]=pulseTime-start[0];

IFS1CLR=0x01; //lastly, clear the interrupt status flag }

#ifdef __cplusplus } #endif

I hope this helps someone. If it doesn't work, sorry.

I am pursuing this because I am building a controller for a quadcopter. I have used similar code on an Arduino and it works very well for reading servo output pulses from an RC receiver. As I start to make more progress on the project, I will be posting in the "What are you developing" section.


docwelch

Wed, 14 Dec 2011 04:21:54 +0000

Just realized an error in the ISR code. Below is the corrected:

#ifdef __cplusplus extern "C" { #endif

void __ISR(_CHANGE_NOTICE_VECTOR, single) ChangeNoticeHandler(void) { pulseTime=micros(); newData=PORTB; //read PORTB (clears mismatch condition) changed=newData^oldData;

//determine pulse length if (changed&1<<1) //Throttle if (newData&1<<1) start[0]=pulseTime; else pulseLength[0]=pulseTime-start[0];

if (changed&1<<2) //Roll if (newData&1<<2) start[0]=pulseTime; else pulseLength[0]=pulseTime-start[0];

if (changed&1<<3) //Pitch if (newData&1<<3) start[0]=pulseTime; else pulseLength[0]=pulseTime-start[0];

if (changed&1<<4) //Yaw if (newData&1<<4) start[0]=pulseTime; else pulseLength[0]=pulseTime-start[0];

if (changed&1<<5) //Mode if (newData&1<<5) start[0]=pulseTime; else pulseLength[0]=pulseTime-start[0];

IFS1CLR=0x01; //lastly, clear the interrupt status flag }

#ifdef __cplusplus } #endif


KurtE

Wed, 14 Dec 2011 05:35:40 +0000

Thanks for sharing. Looks nice. My guess is there may be some minor bugs still with the ISR, in that all of the different pins all set start[0] and pulseTime[0]. But is looks nice and simple which is great.

Thanks again Kurt


docwelch

Wed, 14 Dec 2011 13:03:31 +0000

Kurt, You are correct. They should each use a different element in the array. Copy and paste can be dangerous!


Jacob Christ

Mon, 16 Apr 2012 02:26:15 +0000

We just posted the chipKIT source for the PONTECH UAV100 to github today:

https://github.com/pontech/uav100

Product details:

http://www.pontech.com/details/138

In this code we are capturing 8 PPM signals with 15-bit resolution using the CN interrupts.

This may help some people trying to do the same.

Jacob