Help with PWM signal

Status
Not open for further replies.

turtle9er

Well-known member
Hi Everyone,

I should start out by explaining the project i'm working on. I am a masters student working on my thesis and for my project I am trying to implement a cycle ergometer with the ability to measure every 6 degrees that the pedal moves. Along with this the bike already has a special crank arm on it that can measure force (company is called SRM). The way this bike works is that the force is measured within the rotating crank, to get the data out of the crank (since you can't use a wire since rotating) the signal converted to a PWM (hz and cadence) signal that is then sent to a display unit on the bike. I am able to connect to this unit and read cadence and hz separately. I am running into issues reading this hz signal. I thought I could just measure the time between rising edges, however I am getting a lot of crazy signals (see attached image, around 1.2seconds worth of data). My area of study is actually exercise physiology and like to play with electronics, however this is beyond me and was hoping I could get a bit of guidance. Attached is an image of the output I see, you can see how it should look, like a sine wave due to nature of how power is produced during each down stroke of the leg. Also is an image from the SRM manual that shows how the data flows. Also is my code, which is working perfect with the two optical slotted switches that measure when a tooth goes by (60 teeth total) or when an index hole goes by. Right now all the data is being read via serial into labview where I do all the calculations. I was wondering if I should use pulseIn? I am sure more information will be needed from my end, just let me know and I will try to get it for you. Hope someone can at least lead me in a better direction. Thanks.
Code:
const byte hzPin = 2; // HZ
const byte hzInterrupt = 2; // = pin 2
const byte degPin = 3; // Chain ring interrupt
const byte degInterrupt = 3; // = pin 3
const byte cPin = 4; // Cadence interrupt
const byte cInterrupt = 4; // = pin 4
const int BUFFERSIZE=20;
int HZ[BUFFERSIZE];
int degnumber = 0;
int cadence = 0;
int start;
int startDT;
int DT;
int startHT;
int HT;
int elapsed;
int lastlevel=0;
long HZ2;
volatile byte i=0;
volatile byte bufind=0;
volatile  int  encoder0Pos = 0; //Degree Time sensor position
volatile  int  index1=-1; //Count total pedal revolutions
volatile unsigned int count1=0;
volatile int value=0;
boolean DTready = false;

void setup(void) 
{ 
  Serial.begin(115200); 
  pinMode(hzPin, INPUT); 
  pinMode(degPin, INPUT); 
  pinMode(cPin, INPUT); 
  attachInterrupt(hzInterrupt, counterhz, RISING);
  attachInterrupt(degInterrupt, counterdeg, RISING);
  attachInterrupt(cInterrupt, doIndex, RISING);
}

void loop(void) 
{ 
  if (Serial.available()>0){ //reset index to -1
    Serial.read();
    index1 = -1;
    Serial.flush();
  }
  if(DTready == true ){
    Serial.print ("I:");
    Serial.print (index1);
    Serial.print ("DT:");
    Serial.print (DT);
    Serial.print ("CT:");
    Serial.print (elapsed);
    Serial.print ("P:");
    Serial.print (encoder0Pos);
    Serial.print ("HZ:");

    for (i=0; i<bufind; i++) { //print HZ array
      Serial.print(HZ[i]); 
      if (i<bufind-1){
        Serial.print(",");
      }
    }
    Serial.println();
    i=0;
    memset(HZ,0,sizeof(HZ));
    bufind=0;
    DTready=false;
  }

}

void doIndex() { //Cadence
  encoder0Pos=0;            //the encoder Home position will equal the current encoder position.
  elapsed=millis()-start;
  start=millis(); 
  index1++;
}

void counterhz() //Frequency
{ 
  HT=micros()-startHT;
  startHT=micros();
  HZ2=1000000 / HT;
  if ( bufind < BUFFERSIZE ) { //fill array with all HZ per chaing ring tooth
    HZ[bufind++] = HZ2;
  }
}

