I want to use a PIC16F877A (I have a boot loader on it) to control the speed of an electric motor. I also want to use the PIC for a few other things not related to this problem so I want to program it in assembly for better programming control (and for the fun of it). Motor speed will be controlled using a potentiometer as input in a voltage divider. The voltage will be measured using one of the analog pins on the PIC (I don't care which one). The measured voltage will used to set the value of a PWM signal that will go to an H-bridge.

My circuit is pretty much already built but I've lost my notes from mechatronics class years ago. How can I program the PIC16F877A in assembly code to measure voltage at one of the analog pins and output a PWM signal to an H-bridge?

Dani AI

Generated

Good progress, — the approach is right, but a few register/bit misunderstandings in your posts will prevent the PWM from following the ADC correctly.

Key fixes and checklist

  • TRIS bits are inverted: 1 = input, 0 = output. Your first comment said "Set PORTA to output" while writing 0xFF (inputs). RA0 must be an input for the ADC.
  • ADC justification: pick one and set it explicitly. With left-justify the top 8 ADC bits live in ADRESH and the two LSBs are in ADRESL bits 7:6 — that makes mapping to CCP1 straightforward.
  • PWM mapping: the 10-bit PWM duty uses CCPR1L (bits 9:2) and CCP1CON<5:4> (bits 1:0). Do not write to CCPR1H for PWM.
  • You need Timer2/PR2 and TMR2 prescaler set before enabling PWM; also make RC2/CCP1 an output (TRISC2 = 0) and set CCP1CON lower nibble to PWM mode (0b1100).
  • After selecting an ADC channel add an acquisition delay before GO/DONE, then poll GO until clear.

Straightforward mapping (left-justified ADC)

  • Set CCPR1L = ADRESH.
  • Set CCP1CON<5:4> = (ADRESL >> 6).
  • Ensure you don’t clobber the PWM-mode bits when writing CCP1CON.

Example (pseudocode/C) showing the mapping logic:

/* ADC left-justified */
uint8_t hi = ADRESH;                 // bits 9:2
uint8_t two = (ADRESL & 0xC0) >> 6;  // bits 1:0
CCPR1L = hi;
CCP1CON = (CCP1CON & 0xCF) | (two << 4) | 0x0C; // keep mode=1100

Troubleshooting tips

  • Verify PWM on RC2 with an oscilloscope or frequency-capable multimeter.
  • Start with a slow PWM freq (few hundred Hz) to check behavior, then tune PR2 for desired frequency using PR2 = Fosc / (4 Fpwm prescale) - 1.
  • Ensure the H-bridge has proper flyback protection and the PIC drives only the logic inputs (don’t drive motor coils directly).
  • If values look wrong, add a small fixed acquisition delay (few microseconds) after channel select before starting conversion.

Following these corrections will make the ADC value reliably control CCP1 PWM duty.

Do I need to reply to my own post? I wrote some of the A2D but I'm not sure if it's correct because I don't have any way to output the result (like PWM and motor speed).

In this code, I want to use RA0/AN0 as the A2D pin. After this, I'm stuck.

LIST P=16F877A
INCLUDE P16F877.INC

org 0x1F00 ; For the boot loader
goto Start

Start
; 01 = B1
bsf STATUS, RP0
bcf STATUS, RP1

; Set PORTA to output
MOVLW B'11111111'
MOVWF TRISA

; Set PORTE to output
MOVLW B'11111111'
MOVWF TRISE

; Justify left the A2D inputs
; All possible pins are analog, VREF+ = VDD, VREF- = VSS
clrf ADCON1

; 00 = B0
bcf STATUS, RP0

; Fosc / 8, RA0/AN0, AD on
movlw B'01000001'
movwf ADCON0

; Get the AD measurement
GetAD
bsf ADCON0, GO
Pause
btfsc ADCON0, GO
goto Pause
return

Main
call GetAD ; Get the AD measurement
goto Main ; Loop back
END

I revised my previous code and added the PWM from what I read about it. Can someone please help?

LIST P=16F877A
INCLUDE P16F877.INC

org 0x1F00 ; For the boot loader
__CONFIG _WDT_OFF

Start
; 01 = B1
bsf STATUS, RP0
bcf STATUS, RP1

; Set PORTA to input
movlw B'11111111'
movwf TRISA

; Set PORTE to input
movlw B'11111111'
movwf TRISE

; Justify left the A2D inputs
; All possible pins are analog, VREF+ = VDD, VREF- = VSS
clrf ADCON1

; 00 = B0
bcf STATUS, RP0

; Fosc / 8, RA0/AN0, AD on
movlw B'01000001'
movwf ADCON0

; 00 = not used, 11 = LSB, 1100 = PWM
movlw B'00111100'
movwf CCP1CON

goto Main
; call GetAD

; Move the new values from the A2D, ADRESH and ADRESL,
; to the PWM values, CCPR1H and CCPR1L, respectively
SetPWM
movf ADRESH, CCPR1H
movf ADRESL, CCPR1L
return

; Get the AD measurement
GetAD
bsf ADCON0, GO
Pause
btfsc ADCON0, GO
goto Pause
return

Main
call GetAD ; Get the AD measurement
call SetPWM ; Set the PWM values
goto Main ; Loop back

end

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.