PWM bit banging

キャンセル
次の結果を表示 
表示  限定  | 次の代わりに検索 
もしかして: 

PWM bit banging

2,612件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by bobi-one on Wed May 11 04:02:30 MST 2011
The only thing I use PWM is for controlling brightness of LED's but recently I discovered that I've connected the transistor for the back-light of my LCD  to a non PWM output. PIO1.0 is of the LPC1343 to be precise. And the only think i can do is turn on and off the back-light.
So my Goal is to create simple fade-in and fade-out functions, before switching to constant state (ON/OFF) the back-light. I understand that I don't need to use constantly some timer and only when within the fade functions.
So I am open for ideas how to do those functions. I am familiar with the PWM principle, but if someone can share some experience or example it will be greatly appreciated. I tried to do some bit banging(not even sure about this expression :D) with gpio functions and delay functions but with my lack of an oscilloscope I wasn't very successfully. Just made the LED blink epileptic.
0 件の賞賛
返信
14 返答(返信)

2,544件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by bobi-one on Thu May 12 06:50:54 MST 2011
10s for the help. I really learned a lot of new stuff for today :)
And now my code is starting to look decent :)
http://www.youtube.com/watch?v=g5UzNlUEfok
0 件の賞賛
返信

2,544件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Ex-Zero on Thu May 12 06:42:19 MST 2011

Quote: bobi-one
But the only problem is that it takes a lot of memory.



And even worse, it's not very smart :mad:
Just use a flag and let your interrupt increment/decrement and switch your PWM off. No stupid while loops and ISR is doing the work for you


#define DIM_OFF     0    
#define DIM_UP      1    
#define DIM_DOWN    2

unsigned char dim = DIM_OFF; 

//add to PWM interrupt
switch(dim)
 {
  case(DIM_UP): //count up, if complete ON: reset dim and switch PWM off
                break;
  case(DIM_DOWN)://count down,  if complete OFF:reset dim and switch PWM off
                break;
  default:        break;
 }
0 件の賞賛
返信

2,544件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by bobi-one on Thu May 12 06:14:53 MST 2011
volatile unsigned int counts=0;

          void TIMER16_0_IRQHandler(void)
           {
             if (LPC_TMR16B0->IR & (1<<0) ) //match0
              {
               LPC_TMR16B0->IR = (1<<0); //LED OFF
               BLED0;
              }

              if ( LPC_TMR16B0->IR & (1<<3) ) //match2
               {
               LPC_TMR16B0->IR = (1<<3); //reset timer & LED ON
               BLED1;
               }
              counts++;
}


void init_tmr0pwm(unsigned short init_period, unsigned short init_on)
{
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7); //enables CT16B0
LPC_TMR16B0->TCR = 0;
LPC_TMR16B0->PR = 7200-1; //set prescaler to 0.1ms
LPC_TMR16B0->PWMC = (1<<0)|(1<<3); //match 0 & match3
LPC_TMR16B0->MR3 = init_period;
LPC_TMR16B0->MR0 = init_on;
LPC_TMR16B0->MCR = (1<<0)|(1<<9)|(1<<10); //int on Match 0 & 3, reset match 3
NVIC_EnableIRQ(TIMER_16_0_IRQn);
LPC_TMR16B0->TCR = 1;
}



void fadeIN(volatile unsigned short pwm_period)
{  volatile unsigned short pwm_on;
 pwm_on = 0;
  init_tmr0pwm(pwm_period, pwm_on);
 while(1)
           {
            LPC_TMR16B0->MR0 = pwm_on;
            if(counts>2)
            {
                pwm_on++;
                counts=0;
            }
                if(pwm_on>pwm_period)
                {
                LPC_TMR16B0->TCR = 0;

                BLED1;
                return;
                }
           }

}
void fadeOUT(volatile unsigned short pwm_period)
{  volatile unsigned short pwm_on;
 pwm_on = pwm_period;
  init_tmr0pwm(pwm_period, pwm_on);
 while(1)
           {
         LPC_TMR16B0->MR0 = pwm_on;

         if(counts>2)
                     {
                         pwm_on--;
                         counts=0;
                     }
                if(pwm_on<1)
                {
                LPC_TMR16B0->TCR = 0;

                BLED0;
                return;
                }
          }
}