void counterdeg()// Degree Time
{ 
  DT=micros()-startDT;
  startDT=micros(); 
  DTready = true;
  encoder0Pos+=6;
}
Raw_HZ.JPG
SRM_workings.JPG
 
If you're using Teensy 2.0 or Teensy++ 2.0, you could try the FreqMeasure library (it has not yet been ported to Teensy 3.0, but I am planning to do that within the next few months).

http://www.pjrc.com/teensy/td_libs_FreqMeasure.html

This may or may not solve the problem.... but it's a very well tested library, so it's worth a shot.

If the problem persists, it may be a problem with the actual signal, probably requiring analog filtering before you measure, or software to clean up the results after measuring. But first just try FreqMeasure and see what sort of results you get.
 
Sorry, guess I should of stated that I am using the teensy 3. The issues is the frequency changes often, so in one pedal revolution it can change up to 1000 times. Most of the HZ measure programs measure over a set time. How would I implement the pulseIn function, does it work as well, or just as easy to use the micro timer? I was also thinking of taking every 2-4 measurements and average those, however right now the data prints out each time a tooth goes by, so there could be 20 HZ samples or there could only be 10 samples depending on how fast the person is pedaling and how low the HZ is.
 
It sounds like you need to determine where the 'noise' comes from -- perhaps the signal from the V-F converter doesn't have sharp edges, and therefore the Teensy double- (or more) counts the edges. You might be able to put an 'ignore' in the interrupt routine -- basically, if a new edge arrives unexpectedly close (say 10 us) to the previous one, just ignore it.

Of course strain gauges have very small signals, and you would need to confirm that it's output is clean. If it isn't perhaps the V-F converter is getting confused ? If you can modify that circuit, try putting a capacitor (don't know your circuit, so I can't recommend a value; perhaps 0.1 uF) from the output of the opamp to GND.
 
I don't exactly know how this piece of equipment works, but this is my understanding. On the bike the load cells voltage is converted to a frequency and then sent via a sensor wire to the display unit on the bike. Along with the HZ signal, there is a signal for cadence, this signal is demodulated within the display unit and it does its own calculations to give speed, cadence and power. Now the unit I have is special in the fact that the demodulated signal is also directly available to me via another connection on the back. This connection has 3 wires, a ground, the cadence signal and the HZ signal. I am assuming this signal is already filtered before it is sent out. Right now the bike operates by plugging into a super old laptop with a 25 pin connector and the HZ is recorded using DOS. One reason for what I am doing is the fact that this piece of equipment calculates power output by averaging all the HZ that is sees during one pedal revolution and then using the cadence signal to determine angular velocity. Now this means they are assuming that a cyclist pedals with a constant angular velocity. So I want to measure the change in HZ for each 6 deg segment of the pedal, and use that angular velocity to calculate power. Sorry, this is getting long winded. Guess bottom line is I am not sure how I would clean the signal. Could I just put a 0.1uf between the hz cable and its ground, which goes to the teensy ground. Can they share a common ground (this bike is getting power from a special power supply, which also powers the display unit).
Also thinking about how to program the ignore function, would I just add something to the If statement to only add the HZ to the array if below a certain value? Are there any issues with how my interrupts are interacting. Do I need to do anything in my main loop, read something about turning interrupts off and then back on. Like I said, I am new to most of this, so learning as I go, however any suggestions on how I can improve my code that would be great. Thanks again for the input.
 
From the schematic, It looks to me that the output signal from the acquisition unit is PWM, not variable frequency. Are you sure that the modified demodulator unit is converting this signal or is it also outputting a PWM signal to your teensy?
 
I'll hook up a function generator here if the problem persists.... but first, could you please try editing this code:

Code:
    for (i=0; i<bufind; i++) { //print HZ array
      Serial.print(HZ[i]); 
      if (i<bufind-1){
        Serial.print(",");
      }
    }
    Serial.println();
    i=0;
    memset(HZ,0,sizeof(HZ));
    bufind=0;
    DTready=false;

