Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

16F88 PWM and A/D conversion

Status
Not open for further replies.
Willi,

When you get past the programmer hurdle, would you let me know if you've got ADC code for the 'F88? I just found some old 'F88 ADC code...

This looks like setup code and a subroutine to perform an ADC reading but I'm going to have to read the Data Sheet to refresh my memory...
Code:
;
; setup ADC module for RA0 as an analog input, Fosc/32 conversion
; clock (20-Mhz), and internal Vdd/Vss Voltage Reference.
;
        bsf     STATUS,RP0      ; select Bank 1                   |B1
        movlw   b'00000001'     ;                                 |B1
        movwf   TRISA           ; set RA0 pin as input            |B1
        movlw   b'00000001'     ; set RA0/AN0 pin as analog and   |B1
        movwf   ANSEL           ; other 6 ANx pins as digital     |B1
        movlw   b'01000000'     ; ADSC2 clock source/2            |B1
        movwf   ADCON1          ; left justified, internal Vref   |B1
        bcf     STATUS,RP0      ; select Bank 0                   |B0
;       movlw   b'01000000'     ; Fosc/08, ch 0, ADC off ( 8-mhz) |B0
        movlw   b'10000000'     ; Fosc/32, ch 0, ADC off (20-mhz) |B0
        movwf   ADCON0          ;                                 |B0
Code:
;
; turn on the ADC module, take a reading, return with 8 most
; signigicant bits of reading in W
;
ADC     bsf     ADCON0,ADON     ; turn on AD module               |B0
        bcf     PIR1,ADIF       ; clear AD interrupt flag         |B0
        movlw   d'33'           ; use 33 (20 mhz) or 13 (8 mhz)   |B0
        movwf   TEMP            ;                                 |B0
ACQ     decfsz  TEMP,F          ; wait 20 usec  to acquire        |B0
        goto    ACQ             ;                                 |B0
        bsf     ADCON0,GO_DONE  ; start conversion                |B0
ADX     btfsc   ADCON0,GO_DONE  ; conversion complete?            |B0
        goto    ADX             ; no, loop                        |B0
        movf    ADRESH,W        ; get result high byte            |B0
        bcf     ADCON0,ADON     ; turn off AD module              |B0
        return                  ;                                 |B0

Regards, Mike
 
i stumbled across a circuit earlier today which showed RB3 connected to ground through a resistor .. do i need to do this , on the F88 when programming??
Except for the Chip Erase command & End Programming command the two (F628 & F88) are identical.. reason being ( which i forgot) they are programmed LSB first , when you look at the diagrams that way you can see they are identical..the F88 only uses 5 command bits where the F628 uses 6..
 
williB said:
i stumbled across a circuit earlier today which showed RB3 connected to ground through a resistor .. do i need to do this , on the F88 when programming??
The resistor is needed only when you use LVP - Low Voltage Programming. Because setting RB3 High will enter Programming mode (Regardless of MCLR). I highly recomend not use this mode, so in every program you write, simply disable LVP bit, so you can use RB3 as general purpose I/O. All new chips have LVP set on from the Factory, so that you can program them with LVP style programmer. :lol:
 
SE the documentation says that you can use MCLR even when LVP is enabled..
i am almost there :) ..with the reprogramming..
i can alter a single location at will..
one step forward two steps back *sigh*
 
williB said:
SE the documentation says that you can use MCLR even when LVP is enabled..
i am almost there :) ..with the reprogramming..
i can alter a single location at will..
one step forward two steps back *sigh*
MCLR is always possible, LVP can be disabled..
 
Thanks SE.. , for your comment....

The programmer is doing a lot better , it just needed a little tweaking ..
the problem i noticed was that , it wasnt erasing properly but now its good..
 
below i have combined the two modules ..
It works!! sort of , what it really does is turn the pwm on and off though(not what its supposed to do , which is to pass an A to D reading to the PWM module.)
just a little more tweeking should do it though..
i have moved part of the PWM module into the ADC module , where Mike put in a little delay..
i am not sure if this is causing the problem..


