Pic Lab. PIC18. Experiment #7. The PWM module

Task: To study the PWM module usage for LED brightness control

Tools: PIC18f1230, scope

scope screenshot with pwm module enabled

As usual all the time we should start from datasheet reading. Read it a couple of times and then we have some ideas what is packed inside.

the pic18f1230 datasheet PWM block diagram

My main task is to have a two PWM outputs to adjust the brightness of different LED temperatures, mixing will allow to regulate the transition from yellowish to whitish.

The PIC18F1230 has 3 pairs of PWM outputs (6 pins), controlled by three separate modules and grouped 0-1, 2-3 and 4-5.

Out from my datasheet reading I drafted the next set of conclusions:

  • I can use either free running mode, either continuous up/down counting. The free running mode looks simpler to me, so why not use it.
  • Complementary mode of operation should do exactly what I wanted initially, I need to change the duty cycle just once and the opposite pin will carry inverted waveforms (later on I realized it will not work for my project, but anyway)

OK, keeping things simple, lets check what do we need to make this happen. The first thing is to setup the PWM period, apparently this microcontroller has a separate 12 bit timer, and if you don’t need bells and whistles like me here- then all we need to know is that the timer is controlled through PTCON0 and PTCON1 registers. Which I am about to show:

PTCON0 and PTCON1 registers for PWM module

OK, so to enable the time base we should put PTEN to 1, set the free running mode, and I am not sure yet about which prescaler setup I need.

Then we need to take a look at the register PWMCON0:

PWMCON0 register

Here, I would need to set PWMEN2:PWMEN0 to 010 (later on I realized I need a different setup), since I’m going to use just these two pins. And PMOD0 to 0, to enable the complementary mode.

Now to, basically, the setup:

In a free running mode the 12 bit timer is ticking until the PTMR register will not match the PTPER register (PTPERL and PTPERH). Here I had a question about alignment of bits immediately, well guess no more:

PTMR and PTPER registers

Now we can do some calculations: without a prescaler we work with Focs/4 base (0.5us for 8MHz) so 12 bit timer can produce 2048us, or about 2ms period, which is 0.5 KHz, I wanted to push this value to higher frequency, like to ~2KHz, in this case I need PTPER to be settled to 1024. OK, noted, moving on.

The last task would be (hopefully) to set the duty cycle:

PWM period and the duty cycle for free running mode

The duty cycle is controlled by PDCx register for 3 output pairs, but there is one thing which we should take into account:

duty cycle registers

To set the correct value in the PDC registers we always need multiply value by 4, to get a 2 bits shift for those Q clocks, which I don’t exactly know for what and for a moment not even going to dig into.

This should be it, the whole routine then will look like this:

  • Set the PTPER registers to 1024dec
  • Set PTMOD1:PTMOD0 to 00 for a free running mode
  • Set PWMEN2:PWMEN0 to 010 to have only PWM0 and PWM1 running
  • Set PMOD0 to 0 to have the complementary mode in place
  • Set the duty cycle with PDC0H<5:0> and PDC0L<7:2>
  • Launch the module with PTEN = 1

All looks quite simple, huh.

The simple code to get thing going:

 //PWM setups and initial launching
 //period to about 0.5ms (2KHz)
 PTPERH = 4;
 PTPERL = 0;
 //to free running mode
 PTMOD1 = 0;
 PTMOD0 = 0;
 //select PWM 0/1 
 PWMEN2 = 0;
 PWMEN1 = 1;
 PWMEN0 = 0;
 //and make them complementary
 PMOD0 = 0;
 //set the duty cycle, 0/1 pair to dc state
 PDC0H = 0;
 PDC0L = 0;
 //and launch the pwm timer
 PTEN = 1;

 //End of PWM setups
complementary mode on a scope

The code is so simple that I even did not make a separate folder in the lab for it.

Leave a Reply

Your email address will not be published.