This code is reading (and writing) variables that are written by the interrupt, without disabling the interrupt. That's a big red flag! Rarely is that a good idea.

Please rewrite this. Use noInterrupts(), then copy the "bufind" to a local variable, and copy the array to a local array. Then set them to zero, clear the flag and finish all read/write access to any variables used by the interrupt code. Only reenable interrupts with interrupts() after you've finished the all copy and zero operations. After you've made a local copy of everything and turned interrupts back on, then print the data.

Odds are good that may fix your problem.

If that doesn't work, please post the complete new code, and I'll give it a try here with a clean waveform from a function generator.
 
Unfortunately the documentation on this device is not great. The best information is from that image from my original post. I assume that the PWM is demodulated, however I am not sure what it does after that. The cadence signal is 0-5V logic signal, and the hz seems the same when I look at it with an oscilloscope.
Paul- I will see what I can do, like I said, new to all this so might take a bit of time to actually figure out what you actually wrote. I found this website, http://rcarduino.blogspot.co.uk/2012/04/how-to-read-multiple-rc-channels-draft.html and I think it may help me get on the right path.

Thanks again, and will post back once I figure out the coding.
 
Yes, that page looks pretty good.

However, I would not recommend their approach for bit flags. Disable interrupts for accessing those too.
 
So been playing around and re wrote the code, it is not working correct yet. The main issue is the HZ array, I am not sure how to clear it, or which array to clear after I copy the array over. Plus when I just used [] when copying my interrupt array to my local array it gave me an error.
Here is a bit of the output right now.
Code:
I:10DT:45209CT:1051P:288HZ:0,0,0,0,0,0,0,673,675,668,1946,1978,1558,1904,1266,678,676,677,680,674
I:10DT:48903CT:1051P:294HZ:0,0,0,0,0,0,0,673,675,668,1946,1978,1558,1904,1266,678,676,677,680,674
I:10DT:50366CT:1051P:300HZ:0,0,0,0,0,0,0,673,675,668,1946,1978,1558,1904,1266,678,676,677,680,674
I:10DT:52629CT:1051P:306HZ:0,0,0,0,0,0,0,673,675,668,1946,1978,1558,1904,1266,678,676,677,680,674
I:10DT:54752CT:1051P:312HZ:0,0,0,0,0,0,0,673,675,668,1946,1978,1558,1904,1266,678,676,677,680,674
I:10DT:54109CT:1051P:318HZ:0,0,0,0,0,0,0,673,675,668,1946,1978,1558,1904,1266,678,676,677,680,674
I:10DT:57761CT:1051P:324HZ:0,0,0,0,0,0,0,673,675,668,1946,1978,1558,1904,1266,678,676,677,680,674
I:10DT:64403CT:1051P:330HZ:0,0,0,0,0,0,0,673,675,668,1946,1978,1558,1904,1266,678,676,677,680,674
I:10DT:70562CT:1051P:336HZ:0,0,0,0,0,0,0,673,675,668,1946,1978,1558,1904,1266,678,676,677,680,674
I:10DT:76696CT:1051P:342HZ:0,0,0,0,0,0,0,673,675,668,1946,1978,1558,1904,1266,678,676,677,680,674
I:10DT:86630CT:1051P:348HZ:0,0,0,0,0,0,0,673,675,668,1946,1978,1558,1904,1266,678,676,677,680,674
I:10DT:98858CT:1051P:354HZ:0,0,0,0,0,0,0,673,675,668,1946,1978,1558,1904,1266,678,676,677,680,674
I:10DT:120382CT:1051P:360HZ:0,0,0,0,0,0,0,673,675,668,1946,1978,1558,1904,1266,678,676,677,680,674
I:11DT:177293CT:2332P:6HZ:0,0,0,0,0,0,0,673,675,668,1946,1978,1558,1904,1266,678,676,677,680,674

And here is my code
Code:
#define hzPin 5
#define degPin 3
#define cadencePin 4