Code:
; setup ADC module for RA0 as an analog input, Fosc/32 conversion 
; clock (20-Mhz), and internal Vdd/Vss Voltage Reference. 
; 
        bsf     STATUS,RP0      ; select Bank 1                   |B1 
        movlw   b'00000001'     ;                                 |B1 
        movwf   TRISA           ; set RA0 pin as input            |B1 
        movlw   b'00000001'     ; set RA0/AN0 pin as analog and   |B1 
        movwf   ANSEL           ; other 6 ANx pins as digital     |B1 
        movlw   b'01000000'     ; ADSC2 clock source/2            |B1 
        movwf   ADCON1          ; left justified, internal Vref   |B1 
        bcf     STATUS,RP0      ; select Bank 0                   |B0 
;       movlw   b'01000000'     ; Fosc/08, ch 0, ADC off ( 8-mhz) |B0 
        movlw   b'10000000'     ; Fosc/32, ch 0, ADC off (20-mhz) |B0 
        movwf   ADCON0          ;                                 |B0 
 

 
; turn on the ADC module, take a reading, return with 8 most 
; signigicant bits of reading in W 
; 
ADC     bsf     ADCON0,ADON     ; turn on AD module               |B0 
        bcf     PIR1,ADIF       ; clear AD interrupt flag         |B0 
        movlw   d'33'           ; use 33 (20 mhz) or 13 (8 mhz)   |B0 
        movwf   TEMP            ;                                 |B0 
ACQ     decfsz  TEMP,F          ; wait 20 usec  to acquire        |B0 
        goto    ACQ             ;                                 |B0 
        bsf     ADCON0,GO_DONE  ; start conversion                |B0 
ADX     btfsc   ADCON0,GO_DONE  ; conversion complete?            |B0 
        goto    ADX             ; no, loop                        |B0 
        movf    ADRESH,W        ; get result high byte            |B0 
        bcf     ADCON0,ADON     ; turn off AD module              |B0 
        return                  ;                                 |B0 
;******************************************************************

Code:
        org   0x0000 

RESET   clrf    STATUS          ;                                 |B0 
        clrf    PORTA           ; clear Port A data latches       |B0 
        clrf    PORTB           ; clear Port B data latches       |B0 
        movlw   h'07'           ;                                 |B0 
        movwf   CMCON           ; turn comparator off             |B0 
        bsf     STATUS,RP0      ; select bank 1                   |B1 
        clrf    TRISA           ; port A all outputs              |B1 
        clrf    TRISB           ; port B all outputs              |B1 
        bcf     STATUS,RP0      ; select bank 0                   |B0 
; 
;  setup PWM (looks like CCPR1L uses Tosc while PR2 uses Tcyc) 
; 
        clrf    T2CON           ; TMR2 prescale:1                 |B0 
        movlw   d'100'          ; same as (100*4)>>2              |B0 
        movwf   CCPR1L          ; 100 usecs, 50% duty cycle?      |B0 
        bsf     STATUS,RP0      ; select bank 1                   |B1 
        movlw   d'200'-1        ; 200 1.0 usec 'ticks'            |B1 
        movwf   PR2             ; Period=200 usecs, Freq=5.0 KHz  |B1 
        bcf     STATUS,RP0      ; select bank 0                   |B0 
        movlw   b'00001100'     ;                                 |B0 
        movwf   CCP1CON         ; put CCP module in PWM mode      |B0 
        bsf     T2CON,TMR2ON    ; turn on TMR2                    |B0 
; 
;  now test to see if the LED on RB3 is glowing at 50% brightness 
; 
LOOP    goto    LOOP            ; loop indefinately               |B0 
; 
;***************************************************************

