Simple PWM generator with TR only

2000px-Pulse_wide_wave

One year ago, I published two posts about PWM module driven by IQRF [1][2]. But there is also another way, how to generate PWM signal from IQRF TR module only. Just simple use PIC peripherals. This post is based on one of Advanced examples, which is part of IQRF Startup Package.

What do you need

  • CK-USB-04 or some other device for IQRF TR module programming.
  • Two IQRF TR-52D (I have OS 3.03, but any newer have to work of course)
  • Some baseboard for PWM generating TR module (I use DK-EVAL-04)
  • Optionally you can use some USB-UART interface for master module.
blockdiagpwm

IQRF PWM block diagram

We are going to have two TR modules. One serves as communication module, which collects commands from PC and transmits them to the second TR module. We will call this module as a Master. You have two possibilities how to make Master:

  1. Use (as me) the UART_LINK.c example from advanced examples, which retransmits RF to UART and back. In this case you have to use some Serial terminal and UART interface for TR module. I published USB gateway, which works well in this role.
  2. Or use E03-TR.c example from Basic examples and CK-USB-04 with IQRF IDE.

The second TR module is Slave module, which sets the PWM against the received value. Does it seems simple? It is… Just use TR_PWM.c from Advanced examples.

Slave module

As I wrote, we are going to use TR_PWM.c from Advanced examples:

#include "includes/template-basic.h"    // System header files inclusion
 
void initPWM();
 
void APPLICATION()
{
    setWDTon_4s();  // WDT on, timeout 4 s (see IQRF-macros.h)
    initPWM();
 
    while (1)
    {	
        clrwdt();  
 
        if (RFRXpacket())               
        {
            if (bufferRF[0] == 'P')
            {
                pulseLEDR();
                CCPR3L = bufferRF[1];
            }
        }		
    }
}

Source code above waits for command. If some data arrived, command byte is checked, if command is equal to character “P”. Then pulse width is set to CCPR3L register against the second received byte. As you can see below, the full period is set to 250 clocks. So if you send “P#125” you have PWM with 50% duty cycle.

// CCP3 is used with C5 pin as PWM output
void initPWM() 
{
    TRISC.6 = 1;    // C5 as input 
    TRISA.5 = 1;
    TRISB.4 = 1;
 
    CCPTMRS0 = 0b00100000;    // Set timer6 as CCP3 timer		
    CCP3CON = 0b00111100;     // CCP3 single mode
    CCPR3L = 25;              // PWM duty cycle: 0.2ms
    CCP3SEL = 0;              // Select RC6 as P3A function output
 
    T6CON = 0b00000010;       // Prescaler: 1:16
    PR6 = 250;                // PWM period: 2ms
    TMR6IF = 0;
    TMR6ON = 1;
    // To send a complete duty cycle and period on the first PWM
    while (!TMR6IF);         
    TRISC.6 = 0;             // C5 as output
}

Now, you can see nice 500Hz PWM on the oscilloscope. It is because main clock is 2MHz, clock prescaler is set to 16 and we use 250 clock periods to full PWM period, so:

2 000 000 / 16 / 250 = 500 Hz (2 ms).

But, what you can do, If you want 5kHz PWM? Just use:

    T6CON = 0b00000001;       // Prescaler: 1:4 (500 kHz)
    PR6 = 100;                // PWM period: 0.2ms (5 kHz)

It is easy, isn’t it?

The second PWM output

If you want, you can add second PWM output. You have to have same period for both, because you have to use Timer6 for both CCP units. I used CCP1 and its output on the pin RC2. We have to change initPWM function:

// CCP3 is used with C5 pin as PWM output
// CCP1 is used with C2 pin as PWM output
void initPWM() 
{
    TRISC.6 = 1;    // C5 as input 
    TRISA.5 = 1;
    TRISB.4 = 1;
 
    TRISC.2 = 1;    // C2 as input
 
    CCPTMRS0 = 0b00100010;    // Set timer6 as CCP3 and CCP1 timer			
 
    CCP3CON = 0b00111100;     // CCP3 single mode	
    CCP1CON = 0b00111100;     // CCP1 single mode	
 
    CCPR3L = 25;              // PWM duty cycle: 0.2ms
    CCPR1L = 25;              // PWM duty cycle: 0.2ms	
 
    CCP3SEL = 0;              // Select RC6 as P3A function output
 
    T6CON = 0b00000001;       // Prescaler: 1:16
    PR6 = 100;                // PWM period: 2ms
    TMR6IF = 0;
    TMR6ON = 1;
 
    // To send a complete duty cycle and period on the first PWM
    while (!TMR6IF);         
 
    TRISC.6 = 0;             // C5 as output
    TRISC.2 = 0;             // C2 as output
}

Now we have to extend “P” command and add one byte for CCP1:

if (RFRXpacket())               
{
    if (bufferRF[0] == 'P')
    {
        pulseLEDR();
        CCPR3L = bufferRF[1];
        CCPR1L = bufferRF[2];
    }
}

And we have done…

Enjoy.

4 thoughts on “Simple PWM generator with TR only

  1. Pingback: Simple PWM generator with TR only – Ráj Bastlířů

  2. Mikolaj

    Hi,
    I was wondering if it’s possible to use second PWM on C1 pin (RC2) with Custom DPA User Peripheral on DCTR-72.
    I’ve got “CustomDpaHandler-UserPeripheral-PWM.c” example working, but I could not implement second PWM output.
    I need help… Could You share some advices? or even better example?

    Reply
  3. Mikolaj

    btw. working with Osciloscope we’ve found that:
    1. Clock is accually 4MHz 002.125.64 [02.7D.40] – gives 2kHz PWM with 50% duty
    2. “period” count start from 0 (same as “prescaler”) eg. 000.000.001 give one cycle, so `PR6 = 250;` is 251 periods per cycle

    Reply
    1. Mikolaj

      there is an error in my previous comment:
      Clock is 4MHz, but one PWM cycle takes 2 clock ticks so
      4MHz 002.125.64 [02.7D.40] -> PWM fq. = 4 000 000/16/125 / 2 = 1 000 (or accually it should be 4000000/16/126/2, as period 0 – one full PWM cycle)

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *