For a battery charging project, i am using an ARDUINO UNO which is driving a buck converter to provide the battery with the charging current. I am using external ADCs to measure the battery's charging voltage and current. The test setup is working fine, but right now i am just measuring the voltage and current at random time instants. However to measure it correctly, battery voltage reading should be done at an instant when the controller's PWM output is LOW (OFF duty cycle), and battery current reading should be done when PWM output is HIGH (ON duty cycle).

I looked into interrupts and learned about them that there are 2 types of interrupts in Arduino namely "external", and "pin change". So i downloaded the "PinChangeInt Library" to test it. I wrote a small test program where a PWM of 62.5kHz is being generated on pin3, and i am calling rising and falling edge triggered interrupts on this pin, i.e to display one value when rising edge occurs, and other value when falling edge occurs. My code looks like following:

    #include <PinChangeInt.h>
    #define pw 3
    int pwm;

    void setup() {
      // put your setup code here, to run once:
      pinMode(pw, OUTPUT) ;
      pwm_setup();             // Set PWM to a frequency of 62.5kHz

      PCintPort::attachInterrupt(pw,rise,RISING);    // call void rise() when PWM is HIGH
      PCintPort::attachInterrupt(pw,fall,FALLING);   // call void fall() when PWM is LOW


    void loop() {
      // put your main code here, to run repeatedly:
      pwm = 127;                                                // 50% duty cycle

    void pwm_setup()

      TCCR2A = 0x23 ;
      TCCR2B = 0x09 ; // mode 7, clock prescale by 1
      OCR2A = 256-1 ;  // 256 clock periods = 16us per cycle
      OCR2B =0 ;
      TCNT2 =0 ;

    void pwm_on(double)
        OCR2B = pwm ;

    void rise()

void fall()

But it is not working the way i think it should. It keeps on displaying "fall" every time, whereas according to me it should display "RISE" and "fall" alternatively, right ? However if i remove the "PCintPort::attachInterrupt(pw,fall,FALLING); " line, it starts to display "RISE" as it is supposed to.

Since its my first time programming with Arduino, and i am just a beginning level programmer, i might be making some stupid mistake here. So can anyone point it out please ?

Your helpful comments and suggestions would be appreciated. Thankyou!

  • 1
    I'm totally against big interrupt service routines. They are against everything ISR represent. So, in my opinion, you'd better throw away the library and start coding yourself. Register on the proper ISR (you can attach to the interrupt INT1, for instance, or to the portD pin change) and, in this ISR, check the pin value. Then set a flag and, in the loop, call the serial function. NEVER use serial inside an ISR, since serial functions are really slow (and you should stay in ISRs the minimum time you need)
    – frarugi87
    Nov 12 '15 at 9:40

Here are a few items to consider.

  1. From #define pw 3 you are working with PD3, INT1. As one of the Atmega328 Uno's two external interrupts, INT1 supports RISING and FALLING events separately. Pin change interrupts (PCIs) on pins other than 2 or 3 of the Uno won't separate them and in the interrupt routine (one interrupt routine in that case, not two) you will need to test which occurs by reading the pin.

  2. Serial communication depends on interrupts and won't work in an interrupt handler. Instead, set a flag (via a volatile byte variable) to test in your loop().

  3. For testing, put pwm_on(pwm) in setup() instead of loop().

  4. In loop(), compute seconds = milli()/1000 and if seconds != prevsec, set prevsec=seconds, enable your PCI or external interrupt, then enter a loop that waits for a flag and takes a reading. Or, in the interrupt handler, both set a flag and take a reading into a volatile variable.

  5. If you only need to measure the quantities a few times per second, or a few times per minute, you could just busy-wait for the condition, then trigger a sample-and-hold, then convert analog to digital via analogRead().

Regarding sample-and-hold: Unless you change the ADC sampling rate, analogRead() will take up to 100 microseconds per reading. If your input waveform is 62.5 KHz, it will toggle about about six times while being measured, which if measured directly is likely to give a random result. However, if you use a sample-and-hold circuit, you can hold the ADC input constant until your measurement completes.

An alternative to using a sample-and-hold is changing the ADC sampling speed. See post #5 in thread Faster Analog Read? on forum.arduino.cc. If you get the sample time down to 8 to 12 microseconds you can do ok with a busy-wait method. Ie, when your code wants a reading, it has a loop that measures the PWM line over and over, and starts an ADC conversion shortly after the PWM gets to the state desired for the measurement.

  • 1 applies (partly) to standard interrupt handling. The OP is using a library that handles the pinchange events checking the pin values. Your sentence, however, is wrong, since the arduino interrupt handling does not allow for multiple functions in the ISR, so if you attach both rising and falling you won't be able to use the former you attached
    – frarugi87
    Nov 12 '15 at 9:38
  • The PinChangeInt library checking pin values is one thing that makes it slow; I used it a few months ago but found it too slow, so used ISR()'s instead. ... At the hardware level, PCI doesn't distinguish rising/falling, merely detects changes. ... I didn't imply "multiple functions in the ISR", but instead reading a pin and branching to one section of the ISR or another depending on its value. Thanks for the comment! Nov 12 '15 at 21:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.