Code:
        org   0x0000 

        clrf    STATUS          ;                                 |B0 
        clrf    PORTA           ; clear Port A data latches       |B0 
        clrf    PORTB           ; clear Port B data latches       |B0 
        movlw   h'07'           ;                                 |B0 
        movwf   CMCON           ; turn comparator off             |B0 
        bsf     STATUS,RP0      ; select bank 1                   |B1 
        clrf    TRISA           ; port A all outputs              |B1 
        clrf    TRISB           ; port B all outputs              |B1 
 
        movlw   b'00000001'     ;                                 |B1 
        movwf   TRISA           ; set RA0 pin as input            |B1 
        movlw   b'00000001'     ; set RA0/AN0 pin as analog and   |B1 
        movwf   ANSEL           ; other 6 ANx pins as digital     |B1 
        movlw   b'01000000'     ; ADSC2 clock source/2            |B1 
        movwf   ADCON1          ; left justified, internal Vref   |B1 
        bcf     STATUS,RP0      ; select Bank 0                   |B0 



        movlw   b'01000000'     ; Fosc/08, ch 0, ADC off ( 8-mhz) |B0 
        movwf   ADCON0          ;                                 |B0 
  
ADC     bsf     ADCON0,ADON     ; turn on AD module               |B0 
        bcf     PIR1,ADIF       ; clear AD interrupt flag         |B0 
       
        clrf    T2CON           ; TMR2 prescale:1                 |B0 
        movf	 RESULT,W      ;			   B0
        movwf   CCPR1L          ; 100 usecs, 50% duty cycle?      |B0 
        bsf     STATUS,RP0      ; select bank 1                   |B1 
        movlw   d'200'-1        ; 200 1.0 usec 'ticks'            |B1 
        movwf   PR2             ; Period=200 usecs, Freq=5.0 KHz  |B1 
        bcf     STATUS,RP0      ; select bank 0                   |B0 
        movlw   b'00001100'     ;                                 |B0 
        movwf   CCP1CON         ; put CCP module in PWM mode      |B0 
        bsf     T2CON,TMR2ON    ; turn on TMR2                    |B0 

        bsf     ADCON0,GO_DONE  ; start conversion                |B0 
ADX     btfsc   ADCON0,GO_DONE  ; conversion complete?            |B0 
        goto    ADX             ; no, loop                        |B0 
        movf    ADRESH,W        ; get result high byte            |B0 
        movwf   RESULT          ; store in temp register           B0
        bcf     ADCON0,ADON     ; turn off AD module              |B0
 
Hi Willi,

Don't you want PR2 = 255 to match the 0-255 ADC reading? Are you using a 20-MHz crystal?

You probably want to read the ADC to get a 0-255 value and update CCPR1L with this new duty cycle value and repeat the process...

Leave ADC as a subroutine and use code similar to below, or, add a few lines at the end of the ADC routine to copy RESULT to CCPR1L and jump to ADC to repeat the process...

Good luck... Regards, Mike

Code:
        org   0x0000

RESET   clrf    STATUS          ;                                 |B0
        clrf    PORTA           ; clear Port A data latches       |B0
        clrf    PORTB           ; clear Port B data latches       |B0
        movlw   h'07'           ;                                 |B0
        movwf   CMCON           ; turn comparator off             |B0
        bsf     STATUS,RP0      ; select bank 1                   |B1
        clrf    TRISA           ; port A all outputs              |B1
        clrf    TRISB           ; port B all outputs              |B1
        bcf     STATUS,RP0      ; select bank 0                   |B0
;
;  setup PWM (looks like CCPR1L uses Tosc while PR2 uses Tcyc)
;
        clrf    T2CON           ; TMR2 prescale:1                 |B0
        movlw   d'000'          ;                                 |B0
        movwf   CCPR1L          ; 00% duty cycle initially        |B0
        bsf     STATUS,RP0      ; select bank 1                   |B1
        movlw   d'255'-1        ; 255 200-nsec 'ticks'            |B1
        movwf   PR2             ; Period=255 (*200-nsecs)         |B1
        bcf     STATUS,RP0      ; select bank 0                   |B0
        movlw   b'00001100'     ;                                 |B0
        movwf   CCP1CON         ; put CCP module in PWM mode      |B0
        bsf     T2CON,TMR2ON    ; turn on TMR2                    |B0 
