Main Site Documentation

Generating PWM signals more often than once per millisecond


#1

I’m currently playing around with driving a radio mic with a PWM signal via a Panda for RTTY. I’ve gotten it working well enough that I can have it transmit and receive on another radio an “RYRYRY” and “464646”, but the way I implemented the bit rate is by taking 1000 / baud and sleeping that long between bits sent.

That works ok for 75 baud RTTY but how would bit rates above 1000 be implemented? I’m guessing I’ll need some kind of clock signal I can use instead of Thread.Sleep that is more accurate. I took a peek through the GHI manual on RTC and didn’t see anything relevant. Anybody know of a similar block of code somewhere I can look at to experiment with this?


#2

Looks like I’ll need to get lower level on this using RLP. I’m going to explore this path, but if anyone knows of a sample signal generation using RLP or any other simpler method I’d love to hear it. Going off of this basic introduction for now:

http://www.itcrowd.be/getting-started-with-ghi-rlp


#3

Yes RLP should do the job


#4

Are you planning to use the chip’s PWMs or to use your own bit banging?

If using the PWMs, I just went through that exercise and can help if you have questions. Though my usage is fairly simplistic. As a starting point, I’d suggest getting the chip’s user manual (called UM10211), and the sample code. Not sure if posting a link to the docs and code is allowed by the terms of use, but you can do a search for the LPC2387 at Keil’s web site.

HTH.


#5

I’d like to keep things as simple as possible so I would definitely prefer to do PWM if possible, just on a finer time scale. You can email me at tinyclr at webpaul dot net and we can talk it over or if you can post more details here that works too. From what I understand as long as you aren’t trying to reverse engineer and sell your own stuff the licensing isn’t an issue.


#6

I’m with you. I’d prefer to post here since I guess it works better for the forum overall. I’ll email you the links to the documents and sample source I found on the net, in case you don’t have them already.

These are the snippets I use to initialize three PWMs. From my experiments I figured that Pclk runs at 18 MHz in the Panda making it 18 cycles to a microsecond. Easy enough. For my app, I run the PWMs at 200 Hz.


 #define MICROSECOND   18 

 #define CYCLE_WIDTH     5000 * MICROSECOND
 #define NOMINAL_CENTER    1500 * MICROSECOND

