Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 6 of 6

Thread: Loopback PWM

  1. #1
    Senior Member brtaylor's Avatar
    Join Date
    Mar 2016
    Location
    Portland, OR
    Posts
    407

    Loopback PWM

    I'm experiencing issues trying to loopback PWM. I have pin 5 connected to pin 23 using a jumper, that is the only connection.

    Here is what I am trying to do:
    1. Create a 1 Hz sine wave
    2. Scale the sine wave from +/-1 to 1000 to 2000
    3. Use analogWrite to send this as a 50 Hz, 16 bit resolution PWM to pin 5
    4. Have my ISR measure the pulse width on pin 23
    5. Print the results

    In theory the PWM that was sent on pin 5 should be roughly the same as measured on pin 23. Individually, these functions work, I can send a PWM to a servo and have it move according to the 1 Hz sine wave. I can connect an RC receiver to pin 23 and see the PWM commands. But in this loopback function, I can see my sent PWM, but my received PWM remains 0. I am using elapsedMicros for measuring the pulse width and analogWrite for sending the PWM.

    My code is below, any thoughts?

    Code:
    /* PWM read pin */
    const unsigned int ReadPin = 23;
    /* PWM write pin */
    const unsigned int WritePin = 5;
    /* used for reading the PWM */
    elapsedMicros elapsedTime_us;
    /* storing the read value */
    volatile unsigned int ReadVal_us;
    /* storing the write value */
    unsigned int WriteVal_us;
    /* PWM update frequency, 50 Hz */
    const unsigned int Freq = 50;
    /* PWM update period */
    const unsigned int Period_us = 1000000 / Freq;
    /* PWM resolution */
    const unsigned int PWM_Resolution = 16;
    /* Time, ms */
    elapsedMillis time_ms;
    
    void meas_pwm() 
    {
      /*
      * Read the value, if it's high, we just started the pulse and need
      * to zero the timer. If it's low, we just finished the pulse and
      * should store the pulse width result.
      */
      if (digitalReadFast(ReadPin)) {
        elapsedTime_us = 0;
      } else {
        ReadVal_us = elapsedTime_us;
      }
    }
    
    void setup()
    {
      /*
      * Start USB serial to display results
      */
      Serial.begin(115200);
      while(!Serial) {}
     
      /* setting the analog frequency to 50 Hz and resolution to 16 bit */
      analogWriteFrequency(WritePin, Freq);
      analogWriteResolution(PWM_Resolution);
     /*
      * Assign PWM pin as input
      */
      pinMode(ReadPin,INPUT);
      /*
      * Attach our ISRs to changes in the respective pins
      */
      attachInterrupt(ReadPin,meas_pwm,CHANGE);
    }
    
    void loop()
    {
      /*
      * Print the results
      */
      Serial.print(ReadVal_us);
      Serial.print("\t");
      Serial.println(WriteVal_us);
      /* 1 Hz sine wave */
      float Cmd = sinf(2.0f * M_PI * time_ms / 1000.0f);
      /* Scale from +/- 1 to a range of 1000 us to 2000 us */
      Cmd = Cmd * 500.0f + 1500.0f;
      /* Command channel */
      WriteVal_us = Cmd;
      analogWrite(WritePin,WriteVal_us / Period_us * powf(2,PWM_Resolution));
      delay(20);
    }

  2. #2
    Senior Member brtaylor's Avatar
    Join Date
    Mar 2016
    Location
    Portland, OR
    Posts
    407
    Please disregard - silly mistake on my part with integer division in the analogWrite!

  3. #3
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,986
    16 bit resolution on PWM says 0 to 65535.

    Until it hits near HALF scale it won't show as a 1 on a digital input. Anything less is read as Zero

    Not sure this was right but putting " Cmd = Cmd * 500.0f + 1500.0f;" to :: Cmd = Cmd * 20000.0f + 4500.0f;

    Gives the following where added iCnt shows the isr() trigger count and 0[# or 1[# lines show how many times that value ReadFast during the wait:
    Code:
    220067	 Read <  > iCnt 0	0
    220067	 Read <  > iCnt 0	1247
    220067	 Read <  > iCnt 0	3746
    220067	 Read <  > iCnt 0	6257
    220067	 Read <  > iCnt 0	8740
    220067	 Read <  > iCnt 0	11156
    220067	 Read <  > iCnt 0	13467
    220067	 Read <  > iCnt 0	15637
    220067	 Read <  > iCnt 0	17631
    220067	 Read <  > iCnt 0	19418
    0[3695091
    220067	 Read <  > iCnt 1	20970
    220067	 Read <  > iCnt 0	22262
    220067	 Read <  > iCnt 0	23274
    220067	 Read <  > iCnt 0	23990
    220067	 Read <  > iCnt 0	24399
    220067	 Read <  > iCnt 0	24493
    220067	 Read <  > iCnt 0	24273
    220067	 Read <  > iCnt 0	23740
    220067	 Read <  > iCnt 0	22904
    220067	 Read <  > iCnt 0	21778
    220067	 Read <  > iCnt 0	20379
    1[1042184
    220068	 Read <  > iCnt 1	18730
    220068	 Read <  > iCnt 0	16857
    220068	 Read <  > iCnt 0	14788
    220068	 Read <  > iCnt 0	12558
    220068	 Read <  > iCnt 0	10200
    220068	 Read <  > iCnt 0	7752
    220068	 Read <  > iCnt 0	5253
    220068	 Read <  > iCnt 0	2742
    220068	 Read <  > iCnt 0	259
    220068	 Read <  > iCnt 0	0
    Where I used this code edit to count how many times the interrupt triggers, and instead of delay(20) I watched the ReadPin during that time:
    Code:
    // https://forum.pjrc.com/threads/54270-Loopback-PWM
    /* PWM read pin */
    const unsigned int ReadPin = 23;
    /* PWM write pin */
    const unsigned int WritePin = 5;
    /* used for reading the PWM */
    elapsedMicros elapsedTime_us;
    /* storing the read value */
    volatile unsigned int ReadVal_us;
    /* storing the write value */
    unsigned int WriteVal_us;
    /* PWM update frequency, 50 Hz */
    const unsigned int Freq = 50;
    /* PWM update period */
    const unsigned int Period_us = 1000000 / Freq;
    /* PWM resolution */
    const unsigned int PWM_Resolution = 16;
    /* Time, ms */
    elapsedMillis time_ms;
    
    volatile uint32_t iCnt = 0;
    void meas_pwm()
    {
      /*
        Read the value, if it's high, we just started the pulse and need
        to zero the timer. If it's low, we just finished the pulse and
        should store the pulse width result.
      */
      if (digitalReadFast(ReadPin)) {
        elapsedTime_us = 0;
      } else {
        ReadVal_us = elapsedTime_us;
      }
      iCnt++;
    }
    
    void setup()
    {
      /*
        Start USB serial to display results
      */
      Serial.begin(115200);
      while (!Serial) {}
    
      /* setting the analog frequency to 50 Hz and resolution to 16 bit */
      analogWriteFrequency(WritePin, Freq);
      analogWriteResolution(PWM_Resolution);
      /*
         Assign PWM pin as input
      */
      pinMode(ReadPin, INPUT);
      /*
        Attach our ISRs to changes in the respective pins
      */
      attachInterrupt(ReadPin, meas_pwm, CHANGE);
    }
    
    elapsedMicros usWait;
    uint32_t rpLast = 0, ii = 0;
    void loop()
    {
      /*
        Print the results
      */
      Serial.print(ReadVal_us);
      Serial.print("\t Read <  > iCnt ");
      Serial.print(iCnt);
      iCnt = 0;
      Serial.print("\t");
      Serial.println(WriteVal_us);
      /* 1 Hz sine wave */
      float Cmd = sinf(2.0f * M_PI * time_ms / 1000.0f);
      /* Scale from +/- 1 to a range of 1000 us to 2000 us */
      Cmd = Cmd * 20000.0f + 4500.0f;
      /* Command channel */
      WriteVal_us = Cmd;
      analogWrite(WritePin, WriteVal_us / Period_us * powf(2, PWM_Resolution));
      usWait = 0;
      //delay(20);
      uint32_t rpNow;
      while ( usWait < 20000 ) {
        rpNow = digitalReadFast(ReadPin);
        if ( rpNow == rpLast ) {
          ii++;
        }
        else {
          Serial.print(rpLast);
          Serial.print("[");
          Serial.println(ii);
          rpLast = rpNow;
          ii = 1;
    
        }
      }
    }

  4. #4
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,986
    Too Late ...

    Maybe my 'Not sure this was right ' part just moved the decimal enough? Would like to see your updated code.

  5. #5
    Senior Member brtaylor's Avatar
    Join Date
    Mar 2016
    Location
    Portland, OR
    Posts
    407
    Here's the fixed code:

    Code:
    /* PWM read pin */
    const unsigned int ReadPin = 23;
    /* PWM write pin */
    const unsigned int WritePin = 5;
    /* used for reading the PWM */
    elapsedMicros elapsedTime_us;
    /* storing the read value */
    volatile unsigned int ReadVal_us;
    /* storing the write value */
    unsigned int WriteVal_us;
    /* PWM update frequency, 50 Hz */
    const unsigned int Freq = 50;
    /* PWM update period */
    const unsigned int Period_us = 1000000 / Freq;
    /* PWM resolution */
    const unsigned int PWM_Resolution = 16;
    /* Time, ms */
    elapsedMillis time_ms;
    
    void meas_pwm() 
    {
      /*
      * Read the value, if it's high, we just started the pulse and need
      * to zero the timer. If it's low, we just finished the pulse and
      * should store the pulse width result.
      */
      if (digitalReadFast(ReadPin)) {
        elapsedTime_us = 0;
      } else {
        ReadVal_us = elapsedTime_us;
      }
    }
    
    void setup()
    {
      /*
      * Start USB serial to display results
      */
      Serial.begin(115200);
      while(!Serial) {}
     
      /* setting the analog frequency to 50 Hz and resolution to 16 bit */
      analogWriteFrequency(WritePin, Freq);
      analogWriteResolution(PWM_Resolution);
     /*
      * Assign PWM pin as input
      */
      pinMode(ReadPin,INPUT);
      /*
      * Attach our ISRs to changes in the respective pins
      */
      attachInterrupt(ReadPin,meas_pwm,CHANGE);
    }
    
    void loop()
    {
      /*
      * Print the results
      */
      Serial.print(ReadVal_us);
      Serial.print("\t");
      Serial.println(WriteVal_us);
      /* 1 Hz sine wave */
      float Cmd = sinf(2.0f * M_PI * time_ms / 1000.0f);
      /* Scale from +/- 1 to a range of 1000 us to 2000 us */
      Cmd = Cmd * 500.0f + 1500.0f;
      /* Command channel */
      WriteVal_us = Cmd;
      analogWrite(WritePin,(float) WriteVal_us / Period_us * powf(2,PWM_Resolution));
      delay(20);
    }
    The only difference is casting WriteVal_us to a float in the analogWrite so the division goes as expected. I was just using Cmd in a previous version, but I wanted to print the value written and value received, which is why this was changed in the first place.

  6. #6
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,986
    Thanks. With that the iCnt code added in isr() and the checking during the 'delay(20)' period works with that to catch the changes that happen with the (float) cast.

    Just got a $20 Logic Analyzer from SparkFun and it catches transitions - though the exaggerated math looks more interesting.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •