Go Down

Topic: Help with Interrupts and PWM read code. (Read 1 time) previous topic - next topic

kenl

Aug 26, 2016, 01:49 am Last Edit: Aug 26, 2016, 10:11 am by kenl
Can someone please help me with the following code, it was written to read 4 channels from a RC receiver, I have tried to extend the code to read 8 channels but it won't read past the fifth channel.

The author of the original code pointed to Chapter 13 of the atMega328 data sheet for help with the PINB & B00000010 part of the code where I think my problem lies, but I haven't been able to work it out.

Any help would be gratefully appreciated.

Ken


Code: [Select]
//code by Kamran A on you tube

unsigned long timer[9];
byte last_channel[8];
int input[8];

void setup() {
  PCICR |= (1 << PCIE0);
  PCMSK0 |= (1 << PCINT0);//pin 8...Rx Channel 1
  PCMSK0 |= (1 << PCINT1);//pin 9...Rx Channel 2
  PCMSK0 |= (1 << PCINT2);//pin 10...Rx Channel 3
  PCMSK0 |= (1 << PCINT3);//pin 11...Rx Channel 4
  PCMSK0 |= (1 << PCINT4);//pin 12...Rx Channel 5
  PCMSK0 |= (1 << PCINT5);//pin 5...Rx Channel 6
  PCMSK0 |= (1 << PCINT22);//pin 6...Rx Channel 7
  PCMSK0 |= (1 << PCINT23);//pin 7...Rx Channel 8
  Serial.begin(9600);
}

void loop() {
  print();
}

ISR(PCINT0_vect) {
  timer[0] = micros();
  // channel 1 ---------------
  if(last_channel[0] == 0 && PINB & B00000001 ) {
    last_channel[0] = 1;
    timer[1] = timer[0];
  }
  else if(last_channel[0] == 1 && !(PINB & B00000001) ) {
    last_channel[0] = 0;
    input[0] = timer[0] - timer[1];
  }
  
  // channel 2 ---------------
  if(last_channel[1] == 0 && PINB & B00000010 ) {
    last_channel[1] = 1;
    timer[2] = timer[0];
  }
  else if(last_channel[1] == 1 && !(PINB & B00000010) ) {
    last_channel[1] = 0;
    input[1] = timer[0] - timer[2];
  }
  
  // channel 3 ---------------
  if(last_channel[2] == 0 && PINB & B00000100 ) {
    last_channel[2] = 1;
    timer[3] = timer[0];
  }
  else if(last_channel[2] == 1 && !(PINB & B00000100) ) {
    last_channel[2] = 0;
    input[2] = timer[0] - timer[3];
  }
  
  // channel 4 ---------------
  if(last_channel[3] == 0 && PINB & B00001000 ) {
    last_channel[3] = 1;
    timer[4] = timer[0];
  }
  else if(last_channel[3] == 1 && !(PINB & B00001000) ) {
    last_channel[3] = 0;
    input[3] = timer[0] - timer[4];
  }
  
  // channel 5 ---------------
  if(last_channel[4] == 0 && PINB & B00010000 ) {
    last_channel[4] = 1;
    timer[5] = timer[0];
  }
  else if(last_channel[4] == 1 && !(PINB & B00010000) ) {
    last_channel[4] = 0;
    input[4] = timer[0] - timer[5];
  }
  
  // channel 6 ---------------
  if(last_channel[5] == 0 && PINB & B00100000 ) {
    last_channel[5] = 1;
    timer[6] = timer[0];
  }
  else if(last_channel[5] == 1 && !(PINB & B00100000) ) {
    last_channel[5] = 0;
    input[5] = timer[0] - timer[6];
  }
  
  // channel 7 ---------------
  if(last_channel[6] == 0 && PINB & B01000000 ) {
    last_channel[6] = 1;
    timer[7] = timer[0];
  }
  else if(last_channel[6] == 1 && !(PINB & B01000000) ) {
    last_channel[6] = 0;
    input[6] = timer[0] - timer[7];
  }
  
  // channel 8 ---------------
  if(last_channel[7] == 0 && PINB & B10000000 ) {
    last_channel[7] = 1;
    timer[8] = timer[0];
  }
  else if(last_channel[7] == 1 && !(PINB & B10000000) ) {
    last_channel[7] = 0;
    input[7] = timer[0] - timer[8];
  }

}