;
; setup ADC module for RA0 as an analog input, Fosc/32 conversion
; clock (20-Mhz), and internal Vdd/Vss Voltage Reference.
;
        bsf     STATUS,RP0      ; select Bank 1                   |B1
        movlw   b'00000001'     ;                                 |B1
        movwf   TRISA           ; set RA0 pin as input            |B1
        movlw   b'00000001'     ; set RA0/AN0 pin as analog and   |B1
        movwf   ANSEL           ; other 6 ANx pins as digital     |B1
        movlw   b'01000000'     ; ADSC2 clock source/2            |B1
        movwf   ADCON1          ; left justified, internal Vref   |B1
        bcf     STATUS,RP0      ; select Bank 0                   |B0
;       movlw   b'01000000'     ; Fosc/08, ch 0, ADC off ( 8-mhz) |B0
        movlw   b'10000000'     ; Fosc/32, ch 0, ADC off (20-mhz) |B0
        movwf   ADCON0          ;                                 |B0
;
LOOP    call    ADC             ; read potentiometer (000..255)   |B0
        movwf   CCPR1L          ; update duty cycle               |B0
        goto    LOOP            ; loop forever                    |B0
; 
; turn on the ADC module, take a reading, return with 8 most
; signigicant bits of reading in W
;
ADC     bsf     ADCON0,ADON     ; turn on AD module               |B0
        bcf     PIR1,ADIF       ; clear AD interrupt flag         |B0
        movlw   d'33'           ; use 33 (20 mhz) or 13 (8 mhz)   |B0
        movwf   TEMP            ;                                 |B0
ACQ     decfsz  TEMP,F          ; wait 20 usec  to acquire        |B0
        goto    ACQ             ;                                 |B0
        bsf     ADCON0,GO_DONE  ; start conversion                |B0
ADX     btfsc   ADCON0,GO_DONE  ; conversion complete?            |B0
        goto    ADX             ; no, loop                        |B0
        movf    ADRESH,W        ; get result high byte            |B0
        bcf     ADCON0,ADON     ; turn off AD module              |B0
        return                  ;                                 |B0
;******************************************************************
 
i cant thank you enough really , thank you!!
as far as PR2 yes that is what i wanted to do , but i didnt know enough to know where to change it..
 
i've been over and over the code , and i cant find anything wrong with it..
but since i am only running the chip at 333KHZ i am going to try clearing ADCON0 and ADCON1 in the ADC setup..and using a smaller loop value..
also why are you turning off the A/D module just to turn it back on after you get the result??
the PWM is working perfectly btw..but the adc section , not so much....
 
I have used the PIC on a motor driver application in which the PWM duty cycle is a function of the ADC output and here are few tips I can give.

In your application as in mine, the A/D result is used to calculate the PWM duty. On the PWM module, the duty cycle can only change at timer2 overflow. In your case, it's at a 5Khz rate. This is regardless of how many times you load CCPR1L. Therefore, the 5Khz rate should dictate the rate you make A/D conversions and not the other way around (i.e., the A/D conversion rate dictating how fast you update CCPR1L).

A 5Khz A/D conversion rate is well within the limits of the PIC ADC. Selecting 8Tosc for Tad, the A/D conversion time is 12Tad or about 20usec. Since at 5Khz there is 200usec between A/D conversion intervals, there is 180usec of sampling time allowed.

All these is easily done if A/D conversion is synchronized with timer2 overflow. That is, you don't need to check the GO/DONE bit but instead poll the TMR2IF bit.

1. Poll the TMR2IF bit until it is set.
2. Clear TMR2IF bit.
3. Read the result from the previous A/D conversion and save result.
4. Use the A/D result to set the PWM duty.
5. Start a new conversion. This step can be done ahead of step 4.
6. Repeat step 1. While waiting for TMR2IF to be set in 200usec, the A/D conversion is completed after 20usec and the rest of the time is spent sampling.

BTW, you can also use interrupts here, using TMR2IF to initiate interrupt.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top