Your code works marvellous, I've modified it to serve my purpose and the back-light is dimming really smooth. But the only problem is that it takes a lot of memory.
0 件の賞賛
返信

2,544件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Ex-Zero on Thu May 12 05:47:05 MST 2011
Your interrupt is delivering an independent time base (0.1 ms in my sample). So how fast do you want to change your pulse width? If it's too fast, use a counter. If it's too slow, add more than 1 to your MR0 :)
0 件の賞賛
返信

2,544件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by bobi-one on Thu May 12 05:41:30 MST 2011
Hmm, if I increment or decrement in the interrupt handler, doesn't that mean there will be only one pulse with the specific "on" width in every fade sequence?
0 件の賞賛
返信

2,544件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Ex-Zero on Wed May 11 15:09:57 MST 2011
Of course you're fading too fast :mad:

You can also use the interrupt to increment or decrement  :)

And if you want to stop PWM use TCR:
[INDENT] LPC_TMR16B0->TCR = 0;[/INDENT]
0 件の賞賛
返信

2,544件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by bobi-one on Wed May 11 15:00:24 MST 2011
Now that is a code I'll try to fully understand
so after using my math i came to the conclusion that the fallowing changes are important:

LPC_TMR16B0->PR = 18000-1; 

volatile unsigned short pwm_period = 8000;

My functions worked ( of some kind) and the  back light was fading out without flicering. But fading in was instantaneous. (my main is switching the both functions). So i am wondering if my functions are broken, or just fading in happens too rapid.

void fadeIN(){


 pwm_on = 1;
 while(1)
           {

                pwm_on++;
                if(pwm_on==pwm_period){LED1; return; }

                LPC_TMR16B0->MR0 = pwm_on; 


          }

}
void fadeOUT(){


 pwm_on = pwm_period;

 while(1)
           {


                pwm_on--;
                if(pwm_on<1){LED0; return;} 

                LPC_TMR16B0->MR0 = pwm_on; 


          }

}
0 件の賞賛
返信

2,544件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Ex-Zero on Wed May 11 13:49:16 MST 2011
As suggested earlier a smarter way to switch your LED is to use PWM (with prescaler) and it's interrupts:


Quote:

#define LED1      (1 << 8)
#define LED1_ON   LPC_GPIO0->DATA |= LED1;        //LED ON
#define LED1_OFF  LPC_GPIO0->DATA &=~LED1;        //LED OFF
#define LED1_TOG  LPC_GPIO0->DATA ^= LED1;        //LED TOG

volatile unsigned short pwm_period = 1000;        //with prescaler 0.1 ms = 100ms period
volatile unsigned short pwm_on = 200;            //with prescaler 0.1 ms =  20ms ON

//ISR timer 16B0 for PWM
void TIMER16_0_IRQHandler(void)
{
if (LPC_TMR16B0->IR & (1<<0) )        //match0
{
  LPC_TMR16B0->IR = (1<<0);            //LED OFF
  LED1_OFF;
}
if ( LPC_TMR16B0->IR & (1<<3) )    //match2
  {
   LPC_TMR16B0->IR = (1<<3);        //reset timer & LED ON
   LED1_ON;
  }
}