void print() {
  Serial.print(input[0]);
  Serial.print(" - ");
  Serial.print(input[1]);
  Serial.print(" - ");
  Serial.print(input[2]);
  Serial.print(" - ");
  Serial.print(input[3]);
  Serial.print(" - ");
  Serial.print(input[4]);
  Serial.print(" - ");
  Serial.print(input[5]);
  Serial.print(" - ");
  Serial.print(input[6]);
  Serial.print(" - ");
  Serial.println(input[7]);
}

 

zhomeslice

This is my code to attach interrupts to pins for RC controllers it should allow you to have as many inputs as there are pins.

to attach a pin use
Code: [Select]
pciSetupRC(PinNumger);

there is two additional function 
 Alive() see if we are getting new values
Print(uint16_t SpamDelay) shows you how to get the values from the array


This is to help you with getting the additional pins the code came from a larger class i created and may not be 100% complete although it works as a class




Code: [Select]
volatile uint8_t  GroupStartPin[3]  = {0,8,14};
volatile uint8_t  GroupEndPin[3]    = {7,13,19};
union Mask{
volatile uint32_t All;
volatile uint8_t  Port[4];
};

Mask Now;
volatile uint32_t Changed; //volatile uint8_t  mask[3]; // Place holder for general Mask Calculations
volatile uint32_t M;    //Localizes altered Mask
volatile uint32_t PCintLast; //volatile uint8_t  PCintLast[3]; // looking fo pin changes using bianary
volatile uint8_t  PinArray[20];                 // Key = Pin Val = Position List of pins that could be used as ping inputs:
volatile uint8_t  PinNumber[22] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, -1, -1, 14, 15, 16, 17, 18, 19};                 // Key = Bit Val = Position List of pins that could be used as ping inputs:
volatile uint8_t  BitNumber[20] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21};                 // Key = Pin Val = Position List of pins that could be used as ping inputs:
volatile uint16_t RCedgeTime[20];      // Pulse HIGH start time for RCInputs & PingInputs

#define RCInputs 3
volatile uint32_t rcPinMask; //volatile uint8_t  rcPinMask[3]; // RC Pin Mask for active RC Pins
volatile uint16_t rcValue[RCInputs];            // interval [1000;2000]
volatile uint16_t rcValueX[RCInputs];           // interval [0;65535]  
volatile uint16_t rcValueMin[RCInputs];         //  
volatile uint16_t rcValueMax[RCInputs];         //
volatile uint16_t rcValueCenter[RCInputs];      //  
volatile uint32_t AliveBits = 0;                // a reading Since Last Test,
volatile int8_t   RCIsAlive = 0;
volatile bool     FailSafe = true;
volatile uint8_t  RCPinCtr = 0;                 // Number of Pins Activated
volatile uint16_t RCEdgeTime[RCInputs];      // Pulse HIGH start time for RCInputs & PingInputs

static inline void RCTimers(uint16_t cTime) {
//cTime Note micros() return a uint32_t, but it is not usefull to keep the whole bits => we keep only 16 bits
M = Changed & rcPinMask;// only keep RC Data
if(!M)return;                    //No RC Data move on
for (uint8_t Position = 0; (Position < RCPinCtr); Position++) { // Cycle throug all the pins
uint8_t Bit = BitNumber[PinNumber[Position]];
if (M >> Bit & 1) {        // See if the pin has changed
if ((Now.All >> Bit & 1)) RCEdgeTime[Position] = cTime; //Pulse went HIGH store the start time
else {                         // Pulse Went low calculate the duratoin
uint16_t dTime = cTime - RCEdgeTime[Position]; // Calculate the change in time
if (900 < dTime && dTime < 2100){    // Time is within proper RC range
rcValue[Position] = dTime; // only save proper PWM Pulse
rcValueMin[Position] = min(rcValueMin[Position], dTime); // only save proper PWM Pulse
rcValueMax[Position] = max(rcValueMax[Position], dTime); // only save proper PWM Pulse
}
rcValueX[Position] = dTime; // Lets Store any duration up to 65535 micro seconds
AliveBits = AliveBits | 1 << Position;
}
}
}
}

