PWM on the Microchip PIC 16F690

Years ago I put up my first video on YouTube. It was a very poorly shot and edited video on PWM with a PIC 16F690.  However as its a reasonably difficult process writing for this chip and the chip is quite popular as it came with the PICKIT 2 it has had a fair few views. I have added some content here from what I remember about the PIC for those who are using it.   First here is the original video.

This is an example of a Microchip PIC16F690 PWM (Pulse Width Modulation) example. Used to vary the brightness on an LED or as a speed controller on a DC motor.

PWM in a PIC basically involves reading the current ADC port register values
(ie a memory location which changes its value as the incoming analog voltage changes) and then using a calculation to convert them and copy them directly in to the PWM register.

REGISTERS are memory locations that are connected to physical hardware. We probably all understand that memory is somewhere you can store a value, but with a REGISTER, the memory contents are connected to hardware.

For example, a row of 6 pins are all part of the same PORTA register, the first 2 pins are being driven OFF and the next 4 pins are being driven ON by an external circuit.

The value of the PORTA register when read would be 111100 in binary.

The state of the pins actually effect the memory directly! Dig it?

Now if i was to write the value 001100 to the PORTA register, can you guess what would happen? The pins would now have 5 volts or 0 volts ie ON or OFF depending on the register values i had written, in this case 1 and 2 OFF, 3 and 4 ON, 5 and 6 OFF. Now your getting the picture?

The main routines in this microcontroller example involve moving values from the ADC memory register to the working memory – (MOVLW) then reformatting and resizing the value till its suitable to be moved again to the PWM register – (MOVWF)

SO ESSENTIALLY – the value on the analog pin is copied to the PWM output pin.

THE CODE:

We start by giving all the data values/memory locations plain english names, well sort of.. (some are also defined already inside the included libraries as standard names for things)

Then we do some setup, write to some registers that set hardware options inside the chip rather than hardware outside the chip (cool isn’t it! This is SO RETRO!)

We do all this by making up a value, usually in HEX, and copying it to a thing we call the working memory register, basically a holding location, then we move it on the the finally resting place in memory, that we want to put it.

Example movlw 0xF7 , this moves hexadecimal F7 into the working memory.
Example movwf ANSEL , this moves the content of the working memory to the ANSEL register.

Because we can’t transfer directly from one location to another, it has to go through the working memory. After all, that’s really what a processor does.

once the initialising routine is complete, we basically just loop between reading the ADC register, ie, the incoming voltage, and writing the value to the PWM register.

Example movf ADRESH,w ; Copy the value to the working memory from ADC result
Example MOVWF CCPR1L ;Copy the working memory contents to the PWM register
CODE LISTING:
I hope the code is still valid for everybody..

Unfortunately YouTube seems to be destroying the formatting. PM me for a better version if you need it.
; 16F690 PWM out on RC5 0-5V, and in via RA0, using potentiometer to control PWM duty cycle.

LIST P=16F690, W=2, X=ON, R=DEC
#INCLUDE P16F690.INC

cblock 0x20 ;start of general purpose registers
temp ;temp storage
duty ;final duty cycle

endc

movlw d’0′
movwf duty

movlw 0x10 ; A2D Clock Fosc/8
movwf ADCON1
bcf STATUS,RP0 ; back to Register Page 0

bsf STATUS,RP1
movlw 0xF7 ; we want all Port A pins Analog, except RA3
movwf ANSEL
bcf STATUS,RP0 ; address Register Page 0
bcf STATUS,RP1

movlw 0x01
movwf ADCON0 ; configure A2D for Channel 0 (RA0), Left justified, and turn on the A2D module
START CALL Initialise

MainLoop:

call A2D
CALL Pwm
GOTO MainLoop

Initialise:

BANKSEL ADCON1 ;turn off A2D
MOVLW 0x06
MOVWF ADCON1
BANKSEL PORTA
BANKSEL TRISC
MOVLW 0 ;set PORTC as all outputs
MOVWF TRISC
BANKSEL PORTC

MOVF CCP1CON,W ;set CCP1 as PWM
ANDLW 0xF0
IORLW 0x0C
MOVWF CCP1CON

MOVLW 126 ;set highest PWM value
BANKSEL PR2 ;over this (127) is permanently on
MOVWF PR2
BANKSEL TMR2

MOVF T2CON,W ;set prescaler to 16
ANDLW 0xF8 ;PWM at 2500HZ
IORLW 0x02
MOVWF T2CON

MOVF T2CON,W ;set postscaler to 1
ANDLW 0x07
IORLW 0x00
MOVWF T2CON

CLRF CCPR1L ;set PWM to zero

BSF T2CON, TMR2ON ;and start the timer running

RETURN

Pwm: ;use value in W to set speed (0-127)

MOVWF CCPR1L
RETURN

A2D:
nop ; wait 5uS for A2D amp to settle and capacitor to charge.
nop ; wait 1uS
nop ; wait 1uS
nop ; wait 1uS
nop ; wait 1uS
bsf ADCON0,GO ; start conversion
btfss ADCON0,GO ; this bit will change to zero when the conversion is complete
goto $-1
movf ADRESH,w ; Copy the value to the working memory
return

end

No Comments Yet.

Leave a comment