int PwmInit(unsigned int *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{
    PINSEL3 |= 0x20220;	/* set GPIOs for all PWM1, PWM2 & PWM5 */

    PWM1TCR = TCR_RESET;	/* Counter Reset */ 
		
    PWM1PR = 0x00;			/* count frequency:Fpclk */
    PWM1MCR = PWMMR0R;	    /* reset on PWMMR0, reset TC if PWM0 matches */				

    PWM1MR0 = CYCLE_WIDTH;		/* set PWM cycle */

    PWM1MR1 = NOMINAL_CENTER;
    PWM1MR2 = NOMINAL_CENTER;
    PWM1MR5 = NOMINAL_CENTER;
    
    /* PWM latches enabled, ready to start */
    PWM1LER = LER0_EN | LER1_EN | LER2_EN | LER5_EN;
    return (TRUE);
}

int PwmStart(unsigned int *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{
    /* All single edge, all enable */
    PWM1PCR = PWMENA1 | PWMENA2 | PWMENA5;
    PWM1TCR = TCR_CNT_EN | TCR_PWM_EN;	    /* counter enable, PWM enable */
    return (TRUE);
}

void PWM_Stop()
{
    PWM1PCR = 0;
    PWM1TCR = 0x00;		/* Stop all PWMs */
}

The code draws heavily (and shamelessly :wink: ) from the samples (in pwm.h &pwm.c). I just removed extra fluff and customized a bit to my needs. I hope this will work as a starting point.

Cheers.


#7

I don’t completely understand this, though I get the general idea. I haven’t seen the email yet, so if you have already sent it let me know, maybe you need to give me your email and I’ll email you. I have a couple more questions, which will probably give away my newb-ness to this, here goes:

You are controlling 3 pins (1,2 and 5) with PWM, running them at 200Hz and turning off/on from .NET, correct? It doesn’t look like you are precisely controlling when the 200Hz starts and stops. Why aren’t you able to just take PWM pin from .NET and set it to go at 200Hz?

There are lots of variables set here that don’t appear to be used so I’m guessing they are used elsewhere, such as PWM1TCR. Can you give more context so I can see how they are used or are they in pwm.c/h? Where is pwm.c/h found or is that what you are emailing me?

Is Pclk the clock used to drive PWM? How did you arrive at 18Mhz?

I get how the cycle width gives you 200Hz (5000 * 200 = 1M), what is nominal center for? Is it because there is some delay in the PWM turning on and off which makes it 3000 and not 5000 cycles long?

In my case I need to take a given binary rep of a byte, say 10010100 and generate PWM in sequence - A low tone, say 1000Hz for all the zeros and a high tone, say 1200Hz for the ones with the timing between bits based on the baud rate. I think I’ll just be doing the same thing you have in here, but I’ll be doing the content of Pwm_Start and Stop inside one method based on an incoming byte array, doing the above per byte. Sound reasonable?

Here is the code I’m using right now that only works for low baud rates since once I reach 100 baud the Thread.Sleep is around 10ms which becomes not terribly reliable.


        const int CHARBITS = 5;
        const int START = 1;
        const int STOP = 1;
        const int BAUD = 75;
        static int INTER_BIT_DELAY = (1000 / BAUD);

        //tones in Hz for low(space) and high(mark)
        const int HIGH = 2295;
        const int LOW = 2125;

        private static void SendByte(PWM signal, byte c)
        {
            for (int i = 0; i < START; i++)
                RTTYBit(signal, 0);

            int b = 1;
            for (int i = 0; i < CHARBITS; i++, b *= 2)
                RTTYBit(signal, c & b);

            for (int i = 0; i < STOP; i++)
                RTTYBit(signal, 1);
        }

        static void RTTYBit(PWM signal, int b)
        {
            signal.Set(b > 0 ? HIGH : LOW, 90);

            Thread.Sleep(INTER_BIT_DELAY);
        }


#8

I may be simplistic, but I think I would go about things like this.

  1. Set PWM channel to correct frequency
  2. Feed PWM output through an AND gate
  3. Use output compare to modulate the output by controlling other input to AND gate

#9

That’s an interesting idea, I may take a look at that if I can’t get a software only solution to work. I’ve been looking at the RLP docs and it has a delay method that takes microseconds and some scheduling methods so I think I may be able to do almost my original method below once I can get PWM working in RLP.


#10

If I remember well, RTTY is not a modulation with a frequency or nothing like CW/morse code, but between 2 different frequencies for the 0 and the 1s (“the shift”). so an AND gate would probably not do it with one PWM.

You would have to use 2 PWM: one for the space, one for the mark frequency sending their outputs at all time. And use a little more logic than just and AND gate go have the 0 select one PWM, and the 1 select the other PWM…

maybe a first AND gate with first PWM and you outputcompare pin
another AND gate for second PWM with an inversing input for the outputcompare pin
And a last OR gate for combine both AND outputs.

Not sure however this is possible to have 2 PWM with 2 différent frequencies, as the document says:

If you don’t want to go hardware, go RLP :wink:


#11

It is two frequencies but never both at the same time. That is a bummer I can’t start the two PWMs and leave them running then switch in the one that is needed though. I guess I’ll have to turn each one on and off and somehow deal with the delay of doing so.


#12

Oops. I guess my post was more of a hindrance than a help. Sorry about that.

One problem of going down to the RLP level is that one may have to deal with the processor in its unpolished, raw version. I did send an email with the links to code and docs. But I’ll just go ahead and post them here and ask for forgiveness later if it isn’t appropriate. :slight_smile:

Manual: http://ics.nxp.com/support/documents/microcontrollers/pdf/user.manual.lpc23xx.pdf

Code: http://www.standardics.nxp.com/support/documents/microcontrollers/zip/code.bundle.lpc23xx.lpc24xx.uvision.zip

The weird variables you see sprinkled around in the code are registers in the processor. They are declared in the main header file for the chip LPC23xx.h and in pwm.h. Both headers are included in the sample code available at the link. These registers are the means to configure the functions of the chip. The Pclk I mentioned is just a value used by the PWM to generate its timings and is described in the chip manual. But I think you can ignore that as long as you know that the minimum possible period for a cycle of the PWM is 1/18 of a microsecond.

The way I have this configured, .NET initializes the PWMs using the init function and then starts them. Afterward it further controls them with other functions not shown in the example. But all the logic needed to manipulate the PWMs (in my case) is included in the code above.

The other values, like CYCLE_WIDTH and NOMINAL_CENTER relate to servo motors, so they probably won’t make sense in your context. Anyhow, the net effect is to create a wave with a total width of 5ms of which 1.5ms is active (that is, non-zero) resulting in a duty cycle of 30%.

Since .NET and the low-level C world of RLP are not directly compatible, there is some marshaling happening when you send or receive values from the “other” side. In view of this it may be better to minimize exchanges of data. One way I would do this is passing the binary value to represent as tones down to the RLP level and do all the waveform generation there.

From what I see in your description, it seems to me that you could have a permanent 50% duty cycle and play with the width of the full cycle instead (the frequency of the resulting wave). If you use the task scheduling part of the RLP class, you can set very precise delays where you wait to generate the next tone.

Again, sorry if you didn’t get the email. Knowing myself I probably botched the address.Mine is mailbox then my name sans the q and then net as the last component.

Cheers.


#13

Hmm, i do seem to recall that all the PWM channels share the same clock. If the two frequencies can be multiples of each other than you could still use one PWM at the higher (or lower) frequency and use a frequency halving/doubler circuit to change between mark and space. I know it means more hardware but I’m throwing it out there as food for thought.

I’m thinking back to my discrete logic days to try and think of a way to double/halve a frequency with just a few NAND gates. Changing output states on each edge of the incoming signal would double the output frequency easy enough.

A scheduled task in RLP is probably a very good way to handle the problem though…

Oh, just thought of something else. You can create an astable multivibrator with two NAND gates and few discrete components.

EDIT: Just found this with Bing: http://freecircuitdiagram.com/2010/05/30/frequency-doubler-with-4011/

EDIT#2: More thinking and searching and I found this: http://www.electronics-tutorials.ws/sequential/seq_4.html , frequency divider with a flip-flop. I should have though of that earlier but if you don’t do a lot of discrete logic circuits for years you forget stuff :slight_smile: So now I am musing about how to use the second flip flop in a standard IC to select the full or divided frequency…


#14

I don’t think the divider stuff will work because the lower freq needs to be 170hz away from the upper, the freqs in the example are the typical ones used. Also I’d like to try and just use my Panda if possible. I’ll try out this stuff tonight and see how far I get and will probably post some more specific questions later.


#15

David, the stuff you posted was really helpful (though getting that source to compile was another matter), I’m getting very close. With the below code I can call from .NET like the below. I won’t actually implement this way, this is just a convenient way to test it and look at it on an oscilloscope.


//frequency in hz, % duty cycle, microseconds duration
 PWMBlink.Invoke(1200, 50, 10000 * 1000);

This gives a 1200hz signal at 50% duty cycle for 20 seconds.

RLP:


 #include "../RLP.h"

 #include "LPC23xx.h"
 #include "type.h"
 #include "pwm.h"

 #define MICROSECOND   18 
 
int PwmStart(unsigned int frequencyHz, unsigned char duty)
{
    PINSEL3 |= 0x20220;	/* set GPIOs for all PWM1, PWM2 & PWM5 */
 
    PWM1TCR = TCR_RESET;	/* Counter Reset */ 
 
    PWM1PR = 0x00;			/* count frequency:Fpclk */
    PWM1MCR = PWMMR0R;	    /* reset on PWMMR0, reset TC if PWM0 matches */				
 
	long CYCLE_WIDTH = (1000000 / frequencyHz) * MICROSECOND;
    PWM1MR0 = CYCLE_WIDTH;		/* set PWM cycle */
 
	long rate = CYCLE_WIDTH * (duty / 100.0);
    PWM1MR1 = rate;
    PWM1MR2 = rate;
    PWM1MR5 = rate;
 
    /* PWM latches enabled, ready to start */
    PWM1LER = LER0_EN | LER1_EN | LER2_EN | LER5_EN;

    /* All single edge, all enable */
    PWM1PCR = PWMENA1 | PWMENA2 | PWMENA5;
    PWM1TCR = TCR_CNT_EN | TCR_PWM_EN;	    /* counter enable, PWM enable */

    return (TRUE);
}
 
void PwmStop()
{
    PWM1PCR = 0;
    PWM1TCR = 0x00;		/* Stop all PWMs */
}

int PWMBlink(unsigned int *generalArray, void **args, unsigned int argsCount, unsigned int *argSize)
{
	unsigned int frequencyHz = *(unsigned int*)args[0];
	unsigned char duty = *(unsigned char*)args[1];
	unsigned long microseconds = *(long*)args[2];

	PwmStart(frequencyHz, duty);
	RLPext->Delay(microseconds);
	PwmStop();
	
	return 0;
}


#16

Make use of the code tags…they make your code more readable !

Greetings


#17

Paul,

I’m glad it helped you a bit after all. Last night I was thinking that I had only succeeded in muddying the waters. At any rate, it took me a while to wrap my head around the whole .NET/RLP thing, and that after being an embedded developer for quite some time already. I wish the required docs and guidance were more readily available. Yet, gotta love this lively forums, they are a lot of help. :slight_smile:

Cheers.