0

i'm trying to control my led in 256 (0-255) different levels of brightness. my controller is set to 80mhz and running on rtos. i'm setting the clock module to interrupt every 5 microseconds and the brightness e.g. to 150. the led is dimming, but i'm not sure if it is done right to really have 256 different levels

int counter = 1;
int brightness = 0;


void SetUp(void)
{

SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ);
GPIOPinTypeGPIOOutput(PORT_4, PIN_1);

Clock_Params clockParams;
Clock_Handle myClock;
Error_Block eb;
Error_init(&eb);
Clock_Params_init(&clockParams);
clockParams.period = 400; // every 5 microseconds
clockParams.startFlag = TRUE;
myClock = Clock_create(myHandler1, 400, &clockParams, &eb);
if (myClock == NULL) {
    System_abort("Clock create failed");
}

}


void myHandler1 (){

brightness = 150;


while(1){
    counter = (++counter) % 256;
    if (counter < brightness){
        GPIOPinWrite(PORT_4, PIN_1, PIN_1);
    }else{
        GPIOPinWrite(PORT_4, PIN_1, 0);
    }
}
}
Liam
  • 3
  • 2
  • You are not sure it is right? Have you tried it? An interrupt handler with an endless-loop is unlikely to work at all! If counter were a `uint8_t` then you don't need the `% 256` either. – Clifford Jan 21 '17 at 18:07

2 Answers2

3

A 5 microsecond interrupt is a tall ask for an 80 MHz processor, and will leave little time for other work, and if you are not doing other work, you need not use interrupts at all - you could simply poll the clock counter; then it would still be a lot of processor to throw at a rather trivial task - and the RTOS is overkill too.

A better way to perform your task is to use the timer's PWM (Pulse Width Modulation) feature. You will then be able to accurately control the brightness with zero software overhead; leaving your processor to do more interesting things.

Using a PWM you could manage with a far lower performance processor if LED control is all it will do.

If you must use an interrupt/GPIO (for example your timer does not support PWM generation or the LED is not connected to a PWM capable pin), then it would be more efficient to set the timer incrementally. So for example for a mark:space of 150:105, you would set the timer for 150*5us (9.6ms), on the interrupt toggle the GPIO, then set the timer to 105*5us (6.72ms).

A major problem with your solution is the interrupt handler does not return - interrupts must run to completion and be as short as possible and preferably deterministic in execution time.

Without using hardware PWM, the following based on your code fragment is probably closer to what you need:

#define PWM_QUANTA = 400 ; // 5us
static volatile uint8_t brightness = 150 ;
static Clock_Handle myClock ;


void setBrightness( uint8_t br )
{
  brightness = br ;
}

void SetUp(void)
{
  SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ);
  GPIOPinTypeGPIOOutput(PORT_4, PIN_1);

  Clock_Params clockParams;
  Error_Block eb;
  Error_init(&eb);
  Clock_Params_init(&clockParams);

  clockParams.period = brightness * PWM_QUANTA ;

  clockParams.startFlag = TRUE;
  myClock = Clock_create(myHandler1, 400, &clockParams, &eb);
  if (myClock == NULL)
  {
    System_abort("Clock create failed");
  }
}

void myHandler1(void)
{
  static int pin_state = 1 ;

  // Toggle pin state and timer period
  if( pin_state == 0 )
  {
    pin_sate = 1 ;
    Clock_setPeriod( myClock, brightness * PWM_QUANTA ) ;
  }
  else
  {
    pin_sate = 0 ;
    Clock_setPeriod( myClock, (255 - brightness) * PWM_QUANTA ) ;
  }

  // Set pin state
  GPIOPinWrite(PORT_4, PIN_1, pin_state) ;
}
Clifford
  • 88,407
  • 13
  • 85
  • 165
  • A simple alternative, if the hardware happens to lack suitable PWM channels, is to use delta-sigma modulation instead and reduce the frequency while maintaining resolution. This boils down to adding the brightness to a modular accumulator and lighting the LED on interrupt cycles at which overflow occurred. The drawback is that nearly zero/full brightness values resulting may need to be clamped to avoid low-frequency switching. – doynax Jan 21 '17 at 15:19
  • 1
    @doynax : That should be posted as an answer ot a comment to the question; I am not sure it is relevant as a comment to this answer. – Clifford Jan 21 '17 at 17:23
  • Shrug.. It is an alternative strategy for the issue noted in the answer in case the hardware designed proves incompatible. There are likely additional issues as the original solution should otherwise still perform dimming, if at a reduced frequency and while hogging the CPU. Besides, posting full answers is more work ;) – doynax Jan 21 '17 at 17:31
  • 2
    @doynax : I appreciate that, but if you have an alternative solution, you have an alternative answer. My point is that your comment does not directly relate to my answer. Moreover I am not sure I understand your solution; posting an answer will allow you to elaborate, which will help the OP and the community. – Clifford Jan 21 '17 at 18:00
  • thank you guys! really helped me to understand this whole topic better! i'm new to microcontroller programming – Liam Jan 21 '17 at 23:06
0

On the urging of Clifford I am elaborating on an alternate strategy for reducing the load of software dimming as as servicing interrupts every 400 clock cycles may prove difficult. The preferred solution should of course be to use hardware pulse-width modulation whenever available.

One option is to set interrupts only at the PWM flanks. Unfortunately this strategy tends to introduce races and drift as time elapses while adjustments are taking place and scales poorly to multiple channels.

Alternative we may switch from pulse-width to delta-sigma modulation. There is a fair bit of theory behind the concept but in this context it boils down to toggling the pin on and off as quickly as possible while maintaining an average on-time proportional to the dimming level. As a consequence the interrupt frequency may be reduced without bringing the overall switching frequency down to visible levels.

Below is an example implementation:

// Brightness to display. More than 8-bits are required to handle full 257-step range.
// The resolution also course be increased if desired.
volatile unsigned int brightness = 150;

void Interrupt(void) {
 // Increment the accumulator with the desired brightness
 static uint8_t accum;
 unsigned int addend = brightness;
 accum += addend;
 // Light the LED pin on overflow, relying on native integer wraparound.
 // Consequently higher brightness values translate to keeping the LED lit more often
 GPIOPinWrite(PORT_4, PIN_1, accum < addend);
}

A limitation is that the switching frequency decreases with the distance from 50% brightness. Thus the final N steps may need to be clamped to 0 or 256 to prevent visible flicker.

Oh, and take care if switching losses are a concern in your application.

doynax
  • 4,285
  • 3
  • 23
  • 19