Way to hack the digital pins on Nano?

I want to try to make the built in LED on my Nano fade using code. Is that possible even though it isn't on a PWM pin?

or read up on software pwm.
It’s not too hard, once you understand what PWM is.

I’ve run 72 channels with no problems.
(24x RGB LEDs)

1 Like

You can use a technique called "bit angle modulation" and here are two demonstrations, one doing it all in a loop and another using a timer interrupt. The version using the timer makes the brightness fading a little smoother to my eye. If you go on Youtube and search for "Arduino bit angle modulation", you can watch a video I made about the technique.

Version one, not using a timer interrupt:

#define PIN_LED 13

byte _bamCount;
uint8_t _ledBrightness = 0;
uint8_t _fadeUp = 1;
uint32_t _currentMillis, _lastUpdateMillis;

void setup()
{
  pinMode(PIN_LED, OUTPUT);
}

void loop()
{ 
  _currentMillis = millis();
  
  // Once every 50 milliseconds, change the led brightness.
  // It cannot go over 15 so when it gets that bright, change it back to 0.
  if (_currentMillis - _lastUpdateMillis > 49)
  {
    _lastUpdateMillis = _currentMillis;

    if (_fadeUp)
    {
      _ledBrightness++;
      if (_ledBrightness == 15) _fadeUp = 0;
    }
    else
    {
      _ledBrightness--;
      if (_ledBrightness == 0) _fadeUp = 1;
    }
  }
  
  bitAngleModulationHandler();
}

void bitAngleModulationHandler()
{
  _bamCount++;
  
  if (_bamCount > B1111) _bamCount = B0001; // Ensure counter goes from 1 to 15
  
                                                  // if led brightness is 10 (binary B1010)
  if      (_bamCount == B0001) { updateLeds(0); } // cycle  1 led off
  else if (_bamCount == B0010) { updateLeds(1); } // cycle  2 led on
                     // B0011                        cycle  3 led still on
  else if (_bamCount == B0100) { updateLeds(2); } // cycle  4 led off
                     // B0101                        cycle  5 led still off
                     // B0110                        cycle  6 led still off
                     // B0111                        cycle  7 led still off
  else if (_bamCount == B1000) { updateLeds(3); } // cycle  8 led on
                     // B1001                        cycle  9 led still on
                     // B1010                        cycle 10 led still on
                     // B1011                        cycle 11 led still on
                     // B1100                        cycle 12 led still on
                     // B1101                        cycle 13 led still on
                     // B1110                        cycle 14 led still on
                     // B1111                        cycle 15 led still on
                     
                     // So out of the available 15 cycles, a brightness of 10 sets the
                     // led to be on for 10 of them.
}

void updateLeds(byte bitPosition) {
  
  // if brightness is 10 (binary B1010) and bit position is 0
  // bitRead(B1010, 0) = 0 = false
  
  bool isEnabled = bitRead(_ledBrightness, bitPosition);
  digitalWrite(PIN_LED, isEnabled);
}

Version 2 using a timer:

#define PIN_LED 13

volatile byte _bamCount;
volatile uint8_t _ledBrightness;
volatile uint8_t _increaseBrightness;
uint32_t _currentMillis, _lastUpdateMillis;

void setup()
{
  pinMode(PIN_LED, OUTPUT);
  
  // Setup timer2 to cause an interrupt every 640 microseconds.  1024 prescaler / 16000000 clock frequency Hz * 10 counts
  TCCR2A = _BV(WGM21);  // CTC
  TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20); // 1024 prescaler
  OCR2A = 10;           // Output Compare Register A
  TIMSK2 = _BV(OCIE2A); // Output Compare Interrupt Enable Match A 
}

void loop()
{
  _currentMillis = millis();
  
  // Once every 50 milliseconds, change the led brightness.
  // It starts at 0 and goes up to 15, then back down to 0, and repeats.
  if (_currentMillis - _lastUpdateMillis > 49) {
    _lastUpdateMillis = _currentMillis;
        
    if      (_ledBrightness == 0)  _increaseBrightness = true;
    else if (_ledBrightness == 15) _increaseBrightness = false;
    
    if (_increaseBrightness) 
      _ledBrightness++;
    else
      _ledBrightness--;
  }
}

ISR(TIMER2_COMPA_vect) // timer interrupt service routine
{ 
  _bamCount++;
  if (_bamCount > 15) _bamCount = 1; // Ensure counter goes from 1-15.
  
  if      (_bamCount == B0001) { updateLeds(0); }
  else if (_bamCount == B0010) { updateLeds(1); }
  else if (_bamCount == B0100) { updateLeds(2); }
  else if (_bamCount == B1000) { updateLeds(3); }
}

void updateLeds(byte bitPosition)
{
  digitalWrite(PIN_LED, bitRead(_ledBrightness, bitPosition));
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.