please dont rip this site

A Virtual Peripheral DAC: Implementing Pulse Width Modulation


This application note presents programming techniques for producing a programmable output voltage by smoothing a pulse width modulated (PWM) digital output with a simple resistor-capacitor low-pass filter, and essentially creating a digital to analog converter. This implementation uses the SX's internal interrupt feature to allow background operation of the code as a virtual peripheral, and uses the Parallax demo board, taking advantage of Parallax' SX demo software user interface and UART features to allow the SX to communicate simply and directly with a personal computer via a serial RS232C port.

;       Virtual Peripheral: Pulse Width Modulators
;       Length: 36 bytes (total)
;       Authors: Chip Gracey, President, Parallax Inc.
;		   modified by Craig Webb, Consultant to Scenix Semiconductor, Inc.
;       Written: 97/03/10 to 98/6/03
;****** Assembler directives
; uses: SX28AC, 2 pages of program memory, 8 banks of RAM, high speed osc.
;       operating in turbo mode, with 8-level stack & extended option reg.
		DEVICE  pins28,pages2,banks8,oschs
		DEVICE  turbo,stackx,optionx
		ID      'PWMS'			;program ID label
		RESET   reset_entry             ;set reset/boot address
;******************************* Program Variables ***************************
; Port Assignment: Bit variables
pwm0_pin        EQU     rc.0                    ;Pulse width mod. PWM0 output
pwm1_pin        EQU     rc.2                    ;Pulse width mod. PWM1 output
;****** Register definitions (bank 0)
		org     8                       ;start of program registers
main            =       $                       ;main bank
temp            ds      1                       ;temporary storage
byte            ds      1                       ;temporary UART/I2C shift reg.
flags           DS      1                       ;program flags register
delay1		DS	1			;temporary delay variable
delay2		DS	1			;temporary delay variable
		org     50h                     ;bank2 variables
analog          =       $                       ;pwm and ADC bank
port_buff       ds      1                       ;buffer - used by all
pwm0            ds      1                       ;pwm0 - value
pwm0_acc        ds      1                       ;     - accumulator
pwm1            ds      1                       ;pwm1 - value
pwm1_acc        ds      1                       ;     - accumulator
int_period	=       163                     ;period between interrupts
;**************************** INTERRUPT CODE *******************************
; Note: The interrupt code must always originate at 0h.
;       Care should be taken to see that any very timing sensitive routines
;       (such as adcs, etc.) are placed before other peripherals or code
;       which may have varying execution rates (like the UART, for example).
interrupt       ORG     0                       ;interrupt starts at 0h
;***** Virtual Peripheral: Pulse Width Modulators
; These routines create an 8-bit programmable duty cycle output at the
; respective pwm port output pins whose duty cycle is directly proportional
; to the value in the corresponding pwm register. This value is added to an
; accumulator on each interrupt pass interrupt. When the addition causes a
; carry overflow, the ouput is set to the high part of its duty cycle.
; These routines are timing critical and must be placed before any
; variable-execution-rate code (like the UART, for example).
;       Input variable(s) : pwm0,pwm0_acc,pwm1,pwm1_acc
;       Output variable(s) : pwm port pins
;       Variable(s) affected : port_buff, pwm0_acc, pwm1_acc
;       Flag(s) affected : none
;	Size : 2 bytes + 4 bytes (per pwm)
;		+ 2 bytes shared with adc code (see below)
;       Timing (turbo) : 2 cycles + 4 cycles (per pwm)
;			 + 2 cycles shared with adc code (see below)
:set_analog	bank    analog                  ;switch to adc/pwm bank
		clr     port_buff               ;clear pwm/adc port buffer
:pwm0           add     pwm0_acc,pwm0           ;adjust pwm0 accumulator
		snc                             ;did it trigger?
		setb    port_buff.0             ;yes, toggle pwm0 high
:pwm1           add     pwm1_acc,pwm1           ;adjust pwm1 accumulator
		snc                             ;did it trigger?
		setb    port_buff.2             ;yes, toggle pwm1 high

:update_RC	mov     rc,port_buff            ;update cap. discharge pins
		mov     w,#-int_period          ;interrupt every 'int_period' clocks