//init timer 16B0 for PWM
void init_tmr0pwm(unsigned short init_period, unsigned short init_on)
{
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7);
LPC_TMR16B0->TCR = 0;
LPC_TMR16B0->PR  = 7200-1;                //set prescaler to 0.1ms
LPC_TMR16B0->PWMC = (1<<0)|(1<<3);        //match 0 & match3
LPC_TMR16B0->MR3 = init_period;
LPC_TMR16B0->MR0 = init_on;
LPC_TMR16B0->MCR = (1<<0)|(1<<9)|(1<<10);        //int on Match 0 & 3, reset match 3
NVIC_EnableIRQ(TIMER_16_0_IRQn);
LPC_TMR16B0->TCR = 1;
}

int main(void)
{
    LPC_GPIO0->DIR = LED1;                //set LED1 output
    init_tmr0pwm(pwm_period, pwm_on);
    // Enter an infinite loop, just incrementing a counter
    volatile static int i = 0 ;
    while(1)
    {
     if(i>100000)                        //delay
     {
      i=0;
      pwm_on++;
      if(pwm_on>800)pwm_on=200;            //change 20-80ms ON
       LPC_TMR16B0->MR0 = pwm_on;        //write to match0
     }
     i++ ;
    }
    return 0 ;
}

No delay, just setting period and On-time in MR3/MR0 :)
0 件の賞賛
返信

2,544件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by bobi-one on Wed May 11 12:03:33 MST 2011
Indeed the lack of oscilloscope have made me a sloppy engineer. :D
so i will init the timer on specific frequency
init_timer32(0, ((SystemCoreClock/LPC_SYSCON->SYSAHBCLKDIV)/4000));
and than just need to put delay functions on the right places.
I was thinking "LPCXpresso1343_systick_twinkle" have as similar principle, maybe is a good choice of reference.
0 件の賞賛
返信

2,544件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Ex-Zero on Wed May 11 11:22:47 MST 2011

Quote:

The only thing I use PWM is for controlling brightness...

Why don't you use this code to start with?


Quote:

...lack of an oscilloscope...

That's really brave, flying blind... :)

Back to work:

init_timer32(0, ((SystemCoreClock/LPC_SYSCON->SYSAHBCLKDIV)/100 ));

is probably setting a match register to 1/100 SystemCoreClock (if SYSAHBCLKDIV is 1).

So your interrupt is executed every 1/100s = 10ms.
0 件の賞賛
返信

2,544件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by bobi-one on Wed May 11 09:38:36 MST 2011
Timers can really confuse me.
as I understand this prescaler manages the frequency of the interupt occurance of the timer. So it will determine the PWM freq? In that case in the handler the LED must stay low with some kind of delay?
SystemCoreClock/LPC_SYSCON->SYSAHBCLKDIV)/100
extern volatile uint32_t timer32_0_counter;

void BLEDfade()
         {

 init_timer32(0, ((SystemCoreClock/LPC_SYSCON->SYSAHBCLKDIV)/100 ));
 timer32_0_counter = 0;
         enable_timer32(0);
         while(1)
                 {
         SET_LED1;
         }

}

void TIMER32_0_IRQHandler(void)
{
  LPC_TMR32B0->IR = 1;
  SET_LED0;
  timer32_0_counter++;
  return;
}
0 件の賞賛
返信

2,544件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by gbm on Wed May 11 06:29:35 MST 2011
For LED dimming in my designs, I usually use PWM period of 256 with prescaler set in such way that the PWM operates at 150..200 Hz (prescaler = timer_input_freq / 256 / target freq.)  Match register is updated (incremented/decremented) in PWM period match ISR based on target value variable set by application software.
0 件の賞賛
返信

2,544件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by bobi-one on Wed May 11 06:07:54 MST 2011
Yes, that is what is the basic idea, but what kind of delays ,timers and frequencies i need to use in order to achieve PWM result
0 件の賞賛
返信

2,544件の閲覧回数
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by gbm on Wed May 11 04:09:23 MST 2011
Do it exactly as you would if your LED was controlled by hardware PWM, then turn the LED off in ISR for period match and turn it off in ISR for pulse width match.
0 件の賞賛
返信