Using both SPI nd PWM modules on one 18F -> TMR2 conflict. Is there a work around?

Status
Not open for further replies.

astronomerroyal

New Member
Hi,

I'm using an 18F4620 (but may migrate to 18F67K22) and need to implement:

1). LED backlight control using PWM (the leanest, most discreet method).
2). SPI communication at 80kHZ and 500kHz. This is the heart of my application.

The problem is that they both employ TMR2. The PWM module requires TMR2. The SPI module (switching between these two low clock speeds) is ideally suited for use with TMR2 rather than the primary oscillator (fosc/4, fosc/16, or fosc/64).

Separately my PWM and SPI routine work very well, but together it becomes a little socially awkward*. I'd appreciated any wise words regarding the decision-making process at this juncture.

Thanks in advance.

* Specifically, when I switch SPI clock speed (i.e. changing PR2, which I do often) it implies a change in the PWM duty cycle (screen brightness). That's awkward.
 
I am sure you can get the compare mode (TMR1) to work for the led PWM. On the flip side the 18f26k22 chip has three dedicated PWM timers (TMR1, TMR3, TMR5), very nice. The 18fxxk22 have lots more flexibility and features, at a cheaper price than the 18fxx20 series.
 
Use timer1 with the pwm module (special event trigger) to setup an interrupt to do the PWM. If you aim for a frequency of 50Hz and just vary the on time as required. So for 25% on time using a 4MHz oscillator you would have a total time of 20,000 and on time of 5,000. So in your interrupt you would add 5,000 and 15,000 to the ccpl/h register. For an example of using the special event trigger see this thread. The interrupt version is later in the thread.

Mike.
 
Thanks very much for the feedback (and sorry for the delay in replying). T'is what I feared. I had hoped to avoid having to service interrupts - not sure something as lowly as LCD contrast adjust really warrants it. I'll stick with the multiturn pot. Mike, I have used your special event method for controlling servos in the past - most efficient method I've tried yet.

I'm trying to migrate to more recent/powerful chips, for reasons of features and price, but am a die-hard breadboard user, and have not had much luck with TQFP to DIP adapters. Would love to start some projects with the 24F.
 
The LCD backlight PWM only needs quite a low frequency, somewhere around 100Hz or higher. You don't need a high performance PWM like if it was a SMPS etc.

I would drive the LCD backlight PWM in a TMR0 interrupt, which is very fast on a PIC 18F and quite trivial.

Here is C code I have tested and used for 3 way RGB LED PWM on a PIC 18F in an interrupt;
Code:
unsigned char intcount;     // int requires just this one variable
unsigned char pwm_red;     // these 3 vars set the backlight PWM
unsigned char pwm_green;
unsigned char pwm_blue;

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void interrupt()
{
  //-------------------------------------------------------
  // TMR0 overflow interrupt; prescale 1:1
  // The TMR0 int performs PWM on the backlight RGB LED.
  // This is a fast smiple int, basically it just compares the int_count
  // to the PWM value for each colour.
  // TMR0L bit7 is forced hi, so int happens every 128+2 ticks
  // (total PWM cycle is about 240 Hz)
  //-------------------------------------------------------
  if(!int_count)    // when int_count==0, turn off all LEDs
  {
    BacklightRed   = 1;    // 1 is off 
    BacklightGreen = 1;  
    BacklightBlue  = 1;  
  }
  else       // for 1-255, some LEDs might be on
  {
    // turn each LED on when reaches pwm value
    if(int_count == pwm_red)   BacklightRed   = 0;     // 0 is on
    if(int_count == pwm_green) BacklightGreen = 0;
    if(int_count == pwm_blue)  BacklightBlue  = 0;
  }
  int_count--;          // inc sequence (must count DOWN)
  TMR0L.F7 = 1;         // make TMR0 roll over every 128+2 ticks
  INTCON.TMR0IF = 0;    // clear the int flag before exit
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
I may have missed somethign here... but let me try and get things clear.

TMR2 is indeed used by both the PWM of the CCP modules (either), and the SPI module.

The way TMR2 works for SPI clock is, you set the maximum count of the counter, the prescale, and postscale. The timer counts from 0 up, until it reaches the maximum count - the period register (PR2). So, if you have the PR2 register set at....I dunno, 188, the counter will count 0-188, then back to 0. I'm sure you know this, but it helps just to go over the operation

For PWM, the counter will run from 0, to its maximum (set by PR2) then reset, just like above. However, the 'PWM' part is simply yet another register (CCP1L, CCP1H for CCP1 module) which compares its value to the value in TMR2.

Now, when you change your SPI clock, what you are doing I assume is changing PR2. This means the TMR2 counter, counts lower or higher. Say it was set to 188, and you change it to 94 (half the value), and your CCPR1L had the value 56 in it. When you change PR2, the clock to your SPI doubles in frequency (it only has to count to half what it was before, then resets), BUT.... what happens to your PWM? Before, the PWM pin would be high when TMR2 < 56, and low afterwards.

With the original PR2 value of 188, you'll have a PWM duty cycle of 56/188 = 29.8% Change the PR2 value to change the clock speed of your SPI, to 94... and now your PWM period is 56/94 = 59.6% So that doubles your PWM duty cycle.

In order to correct that, you would need to scale the value you're putting into the CCPxL/H registers to compensate for the change in TMR'2 maximum count. I guess you could do this with a look up table. Also note, that if TMR2 has a maximum count of say, 130, and your PWM period register is set to anything above this, say 150... your PWM pin will remain high as the timer never reaches 130... it is reset once it get to PR2's value.

So ultimtely, every time you change PR2 in order to change your SPI's clock, you need to adjust the PWM period registers value accordingly in order to maintain the same duty cycle. I guess how you do it, depends on how you change the PR2 values. If you simply double, or half them, then you can do exactly the same to the PWM period register. If they are abitrary values to get an absolute clock frequency, then some clever maths should be used: read in your PWMperiods register value, divide it by the PR2 registers value to get your duty cycle in %, then multiple it by your new PR2's value you've written to change the SPI's clock.

I would say 'simple' but I just re-read what I typed and it looks mental lol

Edit: just noticed that the PWM module also uses the postscale bits of TMR2 for an extra 2-bits, given a PWM of 10-bits. You don't need 10-bit PWM for an LCD backlight, but writing a new PWM period would use the postscale bits to compare. If you have your postscale bits set to 00, then its all good.
 
Last edited:
Also, maybe its just me but using PWM for lighting, be it a single LED, or an LCD backlight (I used it for both of them quite often), when the PWM duty cycle gets to around <20%, I see flicker if the frequency is less than 150Hz. Even at 200Hz, a slight turn of the head/eyes, with a low duty cycle and I can see it. Frankly it annoys me.

I may be 'odd', or jsut picky but I never use less than 400hz for lighting PWM, if the duty cycle is going to be very low, I either use interpolation to reduce the maximum off period, or up the frequency to 1kHz. Either can be done with an interrupt driven software PWM
 

I've managed to see LED flicker at 5 kHz and I agree that many PWM schemes exhibit really annoying flicker. Different people seem to have very different sensitivities to flicker. However, you can also use PWM at quite low frequencies and filter the output. I've found that as long as the current in the LED doesn't drop to zero, it can vary massively and it is very difficult to see any flicker. You certainly don't need to filter anywhere near as well as you would have to do for a power supply.
 
It's easier to detect PWM flicker with a tiny pinpoint of light (like a waterclear 3mm LED) when you move your eyes relative to the LED.

Flicker of a large area like a LCD backlight is much harder to detect and PWM freq >200Hz should be more than adequate.
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…