#define DEGREE_FLAG 1
#define Cadence_FLAG 2

volatile uint8_t bUpdateFlagsShared; //hold the updated flag
// shared variables are updated by the ISR and read by loop.
const int BUFFERSIZE=20;
volatile int DegreeInShared;
volatile int CadenceInShared;
volatile byte BufferInShared = 0;
volatile int HZInShared[BUFFERSIZE];
volatile int IndexInShared;
volatile int PositionInShared;

int HZStart;
int HZPeriod;
int HZ2;
int DegreeStart;
int CadenceStart;

//int HZ[BUFFERSIZE];
volatile byte i=0;
volatile  int  index1=-1; //Count total pedal revolutions
volatile unsigned int count1=0;
volatile int value=0;

void setup(void) 
{ 
  Serial.begin(115200); 
  pinMode(hzPin, INPUT); 
  pinMode(degPin, INPUT); 
  pinMode(cadencePin, INPUT); 
  attachInterrupt(hzPin, counterhz, RISING);
  attachInterrupt(degPin, counterdeg, RISING);
  attachInterrupt(cadencePin, doIndex, RISING);
}

void loop(void) 
{
  static int DegreeIn;
  static int CadenceIn;
  static int HZIn[BUFFERSIZE];
  static int IndexIn;
  static int PositionIn;
  static uint8_t bUpdateFlags;
  static byte BufferIn = 0;

  if (Serial.available()>0){ //reset index to -1
    Serial.read();
    index1 = -1;
    Serial.flush();
  }
  if(bUpdateFlagsShared)
  {
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables

      // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;

    if(bUpdateFlags & DEGREE_FLAG)
    {
      DegreeIn = DegreeInShared;
      BufferIn = BufferInShared;
      IndexIn = IndexInShared;
      HZIn [BufferIn] = HZInShared [BufferInShared];
      PositionIn = PositionInShared;
    }
    bUpdateFlagsShared = 0;
    BufferInShared=0;
    interrupts(); 
  }

  if(bUpdateFlags & DEGREE_FLAG ){
    Serial.print ("I:");
    Serial.print (IndexIn);
    Serial.print ("DT:");
    Serial.print (DegreeIn);
    Serial.print ("CT:");
    Serial.print (CadenceInShared);
    Serial.print ("P:");
    Serial.print (PositionIn);
    Serial.print ("HZ:");

    for (i=0; i<BufferIn; i++) { //print HZ array
      Serial.print(HZIn[i]); 
      if (i<BufferIn-1){
        Serial.print(",");
      }
    }
    Serial.println();
    i=0;
    bUpdateFlags = 0;
    //memset(HZInShared,0,sizeof(HZInShared)); //Not sure where or how to reset this array
  }

}

void doIndex() { //Cadence
  PositionInShared=0;            //the encoder Home position will equal the current encoder position.
  CadenceInShared=millis()-CadenceStart;
  CadenceStart=millis(); 
  IndexInShared++;
}

void counterhz() //Frequency
{ 
  HZPeriod=micros()- HZStart;
  HZStart=micros();
  //HZ2=1000000 / HZPeriod;
  if ( BufferInShared < BUFFERSIZE ) { //fill array with all HZ per chaing ring tooth
    HZInShared[BufferInShared++] = HZPeriod;
  }
}

void counterdeg()// Degree Time
{ 
  DegreeInShared=micros()-DegreeStart;
  DegreeStart=micros(); 
  bUpdateFlagsShared |= DEGREE_FLAG;
  PositionInShared+=6;

}
 
I realize now I was not populating the array using a for loop, hence the values did not change. I was also wondering if I should move the nointerrupts() right before the if(bUpdateFlagsShared)? How will it affect things if not inside an if statement? Also do I have to reset the array each time, or is that taken care of when I print the array? Sorry for all the q's, if you can't tell i'm a noob.
 
Status
Not open for further replies.
Back
Top