// port change Interrupt
ISR(PCINT2_vect) { //this ISR pins 0-7
Now.Port[0] = PIND; // 0-7
Now.Port[1] = PINB; // 8-13
Now.Port[2] = PINC; // A0-A6
Changed = Now.All ^ PCintLast;// doing a ^ between the current interruption and the last one indicates wich pin changed
uint16_t cTime = micros();    // micros() return a uint32_t, but it is not usefull to keep the whole bits => we keep only 16 bits
sei();                        // re enable other interrupts at this point, the rest of this interrupt is not so time critical and can be interrupted safely
RCTimers(cTime);        // Calculate RC Data
PCintLast = Now.All;          // we memorize the current state of all PINs in group

}




ISR(PCINT0_vect) { //this ISR is common to every receiver channel, it is call everytime a change state occurs on a pins 8-13 only need 9-11
Now.Port[0] = PIND; // 0-7
Now.Port[1] = PINB; // 8-13
Now.Port[2] = PINC; // A0-A6
Changed = Now.All ^ PCintLast;// doing a ^ between the current interruption and the last one indicates wich pin changed
uint16_t cTime = micros();    // micros() return a uint32_t, but it is not usefull to keep the whole bits => we keep only 16 bits
sei();                        // re enable other interrupts at this point, the rest of this interrupt is not so time critical and can be interrupted safely
RCTimers(cTime);      // Calculate RC Data
PCintLast = Now.All;          // we memorize the current state of all PINs in group
}

ISR(PCINT1_vect) { //this ISR s A0~A5
Now.Port[0] = PIND; // 0-7
Now.Port[1] = PINB; // 8-13
Now.Port[2] = PINC; // A0-A6
Changed = Now.All ^ PCintLast;// doing a ^ between the current interruption and the last one indicates wich pin changed
uint16_t cTime = micros();    // micros() return a uint32_t, but it is not usefull to keep the whole bits => we keep only 16 bits
sei();                        // re enable other interrupts at this point, the rest of this interrupt is not so time critical and can be interrupted safely
RCTimers(cTime);        // Calculate RC Data
PCintLast = Now.All;          // we memorize the current state of all PINs in group
}

SIGNAL(TIMER0_COMPA_vect){ // Timer to check for signal loss
sei();
static int MSec=0;
if(MSec++ < 10)return; // Check every 10 Miliseconds for change
MSec = 0;
RCIsAlive = (AliveBits > 0) ?   RCIsAlive+1 : RCIsAlive-1  ;
RCIsAlive = constrain(RCIsAlive,-20,+20);// Must have 20 tests good or 20 tests bad for RCIsAlive to change states
if ((RCIsAlive <= 0) && FailSafe){
for (uint8_t c = 0;c < RCPinCtr ;c++) rcValue[c] = rcValueCenter[c];
}
AliveBits = 0;
}

void pciSetupRC(uint8_t pin){
if(bitRead(PinLock,BitNumber[pin])) return;
bitWrite(PinLock, BitNumber[pin], 1);
bitWrite(rcPinMask, BitNumber[pin], 1);
pciSetup( pin,  INPUT);
PinArray[pin] = RCPinCtr;
PinNumber[RCPinCtr] = pin;
rcValue[RCPinCtr] = 1500;
rcValueMin[RCPinCtr] = 1100;
rcValueMax[RCPinCtr] = 1900;
rcValueCenter[RCPinCtr] = 1500;
RCPinCtr++;
}



void pciSetup(uint8_t pin, uint8_t InputType){
pinMode(pin, InputType);// enable interrupt for pin...
*digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin));  // enable pin
PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
}

uint8_t Alive() {
return(RCIsAlive > 0);
}