:end_int	retiw                           ;exit interrupt
;******	End of interrupt sequence
;***************************** SUBROUTINES *********************************
;***************************** MAIN PROGRAM CODE ******************************
;		ORG     100h
; Program execution begins here on power-up or after a reset
		clr     rc                      ;initialize port RC
		mov     !rc,#%10101010          ;Set RC in/out directions
		mov     m,#$D                   ;set input levels
		mov     !rc,#0                  ; to cmos on port C
		mov     m,#$F                   ;reset mode register
		CLR     FSR                     ;reset all ram starting at 08h
:zero_ram       SB      FSR.4                   ;are we on low half of bank?
		SETB    FSR.3                   ;If so, don't touch regs 0-7
		CLR     IND                     ;clear using indirect addressing
		IJNZ    FSR,:zero_ram           ;repeat until done

		mov     !option,#%10011111      ;enable rtcc interrupt
; Main Program Loop
:loop		DJNZ	delay1,:loop		;do inner delay loop
		DJNZ	delay2,:loop		;do outer delay loop
		INC	pwm0			;increase pwm0 - ramp output
		DEC	pwm1			;decrease pwm1 - sawtooth output
; <program code goes here>
		JMP     :loop                   ;back to main loop
		END                             ;End of program code

How the circuit and program work

1 The duty cycle is the proportion of time which the signal is high or active (i.e. charging the capacitor)

The circuit is a simple resistor capacitor network (R5&C1 for pwm0, R7-C2 for pwm1) on each pwm port pin (see figure 1), which acts as a low-pass filter and smoothes the oscillating digital signal output so that it appears as a linear voltage whose value is determined by the duty cycle1 of the signal, which is directly controlled by the 8 bit value in the pwm register.

The interrupt code segment is quite straight forward. The pwm value is added to an accumulator register during each pass through the interrupt and the pwm output is set to high every time the accumulator overflows. This technique allows the eight bit value in the pwm register to directly set the duty cycle of the pwm signal output and hence directly control the voltage on the capacitor.

2 The 3 dB point is the frequency at which the filter cuts the incoming signal level by 50% (to ½ it's original value). For a low-pass filter, signals at frequencies higher than the 3 dB point are attenuated even further.

With the resistor and capacitor values shown, the corner frequency or 3 dB point2 for the low-pass filter is calculated as follows:

frequency3dB = 1 / (2 * p * R * C ) = 1 / (2 * 3.1416 * 10kW * 0.1uF) = 159 Hz

3 With a pwm value of 1, there will only be one carry generated every 256 passes by adding the pwm value to the pwm accumulator. In the case of a pwm value of 0FFh, only 1 of every 256 passes won't generate a carry (and hence a low at the pwm output).

In order to generate a fixed voltage from the pwm output from the changing digital pwm signals, a low-pass filter must be used whose corner frequency is lower than the lowest frequency component of the digital pwm signal. The length of the pwm cycle varies, depending upon the value in the corresponding pwm register, but the worst case is a pwm value of either 1 or 0FFh, in which case 256 interrupt passes are required to complete the pwm cycle3. We can calculate the period between interrupt passes as follows:

* The interrupt is triggered each time the RTCC rolls over (counts past 255 and restarts at 0). By loading the OPTION register with the appropriate value, the RTCC count rate is set to some division of the oscillator frequency (in this case they are equal), which is the external 50 MHz crystal in this case. At the close of the interrupt sequence, a predefined value is loaded into the W register using the RETIW instruction which determines the period of the interrupt in RTCC cycles.

period (sec) = mode * prescaler * RETIW value* / osc. frequency, where mode=1 (turbo) or =4 (normal)

So, for the worst case of 256 interrupt passes, at a crystal frequency of 50 MHz, in turbo mode, with a prescaler of 1, and with an RETIW value of 163, the lowest frequency present in the pwm signal is:

frequencymin = 1 / period * 256 = 50 MHz / (1 * 1 * 163 * 256 ) = 1.2 kHz

By this we can see that the pwm signal will be excellently smoothed by the resistor and capacitor values chosen, since the lowest frequency component possible is well above the corner frequency for the low-pass filter.

4 The charging and discharging times are calculated similarly.

The only inconvenience that the low-pass filter causes is that it also limits the pwm's output settling time (i.e. the maximum frequency and linearity if the pwm is being used as a signal generator). With the RC values shown, the settling time is calculated from the equation V=Vcc (1-e-t/RC ) for the charging4 voltage on the capacitor after time t. If we want to calculate the settling time to eight bit resolution, we have:

V = Vdd (28 - 1) / 28 = Vdd ( 1 - e-t/RC ) so: ln(1/ 28 ) = -t / RC

therefore: tsettle = - RC * ln(1 / 28 ) = - 10kW * 0.1uF * ln(1 / 28 ) = 5.5 msec

5 In practise, circuit noise is usually larger than the 8-bit resoltion of the pwm, so that the settling time can be considered as somewhat less than the calculated value

So, with the above RC values, a maximum frequency of fmax = 1/ tsettle = 1 / 5.5 ms = 180 Hz is possible on the pwm outputs for signals that vary if eight bit resolution is required5.

The second 10kW resistor attached to each pwm output (i.e. R6 and R8), acts as a current limiting resistor to avoid discharging the voltage stored on the capacitor in case the output is driving a source which doesn't have a high input impedance. Ideally, the input impedance of the circuit being driven is at least a couple orders of magnitude larger (i.e. >= 1MW) than this resistor value so that the voltage on the capacitor remains accurate.

Modifications and further options

6 for the 4th pwm output pwm0 should be relaced with pwm3, pwm0_acc with pwm3_acc, and port_buff.0 with port_buff.6

The Parallax demo board is designed so that port pins adc0 and adc1 can be swapped for pwm's simply by adjusting the program code. To do this (if you'd like 1-2 more pwm outputs) requires commenting out (or removing) the adc code section, and reproducing the three lines of pwm0: code once (or twice for two more pwm outputs), while replacing pwm0 with pwm2, pwm0_acc with pwm2_acc, port_buff.0 with port_buff.4 6 in the three code lines. New register definition(s) should also be added in the analog bank for pwm2 & pwm2_acc (and pwm3 & pwm3_acc, if required). An example follows:

