; Panteltje pwr.asm for power_pic ; integer math copyright Microchip, my part is GPL. ; This is a switch mode power supply ; Copyrright Jan Panteltje 2009-always. ; ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2 of the License, or ; (at your option) any later version. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ; CHANGES ; ; 0.1 ; First release ; 0.2 ; back to right adjusted ADC. ; moved LCD RW to pin 2, freeing up pin 17. ; Pin 17 is now an extra analog input, for now marked Uin (could be input voltage). ; calculation power added. ; moved data aquitsion routines to before display, sets variables. ; update LCD now every 200mS (was 1 second). ; removed old tristate DAC stuff that was on pin 2. ; 0.3: ; This version uses an external resistor divider or trimpot on pin 19 from the +5V as reference for the MOSFET current trip point. ; The current trip can no longer be set remotely, asa safety measure, it could be adjusted locally by a poteniometer when used as a current limit . ; Added the 'L' for local command, to transfer vlatge control back to a local potentiometer, selecting a voltage with PnnnENTER transfers control ; back to the serial link. ; Adapted menu options. ; RS232 commands: ; D enters debug mode, continuously prints ADC steps for Uin, Uout, Iout, and flags2. ; d exits debug mode. ; F flash clock separator, status saved in EEPROM. ; f do not flash clock separator, status saved in EEPROM. ; GnnnENTER sets clock calibration, timer1 reload low byte, use 175 for nominal, is saved in EEPROM. ; at first power up (directly after programming) the EEPROM is set with the default value for G(175). ; HnnENTER sets hour, range 0-24. ; h prints help. ; L select local potentiometer for voltage control. ; MnnENTER sets minute, range 0-59. ; PnnnENTER sets maximum PWM value, range 0 to 100, stored in EEPROM. ; S sets soft start speed, range 1-15. ; s prints status: time, local/remote voltage control, max.pwm, pwm, output voltage selection, input voltage, output voltage, output current, power. ; UnnnENTER selects output voltage, range 0-31, stored in EEPROM, this takes control away from the local potentiometer. ; v prints version number. ; Example of reply to status request 's': ; Clock cal 175 ; 0:0 ; Softstart speed 1 ; Remote voltage control ; Uout select 4 ; PWMmax. 14 ; PWM 14 ; Uin 6.0 V ; Uout 4.2 V ; Iout 24.0 A ; 100.8 W ; The math is done in integer. ; CODE IS FOR GPASM. ; Calculations are for a Vref = Vsupply of 5.0V, use LM317 for example. ; comment this out if you do not use a LCD display. #define USE_LCD TRUE equ 1 FALSE equ 0 MSB equ 7 SIGNED equ FALSE ; Set This To 'TRUE' if the routines ; ; for Multiplication & Division needs ; ; to be assembled as Signed Integer ; ; Routines. If 'FALSE' the above two ; ; routines ( D_mpy & D_div ) use ; ; unsigned arithmetic. ; for commands with numeric arguments. #define COMMAND_OFF D'0' #define COMMAND_PRINT_VERSION D'1' #define COMMAND_PRINT_IIME D'2' #define COMMAND_PRINT_HELP D'3' #define COMMAND_SET_MINUTES D'4' #define COMMAND_SET_HOURS D'5' #define COMMAND_CALIBRATE_CLOCK D'6' #define COMMAND_SET_MAX_PWM D'7' #define COMMAND_SELECT_OUTPUT_VOLTAGE D'8' ; time, clock calibration, input voltage, PWM, output voltage setpoint, output current setpoint, output voltage, output current. #define COMMAND_PRINT_STATUS D'9' #define COMMAND_SET_SOFT_START_SPEED D'10' ; mode for main #define MODE_OFF D'0' #define MODE_READ_ADC D'1' #define MODE_PRINT_VERSION D'2' #define MODE_PRINT_STATUS D'3' #define MODE_PRINT_HELP D'4' ; define config fuses ; IF YOU WANT TO RUN VERIFY, BETTER HAVE COPY PROTECTION OFF ;-) ; Internal OSC, pin 3 (RA4,AN3,T1G,OSC2,CLKOUT) available for IO. __CONFIG _FCMEN_OFF & _IESO_OFF & _BOR_OFF & _CPD_OFF & _CP_OFF & _MCLRE_OFF & _PWRTE_OFF & _WDT_OFF & _INTOSCIO ; when !MRCLRE is asserted in INTOSC or RC mode, the internal clock oscillator is disabled. ; include PIC register definitions. PROCESSOR p16f690 include ; LCD stuff #ifdef USE_LCD Dev_Freq EQU D'8000000' ; Device Frequency is 8 MHz LCD_INIT_DELAY EQU (HIGH ((( Dev_Freq / 4 ) * D'46' / D'10000' ) / 3 ) ) + 1 ; for LCD print position for 4 x 20 LCD ;#define LINE_1 D'128' ;#define LINE_2 D'168' ;#define LINE_3 D'148' ;#define LINE_4 D'212' ; for LCD print position for 2 x 16 LCD #define LINE_1 D'128' #define LINE_2 D'192' ; + 64 ; LCD Module commands DISP_ON EQU 0x00C ; Display on DISP_ON_C EQU 0x00E ; Display on, Cursor on DISP_ON_B EQU 0x00F ; Display on, Cursor on, Blink cursor DISP_OFF EQU 0x008 ; Display off CLR_DISP EQU 0x001 ; Clear the Display ENTRY_INC EQU 0x006 ; ENTRY_INC_S EQU 0x007 ; ENTRY_DEC EQU 0x004 ; ENTRY_DEC_S EQU 0x005 ; DD_RAM_ADDR EQU 0x080 ; Least Significant 7-bit are for address DD_RAM_UL EQU 0x080 ; Upper Left coner of the Display ; end LCD stuff #endif ; USE_LCD ; define register file variables ; start at 0x20 hex, 32 dec, total 256 bytes ; 0x20 - 0x7f in bank 0 ; 32 - 127 ; 0x70 - 0x7f in each bank the same ; 112 - 127 ; 0xa0 - 0xef in bank 1 ; 160 - 239 ; 0x120 - 0x16f in bank 2 ; 288 - 376 ;offset equ D'32' delay_count1 equ D'33' ; 1ms delay ;old_minute equ D'34' ;ampere_minutes_h equ D'35' ;ampere_minutes_l equ D'36' flags1 equ D'38' temp1 equ D'39' count equ D'40' temp_l equ D'41' temp_h equ D'42' temp equ D'43' ; temp for binary to BCD bin equ D'44' ; bin+1 for 16 bits equ D'45' bcd equ D'46' R0 equ D'47' ; BCD digit 5 right justified R1 equ D'48' ; BCD digit 4 and 3 R2 equ D'49' ; BCD digit 2 and 1 ii equ D'50' timer1_reload_l equ D'51' ; saved in EEPROM clock calibration old_seconds equ D'52' pwm_pulse_width equ D'53' int_fsr_save equ D'54' ;portc_shadow equ D'55' command equ D'56' digit_in equ D'57' digit_cnt equ D'58' temp3 equ D'59' value equ D'60' flags2 equ D'61' ; saved in EEPROM output_voltage_select equ D'63' pwm_max_pulse_width equ D'64' analog_select equ D'65' minutes equ D'66' seconds_5 equ D'67' seconds equ D'68' MSD equ D'69' ; Temporary register, Holds Most Significant Digit of BIN to BCD conversion LSD equ D'70' ; Temporary register, Holds Least Significant Digit of BIN to BCD conversion TEMP equ D'71' ; Temporary register CHAR equ D'72' ; Temporary register, Holds value to send to LCD module. delay_count2 equ D'73' watts_h equ D'74' watts_l equ D'75' output_voltage_sum equ D'76' old_seconds_5 equ D'77' input_voltage_l equ D'91' input_voltage_h equ D'92' soft_start_speed equ D'93' current_h equ D'95' current_l equ D'96' voltage_h equ D'97' voltage_l equ D'98' ;ampere_hours_h equ D'99' ;ampere_hours_l equ D'100' fsr_save equ D'101' fsr_save2 equ D'102' mode equ D'103' ad_h equ D'104' ad_l equ D'105' ; SAME IN ALL BANKS 112 - 127 hours equ D'112' eeprom_address equ D'113' eeprom_data equ D'114' ACCaLO equ D'115' ACCaHI equ D'116' ACCbLO equ D'117' ACCbHI equ D'118' ACCcLO equ D'119' ACCcHI equ D'120' ACCdLO equ D'121' ACCdHI equ D'122' sign equ D'123' s_pclath equ D'124' s_fsr equ D'125' temp_w equ D'126' temp_s equ D'127' ; define flags1 ONE_MINUTE_FLAG equ D'0' PRINT_DOT_FLAG equ D'1' SAVE_SETTINGS_FLAG equ D'2' FIRST_ZERO_SUPPRESSED_FLAG equ D'3' DIVIDE_BY_TEN_FLAG equ D'4' EEPROM_WRITE_ERROR_FLAG equ D'5' DEBUG_FLAG equ D'6' ONE_SECOND_FLAG equ D'7' ; define flags2, saved in EEPROM USE_FLASHING_CLOCK_FLAG equ D'1' USE_INTERNAL_VOLTAGE_REFERENCE_FLAG equ D'2' ; page macros call_0 macro subroutine_name clrf PCLATH ; Next call will go to page 0 call subroutine_name endm call_1 macro subroutine_name bsf PCLATH, 3 ; Next call or jump will go to page 1 CALL subroutine_name ; Jump to function that starts in this page endm ; macros to save and restore W and status register in interrupt. save_w_stat macro movwf temp_w swapf STATUS,W clrf STATUS ; extra force bank 0 clears IRP, RP1, RP0 movwf temp_s movfw PCLATH movwf s_pclath movfw FSR movwf s_fsr endm restore_w_stat macro movfw s_fsr movwf FSR movfw s_pclath movwf PCLATH swapf temp_s,W movwf STATUS swapf temp_w,F swapf temp_w,W endm ; code start org 0 goto main_init org 4 ; interrupt entry point ; 8 MHz internal OSC ; OPTION_REG is 0xff at power up! ; do interrupt processing here save_w_stat ; save W and status clrf PCLATH ; interrupts routines only call subroutines in page 0 ; what interrupt? ; test for timer 1 interrupt test_timer1_interrupt: banksel PIR1 ; bank 0 btfss PIR1, TMR1IF goto test_rx_interrupt increment_pwm: ; more incf instruction make it start faster, you need at least one. ; Need range check! movfw soft_start_speed addwf pwm_pulse_width ; increment pwm_pulse_width until it equals pwm_max_pulse_width ; if pulse > max set pulse! ; if pulse = max do nothing ; if pulse < max increment pulse ; test equal movfw pwm_max_pulse_width subwf pwm_pulse_width, w btfsc STATUS, Z goto decr_seconds_5 ; test pulse >= max movfw pwm_max_pulse_width subwf pwm_pulse_width, w btfss STATUS, C goto set_pwm ; pulse > max, make equal movfw pwm_max_pulse_width movwf pwm_pulse_width goto set_pwm set_pwm: ; adjust pwm, wait for correct reload moment here? set_pwm: ; adjust pwm, wait for correct reload moment here? banksel 0 btfss pwm_pulse_width, 0 goto clear_bit_0 ; set bit 0 banksel CCP1CON bsf CCP1CON, DC1B0 goto test_bit_1 clear_bit_0: banksel CCP1CON bcf CCP1CON, DC1B0 goto test_bit_1 test_bit_1 banksel 0 btfss pwm_pulse_width, 1 goto clear_bit_1 ; set bit 1 banksel CCP1CON bsf CCP1CON, DC1B1 goto high_bits clear_bit_1: banksel CCP1CON bcf CCP1CON, DC1B1 ; goto high_bits high_bits: banksel 0 ; get pulse width movfw pwm_pulse_width ; use upper 6 bits ; shift right bcf STATUS, C rrf W, 1 ; shift right bcf STATUS, C rrf W, 1 ; put in CCPR1L, bit 7 and 6 are always zero banksel CCPR1L movwf CCPR1L decr_seconds_5: ; decrement seconds / 5, if 0 reload 5, decrement seconds. banksel 0 bcf flags1, ONE_SECOND_FLAG decfsz seconds_5 goto in_5 bsf flags1, ONE_SECOND_FLAG movlw D'5' movwf seconds_5 ; increment seconds incf seconds ; test 60 seconds movlw D'60' subwf seconds, w btfss STATUS, Z goto in_5 one_minute: clrf seconds incf minutes bsf flags1, ONE_MINUTE_FLAG ; to be checked and cleared in main ; test 60 minutes movlw D'60' subwf minutes, w btfss STATUS, Z goto in_5 clrf minutes incf hours ; test 24 hours movlw D'24' subwf hours, w btfss STATUS, Z goto in_5 clrf hours in_5: ; reload 65535 - 50000 makes 5Hz = 200mS ; Calibrate clock speed here ; banksel TMR1H ; bank 0 65535 - 50000 = 15535 reload (upcounter int on overflow), makes 60 175 movlw D'60' movwf TMR1H ; movlw D'175' movfw timer1_reload_l movwf TMR1L ; banksel T1CON ; in bank 0 bsf T1CON, T1CKPS1 ; 11 = 1:8 prescaler bsf T1CON, T1CKPS0 ; ; banksel 0 ; goto int_end test_rx_interrupt: banksel PIR1 btfss PIR1, RCIF ; test if serial port interrupt goto int_end ; read rx status ; test framing error banksel RCSTA btfss RCSTA, FERR goto test_overrun ; framing error goto reset_rx_circuit test_overrun: btfss RCSTA, OERR goto get_rx_char ; overrun error reset_rx_circuit: ; clear error flags bcf RCSTA, FERR bcf RCSTA, OERR ; restart rx bcf RCSTA, CREN bsf RCSTA, CREN get_rx_char: ; have serial char in RCREG ; TEST echo for TEST ; movfw RCREG ; call tx_w ; force immediate response to serial input movfw seconds movwf old_seconds incf old_seconds movlw D'13' subwf RCREG, W btfsc STATUS, Z goto cr_command movlw 'D' subwf RCREG, W btfsc STATUS, Z goto debug_on_command movlw 'd' subwf RCREG, W btfsc STATUS, Z goto debug_off_command movlw 'F' subwf RCREG, W btfsc STATUS, Z goto flashing_clock_on_command movlw 'f' subwf RCREG, W btfsc STATUS, Z goto flashing_clock_off_command movlw 'G' subwf RCREG, W btfsc STATUS, Z goto calibrate_clock_command movlw 'H' subwf RCREG, W btfsc STATUS, Z goto set_hours_command movlw 'h' subwf RCREG, W btfsc STATUS, Z goto print_help_command movlw 'L' subwf RCREG, W btfsc STATUS, Z goto select_local_voltage_control_command movlw 'M' subwf RCREG, W btfsc STATUS, Z goto set_minutes_command movlw 'P' subwf RCREG, W btfsc STATUS, Z goto set_pwm_command movlw 'U' subwf RCREG, W btfsc STATUS, Z goto select_output_voltage_command movlw 'S' subwf RCREG, W btfsc STATUS, Z goto set_soft_start_speed_command movlw 's' subwf RCREG, W btfsc STATUS, Z goto print_status_command movlw 'v' subwf RCREG, W btfsc STATUS, Z goto print_version_command movlw '0' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '1' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '2' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '3' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '4' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '5' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '6' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '7' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '8' subwf RCREG, W btfsc STATUS, Z goto process_digits_command movlw '9' subwf RCREG, W btfsc STATUS, Z goto process_digits_command goto int_end cr_command: ; If zero digits were entered, do nothing movlw COMMAND_OFF subwf digit_cnt, W btfsc STATUS, Z ; if zero digits do nothing goto int_end movlw COMMAND_SET_MAX_PWM subwf command, W btfsc STATUS, Z goto end_set_pwm_command movlw COMMAND_SET_MINUTES subwf command, W btfsc STATUS, Z goto end_set_minutes_command movlw COMMAND_SET_HOURS subwf command, W btfsc STATUS, Z goto end_set_hours_command movlw COMMAND_CALIBRATE_CLOCK subwf command, W btfsc STATUS, Z goto end_calibrate_clock_command movlw COMMAND_SELECT_OUTPUT_VOLTAGE subwf command, W btfsc STATUS, Z goto end_select_output_voltage_command movlw COMMAND_SET_SOFT_START_SPEED subwf command, w btfsc STATUS, Z goto end_set_soft_start_speed_command goto int_end ; these commands have no numeric argument print_status_command: movlw COMMAND_PRINT_STATUS movwf command movlw MODE_PRINT_STATUS movwf mode goto int_end_clr debug_on_command: bsf flags1, DEBUG_FLAG goto int_end_clr debug_off_command: bcf flags1, DEBUG_FLAG goto int_end_clr flashing_clock_on_command: bsf flags2, USE_FLASHING_CLOCK_FLAG call save_settings ; goto int_end_clr goto int_end flashing_clock_off_command: bcf flags2, USE_FLASHING_CLOCK_FLAG call save_settings ; goto int_end_clr goto int_end print_version_command: movlw COMMAND_PRINT_VERSION movwf command movlw MODE_PRINT_VERSION movwf mode goto int_end_clr print_help_command: movlw COMMAND_PRINT_HELP movwf command movlw MODE_PRINT_HELP movwf mode goto int_end_clr ;reset_ampere_hours_command: ; clrf ampere_minutes_h ; clrf ampere_minutes_l ; clrf ampere_hours_h ; clrf ampere_hours_l ; force recalc ; movfw minutes ; movwf old_minutes ; goto int_end_clr select_local_voltage_control_command: ; select external V reference, 0= 2IN+ pin, 1=C2Vref banksel CM2CON0 bcf CM2CON0, C2R banksel 0 bcf flags2, USE_INTERNAL_VOLTAGE_REFERENCE_FLAG call save_settings goto int_end ; these commands have a numeric argument set_pwm_command: movlw COMMAND_SET_MAX_PWM movwf command goto command_end select_output_voltage_command: movlw COMMAND_SELECT_OUTPUT_VOLTAGE movwf command goto command_end set_minutes_command: movlw COMMAND_SET_MINUTES movwf command goto command_end set_hours_command: movlw COMMAND_SET_HOURS movwf command goto command_end calibrate_clock_command: movlw COMMAND_CALIBRATE_CLOCK movwf command goto command_end set_soft_start_speed_command: movlw COMMAND_SET_SOFT_START_SPEED movwf command goto command_end ; final processing of commands with numeric arguments, we have the number in 'value' now. end_set_pwm_command: movfw value movwf pwm_max_pulse_width call save_settings goto command_end end_select_output_voltage_command: movfw value ; mask out lowest 5 bits, 4,3,2,1,0, max value is 31 andlw D'31' movwf output_voltage_select call internal_vref_select ; select internal reference, 0= 2IN+ pin, 1=C2Vref banksel CM2CON0 bsf CM2CON0, C2R banksel 0 bsf flags2, USE_INTERNAL_VOLTAGE_REFERENCE_FLAG call save_settings goto command_end end_calibrate_clock_command: movfw value movwf timer1_reload_l call save_settings goto command_end end_set_minutes_command: movfw value movwf minutes clrf seconds goto command_end end_set_hours_command: movfw value movwf hours goto command_end end_set_soft_start_speed_command: movfw value movwf soft_start_speed call save_settings goto command_end process_digits_command: movlw D'48' subwf RCREG, W ; digit now in W movwf digit_in ; value = value * 10 ; value += RCREG movfw value ; W = original value ; look, I know about rlf, but we have 4096 words available. addwf value ; value * 2 addwf value ; value * 3 addwf value ; value * 4 addwf value ; value * 5 addwf value ; value * 6 addwf value ; value * 7 addwf value ; value * 8 addwf value ; value * 9 addwf value ; value * 10 movfw digit_in ; input digit in W addwf value ; add W to value incf digit_cnt goto int_end ; end of interrupt routines command_end: ; reset the value, so we can continue with nnnENTER banksel 0 clrf value clrf digit_cnt goto int_end int_end_clr: ; for non numeric commands, no digits expected. banksel 0 clrf command int_end: banksel 0 ; OLD IF COMPARATOR movf CMCON, W ; read CMCON to end mismatch because of comparator output change banksel INTCON ; INTCON: bcf INTCON, GIE ; bcf INTCON, PEIE ; bcf INTCON, T0IE ; bcf INTCON, INTE ; bcf INTCON, RABIE ; bcf INTCON, T0IF ; timer 0 overflow interrupt flag bcf INTCON, INTF ; GP2/INT bcf INTCON, RABIF ; port change interrupt flag bit ; PIR1 -- ADIF RCIF TXIF SSPIF CCP1IF TMR2IF TMR1IF banksel PIR1 bcf PIR1, ADIF bcf PIR1, RCIF bcf PIR1, TXIF bcf PIR1, SSPIF bcf PIR1, CCP1IF bcf PIR1, TMR2IF bcf PIR1, TMR1IF ; PIR2 OSFIF C2IF C1IF EEIF -- -- -- -- banksel PIR2 bcf PIR2, OSFIF bcf PIR2, C2IF bcf PIR2, C1IF bcf PIR2, EEIF restore_w_stat ; get back W and status retfie main_init: ; set internal OSC speed, the output on pin 3 (OSC2) is this clock / 4. ; SELECT BANK banksel OSCCON ; clrf OSCCON ; all zero ; bcf OSCCON, 7 ; bit 7 not implemented bsf OSCCON, 6 ; bit 6 osc speed 111=8 MHz, 110=4MHz (default), 101=2MHz, 100=1MHz, 011=500kHz, 010=250kHz, 001=125kHz, 000=32kHz (LFINTOSC) bsf OSCCON, 5 ; bit 5 osc speed bsf OSCCON, 4 ; bit 4 osc speed bcf OSCCON, 3 ; bit 3, HTS 0 = internal OSC, 1 = external ; bcf OSCCON, 2 ; bit 2, HTS HFINTOSC status, 1 = stable ; bcf OSCCON, 1 ; bit 1, LTS LFINTOSC status, 1 = stable bcf OSCCON, 0 ; bit 0, SCS 0 = clock source defined by FOSC<2:0> of the CONFIG register, 1 = internal osc used for system clock ; initialize IO output latches banksel 0 ; GPIO bank 0, address 0x05 clrf PORTA ; init all output latches to zero clrf PORTB clrf PORTC ; clrf portc_shadow banksel 0 ; init async serial out ;12.1.1.6 Asynchronous Transmission Set-up: ;1. Initialize the SPBRGH, SPBRG register pair and ; the BRGH and BRG16 bits to achieve the desired ; baud rate (see Section 12.3 "EUSART Baud ; Rate Generator (BRG)"). ;2. Enable the asynchronous serial port by clearing ; the SYNC bit and setting the SPEN bit. ;3. If 9-bit transmission is desired, set the TX9 con- ; trol bit. A set ninth data bit will indicate that the 8 ; Least Significant data bits are an address when ; the receiver is set for address detection. ;4. Enable the transmission by setting the TXEN ; control bit. This will cause the TXIF interrupt bit ; to be set. ;5. If interrupts are desired, set the TXIE interrupt ; enable bit. An interrupt will occur immediately ; provided that the GIE and PEIE bits of the ; INTCON register are also set. ;6. If 9-bit transmission is selected, the ninth bit ; should be loaded into the TX9D data bit. ;7. Load 8-bit data into the TXREG register. This ; will start the transmission. ; 8 bit async ; SYNC = 0, BRG16 = 0, BRGH=1, at 8MHz: 19231 Baud, 0.16% for SPBRG=25 banksel SPBRG movlw D'25' movwf SPBRG ; default 8 bit mode, SPBRGH not used ; banksel SPBRGH banksel BAUDCTL bcf BAUDCTL, BRG16 banksel TXSTA bsf TXSTA, BRGH bcf TXSTA, SYNC banksel RCSTA bsf RCSTA, SPEN banksel TXSTA bsf TXSTA, TXEN ;12.1.2.8 Asynchronous Reception Set-up: ;1. Initialize the SPBRGH, SPBRG register pair and ; the BRGH and BRG16 bits to achieve the ; desired baud rate (see Section 12.3 "EUSART ; Baud Rate Generator (BRG)"). ;2. Enable the serial port by setting the SPEN bit. ; The SYNC bit must be clear for asynchronous ; operation. ;3. If interrupts are desired, set the RCIE interrupt ; enable bit and set the GIE and PEIE bits of the ; INTCON register. ;4. If 9-bit reception is desired, set the RX9 bit. ;5. Enable reception by setting the CREN bit. ;6. The RCIF interrupt flag bit will be set when a ; character is transferred from the receive shift ; register to the receive buffer. An interrupt will be ; generated if the RCIE interrupt enable bit was ; also set. ;7. Read the RCSTA register to get the error flags ; and, if 9-bit data reception is enabled, the ninth ; data bit. ;8. Get the received 8 Least Significant data bits ; from the receive buffer by reading the RCREG ; register. ;9. If an overrun occurred, clear the OERR flag by ; clearing the CREN receiver enable bit. banksel RCSTA bsf RCSTA, CREN ; SYNC and SPEN already done. banksel 0 ; Load 5 Hz ticck movlw D'5' movwf seconds_5 ; clock to 00:00 clrf seconds clrf minutes clrf hours ;This code block configures the ADC ;for polling, Vdd reference, Frc clock ;and AN0 input. ; ;Conversion start & polling for completion ; are included. ; BANKSEL ADCON1 ; ; MOVLW B'00100000' ; not used, :32, ; Set clock speed for ADC, at 8 Mhz legal is fosc / 16 (101), and fosc / 32 (010). bsf ADCON1, ADCS0 ; set clock / 16 = 2 us bcf ADCON1, ADCS0 bsf ADCON1, ADCS0 ; other bits are not i nuse in 16F690 MOVWF ADCON1 ; ADC channels #define INPUT_VOLTAGE D'2' #define OUTPUT_VOLTAGE D'3' #define OUTPUT_CURRENT D'10' ; LCD Display Commands and Control Signal names. LCD_E EQU 6 ; LCD Enable control line LCD_RW EQU 5 ; LCD Read/Write control line LCD_RS EQU 1 ; LCD Register Select control line LCD_E_PORT EQU PORTB LCD_RW_PORT EQU PORTA LCD_RS_PORT EQU PORTA LCD_DATA_PORT EQU PORTC LCD_DATA_TRIS EQU TRISC ; Resistor values for voltage divider on pin 14 Vout sense: ; Resistor values for voltage divider on pin 3, the ADC input to the volts display, also used for remote RS232 voltage setting: ; ; I want 255 steps of the 10 bit ADC to correspond to 25.5 V output, for easy calculation in the software. ; this gives a max voltage of about 102 V with a 5 V supply used as reference ; With 5 V reference one step is 5 / 1024 = 4.8828 mV. ; For 255 stpes we then need a voltage on the ADC input of 255 * 4.8828 = 1.245 V. ; If we have 1.245 V over 10 k to ground , then the drop over the other resistor in the divider should be 25.5 - 1.245 = 24.255 V ; So the other resistor in the divider should be 24.255 / 1.245 x 10 k = 194.8 k. ; We will use 180 k in series with 15 k. ; ; ; Resistor values for voltage divider on pin 14, the comparator 2 input. ; ; For a max output voltage of 25.5 (design decision) and 10k to ground, we want 5 V on the comparator input. ; So that leaves 25.5 - 5 = 20.5 volts for the other resistor in the divider. ; The other resistor should then be (20.5 / 5) x 10 = 41 k. ; We will use 33 k and 8.2 k in series, that makes 41.2 k (2k2 and 39 k will also work). ; for 25.5 V = 255 steps on the ADC ; Pin assignment: ; pin in/out port type function ; 1 _ Vdd +5V supply LCD p 2 LCD LED+ p 15 ; 2 out RA5 digital I/O LCD RW LCD p 5 ; 3 in RA4 AN3 Vout connect to pin 14 to monitior Vout with ADC. ; 4 in RA3 prog Vpp n.c. free for general digital input. ; 5 out RC5 digital I/O LCD D5 LCD p 12 ; 6 out RC4 digital I/O LCD D4 LCD p 11 ; 7 out RC3 P1C PWM out ; 8 out RC6 digital I/O LCD D6 LCD p 13 ; 9 out[in] RC7 digital I/O LCD D7 used as input when testing LCD status ready LCD p 14 ; 10 out RB7 digital TXD serial port out ; 11 out RB6 digital I/O LCD E LCD p 6 ; 12 in RB5 RXD serial port in ; 13 in RB4 AN10 Iout ; 14 in RC2 AN6 = C12IN2- Uout sense voltage sense ; 15 in RC1 AN5 = C12IN1- Itrip sense current sense ; 16 in RC0 AN4 = C2IN+ Uout reference voltage sense reference ; 17 in RA2 AN2 Uin measure or general puprose analog in. ; 18 out RA1 prog clock LCD RS LCD p 4 ; 19 in RA0 prog data C1IN+ current sense reference n.c. ; 20 _ Vss 0V GND LCD p 1 LCD contrast p 3 LCD LED- p16 ; Move PWM out to P1C, ; then move U out sense analog input to AN3 ; IO pin bit assignment PORTA, PORTB, PORTC ; RA and RB have change interrupt ; PORTA 6 bit wide ; PORTB 4 bit wide ; PORTC 8 bit wide ; 1 = input, 0 = output banksel TRISA bsf TRISA, 0 bcf TRISA, 1 bsf TRISA, 2 bsf TRISA, 3 bsf TRISA, 4 bcf TRISA, 5 ; bcf TRISA, 6 ; does not exist ; bcf TRISA, 7 ; does not exist banksel TRISB ; bcf TRISB, 0 ; does not exist ; bcf TRISB, 1 ; does not exist ; bcf TRISB, 2 ; does not exist ; bcf TRISB, 3 ; does not exist bsf TRISB, 4 bsf TRISB, 5 bcf TRISB, 6 bcf TRISB, 7 banksel TRISC bsf TRISC, 0 bsf TRISC, 1 bsf TRISC, 2 bcf TRISC, 3 bcf TRISC, 4 bcf TRISC, 5 bcf TRISC, 6 bcf TRISC, 7 ; Select which port pins are analog inputs BANKSEL ANSEL ; default is 1111 1111 bcf ANSEL, 0 ; ASN 0 bcf ANSEL, 1 ; ASN 1 bsf ANSEL, 2 ; ASN 2 bsf ANSEL, 3 ; ASN 3 bcf ANSEL, 4 ; ASN 4 bsf ANSEL, 5 ; ASN 5 bsf ANSEL, 6 ; ASN 6 bcf ANSEL, 7 ; ASN 7 BANKSEL ANSELH ; default is 1111 bcf ANSELH, 0 ; ASN 8 bcf ANSELH, 1 ; ASN 9 bsf ANSELH, 2 ; ASN 10 bcf ANSELH, 3 ; ASN 11 ; only lower 4 bits used ; right justify analog result BANKSEL ADCON0 clrf ADCON0 bsf ADCON0, ADFM ; ADFM right justified. bcf ADCON0, VCFG ; Vdd reference ; select an analog channel for the ADC, Vout = AN7 = 0111 bcf ADCON0, 5 ; 0 bsf ADCON0, 4 ; 1 bsf ADCON0, 3 ; 1 bsf ADCON0, 2 ; 1 ; bit 1 is A/D conversion status bit ; start the ADC bsf ADCON0, ADON ; AD on ; OPTION_REG banksel OPTION_REG bsf OPTION_REG, NOT_RABPU ; 1 = global pullups disabled bcf OPTION_REG, INTEDG ; 1 = interupt on risin gedge INT pin bcf OPTION_REG, T0CS ; f/4 internal clock to timer 0 bcf OPTION_REG, T0SE ; increment on low to high transition of T0CKL pin bsf OPTION_REG, PSA ; 0 = prescaler assigned to timer 0 module, 1 = prescaler assigned to watch dog timer bcf OPTION_REG, PS2 ; prescaler rate select bits, 1, 2, 4, 8, 16, 32, 64, 128 bcf OPTION_REG, PS1 bsf OPTION_REG, PS0 ; serial in interrupt enable banksel PIE1 clrf PIE1 bsf PIE1, RCIE ; USART receive interrupt enable ; bsf PIE1, ADIE ; AD converter interrupt enable (12F675 only) bsf PIE1, TMR1IE ; timer 1 overflow interrupt enable ; bsf PIE1, CMIE ; comparator interrupt enable ; PIE2: OSFIE C2IE C1IE EEIE -- -- -- -- banksel PIE2 bcf PIE2, EEIE ; EEPROM write complete interrupt enable ; flags to zero clrf flags1 ; clrf flags2 ; set by load_settings ; init timer0 reload banksel TMR0 movlw D'0' movwf TMR0 ; clear timer 0 interrupt flag ; timer 1 :4 clock = 2MHz, :8 prescaler=250000 Hz, :50000 reload=5Hz interrupt banksel T1CON bcf T1CON, T1GINV ; non invert bcf T1CON, TMR1GE ; no gate enable bsf T1CON, T1CKPS1 ; 11 = 1:8 prescaler bsf T1CON, T1CKPS0 ; bcf T1CON, T1OSCEN ; no external clock bcf T1CON, NOT_T1SYNC ; no sync bcf T1CON, TMR1CS ; select :4 internal osc = 2MHz bsf T1CON, TMR1ON ; timer 1 on ; set timer 1 reload 0, and clear TMR1IF as on page 88 pdf banksel TMR1H ; 50000 reload clrf TMR1H banksel TMR1L movwf TMR1L ; reset timer 1 interrupt flag banksel PIR1 bcf PIR1, TMR1IF ; enable gobal and peripheral interrupt banksel INTCON banksel INTCON clrf INTCON bsf INTCON, PEIE ; peripheral interrupts enable bcf INTCON, T0IE ; timer 0 overflow interrupt enable ; bsf INTCON, INTE ; GP2/INT interrupt enable ; bsf INTCON, GPIE ; port change interrupt enable bsf INTCON, GIE ; global interrupt enable ; banksel WDTCON ; configure watchdog timer ; WDTCON: -- -- -- WDTPS3 WDTPS2 WDTPS1 WDTPS0 SWDTEN ; movlw B'00011111' ; software enable watchdog timer, : 65535 prescaler 31kHz osc, second prescaler : 128, 268 seconds ; movwf WDTCON main: banksel 0 #ifdef USE_LCD ; init LCD ; Initilize the LCD Display Module bcf LCD_E_PORT, LCD_E bcf LCD_RW_PORT, LCD_RW bcf LCD_RS_PORT, LCD_RS DISPLAY_INIT: ; wait > 15ms after power up movlw D'254' call_1 delay_1ms clrf PCLATH MOVLW 0x20 ; Command for 4-bit interface low nibble MOVWF LCD_DATA_PORT BSF LCD_E_PORT, LCD_E BCF LCD_E_PORT, LCD_E ; This routine takes the calculated times that the delay loop needs to ; be executed, based on the LCD_INIT_DELAY EQUate that includes the ; frequency of operation. These uses registers before they are needed to ; store the time. LCD_DELAY: MOVLW LCD_INIT_DELAY ; MOVWF MSD ; Use MSD and LSD Registers to Initilize LCD CLRF LSD ; LOOP2 DECFSZ LSD, F ; Delay time = MSD * ((3 * 256) + 3) * Tcy GOTO LOOP2 ; DECFSZ MSD, F ; END_LCD_DELAY GOTO LOOP2 ; ; Command sequence for 2 lines of 5x7 characters CMD_SEQ: MOVLW 0X20 ; 4-bit high nibble xfer MOVWF LCD_DATA_PORT ; This code for both 4-bit and 8-bit modes BSF LCD_E_PORT, LCD_E BCF LCD_E_PORT, LCD_E MOVLW 0x80 ; 4-bit high nibble xfer MOVWF LCD_DATA_PORT BSF LCD_E_PORT, LCD_E BCF LCD_E_PORT, LCD_E ; Busy Flag should be valid after this point MOVLW DISP_ON CALL SEND_CMD MOVLW CLR_DISP CALL SEND_CMD MOVLW ENTRY_INC CALL SEND_CMD MOVLW DD_RAM_ADDR CALL SEND_CMD ;#define LCD_TEST #ifdef LCD_TEST ; LCD_POS_TEST ; print Panteltje on LCD line 3 movlw LINE_1 call SEND_CMD movlw 'P' call SEND_CHAR movlw 'a' call SEND_CHAR movlw 'n' call SEND_CHAR movlw 't' call SEND_CHAR movlw 'e' call SEND_CHAR movlw 'l' call SEND_CHAR movlw 't' call SEND_CHAR movlw 'j' call SEND_CHAR movlw 'e' call SEND_CHAR #endif ; LCD_TEST #endif ; USE_LCD ; load settings from eeprom 0 banksel 0 call load_settings ; set the voltage reference method, now that we know the flag btfss flags2, USE_INTERNAL_VOLTAGE_REFERENCE_FLAG goto use_input_pin_as_voltage_reference ; select internal V reference banksel CM2CON0 bsf CM2CON0, C2R ; 0= 2IN+ pin, 1=C2Vref goto set_vref_comparator_1 use_input_pin_as_voltage_reference: ; select C2IN+ pin as reference banksel CM2CON0 bcf CM2CON0, C2R ; 0= 2IN+ pin, 1=C2Vref set_vref_comparator_1: ; set Vref comparator1 banksel 0 call internal_vref_select ; Init PWM banksel 0 call_1 pwm_init clrf PCLATH ; set soft start to zero, clock will increase this over time to pwm_max_pulse_width banksel 0 clrf pwm_pulse_width ; adjust pwm, wait for correct reload moment here? banksel 0 movfw pwm_pulse_width banksel CCPR1L movwf CCPR1L ; clear ampere hours ; clrf ampere_minutes_h ; clrf ampere_minutes_l ; clrf ampere_hours_h ; clrf ampere_hours_l ; clrf current_h ; clrf current_l ; force calc of Ah ; movfw minutes ; movwf old_minutes ; gets mysteriously set? bcf flags1, DEBUG_FLAG banksel 0 ; clrf mode main_loop: ; update the display once a second. wait_for_seconds_change: banksel 0 movfw seconds_5 subwf old_seconds_5, w btfss STATUS, Z goto seconds_5_changed ; same second_5 btfss flags1, DEBUG_FLAG ; goto wait_for_seconds_change goto test_print_version ; while waiting for seconds to change if debug we print the ADC steps ; debug mode, print ADC steps for all analog inputs, and flags2 banksel 0 movlw INPUT_VOLTAGE call ad_pri movlw OUTPUT_VOLTAGE call ad_pri movlw OUTPUT_CURRENT call ad_pri movfw flags2 call print_w_ascii_dec call tx_crlf goto wait_for_seconds_change seconds_5_changed: movfw seconds_5 movwf old_seconds_5 ; read ADC and calculate Uin, Uout, Iin, Pout ; get Uin movlw INPUT_VOLTAGE movwf analog_select call_1 read_adc clrf PCLATH ; voltage divider on ADC input is such that 25.5V results in 1023 ADC steps, divided by 4 gives 255, adding a dot makes 25.5 (V) on the LCD display ; the advantage of just using the high byte, is that noise and imterference in the lower bits are not displayed, resulting in a more stable and precise display (more ADC input). ; get high byte ; divide by 4 movfw ad_l movwf ACCbLO movfw ad_h movwf ACCbHI clrf ACCaHI movlw D'4' movwf ACCaLO ; Double Precision Divide ( 16/16 -> 16 ) ( ACCb/ACCa -> ACCb with remainder in ACCc ) : 16 bit output with Quotiont in ACCb (ACCbHI,ACCbLO) and Remainder in ACCc (ACCcHI,ACCcLO). call D_divF banksel 0 movfw ACCbHI movwf input_voltage_h movfw ACCbLO movwf input_voltage_l ; get Uout movlw OUTPUT_VOLTAGE movwf analog_select call_1 read_adc clrf PCLATH ; voltage divider on ADC input is such that 25.5V results in 1023 ADC steps, divided by 4 gives 255, adding a dot makes 25.5 (V) on the LCD display ; the advantage of just using the high byte, is that noise and imterference in the lower bits are not displayed, resulting in a more stable and precise display (more ADC input). ; get high byte ; divide by 4 movfw ad_l movwf ACCbLO movfw ad_h movwf ACCbHI clrf ACCaHI movlw D'4' movwf ACCaLO ; Double Precision Divide ( 16/16 -> 16 ) ( ACCb/ACCa -> ACCb with remainder in ACCc ) : 16 bit output with Quotiont in ACCb (ACCbHI,ACCbLO) and Remainder in ACCc (ACCcHI,ACCcLO). call D_divF banksel 0 movfw ACCbHI movwf voltage_h movfw ACCbLO movwf voltage_l ; get Iout movlw OUTPUT_CURRENT movwf analog_select call_1 read_adc clrf PCLATH movfw ad_h movwf current_h movfw ad_l movwf current_l ; get Pout ; P = U x I movfw voltage_h movwf ACCaHI movfw voltage_l movwf ACCaLO movfw current_h movwf ACCbHI movfw current_l movwf ACCbLO ; Double Precision Multiply ( 16 x 16 -> 32 ( ACCb * ACCa -> ACCb, ACCc ) : 32 bit output with high word, results in ACCb(16 msb's) and ACCc(16 lsb's) call D_mpyS ; U x I now in ACCb (high)and ACCc (low), the max is 255 x 255 = 65535, so we only use ACCc banksel 0 ; to get the dot on the right place we need to divide by 100, but we already divide by 10 by shifting the dot in the display, so still need to divide by 10. ; divide by 10 movfw ACCcHI movwf ACCbHI movfw ACCcLO movwf ACCbLO clrf ACCaHI movlw D'10' movwf ACCaLO ; Double Precision Divide ( 16/16 -> 16 ) ( ACCb/ACCa -> ACCb with remainder in ACCc ) : 16 bit output with Quotiont in ACCb (ACCbHI,ACCbLO) and Remainder in ACCc (ACCcHI,ACCcLO). call D_divF banksel 0 movfw ACCbHI movwf watts_h movfw ACCbLO movwf watts_l ; end data aquisition ; update_lcd #ifdef USE_LCD ; print status LCD ; 2 height x 16 width ; ------------------ ; | 21:37 12.3V 8.2A | never bigger then 9A out ; | 10.1V 14.7W | ; ------------------ never bigger then 99 Watt print_time_lcd: banksel 0 ; print position movlw LINE_1 call SEND_CMD ; Clear, write 6 spaces 'uu:mm ' movlw D'6' call write_spaces ; position again movlw LINE_1 call SEND_CMD bcf flags1, DIVIDE_BY_TEN_FLAG clrf bin movfw hours movwf bin+1 call print_16_ascii_dec_LCD btfss flags2, USE_FLASHING_CLOCK_FLAG goto pri_sep ; flash the ':' btfss seconds, 0 goto pri_sep_space pri_sep: ; print ':' movlw ':' goto pri_separator ; print space pri_sep_space: movlw ' ' pri_separator: call SEND_CHAR clrf bin movfw minutes movwf bin+1 call print_16_ascii_dec_LCD ; print output voltage LCD ; LCD print position movlw LINE_1+D'6' call SEND_CMD ; Clear, write 6 spaces, '12.3V ' movlw D'6' call write_spaces ; LCD print position movlw LINE_1+D'6' call SEND_CMD banksel 0 movfw voltage_h movwf bin movfw voltage_l movwf bin+1 bsf flags1, DIVIDE_BY_TEN_FLAG call print_16_ascii_dec_LCD movlw 'V' call SEND_CHAR ; print output current LCD ; print position movlw LINE_1+D'12' call SEND_CMD ; Clear, write 5 spaces '8.2A ' movlw D'5' call write_spaces ; position again movlw LINE_1+D'12' call SEND_CMD banksel 0 movfw current_h movwf bin movfw current_l movwf bin+1 bsf flags1, DIVIDE_BY_TEN_FLAG call print_16_ascii_dec_LCD movlw 'A' call SEND_CHAR ; print input voltage LCD ; LCD print position movlw LINE_2 call SEND_CMD ; Clear, write 6 spaces, '12.3V ' movlw D'6' call write_spaces ; LCD print position movlw LINE_2 call SEND_CMD banksel 0 movfw input_voltage_h movwf bin movfw input_voltage_l movwf bin+1 bsf flags1, DIVIDE_BY_TEN_FLAG call print_16_ascii_dec_LCD movlw 'V' call SEND_CHAR ; print output Watts LCD ; print position movlw LINE_2+D'6' call SEND_CMD ; Clear, write 7 spaces '99.9W' movlw D'6' call write_spaces ; position again movlw LINE_2+D'6' call SEND_CMD banksel 0 movfw watts_h movwf bin movfw watts_l movwf bin+1 bsf flags1, DIVIDE_BY_TEN_FLAG call print_16_ascii_dec_LCD movlw 'W' call SEND_CHAR #endif ;USE_LCD test_print_version: banksel 0 movlw MODE_PRINT_VERSION subwf mode, w btfss STATUS, Z goto test_print_help ; print version call_1 print_id clrf PCLATH goto main_end test_print_help movlw MODE_PRINT_HELP subwf mode, w btfss STATUS, Z goto test_print_status ; print help call_1 help_pri clrf PCLATH goto main_end test_print_status: movlw MODE_PRINT_STATUS subwf mode, w btfss STATUS, Z goto no_mode_found ; linefeed call tx_crlf ; print clock calibration movlw 'C' call tx_w movlw 'l' call tx_w movlw 'o' call tx_w movlw 'c' call tx_w movlw 'k' call tx_w movlw ' ' call tx_w movlw 'c' call tx_w movlw 'a' call tx_w movlw 'l' call tx_w movlw ' ' call tx_w movfw timer1_reload_l call print_w_ascii_dec call tx_crlf ; print time movfw hours call print_w_ascii_dec movlw ':' call tx_w movfw minutes call print_w_ascii_dec call tx_crlf ; print soft-start speed banksel 0 movlw 'S' call tx_w movlw 'o' call tx_w movlw 'f' call tx_w movlw 't' call tx_w movlw 's' call tx_w movlw 't' call tx_w movlw 'a' call tx_w movlw 'r' call tx_w movlw 't' call tx_w movlw ' ' call tx_w movlw 's' call tx_w movlw 'p' call tx_w movlw 'e' call tx_w movlw 'e' call tx_w movlw 'd' call tx_w movlw ' ' call tx_w movfw soft_start_speed call print_w_ascii_dec call tx_crlf ; print local or remote volatge control status banksel 0 btfss flags2, USE_INTERNAL_VOLTAGE_REFERENCE_FLAG goto local_v_control ; serial link selects V ref movlw 'R' call tx_w movlw 'e' call tx_w movlw 'm' call tx_w movlw 'o' call tx_w movlw 't' call tx_w movlw 'e' call tx_w goto say_vctrl ; local pot selects Vref local_v_control: movlw 'L' call tx_w movlw 'o' call tx_w movlw 'c' call tx_w movlw 'a' call tx_w movlw 'l' call tx_w say_vctrl: movlw ' ' call tx_w movlw 'v' call tx_w movlw 'o' call tx_w movlw 'l' call tx_w movlw 't' call tx_w movlw 'a' call tx_w movlw 'g' call tx_w movlw 'e' call tx_w movlw ' ' call tx_w movlw 'c' call tx_w movlw 'o' call tx_w movlw 'n' call tx_w movlw 't' call tx_w movlw 'r' call tx_w movlw 'o' call tx_w movlw 'l' call tx_w call tx_crlf ; print output voltage select banksel 0 movlw 'U' call tx_w movlw 'o' call tx_w movlw 'u' call tx_w movlw 't' call tx_w movlw ' ' call tx_w movlw 's' call tx_w movlw 'e' call tx_w movlw 'l' call tx_w movlw 'e' call tx_w movlw 'c' call tx_w movlw 't' call tx_w movlw ' ' call tx_w movfw output_voltage_select call print_w_ascii_dec call tx_crlf ; print PWM max banksel 0 movlw 'P' call tx_w movlw 'W' call tx_w movlw 'M' call tx_w movlw 'm' call tx_w movlw 'a' call tx_w movlw 'x' call tx_w movlw '.' call tx_w movlw ' ' call tx_w movfw pwm_max_pulse_width call print_w_ascii_dec call tx_crlf ; print PWM banksel 0 movlw 'P' call tx_w movlw 'W' call tx_w movlw 'M' call tx_w movlw ' ' call tx_w movfw pwm_pulse_width call print_w_ascii_dec call tx_crlf ; print input voltage movlw 'U' call tx_w movlw 'i' call tx_w movlw 'n' call tx_w movlw ' ' call tx_w movlw D'0' movwf bin movfw input_voltage_h movwf bin movfw input_voltage_l movwf bin+1 bsf flags1, DIVIDE_BY_TEN_FLAG call print_16_ascii_dec movlw ' ' call tx_w movlw 'V' ; say A call tx_w call tx_crlf ; print output voltage banksel 0 movlw 'U' call tx_w movlw 'o' call tx_w movlw 'u' call tx_w movlw 't' call tx_w movlw ' ' call tx_w movfw voltage_h movwf bin movfw voltage_l movwf bin+1 bsf flags1, DIVIDE_BY_TEN_FLAG call print_16_ascii_dec movlw ' ' call tx_w movlw 'V' ; say A call tx_w call tx_crlf ; print output current banksel 0 movlw 'I' call tx_w movlw 'o' call tx_w movlw 'u' call tx_w movlw 't' call tx_w movlw ' ' call tx_w movfw current_h movwf bin movfw current_l movwf bin+1 bsf flags1, DIVIDE_BY_TEN_FLAG call print_16_ascii_dec movlw ' ' call tx_w movlw 'A' ; say A call tx_w call tx_crlf ; prints output Watts movfw watts_h movwf bin movfw watts_l movwf bin+1 bsf flags1, DIVIDE_BY_TEN_FLAG call print_16_ascii_dec movlw ' ' call tx_w movlw 'W' call tx_w call tx_crlf ; end print status goto main_end no_mode_found: banksel 0 goto main_loop ; IF NOT FOUND, ONLY CLEAR mode if something found. main_end: banksel 0 clrf mode goto main_loop ; ***************** subroutines ******************** internal_vref_select: banksel VRCON bsf VRCON, VRR ; low range se;ect bcf VRCON, VR3 ; reference select bcf VRCON, VR2 ; reference select bcf VRCON, VR1 ; reference select bcf VRCON, VR0 ; reference select banksel 0 btfss output_voltage_select, 4 ; 16 ? goto test_ct_3 banksel VRCON bcf VRCON, VRR ; high range se;ect test_ct_3: banksel 0 btfss output_voltage_select, 3 ; 8 ? goto test_ct_2 banksel VRCON bsf VRCON, VR3 test_ct_2: banksel 0 btfss output_voltage_select, 2 ; 4 ? goto test_ct_1 banksel VRCON bsf VRCON, VR2 test_ct_1: banksel 0 btfss output_voltage_select, 1 ; 2 ? goto test_ct_0 banksel VRCON bsf VRCON, VR1 test_ct_0: banksel 0 btfss output_voltage_select, 0 ; 1? return banksel VRCON bsf VRCON, VR0 banksel 0 return #ifdef USE_LCD write_spaces: ; write W spaces to LCD at current position movwf temp1 space_loop: movlw ' ' call SEND_CHAR decfsz temp1, F goto space_loop return #endif ; USE_LCD ; send byte in W tx_digit_in_w: addlw '0' ; zero tx_w: banksel TXREG ; is in bank 0 movwf TXREG banksel TXSTA ; is in bank 1 test_tx_empty: ;test_txsta: btfss TXSTA, TRMT goto test_tx_empty ;tx_has_timed_out: banksel 0 return print_w_ascii_dec: ; prints register W in ASCII decimal banksel 0 bsf flags1, FIRST_ZERO_SUPPRESSED_FLAG movwf temp1 clrf count ; number of hundreds found loop_hundreds: movlw D'100' subwf temp1 btfss STATUS, C ; if no carry flag, no more hundreds, go count tenth goto count10 ; substraction failed incf count goto loop_hundreds count10: movlw D'0' subwf count, W btfsc STATUS, Z goto suppress_first_zero bcf flags1, FIRST_ZERO_SUPPRESSED_FLAG movfw count call tx_digit_in_w ; print hundreds suppress_first_zero: movlw D'100' ; restore temp1 from one substract to many addwf temp1 clrf count ; number of tenth found loop_tenth: movlw D'10' subwf temp1 btfss STATUS, C ; if no carry flag no more tenth, only units left goto count1 incf count goto loop_tenth count1: movlw D'0' subwf count, W btfss STATUS, Z goto print_tenth ; tenth not zero ; tenth zero ; test if zero supression was active in hundreds (first digit) btfsc flags1, FIRST_ZERO_SUPPRESSED_FLAG goto print_units ; if first digit was not zero, print this zero print_tenth: movfw count call tx_digit_in_w ; print tenth print_units: movlw D'10' ; restore temp1 from 1 substract to many addwf temp1 ; units movfw temp1 call tx_digit_in_w ; print units banksel 0 return print_16_ascii_dec: ; prints 16 bit value in registers bin (high byte) and bin+1 (low byte) in ASCII decimal banksel 0 call_1 b2bcd clrf PCLATH ; suppress leading zeros bsf flags1, FIRST_ZERO_SUPPRESSED_FLAG movfw bcd call print_bcd_in_w_decimal_2_digits movfw bcd+1 call print_bcd_in_w_decimal_2_digits btfss flags1, DIVIDE_BY_TEN_FLAG goto pri_16_no_dot ; force dot printing just before last digit bsf flags1, PRINT_DOT_FLAG bcf flags1, DIVIDE_BY_TEN_FLAG pri_16_no_dot: movfw bcd+2 call print_bcd_in_w_decimal_2_digits bcf flags1, PRINT_DOT_FLAG ; if the zero suppress flag is still set we had zero, so print zero btfss flags1, FIRST_ZERO_SUPPRESSED_FLAG return movlw D'0' call tx_digit_in_w return print_bcd_in_w_decimal_2_digits: banksel 0 ; high digit first movwf temp ; save w swapf temp, w andlw 0x0f ; test if zero btfss STATUS, Z ; if not zero print it goto print_high_nibble test_lead_zero: ; test if leading zero suppress flag set btfsc flags1, FIRST_ZERO_SUPPRESSED_FLAG goto low_nibble print_high_nibble: bcf flags1, FIRST_ZERO_SUPPRESSED_FLAG call tx_digit_in_w low_nibble: ; test if we need to print a dot here banksel 0 btfss flags1, PRINT_DOT_FLAG goto no_dot movlw '.' call tx_w no_dot: banksel 0 ; low digit second movfw temp ; get w andlw 0x0f ; test if zero btfss STATUS, Z ; if not zero print it goto print_low_nibble ; test if leading zero suppress flag is set btfsc flags1, FIRST_ZERO_SUPPRESSED_FLAG ; return if no zero print return print_low_nibble bcf flags1, FIRST_ZERO_SUPPRESSED_FLAG call tx_digit_in_w return tx_crlf: movlw D'13' call tx_w ; CR movlw D'10' ; LF call tx_w return eeprom_write: banksel INTCON bcf INTCON, GIE ; disable interrupts BANKSEL EEADR ; MOVF eeprom_address, W ; MOVWF EEADR ; Data Memory Address to write MOVF eeprom_data, W ; MOVWF EEDAT ; Data Memory Value to write BANKSEL EECON1 ; BCF EECON1, EEPGD ; Point to DATA memory BSF EECON1, WREN ; Enable writes BCF INTCON, GIE ; Disable INTs. BTFSC INTCON, GIE ; SEE AN576 GOTO $-2 MOVLW 0x55 ; MOVWF EECON2 ; Write 55h MOVLW 0xAA ; MOVWF EECON2 ; Write AAh BSF EECON1, WR ; Set WR bit to begin write BSF INTCON, GIE ; Enable INTs. eeprom_poll_write_complete_loop: banksel EECON1 btfsc EECON1, WR goto eeprom_poll_write_complete_loop BCF EECON1, WREN ; Disable writes eeprom_verify: ; data at EEDADR against eeprom_data banksel 0 call eeprom_read ; EEDATA in w banksel 0 bcf flags1, EEPROM_WRITE_ERROR_FLAG movfw eeprom_data banksel EEDATA subwf EEDATA, W ; compare to requested btfss STATUS, Z ; skip if no error goto eeprom_write_error banksel 0 return eeprom_write_error: call eeprom_report banksel 0 bsf flags1, EEPROM_WRITE_ERROR_FLAG return eeprom_report: ; eeprom write result banksel 0 movlw 'E' call tx_w movlw 'E' call tx_w movlw 'P' call tx_w ; address=nnn movlw 'a' call tx_w movlw '=' call tx_w movfw eeprom_address call print_w_ascii_dec movlw ' ' call tx_w ; original=nnn movlw 'o' call tx_w movlw 'r' call tx_w movlw 'g' call tx_w movlw '=' call tx_w movfw eeprom_data call print_w_ascii_dec movlw ' ' call tx_w ; read=nnn movlw 'r' call tx_w movlw 'd' call tx_w movlw '=' call tx_w ; read eeprom call eeprom_read banksel 0 call print_w_ascii_dec call tx_crlf return eeprom_read: ; address in eeprom_address, returns data in W banksel 0 movfw eeprom_address banksel EEADR movwf EEADR banksel EECON1 bcf EECON1, EEPGD ; access data memory bsf EECON1, RD banksel EEDATA movfw EEDATA ; return data in w return save_settings: banksel 0 movlw D'1' ; clock_calibration movwf eeprom_address ; 1 movfw timer1_reload_l movwf eeprom_data call eeprom_write banksel 0 incf eeprom_address ; 2 movfw pwm_max_pulse_width movwf eeprom_data call eeprom_write banksel 0 incf eeprom_address ; 3 movfw output_voltage_select movwf eeprom_data call eeprom_write incf eeprom_address ; 4 movfw flags2 movwf eeprom_data call eeprom_write incf eeprom_address ; 5 movfw soft_start_speed movwf eeprom_data call eeprom_write banksel 0 return load_settings: ; this will load settings from EEPROM. ; To see if any value was actually programmed in EEPROM, first EEPROM address 255 is read. ; if EEPROM address 255 reads 255, then nothing was programmed, defaults are then written to EEPROM, ; and the value 123 is written to EEPROM address 255 as a marker that it is programmed. banksel 0 movlw D'255' movwf eeprom_address ; 255 call eeprom_read ; returns data in w banksel 0 xorlw D'123' ; test for programmed btfsc STATUS, Z goto eeprom_is_programmed ; program default values in eeprom banksel 0 movlw D'175' movwf timer1_reload_l movlw D'5' movwf pwm_max_pulse_width movlw D'7' movwf output_voltage_select movlw D'1' movwf soft_start_speed clrf flags2 call save_settings ; mark writtem banksel 0 movlw D'123' movwf eeprom_data movlw D'255' movwf eeprom_address call eeprom_write banksel 0 return eeprom_is_programmed: banksel 0 movlw D'1' ; clock calibration movwf eeprom_address ; 1 call eeprom_read ; returns data in w banksel 0 movwf timer1_reload_l incf eeprom_address ; 2 call eeprom_read banksel 0 movwf pwm_max_pulse_width incf eeprom_address ; 3 call eeprom_read banksel 0 movwf output_voltage_select incf eeprom_address ; 4 call eeprom_read banksel 0 movwf flags2 incf eeprom_address ; 5 call eeprom_read banksel 0 movwf soft_start_speed banksel 0 return neg_A comf ACCaLO, F ; negate ACCa ( -ACCa -> ACCa ) incf ACCaLO, F btfsc STATUS, Z decf ACCaHI, F comf ACCaHI, F retlw 0 ; Double Precision Subtraction ( ACCb - ACCa -> ACCb ) D_sub ; call neg_A ; At first negate ACCa; Then add ; inline comf ACCaLO, F ; negate ACCa ( -ACCa -> ACCa ) incf ACCaLO, F btfsc STATUS, Z decf ACCaHI, F comf ACCaHI, F ; Double Precision Addition ( ACCb + ACCa -> ACCb ) D_add movf ACCaLO, W addwf ACCbLO, F ; add lsb btfsc STATUS, C ; add in carry incf ACCbHI, F movf ACCaHI, W addwf ACCbHI, F ; add msb retlw 0 ; division macro ; divMac MACRO LOCAL NOCHK LOCAL NOGO ; bcf STATUS, C rlf ACCdLO, F rlf ACCdHI, F rlf ACCcLO, F rlf ACCcHI, F movf ACCaHI, W subwf ACCcHI, W ;check if a>c btfss STATUS, Z goto NOCHK movf ACCaLO, W subwf ACCcLO, W ;if msb equal then check lsb NOCHK btfss STATUS,C ;carry set if c>a goto NOGO movf ACCaLO, W ;c-a into c subwf ACCcLO, F btfss STATUS, C decf ACCcHI, F movf ACCaHI, W subwf ACCcHI, F bsf STATUS, C ;shift a 1 into b (result) NOGO rlf ACCbLO, F rlf ACCbHI, F ; ENDM ; Double Precision Divide ( 16/16 -> 16 ) ; ; ( ACCb/ACCa -> ACCb with remainder in ACCc ) : 16 bit output ; with Quotiont in ACCb (ACCbHI,ACCbLO) and Remainder in ACCc (ACCcHI,ACCcLO). ; ; NOTE : Before calling this routine, the user should make sure that ; the Numerator(ACCb) is greater than Denominator(ACCa). If ; the case is not true, the user should scale either Numerator ; or Denominator or both such that Numerator is greater than ; the Denominator. setup movlw .16 ; for 16 shifts movwf temp movf ACCbHI, W ; move ACCb to ACCd movwf ACCdHI movf ACCbLO, W movwf ACCdLO clrf ACCbHI clrf ACCbLO retlw 0 ; D_divF ; IF SIGNED CALL S_SIGN ENDIF ; call setup clrf ACCcHI clrf ACCcLO ; ; use the divMac macro 16 times ; divMac divMac divMac divMac divMac divMac divMac divMac divMac divMac divMac divMac divMac divMac divMac divMac ; IF SIGNED btfss sign,MSB ; check sign if negative retlw 0 goto neg_B ; negate ACCa ( -ACCa -> ACCa ) ELSE retlw 0 ENDIF ; Assemble this section only if Signed Arithmetic Needed IF SIGNED ; S_SIGN movf ACCaHI,W xorwf ACCbHI,W movwf sign btfss ACCbHI,MSB ; if MSB set go & negate ACCb goto chek_A ; comf ACCbLO ; negate ACCb incf ACCbLO btfsc STATUS,Z decf ACCbHI comf ACCbHI ; chek_A btfss ACCaHI,MSB ; if MSB set go & negate ACCa retlw 0 goto neg_A ; ENDIF ; Double Precision Multiply ( 16x16 -> 32 ) ; ( ACCb*ACCa -> ACCb,ACCc ) : 32 bit output with high word ; in ACCb ( ACCbHI,ACCbLO ) and low word in ACCc ( ACCcHI,ACCcLO ). D_mpyS ;results in ACCb(16 msb's) and ACCc(16 lsb's) ; IF SIGNED CALL S_SIGN ENDIF ; call setup mloop rrf ACCdHI, F ;rotate d right rrf ACCdLO, F btfsc STATUS,C ;need to add? call D_add rrf ACCbHI, F rrf ACCbLO, F rrf ACCcHI, F rrf ACCcLO, F decfsz temp, F ;loop until all bits checked goto mloop ; IF SIGNED btfss sign,MSB retlw 0 comf ACCcLO, F ; negate ACCa ( -ACCa -> ACCa ) incf ACCcLO, F btfsc STATUS,Z decf ACCcHI, F comf ACCcHI, F btfsc STATUS,Z neg_B comf ACCbLO, F ; negate ACCb incf ACCbLO, F btfsc STATUS,Z decf ACCbHI, F comf ACCbHI, F retlw 0 ELSE retlw 0 ENDIF #ifdef USE_LCD ;******************************************************************* ;* SEND_CHAR - Sends character to LCD * ;* This routine splits the character into the upper and lower * ;* nibbles and sends them to the LCD, upper nibble first. * ;* The data is transmitted on the PORT<7:4> pins * ;******************************************************************* SEND_CHAR: MOVWF CHAR ; Character to be sent is in W call_1 BUSY_CHECK clrf PCLATH MOVF CHAR, W ANDLW 0xF0 ; Get upper nibble MOVWF LCD_DATA_PORT ; Send data to LCD BCF LCD_RW_PORT, LCD_RW ; Set LCD to write BSF LCD_RS_PORT, LCD_RS ; Set LCD to data mode BSF LCD_E_PORT, LCD_E ; toggle E for LCD BCF LCD_E_PORT, LCD_E SWAPF CHAR, W ANDLW 0xF0 ; Get lower nibble MOVWF LCD_DATA_PORT ; Send data to LCD BSF LCD_E_PORT, LCD_E ; toggle E for LCD BCF LCD_E_PORT, LCD_E RETURN ;******************************************************************* ;* SendCmd - Sends command to LCD * ;* This routine splits the command into the upper and lower * ;* nibbles and sends them to the LCD, upper nibble first. * ;* The data is transmitted on the PORT<7:4> pins * ;******************************************************************* ; 4-bit transfers on the low nibble of the PORT SEND_CMD: MOVWF CHAR ; Character to be sent is in W, save in CHAR call_1 BUSY_CHECK clrf PCLATH MOVF CHAR, W ; Get back CHAR to W ANDLW 0xF0 ; Get upper nibble MOVWF LCD_DATA_PORT ; Send data to LCD BCF LCD_RW_PORT, LCD_RW ; Set LCD to write BCF LCD_RS_PORT, LCD_RS ; Set LCD to command mode BSF LCD_E_PORT, LCD_E ; toggle E for LCD BCF LCD_E_PORT, LCD_E SWAPF CHAR, W ANDLW 0xF0 ; Get lower nibble MOVWF LCD_DATA_PORT ; Send data to LCD BSF LCD_E_PORT, LCD_E ; toggle E for LCD BCF LCD_E_PORT, LCD_E RETURN print_16_ascii_dec_LCD: ; prints 16 bit value in registers bin (high byte) and bin+1 (low byte) in ASCII decimal call_1 b2bcd clrf PCLATH ; suppress leading zeros bsf flags1, FIRST_ZERO_SUPPRESSED_FLAG movfw bcd call print_bcd_in_w_decimal_2_digits_LCD movfw bcd+1 call print_bcd_in_w_decimal_2_digits_LCD btfss flags1, DIVIDE_BY_TEN_FLAG goto pri_16_no_dot_LCD ; force dot printing just before last digit bsf flags1, PRINT_DOT_FLAG bcf flags1, DIVIDE_BY_TEN_FLAG pri_16_no_dot_LCD: movfw bcd+2 call print_bcd_in_w_decimal_2_digits_LCD bcf flags1, PRINT_DOT_FLAG ; if the zero suppress flag is still set we had zero, so print zero btfss flags1, FIRST_ZERO_SUPPRESSED_FLAG return movlw '0' call SEND_CHAR return print_bcd_in_w_decimal_2_digits_LCD: ; high digit first movwf temp ; save w swapf temp, w andlw 0x0f ; test if zero btfss STATUS, Z ; if not zero print it goto print_high_nibble_LCD test_lead_zero_LCD: ; test if leading zero suppress flag set btfsc flags1, FIRST_ZERO_SUPPRESSED_FLAG goto low_nibble_LCD print_high_nibble_LCD: bcf flags1, FIRST_ZERO_SUPPRESSED_FLAG addlw '0' call SEND_CHAR low_nibble_LCD: ; test if we need to print a dot here btfss flags1, PRINT_DOT_FLAG goto no_dot_LCD movlw '.' call SEND_CHAR no_dot_LCD: ; low digit second movfw temp ; get w andlw 0x0f ; test if zero btfss STATUS, Z ; if not zero print it goto print_low_nibble_LCD ; test if leading zero suppress flag is set btfsc flags1, FIRST_ZERO_SUPPRESSED_FLAG ; return if no zero print return print_low_nibble_LCD: bcf flags1, FIRST_ZERO_SUPPRESSED_FLAG addlw '0' call SEND_CHAR return #endif ; USE_LCD ad_pri: ; prints ADC channel in W followed by a space movwf analog_select call_1 read_adc clrf PCLATH movfw ad_h movwf bin movfw ad_l movwf bin+1 call print_16_ascii_dec banksel 0 movlw ' ' call tx_w return ; PAGE 1 org D'2048' #ifdef USE_LCD ; 4-bit transfers on the low nibble of the PORT ; ;******************************************************************* ;* This routine checks the busy flag, returns when not busy * ;* Affects: * ;* TEMP - Returned with busy/address * ;******************************************************************* BUSY_CHECK banksel LCD_DATA_TRIS ; MOVLW 0xFF ; set LCD data port to input movlw B'11110111' MOVWF LCD_DATA_TRIS banksel 0 BCF LCD_RS_PORT, LCD_RS BSF LCD_RW_PORT, LCD_RW ; Setup to read busy flag BSF LCD_E_PORT, LCD_E ; Set E high BCF LCD_E_PORT, LCD_E ; Set E low MOVF LCD_DATA_PORT, W ; Read upper nibble busy flag, DDRam address ANDLW 0xF0 ; Mask out lower nibble MOVWF TEMP BSF LCD_E_PORT, LCD_E ; Toggle E to get lower nibble BCF LCD_E_PORT, LCD_E SWAPF LCD_DATA_PORT, W ; Read lower nibble busy flag, DDRam addres ANDLW 0x0F ; Mask out upper nibble IORWF TEMP ; Combine nibbles BTFSC TEMP, 7 ; Check busy flag, high = busy GOTO BUSY_CHECK ; If busy, check again BCF LCD_RW_PORT, LCD_RW banksel LCD_DATA_TRIS ; MOVLW 0x0F movlw B'00000111' MOVWF LCD_DATA_TRIS banksel 0 RETURN #endif ; USE_LCD ; Convert 32-bit binary number at into a bcd number ; at . Uses Mike Keitz's procedure for handling bcd ; adjust; Modified Microchip AN526 for 32-bits. b2bcd: bcf STATUS, C movlw D'16' ; 32 for 32-bits movwf ii ; make cycle counter clrf bcd ; clear result area clrf bcd+1 clrf bcd+2 ; clrf bcd+3 ; clrf bcd+4 b2bcd2: movlw bcd ; make pointer movwf FSR movlw 3 ; was 5 movwf count ; Mike's routine: b2bcd3: movlw 0x33 addwf INDF, f ; add to both nybbles btfsc INDF, 3 ; test if low result > 7 andlw 0xf0 ; low result >7 so take the 3 out btfsc INDF, 7 ; test if high result > 7 andlw 0x0f ; high result > 7 so ok subwf INDF, f ; any results <= 7, subtract back incf FSR, f ; point to next decfsz count goto b2bcd3 ; rlf bin+3,f ; get another bit ; rlf bin+2,f rlf bin+1,f rlf bin+0,f ; rlf bcd+4,f ; put it into bcd ; rlf bcd+3,f rlf bcd+2,f rlf bcd+1,f rlf bcd+0,f decfsz ii,f ; all done? goto b2bcd2 ; no, loop movlw 0 movwf bcd return delay10us: ; delays W * 10 us movlw D'60' ; 10 us movwf delay_count1 delay_loop1: nop nop nop nop nop nop nop nop nop nop decf delay_count1,f btfss STATUS, Z goto delay_loop1 return delay_1ms: ; movlw D'100' ; 100 x 10 us = 1 ms movwf delay_count2 delay_loop2: call delay10us decf delay_count2,f btfss STATUS, Z goto delay_loop2 return read_adc: ; value for AD channel analog_select to ad_h and ad_l banksel 0 movfw analog_select ; TRISC or other ports must be set for input if used for ADC ; ANSEL and ANSELH must have bits set for analog input ; select an analog channel for the ADC ; set bit<5-2> in ADCONO for the selected input number ; mask out lowest 4 bits andlw D'15' movwf temp3 ; to position ; RLF: carry to LSB, MSB to carry ; clear carry bcf STATUS, C ; x 2 rlf temp3, 1 ; clear carry bcf STATUS, C rlf temp3, 1 ; x 4 ; to ADCON0 movfw temp3 ; ADCON0 is in bank 0 BANKSEL 0 clrf ADCON0 movwf ADCON0 bsf ADCON0, ADFM ; ADFM right justified, low byte in ADRESL, highest 2 bits in ADRESH bcf ADCON0, VCFG ; Vdd reference bit 6 ; bit 1 is A/D conversion status bit ; enable the ADC bsf ADCON0, ADON ; AD on bit 0 ; sample time delay ; With 10 k input, Tacq is about 7.67 us. ; At 8 MHz clock, makes 2 MHz system clock, makes .5 us system clock, and 1 system clock period per instruction 'nop', makes 16 nops. ; Note that a big capacitor on the inoput will charge the 10pF sample hold cap very much faster, without much if any voltage loss. nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop ; movlw D'100' ; movwf delay_count1 ;adc_sample_delay: ; decfsz delay_count1, F ; goto adc_sample_delay ; set ADCON0 GO / !DONE bit to start the conversion ; start ADC BSF ADCON0, GO ; Start conversion bit 1 ; wait for conversion ready BTFSC ADCON0, GO ; Is conversion done? GOTO $-1 ; No, test again ; ADRESH is in bank 0 ; BANKSEL ADRESH MOVF ADRESH, W ; Read upper 2 bits MOVWF ad_h ; store in GPR space ; ADRESL is in bank 1 BANKSEL ADRESL MOVF ADRESL, W ; Read lower 8 bits banksel 0 MOVWF ad_l ; Store in GPR space return ; return with zero in ad_l and ad_h pwm_init: ; PWM is set to auto shutdown, and auto restart, comparator 1 and 2 are selected as shutdown source, ; comparator 1 init for auto shutdown and auto restart. ; enable: banksel CM1CON0 bsf CM1CON0, C1ON ; source: select 00=C12IN0, 01=C12In1, 10=C12IN2, 11=C12IN3 ; selectC12IN1- bsf CM1CON0, C1CH0 bcf CM1CON0, C1CH1 ; invert bsf CM1CON0, C1POL ; output enable bit: no output to pin bcf CM1CON0, C1OE ; reference: 0=pin, 1=C1Vref: use pin bcf CM1CON0, C1R ; bit 3 is not implemented, reads 0 ; select reference: ; VRCON voltage reference control register ; reference select, 0=0.6V eference to comp1, 1=resistor divider to comp1 banksel VRCON bsf VRCON, C1VREN ; enable .6V reference always enabled when HF internal osc active. ; bsf VRCON, VP6EN ; This is done by curent_trip_set ; range select ; bsf VRCON, VRR ; low range se;ect ; select divider, 31 steps + 2 ranges selected with VRR ; bcf VRCON, VR0 ; bcf VRCON, VR1 ; bcf VRCON, VR2 ; bcf VRCON, VR3 ; comparator 2 init for auto shutdown and auto restart. ; enable: banksel CM2CON0 bsf CM2CON0, C2ON ; source: select 00=C12IN0, 01=C12In1, 10=C12IN2, 11=C12IN3 ; select C12IN2- bcf CM2CON0, C2CH0 bsf CM2CON0, C2CH1 ; invert bsf CM2CON0, C2POL ; output enable bit: no output to pin bcf CM2CON0, C2OE ; reference: 0= C2IN+ pin, 1=C2Vref: use pin ; done after load settings in main ; bcf CM2CON0, C2R ; bit 3 is not implemented, reads 0 ; select reference: ; VRCON voltage reference control register ; reference select, 0=0.6V eference to comp1, 1=resistor divider to comp1 banksel VRCON bsf VRCON, C2VREN ; enable .6V reference always enabled when HF internal osc active. ; bsf VRCON, VP6EN ; This is done by curent_trip_set ; range select ; bsf VRCON, VRR ; low range se;ect ; select divider, 31 steps + 2 ranges selected with VRR ; bcf VRCON, VR0 ; bcf VRCON, VR1 ; bcf VRCON, VR2 ; bcf VRCON, VR3 ; SETUP FOR PWM OPERATION ; from datasheet page 134 ; The following steps should be taken when configuring the CCP module for PWM operation: ; 1. Disable the PWM pin (CCP1) output driver by setting the associated TRIS bit. ; 2. Set the PWM period by loading the PR2 register. ; 3. Configure the CCP module for the PWM mode by loading the CCP1CON register with the appropriate values. ; 4. Set the PWM duty cycle by loading the CCPR1L register and DC1B<1:0> bits of the CCP1CON register. ; 5. Configure and start Timer2: ; · Clear the TMR2IF interrupt flag bit of the PIR1 register. ; · Set the Timer2 prescale value by loading the T2CKPS bits of the T2CON register. ; · Enable Timer2 by setting the TMR2ON bit of the T2CON register. ; 6. Enable PWM output after a new PWM cycle has started: ; · Wait until Timer2 overflows (TMR2IF bit of the PIR1 register is set). ; · Enable the CCP1 pin output driver by clearing the associated TRIS bit. ; enable auto shutdown banksel ECCPAS ; make auto shutdown force PWM pins P1B and P1D to zero bcf ECCPAS, PSSBD0 ; 00=PWM pins to zero, 01=to one, 11=to tristate bcf ECCPAS, PSSBD1 ; ; make auto shutdown force PWM pins P1A and P1C to zero bcf ECCPAS, PSSAC0 ; 00=PWM pins to zero, 01=to one, 11=to tristate bcf ECCPAS, PSSAC1 ; ; set auto shutdown source ; 000=disabled, 001=compC1, 010=comp C2, 011 either comp C1 or comp C2, 100=Vlow on INT pin, 101=Vlow o nINT pin or comp C1, 110=Vlow on INT pin or comp C2, 111=Vlow on INT pin or comp C1 or comp C2 bsf ECCPAS, 4 ; Select shutdown on comparator 1 and comparator 2 bsf ECCPAS, 5 ; bcf ECCPAS, 6 ; ; status ; bcf ECCPAS, 7 ; auto shutdown event status bit, 1 shutdown has happened. ; enable auto restart banksel PWM1CON bsf PWM1CON, PRSEN ; tristate PWM output pin. NEEDS RESISTOR ON MOSFET GATE banksel TRISC bsf TRISC, 5 ; PWM out p5 banksel TMR2 clrf TMR2 banksel PR2 ; PWM Period ; PWM frequency ; movlw 0x65 ; 8 MHz 1x prescaler, 8 bits resolution, 19.6 1kHz ; movlw D'101' ; movlw 0x19 ; 8 MHz 1x prescaler, 6 bits resolution, 76.92 kHz movlw D'25' ; movlw 0x0C ; 8 MHz 1x prescaler, 5 bits resolution, 153.85 kHz ; movlw D'12' movwf PR2 banksel 0 ; movlw D'0' ; Load Duty cycle into CCPR1L, seems 0 (0V) to - 102 (+5V). ; done by load_settings ; movwf max_pwm_pulse_width ; banksel CCPR1L ; <------------ MSB PWM ; movwf CCPR1L ; CCP1CON: P1Mu1 P1M0 DC1B1 DC1B0 CCP1M3 CCP1M2 CCP1M1 CCP1M0 ; ------------------------^^^^^^^^^^----- LSB PWM movlw b'00001100' banksel CCP1CON movwf CCP1CON ; Setup PWM mode l; PSTRCON pulse steering to either STRA, STRB, STRC, STRD 1=PWM ; select STRC as PWM output banksel PSTRCON bcf PSTRCON, STRA bcf PSTRCON, STRB bsf PSTRCON, STRC bcf PSTRCON, STRD banksel PIR1 bcf PIR1, TMR2IF ; set prescaler 1x banksel T2CON clrf T2CON bcf T2CON, T2CKPS0 ; 00 = /1, 01 = /4, 1x = /16 bcf T2CON, T2CKPS1 ; timer 2 on bsf T2CON, TMR2ON ; wait for timer 1 overflow banksel PIR1 wait_timer2_overflow: btfss PIR1, TMR2IF goto wait_timer2_overflow ; enable PWM output pin banksel TRISC bcf TRISC, 5 ; PWM out p5 banksel 0 return help_pri: banksel 0 movlw HIGH help_menu movwf temp_h movlw LOW help_menu movwf temp_l text_pri_loop: banksel 0 MOVF temp_h, W banksel EEADR ; MOVWF EEADRH ; MS Byte of Program Address to read banksel 0 MOVF temp_l, W banksel EEADR MOVWF EEADR ; LS Byte of Program Address to read BANKSEL EECON1 BSF EECON1, EEPGD ; Point to PROGRAM memory BSF EECON1, RD ; EE Read NOP ; First instruction after BSF EECON1,RD executes normally NOP ; Any instructions here are ignored as program memory is read in second cycle after BSF EECON1,RD BANKSEL EEDAT MOVF EEDAT, W ; W = LS Byte of Program Memory banksel 0 movwf temp ; save in temp banksel EEDATH MOVF EEDATH, W ; W = MS Byte of Program EEDAT banksel 0 movwf temp1 ; have data, high byte in temp1, low byte in temp ; print_temp1 ; bit 7 temp now holds the lower bit of the second character rlf temp1 ; shift temp1 to left, making free bit 0 rlf temp ; bit 7 temp to carry btfss STATUS, C goto was_zero bsf temp1, 0 ; set bit 0 temp if carry goto as_is was_zero: bcf temp1, 0 as_is: ; test for end of string movfw temp1 andlw D'127' xorlw 0 btfsc STATUS, Z return ; print low part andlw D'127' call_0 tx_w bsf PCLATH, 3 ; high part banksel EEDAT movfw EEDAT banksel 0 ; test for end of string xorlw 0 andlw D'127' btfsc STATUS, Z return ; print high part call_0 tx_w bsf PCLATH, 3 ; get next byte incf temp_l movlw D'0' subwf temp_l, W btfss STATUS, Z goto text_pri_loop incf temp_h goto text_pri_loop help_menu: ; help menu, unfortunately gputils (gpasm) does not understand '\ at the end of a line for continuation, like in C. ; \n will empty buffer in Linux / Unix, do not use \n\r at the end, \0 writes zero and is string termination, so always end with \r\n\0. ; Do not fold line ; It seems gpasm adds a zero at the end of a DA statement, causing printing from it to stop. DA "RS232 commands:\n\rD enters debug mode, continuously prints ADC steps for Uin, Uout, Iout, and flags2.\n\rd exits debug mode.\n\rF flash clock separator, status saved in EEPROM.\n\rf do not flash clock separator, status saved in EEPROM.\n\rGnnnENTER sets clock calibration, timer1 reload low byte, use 175 for nominal, is saved in EEPROM.\n\r at first power up (directly after programming) the EEPROM is set with the default value for G(175).\n\rHnnENTER sets hour, range 0-24.\n\rh prints help.\n\rL select local potentiometer for voltage control.\n\rMnnENTER sets minute, range 0-59.\n\rPnnnENTER sets maximum PWM value, range 0 to 100, stored in EEPROM.\n\rS sets soft start speed, range 1-15.\n\rs prints status: time, local/remote voltage control, max.pwm, pwm, output voltage selection, input voltage, output voltage, output current, power.\n\rUnnnENTER selects output voltage, range 0-31, stored in EEPROM, this takes control away from the local potentiometer.\n\rv prints version number.\r\n\0" print_id: banksel 0 movlw HIGH id_text movwf temp_h movlw LOW id_text movwf temp_l goto text_pri_loop id_text: ; Set version here DA "Panteltje power_pic-0.3\r\n\0" end