void Print(uint16_t SpamDelay) {
     static unsigned long _ATimer;
     if ((unsigned long)(millis() - _ATimer) >= (t) ){ // print after SpamDelay miliseconds (No Windup)
_               ATimer = millis();
if (RCPinCtr){
if(!Alive()) {
Serial.print("No Signal ");
} else {
for (int Ctr = 0; Ctr < RCPinCtr; Ctr++) {
Serial.print(Ctr);
Serial.print(F("-"));
Serial.print(PinNumber[Ctr]);
Serial.print(F(": "));
Serial.print(PosTime(Ctr,0));
Serial.print(F("\t"));
}
}
}


Serial.println();
}
}


HC

Isaac96

#2
Aug 26, 2016, 04:54 am Last Edit: Aug 26, 2016, 04:55 am by Isaac96
Here is some code for 6 channell using the enableInterrupt library, which is in the Library Manager in the IDE.
Code: [Select]

#include <EnableInterrupt.h>
#define RX_PIN_THROTTLE  2 //input pins
#define RX_PIN_ROLL  3 
#define RX_PIN_PITCH  4   
#define RX_PIN_YAW  5     
#define RX_PIN_AUX1  6   
#define RX_PIN_AUX2  7
PROGMEM const byte rxPins[6]={//makes the pinmode easier
  RX_PIN_YAW,RX_PIN_ROLL,RX_PIN_PITCH,RX_PIN_AUX1,RX_PIN_AUX2,RX_PIN_THROTTLE};
volatile byte rxState[4]={
  0,0,0,0};
volatile int rxPrev[6]={
  0,0,0,0,0,0};

void rxInit(){
  for(byte i=0;i<6;i++){
    pinMode(pgm_read_byte(&rxPins[i]),INPUT);
    digitalWrite(pgm_read_byte(&rxPins[i]),HIGH);
  }
  enableInterrupt(RX_PIN_THROTTLE,rxGoesHigh1,RISING);
  enableInterrupt(RX_PIN_ROLL,rxGoesHigh2,RISING);
  enableInterrupt(RX_PIN_PITCH,rxGoesHigh3,RISING);
  enableInterrupt(RX_PIN_YAW,rxGoesHigh4,RISING);
  enableInterrupt(RX_PIN_AUX1,rxGoesHigh5,RISING);
  enableInterrupt(RX_PIN_AUX2,rxGoesHigh6,RISING);
}
void rxGoesHigh1(){
  enableInterrupt(RX_PIN_THRoTTLE,rxGoesLow1,FALLING);
  rxPrev[0]=micros();
}
void rxGoesLow1(){
  enableInterrupt(RX_PIN_THROTTLE,rxGoesHigh1,RISING);
  rxVal[0]=micros()-rxPrev[0];
}
void rxGoesHigh2(){
  enableInterrupt(RX_PIN_ROLL,rxGoesLow2,FALLING);
  rxPrev[1]=micros();
}
void rxGoesLow2(){
  enableInterrupt(RX_PIN_ROLL,rxGoesHigh2,RISING);
  rxVal[1]=micros()-rxPrev[1];
}
void rxGoesHigh3(){
  enableInterrupt(RX_PIN_PITCH,rxGoesLow3,FALLING);
  rxPrev[2]=micros();
}
void rxGoesLow3(){
  enableInterrupt(RX_PIN_PITCH,rxGoesHigh3,RISING);
  rxVal[2]=micros()-rxPrev[2];
}
void rxGoesHigh4(){
  enableInterrupt(RX_PIN_YAW,rxGoesLow4,FALLING);
  rxPrev[3]=micros();
}
void rxGoesLow4(){
  enableInterrupt(RX_PIN_YAW,rxGoesHigh4,RISING);
  rxVal[3]=micros()-rxPrev[3];
}
void rxGoesHigh5(){
  enableInterrupt(RX_PIN_AUX1,rxGoesLow5,FALLING);
  rxPrev[4]=micros();
}
void rxGoesLow5(){
  enableInterrupt(RX_PIN_AUX1,rxGoesHigh5,RISING);
  rxVal[4]=micros()-rxPrev[4]; 
}

void rxGoesHigh6(){
  enableInterrupt(RX_PIN_AUX2,rxGoesLow6,FALLING);
  rxPrev[5]=micros();
}
void rxGoesLow6(){
  enableInterrupt(RX_PIN_AUX2,rxGoesHigh6,RISING);
  rxVal[5]=micros()-rxPrev[5]; 
}

