Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

PIC 16C73 PWM question

13 views
Skip to first unread message

Dennis Clark

unread,
Aug 9, 1999, 3:00:00 AM8/9/99
to
Sigh,

No matter how many Microchip poop-sheets I read, I always run up
against the "what the heck does that mean" type of issues. So, for any
in the know out there I have a question (before I fire the query off to
Microchip). Once you have programmed the period and the duty_cycle
sections of the PIC 16C73 (72 or prolly '74 too). It should hold that
PWM value without further processor intervention correct? It _seems_
this is what the sheet is saying, but all of the PWM app notes seem to
want to do programmed I/O on the TMR2 interrupt on rollover. Why the
heck even have a PWM module if it can't keep track of the PWM. So,
which is it, can the PWM outputs keep themselves on target, or is the
"user" required to set the PWM out bit on TMR2 rollover?

thanks ever so much,
DLC
PS
I saw this very same thing with the 68HC11. You could use combinations of
the timer registers with OC on the HC11 to do automatic PWM, but every darn
code snippet I saw for it always did a timer rollover interrupt...
--
------------------------------------------------------------
| Dennis Clark email d...@verinet.com |
| Be well, Do good work, stay in touch -- Garrison Keillor |
------------------------- CUT HERE -------------------------

engr

unread,
Aug 9, 1999, 3:00:00 AM8/9/99
to
Dennis Clark wrote:
>(snip)
> Once you have programmed the period and the duty_cycle
> sections of the PIC 16C73 (72 or prolly '74 too). It should hold that
> PWM value without further processor intervention correct?

Yes, that's right. Here's some C code that I wrote for a project
that used PWM on a PIC. It should demonstrate how to do it.

//-------------------------------------------------------------------------
// The following code was done for a PIC 16C77, with the CCS PCM C
compiler.
// http://www.ccsinfo.com

// The CCS PCM compiler has functions to deal with the PIC's PWM
hardware,
// such as: set_pwm1_duty(), setup_ccp1(), etc.
// The following code provides a higher level interface, so we can
specify
// the PWM duty cycle as a percentage, instead of a hardware value
(which
// is what the CCS functions expect).

//=========================================================================
// Motor speeds (defined in terms of duty cycle)
#define MOTOR_SPEED_ZERO 0
#define MOTOR_SPEED_SLOW 42
#define MOTOR_SPEED_MEDIUM 64
#define MOTOR_SPEED_FAST 91
#define MOTOR_SPEED_MAX 100

// Motor direction
#define MOTOR_DIR_FORWARD 1
#define MOTOR_DIR_REVERSE 0

// The 2nd motor always rotates in the same direction.
// All we can do is define its speed.
// A PWM duty_cycle of 88 gives a speed of 600 rpm.
#define MOTOR2_SPEED_NORMAL 88

// The PIC can only have one PWM freq for both the motors, so we have
// to pick one that works well with both.
// If you go too low, then you can actually hear it whine at that freq
(4 KHz).

#define PWM_FREQ 13 // 13 KHz

//-----------------------------------------------------------------------
// GLOBALS

// PWM variables
char t2_divisor; // Timer 2 is used for pwm
char pwm_freq_in_khz; // 1 to 30 KHz


//==========================================================================
// FUNCTIONS

// This routine inits the PIC for PWM, and also sets the pwm freq.
// Once the freq is set, we don't need to change it again.
// The same freq is used for both motor1 and motor2 (due to PIC design).
// Whenever the freq is changed, the duty cycle values must be updated
// right after that, because they depend on a parameter used by the freq
// setting routine.

void init_pwm(void)
{
// Configure CCP1 and CCP2 for PWM output.
setup_ccp1(CCP_PWM);
setup_ccp2(CCP_PWM);

set_pwm_freq(PWM_FREQ);

// Set duty cycle after setting freq
set_motor1_pwm_duty_cycle(MOTOR_SPEED_ZERO);
set_motor2_pwm_duty_cycle(MOTOR_SPEED_ZERO);
}

//----------------------------------------------------------------------
// How to calculate PWM values for the PIC --

/*
The T2 prescaler (1, 4, or 16) must be chosen to keep the T2 divisor
less than 256, but as close to 256 as possible.

2,000,000
t2_divisor = ------------------------ -1
pwm_freq * t2_prescaler


8,000,000
PWM resolution in bits = The antilog of 2, of -------------
pwm freq


(Note: It looks like I rounded the PWM bits down to the nearest
integer.
The bits column is just for information only. It's not needed by the
code.)

PWM Freq T2 T2 PWM resolution
in KHz Prescalar Divisor in bits (approx.)
1 16 124 7
2 16 249 7
3 4 165 7
4 4 124 7
5 4 99 6
6 4 82 6
7 4 70 6
8 1 249 7
9 1 221 7
10 1 199 7
11 1 180 7
12 1 165 7
13 1 152 7
14 1 141 7
15 1 132 7
16 1 124 6
17 1 116 6
18 1 110 6
19 1 104 6
20 1 99 6
. . .
. . .
30 1 65 6


The t2_divisor and t2_prescaler are programmed with this CCS function:

setup_timer_2(t2_prescaler, t2_divisor, 1);

//-------------------------------------------------------------
// Duty Cycle calculations:
// The duty cycle value is just a percentage of the t2_divisor.


Duty_cycle_in_percent
duty_cycle_value = --------------------- * T2 divisor
100


The duty cycle value must be less than the t2_divisor,
in the chart above. This is guaranteed by only doing 1-99%.
If you want 100% duty cycle, then make the duty cycle value
equal to the t2 divisor.


The duty cycle values are programmed with these CCS functions:

set_pwm1_duty(duty_cycle_value);
set_pwm2_duty(duty_cycle_value);

*/


//-------------------------------------------------------------------------
// Set the PWM duty cycle of the main motor, specified as a percentage,
// from 0 to 100. A value of 0 will shut off the motor, and a value
// of 100 will run it at full speed. The main motor uses PWM1 in the
PIC.
// Note: In this compiler, a char is 8 bits (unsigned), and a long is
// 16 bits (unsigned).

void set_motor1_pwm_duty_cycle(char duty_cycle)
{
char duty_cycle_value; // 8 bit

duty_cycle_value = (char)(((long)duty_cycle * ((long)t2_divisor +1)
)/(long)100);

set_pwm1_duty(duty_cycle_value);
}


//-------------------------------------------------------------------------
// Set the PWM duty cycle of motor2, specified as a percentage,
// from 0 to 100. A value of 0 will shut off the motor, and a value of
// 100 will run it at full speed. This motor uses PWM2 in the PIC.

void set_motor2_pwm_duty_cycle(char duty_cycle)
{
char duty_cycle_value; // 8 bit

duty_cycle_value = (char)(((long)duty_cycle * ((long)t2_divisor +1)
)/(long)100);

set_pwm2_duty(duty_cycle_value);
}

//-------------------------------------------------------------------------
// This routine sets the specified PWM frequency.
// The same PWM freq must be used for both the main motor and the
// other motor. That's due to a limitation in the PIC processor.
// Note that in this compiler, a "long" is a 16 bit unsigned number.

void set_pwm_freq(char pwm_freq_in_khz)
{
char t2_prescaler_programmed_value;
char t2_prescaler;

// Choose the T2 prescaler. (Note: These are not the same as the
constants
// that are programmed for the T2 prescaler. Those are set later on.)

if(pwm_freq_in_khz <= 2)
t2_prescaler = 16;
if((pwm_freq_in_khz > 2) || (pwm_freq_in_khz < 8))
t2_prescaler = 4;
if(pwm_freq_in_khz >= 8)
t2_prescaler = 1;

// Calculate the T2 divisor.
// How we get the magic number of 2000:
// The PIC osc freq of 8000000 MHz osc is divided by 4 to get the
// instruction cycle clock freq of 20000000. Then, since we are
specifying
// the PWM freq in KHz, we can pre-divide the 2000000 by 1000 to get
2000.
// We're doing it this way to use integer math instead of floats,
// which consume a lot of ROM space in the PIC.

t2_divisor = (char)((2000/((long)pwm_freq_in_khz * (long)t2_prescaler ))
- 1);


// Select the T2 prescaler constant that is actually programmed.

switch(t2_prescaler)
{
case 1:
t2_prescaler_programmed_value = T2_DIV_BY_1;
break;
case 4:
t2_prescaler_programmed_value = T2_DIV_BY_4;
break;
case 16:
t2_prescaler_programmed_value = T2_DIV_BY_16;
break;
default:
t2_prescaler_programmed_value = T2_DIV_BY_1;
break;
}


// Program the values we calculated above.

setup_timer_2(t2_prescaler_programmed_value, t2_divisor, 1);


// Since the duty cycle is set as a percentage of the t2_divisor,
// if you change the freq and thus the t2_divisor, you also need
// to update the duty cycle.
// We'll just remember to do this, in external code. Then we
// don't need to deal with two more global pwm variables.

}

//------------------------------------------------------------------------
// end of file

Dennis Clark

unread,
Aug 9, 1999, 3:00:00 AM8/9/99
to
engr (en...@none.com) wrote:

: Dennis Clark wrote:
: >(snip)
: > Once you have programmed the period and the duty_cycle
: > sections of the PIC 16C73 (72 or prolly '74 too). It should hold that
: > PWM value without further processor intervention correct?

: Yes, that's right. Here's some C code that I wrote for a project
: that used PWM on a PIC. It should demonstrate how to do it.
: //-------------------------------------------------------------------------

[snip of code]

Thanks, I _thought_ I was correct this time! Now on to figure math for
trapazoidal servo motor ramps and encoder feedback...

DLC

David Brown

unread,
Aug 10, 1999, 3:00:00 AM8/10/99
to

Dennis Clark wrote in message <7on4i6$oc7$1...@fcnews.fc.hp.com>...

>Sigh,
>
> No matter how many Microchip poop-sheets I read, I always run up
>against the "what the heck does that mean" type of issues. So, for any
>in the know out there I have a question (before I fire the query off to
>Microchip). Once you have programmed the period and the duty_cycle

>sections of the PIC 16C73 (72 or prolly '74 too). It should hold that
>PWM value without further processor intervention correct? It _seems_
>this is what the sheet is saying, but all of the PWM app notes seem to
>want to do programmed I/O on the TMR2 interrupt on rollover. Why the
>heck even have a PWM module if it can't keep track of the PWM. So,
>which is it, can the PWM outputs keep themselves on target, or is the
>"user" required to set the PWM out bit on TMR2 rollover?
>
>thanks ever so much,
>DLC
>PS
> I saw this very same thing with the 68HC11. You could use combinations
of
>the timer registers with OC on the HC11 to do automatic PWM, but every darn
>code snippet I saw for it always did a timer rollover interrupt...
>--

The datasheet is correct - you do not need to do anything on TMR2 interrupt.
It can be useful if you want to synchronise something with the PWM signal,
but the PWM itself runs automatically.

Be very careful when looking at application notes and sample programs (this
applies to most microcontrollers). They are frequently long out of date (on
Nat. Semi.'s list of application notes, brand new details of current
microcontrollers are mixed in with application notes for micros that are 15
years old). I haven't read the application notes you are referring to, but
often old notes make use of tricks or workarounds that are no longer
necessary on modern versions of the parts.

In addition, many of the sample programs are apaulingly badly written -
sparce, meaningless comments, all capitals, terse labels, magic numbers,
fixed memory addresses and the rest. This is a real pain, and it means that
application notes with, for example, floating point arithmetic routines, are
almost unusable in other programs because you cannot link them together.

Of course, this is beside the point - the answer to your question is in the
first two lines. It is just good to rant at the manufacturers every now and
again.

David Brown

David Brown

unread,
Aug 10, 1999, 3:00:00 AM8/10/99
to
It is very generous of you to share your code with us, and it clearly
answers the question of how PWM works on the PIC, but ... can we use this C
code as an example in the assembly vs. C debate running at the moment? You
took some 260 lines of C code for a task that can be accomplished in about
20 lines of assembler. You have an enormous overhead in code size, RAM size
and execution time for calculations (often using big and slow 32-bit
integers) and parameter passing, for code that is mostly based on constants.
Even if you actually needed variable frequencies, your code is way over the
top.

The trick with implementing PWMs (whether in C or assembly) is to pick
scalings and frequencies that avoid too many calculations. For example,
there is often no particular reason for expressing duty cycles as a
percent - a 0 to 255 value may be easier to work with in the rest of the
program, and for many purposes a completly arbitrary range will work just as
well (especially if you use constant values for different speeds, as you
have done here). Similarly, there is no reason for picking 13 KHz as an
exact value - generally you have an idea of roughly the frequency required,
but neither the PIC nor most electronics are going to mind if you have 13
KHz or 13.4 KHz or whatever - pick a frequency that makes the numbers easy
for the PIC, not for thinking in decimal.

As an example, if you have an 8 MHz crystal and set the timer2 prescaler to
1:1, try a timer2 period of 100. This will give you 20 KHz PWM output, with
a 0 to 100 duty cycle. Or a timer2 period of 128 for a 15.6 KHz output and
a 0 to 128 duty cycle. The setup code is about 10 lines of assembly, and
setting a new duty cycle is done with a single "movwf" instruction. The C
code, if you prefer, is equally easy. The code space and execution time is
minimal, and no RAM space is used at all. Best of all (from the development
side), the code is short and clear - quickly written, and you can be sure it
is correct.

David Brown


engr wrote in message <37AF30...@none.com>...


>Dennis Clark wrote:
>>(snip)
>> Once you have programmed the period and the duty_cycle
>> sections of the PIC 16C73 (72 or prolly '74 too). It should hold that
>> PWM value without further processor intervention correct?
>
>Yes, that's right. Here's some C code that I wrote for a project
>that used PWM on a PIC. It should demonstrate how to do it.
>

> (snip about 260 lines of C code)

Stuart Tyrrell

unread,
Aug 11, 1999, 3:00:00 AM8/11/99
to
In message <7op43k$smp$2...@thelma.netpower.no>
"David Brown" <david....@westcontrol.com> wrote:

> It is very generous of you to share your code with us, and it
> clearly answers the question of how PWM works on the PIC, but ...
> can we use this C code as an example in the assembly vs. C debate
> running at the moment? You took some 260 lines of C code for a task
> that can be accomplished in about 20 lines of assembler. You have
> an enormous overhead in code size, RAM size and execution time for
> calculations

[snip examples of how implementation could be optimised]

> The C code, if you prefer, is equally easy. The code space and
> execution time is minimal, and no RAM space is used at all. Best of
> all (from the development side), the code is short and clear -
> quickly written, and you can be sure it is correct.

Ah... now /there's/ the point!

The problem isn't necessarily with the implementation using C per se,
but in that C lends itself to a little too much abstraction from the
machine code itself - C makes it easy to write inefficent code.

I, personally, have always been an "assembler man". I've picked holes
in the list files that PIC C programmers have sent me in the past. I
deliberately steered away from C implementations because they were
inefficient.

It took me a while to realise that a lot of what I saw was down to the
C programmer having little thought to the architecture they were
compiling for.

I've recently started using the CCS compiler for a few projects, and
I'm impressed on the whole. It can be a little inefficient in places,
but this can be aided with sensible coding. If push comes to shove I
can include assembly (I've cursed CCS that their system doesn't
integrate with MPLINK!) - this is essential for some projects as the
CCS interrupt dispatcher is a bit too "general purpose" for most of my
applications.

What I find most interesting is that I'd class myself as a competant
PIC assembly programmer, but the compiler sometimes finds some nice
little trick (especially when doing maths) that I wouldn't have come
up with in a million years!

Stuart.
--
Stuart Tyrrell Developments Stu...@stdevel.demon.co.uk
PO Box 183, OLDHAM. OL2 8FB http://www.stdevel.demon.co.uk
Tel: 01706 848 600 Orange: 0976 255 256 dFax: 0870 164 1604

engr

unread,
Aug 11, 1999, 3:00:00 AM8/11/99
to
David Brown wrote:
>
> It is very generous of you to share your code with us, and it clearly
> answers the question of how PWM works on the PIC, but ... can we use this C
> code as an example in the assembly vs. C debate running at the moment? You
> took some 260 lines of C code for a task that can be accomplished in about
> 20 lines of assembler.

But not with the same functionality. I wrote the code so it
would be easy to understand and maintain, as well as having
a high level interface so it's easy to talk to.

> You have an enormous overhead in code size, RAM size

Not really, given that I use large PICs (16C77 with 8k ROM).
I just did a test, where I compiled a small program which
calls set_motor1_pwm_duty_cycle() and set_pwm_freq().
The compiler uses some resources for overhead, so I compiled
once without calling those 2 routines, to find out what the
overhead is. The results:

Additional ROM used by the two routines: 226 locations
Additional RAM used by the two routines: 12 locations

So with a PIC that has 8192 ROM and 368 RAM, this is about
3% usage for each. That's fine with me.

> and execution time for calculations (often using big and slow 32-bit
> integers)

Execution time is not an issue, because in my application, I'm not
constantly changing the PWM parameters. Also, I'm controlling
a motor, and if it takes 20 usec or 20 ms to setup the parameters,
it doesn't matter.

> and parameter passing, for code that is mostly based on constants.
> Even if you actually needed variable frequencies, your code is way over the
> top.

It's all a matter of philosophy. When I was younger, in the DOS days,
I used to pride myself on doing everything in Intel assembly.
I got older and my values changed.

>
> The trick with implementing PWMs (whether in C or assembly) is to pick
> scalings and frequencies that avoid too many calculations. For example,
> there is often no particular reason for expressing duty cycles as a
> percent - a 0 to 255 value may be easier to work with in the rest of the
> program, and for many purposes a completly arbitrary range will work just as
> well (especially if you use constant values for different speeds, as you
> have done here). Similarly, there is no reason for picking 13 KHz as an
> exact value - generally you have an idea of roughly the frequency required,
> but neither the PIC nor most electronics are going to mind if you have 13
> KHz or 13.4 KHz or whatever - pick a frequency that makes the numbers easy
> for the PIC, not for thinking in decimal.

That's all true, but I wanted to do it my way. We wanted to be able to
hook up a portable computer to the motor controller board, and send
commands through the serial port. And, we wanted to be able to
do it from a high level, conceptually. We wanted percentages.

> As an example, if you have an 8 MHz crystal and set the timer2 prescaler to
> 1:1, try a timer2 period of 100. This will give you 20 KHz PWM output, with
> a 0 to 100 duty cycle. Or a timer2 period of 128 for a 15.6 KHz output and
> a 0 to 128 duty cycle. The setup code is about 10 lines of assembly, and

> setting a new duty cycle is done with a single "movwf" instruction. The C


> code, if you prefer, is equally easy. The code space and execution time is
> minimal, and no RAM space is used at all. Best of all (from the development
> side), the code is short and clear - quickly written, and you can be sure it
> is correct.
>

> David Brown
>
> engr wrote in message <37AF30...@none.com>...

> >Dennis Clark wrote:
> >>(snip)
> >> Once you have programmed the period and the duty_cycle
> >> sections of the PIC 16C73 (72 or prolly '74 too). It should hold that
> >> PWM value without further processor intervention correct?
> >
> >Yes, that's right. Here's some C code that I wrote for a project
> >that used PWM on a PIC. It should demonstrate how to do it.
> >

David Brown

unread,
Aug 12, 1999, 3:00:00 AM8/12/99
to

Stuart Tyrrell wrote in message <493007466E%Stu...@stdevel.demon.co.uk>...
>In message <7op43k$smp$2...@thelma.netpower.no>

> "David Brown" <david....@westcontrol.com> wrote:
>
>> It is very generous of you to share your code with us, and it
>> clearly answers the question of how PWM works on the PIC, but ...
>> can we use this C code as an example in the assembly vs. C debate
>> running at the moment? You took some 260 lines of C code for a task
>> that can be accomplished in about 20 lines of assembler. You have
>> an enormous overhead in code size, RAM size and execution time for
>> calculations
>

>[snip examples of how implementation could be optimised]
>
>> The C code, if you prefer, is equally easy. The code space and
>> execution time is minimal, and no RAM space is used at all. Best of
>> all (from the development side), the code is short and clear -
>> quickly written, and you can be sure it is correct.
>
>Ah... now /there's/ the point!
>
>The problem isn't necessarily with the implementation using C per se,
>but in that C lends itself to a little too much abstraction from the
>machine code itself - C makes it easy to write inefficent code.
>
>I, personally, have always been an "assembler man". I've picked holes
>in the list files that PIC C programmers have sent me in the past. I
>deliberately steered away from C implementations because they were
>inefficient.
>
>It took me a while to realise that a lot of what I saw was down to the
>C programmer having little thought to the architecture they were
>compiling for.
>

To write C for small 8-bit microcontrollers which compiles to efficient
(ROM, RAM and time) code involves a lot of optomizing of the source code.
You need to be thinking about how the compiler will generate the code, and
write accordingly - choices such as when to use automatic variables or
static variables can make a huge difference.


>I've recently started using the CCS compiler for a few projects, and
>I'm impressed on the whole. It can be a little inefficient in places,
>but this can be aided with sensible coding. If push comes to shove I
>can include assembly (I've cursed CCS that their system doesn't
>integrate with MPLINK!) - this is essential for some projects as the
>CCS interrupt dispatcher is a bit too "general purpose" for most of my
>applications.
>
>What I find most interesting is that I'd class myself as a competant
>PIC assembly programmer, but the compiler sometimes finds some nice
>little trick (especially when doing maths) that I wouldn't have come
>up with in a million years!
>

It's a two-way game. Most of the time you will be able to write more
efficient code than the compiler, but sometimes the compiler will produce
very neat code - it depends on the compiler-writer, and also very much the
library writer. The libraries are often written in heavily hand-optomized
assembly code.

Doing heavy maths is a lot easier in C than assembly. It is also sometimes
more efficient, as for difficult problems you need to emphasize readability
over efficiency to keep track of the algorithm in assembly. Either way,
heavy maths is time and code consuming, and best avoided if possible on
chips like the PIC.

David Brown

David Brown

unread,
Aug 12, 1999, 3:00:00 AM8/12/99
to

engr wrote in message <37B1D6...@none.com>...

>David Brown wrote:
>>
>> It is very generous of you to share your code with us, and it clearly
>> answers the question of how PWM works on the PIC, but ... can we use this
C
>> code as an example in the assembly vs. C debate running at the moment?
You
>> took some 260 lines of C code for a task that can be accomplished in
about
>> 20 lines of assembler.
>
>But not with the same functionality. I wrote the code so it
>would be easy to understand and maintain, as well as having
>a high level interface so it's easy to talk to.

I have just written assembly routines to control a PWM channel. The high
level interface has a routine to set the frequency, and a routine to set the
duty cycle as a 0..255 value, independant of the frequency. This comes to
about 100 lines of assembly (have of which are comments or blank lines for
easy reading), plus a 20 line multiply routine which is also used elsewhere
in the program. In my (completly biased) opinion, it is a lot easier to
understand and maintain than your code.

>
>> You have an enormous overhead in code size, RAM size
>

>Not really, given that I use large PICs (16C77 with 8k ROM).
>I just did a test, where I compiled a small program which
>calls set_motor1_pwm_duty_cycle() and set_pwm_freq().
>The compiler uses some resources for overhead, so I compiled
>once without calling those 2 routines, to find out what the
>overhead is. The results:
>
>Additional ROM used by the two routines: 226 locations
>Additional RAM used by the two routines: 12 locations
>
>So with a PIC that has 8192 ROM and 368 RAM, this is about
>3% usage for each. That's fine with me.
>

And what about the library code, with all those maths functions? How much
ROM and RAM is used when the program does nothing but call these two
routines?

>> and execution time for calculations (often using big and slow 32-bit
>> integers)

>
>Execution time is not an issue, because in my application, I'm not
>constantly changing the PWM parameters. Also, I'm controlling
>a motor, and if it takes 20 usec or 20 ms to setup the parameters,
>it doesn't matter.
>


That's the key to producing bloatware - I have lots of space and time, so it
does not matter how inefficient the program is. That makes a lot of sense
if you are saving significantly on development time or maintainance time,
but in this case I really don't think you are.


>> and parameter passing, for code that is mostly based on constants.
>> Even if you actually needed variable frequencies, your code is way over
the
>> top.
>
>It's all a matter of philosophy. When I was younger, in the DOS days,
>I used to pride myself on doing everything in Intel assembly.
>I got older and my values changed.
>

Intel assembly is horrible - I can see why you wanted to change.

For constants that might be changed during development, but not at run-time,
assembly variables let me keep everything as flexable as your program but it
is the assembler that does the work in the calculations, not the PIC. You
can do the same with C #defines, but then you make a mess of the C program
and lose any readability advantages you may have.


>>
>> The trick with implementing PWMs (whether in C or assembly) is to pick
>> scalings and frequencies that avoid too many calculations. For example,
>> there is often no particular reason for expressing duty cycles as a
>> percent - a 0 to 255 value may be easier to work with in the rest of the
>> program, and for many purposes a completly arbitrary range will work just
as
>> well (especially if you use constant values for different speeds, as you
>> have done here). Similarly, there is no reason for picking 13 KHz as an
>> exact value - generally you have an idea of roughly the frequency
required,
>> but neither the PIC nor most electronics are going to mind if you have 13
>> KHz or 13.4 KHz or whatever - pick a frequency that makes the numbers
easy
>> for the PIC, not for thinking in decimal.
>
>That's all true, but I wanted to do it my way. We wanted to be able to
>hook up a portable computer to the motor controller board, and send
>commands through the serial port. And, we wanted to be able to
>do it from a high level, conceptually. We wanted percentages.
>

Fair enough. My suggestions were for typical small applications - your
needs are different here.

David Brown

>> As an example, if you have an 8 MHz crystal and set the timer2 prescaler
to
>> 1:1, try a timer2 period of 100. This will give you 20 KHz PWM output,
with
>> a 0 to 100 duty cycle. Or a timer2 period of 128 for a 15.6 KHz output
and
>> a 0 to 128 duty cycle. The setup code is about 10 lines of assembly, and

>> setting a new duty cycle is done with a single "movwf" instruction. The


C
>> code, if you prefer, is equally easy. The code space and execution time
is
>> minimal, and no RAM space is used at all. Best of all (from the
development
>> side), the code is short and clear - quickly written, and you can be sure
it
>> is correct.
>>

>> David Brown
>>
>> engr wrote in message <37AF30...@none.com>...

>> >Dennis Clark wrote:
>> >>(snip)
>> >> Once you have programmed the period and the duty_cycle
>> >> sections of the PIC 16C73 (72 or prolly '74 too). It should hold that
>> >> PWM value without further processor intervention correct?
>> >
>> >Yes, that's right. Here's some C code that I wrote for a project
>> >that used PWM on a PIC. It should demonstrate how to do it.
>> >

Wayde Nie

unread,
Aug 12, 1999, 3:00:00 AM8/12/99
to David Brown
On Thu, 12 Aug 1999, David Brown wrote:

> I have just written assembly routines to control a PWM channel. The high

Can you post your assembly version?

> ----------------------------------------------------------------------- <
> Wayde Nie <
> Software Analyst Computing and Information Services <
> phone: (905)525-9140 ext 23856 McMaster University, CANADA <
> fax: (905)524-5288 ni...@mcmaster.ca <
> -------------------------[\]--->=\o.:---[\]---------------------------- <


David Brown

unread,
Aug 13, 1999, 3:00:00 AM8/13/99
to

Wayde Nie wrote in message ...

>On Thu, 12 Aug 1999, David Brown wrote:
>
>> I have just written assembly routines to control a PWM channel. The high
>
>Can you post your assembly version?
>
>> ----------------------------------------------------------------------- <
>> Wayde Nie <
>> Software Analyst Computing and Information Services <
>> phone: (905)525-9140 ext 23856 McMaster University, CANADA <
>> fax: (905)524-5288 ni...@mcmaster.ca <
>> -------------------------[\]--->=\o.:---[\]---------------------------- <
>

You are going to have to guess at my macro definitions, but most of the code
should be clear enough. The mulitply routine runs to about another 20 bytes
and multiplies varALo and varBLo to get a 16-bit result varCHi:varCLo (these
variables and routines are used elsewhere in the program - hence the
slightly odd names).

; Implementation file for PWM control

; Exported routines:
; InitPwm = Initialise the pwm subsystem
; KillPwm = Stop all output
; SetFrequency = Set the desired pwm frequency (w = 0..127 for 12..25 kHz)
; SetDutyCycle = Set the duty cycle to w/256

; Exported data:
; dutyCycle = duty cycle 0..255

#define __pwmImplementation__

Include "pic1673a.inc"
Include "design.inc"
Include "maths.inc"

PwmData: BeginData

pwmTemp: byte ; Temporary storage during calculations
dutyCycle: byte ; Storage for the set duty cycle


Global dutyCycle

PwmCode: BeginCode

InitPwm:
Global InitPwm
KillPwm:
Global KillPwm
BankSel ccp1Con
movlw 0x00
movwf ccp1Con ; CCP1 off
movwf ccp2Con ; CCP2 off
ResetBit pwmFrequency ; Freq. pin off
ResetBit pwmVoltage ; Voltage pin off
BankSel tmr2Period
movlw 205
movwf tmr2Period ; Initial period = 205 for 12 kHz
Bank0
movlw 0
movwf ccpReg1Lo ; Initial duty cycle of 0
movwf ccpReg2Lo ; Initial duty cycle of 0
SetBit tmr2On ; Start timer 2
clrf dutyCycle
return

SetFrequency:
Global SetFrequency
call CalcPeriod ; Get correct period value
movwf pwmTemp
BankSel tmr2Period
DisableInts ; Enter critical section
movwf tmr2Period ; Change period
Bank0
ResetCarry
rrf pwmTemp, w ; Get half period
movwf ccpReg1Lo ; On-time for frequency generator
movlw 0x0c ; Command for CCP as PWM
IfBit carry
iorlw 0x20 ; LSB of PWM
movwf ccp1Con

EnableInts ; Exit critical region
movf dutyCycle, w ; Re-apply duty cycle

SetDutyCycle:
Global SetDutyCycle
movwf dutyCycle
movwf varALo
BankSel tmr2Period
movf tmr2Period, w
Bank0
movwf varBLo
call ByteMult ; Calculate duty cycle * period
movf varCHi, w ; High byte of result is main on-time
movwf ccpReg2Lo ; Set new on-time
rrf varCLo, self
rrf varCLo, w
andlw 0x30 ; Get highest two bits of low byte of
result,
; shifted to match LSBs of ccpReg
on-time
iorlw 0x0c ; Command for CCP as PWM
movwf ccp2Con ; Make sure PWM is active

return


CalcPeriod:
; Calculates period from frequency by table lookup
movwf pwmTemp ; Preserve lookup value
PageSel PeriodTable ; Note that this destroys w
movf pwmTemp, w ; Get lookup value
movwf pcl ; Execute table jump

PeriodTableCode: BeginCode 0x0700 ; Place at end of low page
PeriodTable:
; Start of period calculation table

; Note that we cannot use mpasm to calculate the magic numbers
here,
; since it only works with 16-bit arithmetic. However, the
following
; notes should be easily changed to accomodate new constants if
; necessary.

; We want 12 kHz for a frequency control of 0,
; and 25 kHz for a frequency control of 127.
; At crystal speed of 9600x1024, the timer2 period is
; 9600x1024/(4*prd) = 2457600/prd
; The period at 12 kHz is thus 204.8,
; and at 25 kHz is 98.304
; So for a control value of 0 we want a period of 204.8,
; and for a control value of 127 we want a period of 98.304
; Solving p = A / (f + B) gives
; B = 127 x 98.304 / (204.8 - 98.304) = 117.23
; A = 204.8 x B = 24008.86


_freq set 0
_scaleA set 48018 ; Use double values to aid rounding
_scaleB set 234 ; Use double values to aid rounding

while _freq < 128
dt (_scaleA / (2*_freq + _scaleB)) - 1 ; True
period - 1
_freq set _freq + 1
endW

end

0 new messages