jakeselectronics
Member
So my current project uses PWM to control the speed of a water pump.
I want to get your thoughts on this part of the code. The part that manages the INC and DEC of the PWM DC.
I just feel it's not the best way to be going about it.
Basically I needed a way to Increment, Decrement & Display the PWM.
An online calculator told me that if I am running a 4Mhz clock, and I write (249) 0b11111001 to PR2, and asign TM2's prescaler to 1, I will have a PWM with a frequency of 4kHz and that to achieve 100% Duty, I write (D'1000') 11111010:xx00xxxx to CCPR1L:CCP1CON(5:4).
Sounds good! I like those round numbers!
So I can INC and DEC CCPR1L:CCP1CON(5:4) bit by bit and get 0.1 bits of revolution out of my PWM.
I also wanted to be able to assign a minimum PWM Duty Cycle which is user configured. (So the water does stall when being automatically controlled)
Here's how I'm doing it:
Here are my defined RAM locations:
So the only way I could come up to easily increment and decrement the PWM Duty Cycle was to 'extract' the value from CCPR1L:CCP1CON(5:4), place it some RAM GPR's, do the math (inc/dec, check overflow, etc...) and 'replace' CCPR1L:CCP1CON(5:4) with the newly adjusted figure.
There is also a check in the INC routine that it doesn't increment past 1000, and a check in the DEC routine that it doesn't decrement past the pre-specified PWM_MIN
See below
And with the 10-bit PWM right justified in my PWM_DC_H : PWM_DC_L RAM GPR's it makes it easy to do a BIN2BCD conversion for displaying the PWM.
This is working flawlessly, but can it be done better?
I want to get your thoughts on this part of the code. The part that manages the INC and DEC of the PWM DC.
I just feel it's not the best way to be going about it.
Basically I needed a way to Increment, Decrement & Display the PWM.
An online calculator told me that if I am running a 4Mhz clock, and I write (249) 0b11111001 to PR2, and asign TM2's prescaler to 1, I will have a PWM with a frequency of 4kHz and that to achieve 100% Duty, I write (D'1000') 11111010:xx00xxxx to CCPR1L:CCP1CON(5:4).
Sounds good! I like those round numbers!
So I can INC and DEC CCPR1L:CCP1CON(5:4) bit by bit and get 0.1 bits of revolution out of my PWM.
I also wanted to be able to assign a minimum PWM Duty Cycle which is user configured. (So the water does stall when being automatically controlled)
Here's how I'm doing it:
Here are my defined RAM locations:
Code:
PWM_MIN_L ; User adjustable, stores a pre-defined "minimum" PWM duty cycle
PWM_MIN_H ;
PWM_DC_L ; PWM Duty Cycle L Mirrors CCPR1L:CCP1CON(5:4) but is right justified for easy inc or dec.
PWM_DC_H ; PWM Duty Cycle H
So the only way I could come up to easily increment and decrement the PWM Duty Cycle was to 'extract' the value from CCPR1L:CCP1CON(5:4), place it some RAM GPR's, do the math (inc/dec, check overflow, etc...) and 'replace' CCPR1L:CCP1CON(5:4) with the newly adjusted figure.
There is also a check in the INC routine that it doesn't increment past 1000, and a check in the DEC routine that it doesn't decrement past the pre-specified PWM_MIN
See below
Code:
INC_PWM
CALL EXTRACT_PWM_DC
; Check if already 1000 (100%)
MOVLW B'11101000' ; Lower 8 bits of D'1000'
SUBWF PWM_DC_L, W
BTFSS STATUS, Z
GOTO $+5
MOVLW B'00000011' ; Upper 8 bits of D'1000'
SUBWF PWM_DC_H, W
BTFSC STATUS, Z
RETURN ; Already 1000 (MAX), do not increment further
; Not 1000, increment
INCFSZ PWM_DC_L, F
GOTO $+2
; Overflow, increment upper register
INCFSZ PWM_DC_H, F
; Done
CALL REPLACE_PWM_DC
RETURN
DEC_PWM
CALL EXTRACT_PWM_DC ;
; Check if already at MIN
MOVF PWM_MIN_L, W ; Lower 8 bits of PWM_MIN
SUBWF PWM_DC_L, W ;
BTFSS STATUS, Z ;
GOTO $+5 ;
MOVF PWM_MIN_H, W ; Upper 8 bits of PWM_MIN
SUBWF PWM_DC_H, W ;
BTFSC STATUS, Z ;
RETURN ; Already at 'x' (MIN), do not decrement further
; Not at MIN, decrement
DECF PWM_DC_L, F ;
COMF PWM_DC_L, W ;
BTFSS STATUS, Z ;
GOTO $+5 ;
; Overflow, decrement upper register
MOVF PWM_DC_H, F ;
BTFSC STATUS, Z ;
RETURN
DECF PWM_DC_H, F ;
; Done
CALL REPLACE_PWM_DC ;
RETURN
EXTRACT_PWM_DC
; Extract PWM Duty Cycle from CCPR1L:CCP1CON into PWM_DC_H:PWM_DC_L
; CCPR1L:CCP1CON holds bits 0000 0000:xx00 xxxx.
; PWM_DC_H:PWM_DC_L will then hold xxxx xx00:0000 0000
MOVF CCP1CON, W ;
ANDLW B'00110000' ; Mask bits
MOVWF PWM_DC_L ;
RLF PWM_DC_L, F ;
RLF PWM_DC_L, W ;
ANDLW B'11000000' ; Mask bits
MOVWF PWM_DC_L ;
MOVF CCPR1L, W ;
MOVWF PWM_DC_H ;
BCF STATUS, C ;
RRF PWM_DC_H, F ;
RRF PWM_DC_L, F ;
RRF PWM_DC_H, F ;
RRF PWM_DC_L, F ;
RRF PWM_DC_H, F ;
RRF PWM_DC_L, F ;
RRF PWM_DC_H, F ;
RRF PWM_DC_L, F ;
RRF PWM_DC_H, F ;
RRF PWM_DC_L, F ;
RRF PWM_DC_H, F ;
RRF PWM_DC_L, F ;
RETURN
REPLACE_PWM_DC
; Replace PWM Duty Cycle from PWM_DC_H:PWM_DC_L into CCPR1L:CCP1CON
; PWM_DC_H:PWM_DC_L holds xxxx xx00:0000 0000
; CCPR1L:CCP1CON will now hold bits 0000 0000:xx00 xxxx.
RLF PWM_DC_L, F ; Rotate into C
RLF PWM_DC_H, F ; Rotate out of C
RLF PWM_DC_L, F ; Rotate into C
RLF PWM_DC_H, F ; Rotate out of C
RLF PWM_DC_L, F ; Rotate into C
RLF PWM_DC_H, F ; Rotate out of C
RLF PWM_DC_L, F ; Rotate into C
RLF PWM_DC_H, F ; Rotate out of C
RLF PWM_DC_L, F ; Rotate into C
RLF PWM_DC_H, F ; Rotate out of C
RLF PWM_DC_L, F ; Rotate into C
RLF PWM_DC_H, F ; Rotate out of C
MOVF PWM_DC_H, W ;
MOVWF CCPR1L ;
MOVF CCP1CON, W ;
ANDLW B'00001111' ; Ensure lower nibble of CCP1CON remains unchanged and move bits 5&4 into place
RRF PWM_DC_L, F ;
RRF PWM_DC_L, F ; Move the 2 LSB's into bits <5:4>
IORWF PWM_DC_L, W ;
MOVWF CCP1CON ;
RETURN
And with the 10-bit PWM right justified in my PWM_DC_H : PWM_DC_L RAM GPR's it makes it easy to do a BIN2BCD conversion for displaying the PWM.
This is working flawlessly, but can it be done better?