The values are storeed in rxVal[0-5] in order TAER12
It should be fairly easy to expand.
Do not PM me for help. I will delete immediately.
CONNECT THE GROUNDS!

After Tuesday, even the calendar goes W T F

cattledog

Take a look at the Arduino Uno pin out diagram

It's not simple to get all 8 pins on one Register with a Uno. .

PortD has 8 pins broken out, has the serial RX and TX pins which you want to use in your sketch. You may be able to load the sketch and use SoftwareSerial for the output.

PortB which you are trying to use, does not break out PB6 and PB7 on the UNO, and if you wanted to hack those pins on the ATmega 328 processor, the external oscillator is connected to them.

PortC only has 6 pins broken out.

I think you can do what you want on a Mega or else possible use on of the pin change interrupt libraries as suggested.


nickgammon

Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

kenl

Hmm.. I did mean PWM

I will try it on my Mega and see if I can get it to run there, in the mean time the alternative codes provided will be on my to do list this week end! Many thanks  :)

I am using a Nano to run my test code on but the available pins are much same as the Uno, I have learned something already! Despite having studied pin out diagrams many times I had not noticed the PB, PC and PD nor had I related them to being different ports, whatever that means  :smiley-confuse:

Thank you for your help!!

Ken

nickgammon

That code looks rather lengthy for what it is achieving. Hasn't the author heard of loops?

My page about Interrupts may help you.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

nickgammon

In particular see the example code for pin change interrupts:

Code: [Select]
ISR (PCINT0_vect)
 {
 // handle pin change interrupt for D8 to D13 here
 }  // end of PCINT0_vect

ISR (PCINT1_vect)
 {
 // handle pin change interrupt for A0 to A5 here
 }  // end of PCINT1_vect

ISR (PCINT2_vect)
 {
 // handle pin change interrupt for D0 to D7 here
 }  // end of PCINT2_vect


void setup ()
  {
  // pin change interrupt (example for D9)
  PCMSK0 |= bit (PCINT1);  // want pin 9
  PCIFR  |= bit (PCIF0);   // clear any outstanding interrupts
  PCICR  |= bit (PCIE0);   // enable pin change interrupts for D8 to D13
  }



And there is a table mapping pins to registers:

Code: [Select]
D0   PCINT16 (PCMSK2 / PCIF2 / PCIE2)
D1   PCINT17 (PCMSK2 / PCIF2 / PCIE2)
D2   PCINT18 (PCMSK2 / PCIF2 / PCIE2)
D3   PCINT19 (PCMSK2 / PCIF2 / PCIE2)
D4   PCINT20 (PCMSK2 / PCIF2 / PCIE2)
D5   PCINT21 (PCMSK2 / PCIF2 / PCIE2)
D6   PCINT22 (PCMSK2 / PCIF2 / PCIE2)
D7   PCINT23 (PCMSK2 / PCIF2 / PCIE2)
D8   PCINT0  (PCMSK0 / PCIF0 / PCIE0)
D9   PCINT1  (PCMSK0 / PCIF0 / PCIE0)
D10   PCINT2  (PCMSK0 / PCIF0 / PCIE0)
D11   PCINT3  (PCMSK0 / PCIF0 / PCIE0)
D12   PCINT4  (PCMSK0 / PCIF0 / PCIE0)
D13   PCINT5  (PCMSK0 / PCIF0 / PCIE0)
A0   PCINT8  (PCMSK1 / PCIF1 / PCIE1)
A1   PCINT9  (PCMSK1 / PCIF1 / PCIE1)
A2   PCINT10 (PCMSK1 / PCIF1 / PCIE1)
A3   PCINT11 (PCMSK1 / PCIF1 / PCIE1)
A4   PCINT12 (PCMSK1 / PCIF1 / PCIE1)
A5   PCINT13 (PCMSK1 / PCIF1 / PCIE1)


So you want two or three ISRs rather than one, and handle the appropriate pin in each one. (See the example code above).
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

Go Up