analog 		= 	$ 		;pwm bank
port_buff 	ds 	1 		;buffer - used by all
pwm0 		ds 	1 		;pwm0 - value
pwm0_acc 	ds 	1 		; - accumulator
pwm1 		ds 	1 		;pwm1 - value
pwm1_acc 	ds 	1 		; - accumulator
pwm2 		ds 	1 		;pwm2 - value
pwm2_acc 	ds 	1 		; - accumulator
pwm3 		ds 	1 		;pwm3 - value
pwm3_acc 	ds 	1 		; - accumulator
:pwm0 		add 	pwm0_acc,pwm0	;adjust pwm0 accumulator
		snc 			;did it trigger?
		setb 	port_buff.0 	;yes, toggle pwm0 high
:pwm1 		add 	pwm1_acc,pwm1 	;adjust pwm1 accumulator
		snc 			;did it trigger?
		setb 	port_buff.2 	;yes, toggle pwm1 high
:pwm2 		add 	pwm2_acc,pwm2 	;adjust pwm2 accumulator
		snc 			;did it trigger?
		setb 	port_buff.4 	;yes, toggle pwm2 high
:pwm3 		add 	pwm3_acc,pwm3 	;adjust pwm3 accumulator
		snc 			;did it trigger?
		setb 	port_buff.6 	;yes, toggle pwm3 high
; <adc code removed except for this one line>
		mov 	rc,port_buff 	;update port pins (port RC)

file: /Techref/scenix/lib/io/osi1/pwm/pwm_vp.htm, 13KB, , updated: 2002/3/12 22:49, local time: 2017/10/21 04:26,

 ©2017 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?
Please DO link to this page! Digg it! / MAKE! / 

<A HREF=""> A Virtual Peripheral DAC: Implementing Pulse Width Modulation</A>

After you find an appropriate page, you are invited to your to this massmind site! (posts will be visible only to you before review) Just type in the box and press the Post button. (HTML welcomed, but not the <A tag: Instead, use the link box to link to another page. A tutorial is available Members can login to post directly, become page editors, and be credited for their posts.

Link? Put it here: 
if you want a response, please enter your email address: 
Attn spammers: All posts are reviewed before being made visible to anyone other than the poster.
Did you find what you needed?

  PICList 2017 contributors:
o List host: MIT, Site host, Top posters @20171021 RussellMc, Van Horn, David, James Cameron, Sean Breheny, IVP, alan.b.pearce, Neil, David C Brown, Bob Blick, Denny Esterline,
* Page Editors: James Newton, David Cary, and YOU!
* Roman Black of Black Robotics donates from sales of Linistep stepper controller kits.
* Ashley Roll of Digital Nemesis donates from sales of RCL-1 RS232 to TTL converters.
* Monthly Subscribers: Gregg Rew. on-going support is MOST appreciated!
* Contributors: Richard Seriani, Sr.

Welcome to!