;********************************************************************** ; This file is the firmware for generating a button-to-I2C * ; controller that allows digital pot control without an I2C * ; to computer communications connection. * ; * ; This firmare supports Dallas Parts: DS1845 * ; * ; If the internal RC oscillator is not implemented then the first * ; instruction after the code section "MAIN" is not required. * ; * ; Refer to the MPASM User's Guide for additional information on * ; features of the assembler and linker (Document DS33014F). * ; * ; Refer to the respective PICmicro data sheet for additional * ; information on the instruction set. * ; * ; Template file built using MPLAB V3.99.18 with MPASM V2.15.06 and * ; MPLINK 1.10.11 as the language tools. * ; * ;********************************************************************** ; * ; Filename: button.asm * ; Date: 8/10/06 * ; File Version: 1.0 * ; * ; Author: Sean Donnelly * ; Company: Dallas Semiconductor/MAXIM MS1 group * ; * ; * ;********************************************************************** ; * ; Files required: macros.inc * ; P12F509.inc * ; 12F509.lkr * ; * ;********************************************************************** ; * ; Notes: This program supports DS1845 and DS1855. * ; * ; * ; * ; * ;********************************************************************** list p=12F509 ; list directive to define processor #include ; processor specific variable definitions __CONFIG _CP_OFF & _WDT_ON & _MCLRE_OFF & _IntRC_OSC ;WD enabled, SLEEP disabled ;__CONFIG _CP_OFF & _WDT_OFF & _MCLRE_OFF & _IntRC_OSC ;WD disabled, SLEEP enabled ; '__CONFIG' directive is used to embed configuration word within .asm file. ; The labels following the directive are located in the respective .inc file. ; See respective data sheet for additional information on configuration word. ADDRESS_BYTE_W EQU 0xA0 ; Control byte for I2C writting ADDRESS_BYTE_R EQU 0xA1 ; Control byte for I2C reading SCL EQU 0x04 ;BIT/GPIO # used for setting a bit in the form 0 ;macros to be inserted ;******************************************************************************************** RESET_VECTOR CODE 0x3FF ; processor reset vector ; Internal RC calibration value is placed at location 0x3FF by Microchip ; as a movlw k, where the k is a literal value. ;******************************************************************************************** ;******************** START MAIN PROGRAM **************************************************** ;******************************************************************************************** MAIN CODE 0x000 INIT: movwf OSCCAL ; update register with factory cal value movlw 0x0F ;watchdog W/ 1:128 option ;load the OPTION register movlw 0x3F ;00111111 movwf TRISGPIO ;Preload TRISGPIO W/ "1" tris GPIO ;GP0,1,2,3,4,5 --> HiZ (input) clrf GPIO ;Set all GPs to drive low. Hiz for HIGH level movlw 0x00 movwf POT_VAL ;Preload POT_VAL W/ 0 movwf I2C_ERROR_COUNT ;Preload I2C_ERROR_FLAG W/ 0 movwf POT_SEL_OLD ;Preload POT_VAL_OLD W/ 0 movwf POT_SEL ;Preload POT_SEL_OLD W/ 0 movlw 0x01 movwf READ_I2C_FLAG ;init flag to 1's, so I2C is read the first time on POR ;call I2C_RESET ;reset the I2C bus BLINK_LED_3X ;Blink LED 3X on startup (comment out if SLEEP is enabled) goto INFINITE ;******************************************************************************************** ;******************************** I2C START *********************************************** I2C_START: SDA2HIZ ;hI Z for SDA (SDA=1) nop nop nop SCL2HIZ ;hI Z for SCL (SCL=1) nop SDA2DVR ; SDA now driven low (SDA=0) nop ;I2C START is done, now get SCL=0 for clk pulses SCL2DVR ; SCL now driven low (SCL=0) retlw 0XB0 ;Return with B0. (SDA=0)& (SCL=0) ;******************************************************************************************** ;******************************** I2C STOP ************************************************ I2C_STOP: movwf TEMP ; save retlw value during I2C_WRITE sub. (ACK info from WRITE) SCL2DVR ; SCL now driven low (SCL=0) nop SDA2DVR ; SDA now driven low (SDA=0) nop nop nop SCL2HIZ ;hI Z for SCL (SCL=1) nop nop nop SDA2HIZ ;hI Z for SDA (SDA=1) *Actuall I2C STOP command retlw 0xB3 ;******************************************************************************************** ;******************************** I2C READ ************************************************ I2C_READ: clrf I2C_WORD ;0x00 to read register movlw 0x08 movwf I2C_BIT_CNTR ; Load bit counter SDA2HIZ ;hI Z for SDA (SDA=1) NEXT_RD_BIT: SCL2HIZ ;hI Z for SCL (SCL=1) btfsc GPIO, SDA bsf STATUS, C ; a 1 has been read btfss GPIO, SDA bcf STATUS, C ; a 0 has been read SCL2DVR ; SCL now driven low (SCL=0) rlf I2C_WORD, F ;rotate and save C into read register decfsz I2C_BIT_CNTR, F goto NEXT_RD_BIT ;SDA2DVR ; SDA now driven low (SDA=0) for ACK SDA2HIZ ;hI Z for SDA (SDA=1)for NACK nop ;delay may not be needed SCL2HIZ ;hI Z for SCL (SCL=1) pulse SCL nop SCL2DVR ; SCL now driven low (SCL=0) retlw 0xB2 ;******************************************************************************************** ;******************************** I2C WRITE *********************************************** I2C_WRITE: movlw 0x08 movwf I2C_BIT_CNTR ; Load bit counter NEXT_WRT_BIT: SCL2DVR ; SCL now driven low (SCL=0) rlf I2C_WORD, F ;Rotate I2C_WORD data left, thru carry flag btfsc STATUS, C ;Test carry bit in status reg. skip on clr goto WRITE_1 ; If bit is 1, then goto write_1 WRITE_0: SDA2DVR ; SDA now driven low (SDA=0) goto PULSE_SCL ; Go to pulse_SCL WRITE_1: SDA2HIZ ;hI Z for SDA (SDA=1) PULSE_SCL: SCL2HIZ ;hI Z for SCL (SCL=1), SCL has transitioned from 0 --> 1 (SCL=1) decfsz I2C_BIT_CNTR, F ;Decrement counter, if 0 check for ack goto NEXT_WRT_BIT ;still bits to write, loop back CHECK_ACK: SCL2DVR ; SCL now driven low (SCL=0), ready to read ack (SCL=0) SDA2HIZ ;hI Z for SDA (SDA=1) *Actuall I2C STOP command SCL2HIZ ;hI Z for SCL (SCL=1) btfsc GPIO, SDA ;check if SDA is low, return B1 for ack, else FA goto NACK ACK: SCL2DVR ; SCL now driven low (SCL=0) retlw 0xB1 NACK: SCL2DVR ; SCL now driven low (SCL=0) incf I2C_ERROR_COUNT, F ; increment i2c error counter retlw 0xFA ;******************************************************************************************** ;******************************** I2C WRITE WORD subroutine ******************************* I2C_WRITE_WORD: ;Load chip address into: ADDRESS_BYTE_W and ADDRESS_BYTE_R ;Load register address into: REG_ADD_BYTE ;Load single byte data to: I2C_WORD call I2C_START ;start movf I2C_WORD, W movwf I2C_WRITE_DATA movlw ADDRESS_BYTE_W movwf I2C_WORD call I2C_WRITE ;ADDRESS BYTE movf REG_ADD_BYTE, W movwf I2C_WORD call I2C_WRITE ;REG_ADD_BYTE movf I2C_WRITE_DATA, W movwf I2C_WORD call I2C_WRITE ;POT DATA --MID call I2C_STOP ;stop movlw 0x15 movwf DELAY_VAL_MS call DELAY_MS ;EEPROM Prog. delay ~25ms retlw 0X00 ;******************************************************************************************** ;******************************** I2C READ WORD subroutine ******************************* I2C_READ_WORD: ;Load chip address into: ADDRESS_BYTE_W and ADDRESS_BYTE_R ;Load register address into: REG_ADD_BYTE ;read data will be in: I2C_WORD call I2C_START ;start movlw ADDRESS_BYTE_W movwf I2C_WORD call I2C_WRITE ;ADDRESS BYTE movf REG_ADD_BYTE, W movwf I2C_WORD call I2C_WRITE ;REG_ADD_BYTE call I2C_START ;repeated start movlw ADDRESS_BYTE_R movwf I2C_WORD call I2C_WRITE ;REG_ADD_BYTE call I2C_READ ;read with NACK call I2C_STOP ;stop movf I2C_ERROR_COUNT, F btfsc STATUS, Z ;check if I2C_ERROR_COUNT has any non-zero value ;call I2C_ERROR ;yes, there was an error retlw 0X00 ;******************************************************************************************** ;******************************** I2C RESET subroutine ************************************ I2C_RESET: movlw 0x09 movwf I2C_BIT_CNTR ;set up counter TOGGLE: SCL2DVR ; SCL now driven low (SCL=0) SCL2HIZ ;hI Z for SCL (SCL=1) decfsz I2C_BIT_CNTR, F goto TOGGLE retlw 0xB4 ;******************************************************************************************** ;******************************** LED_OFF SUBROUTINE ************************************** LED_OFF: LED2HIZ retlw 0x01 ;Return with 01. (LED =HIZ & 0) ;******************************************************************************************** ;******************************** LED_ON SUBROUTINE *************************************** LED_ON: LED2DVR retlw 0x01 ;Return with 01. (LED =DRV & 0) ;******************************************************************************************** ;******************************** DELAY SUBROUTINE **************************************** ;**** DELAYS FROM 9us in loop +3us on exit w/ DELAY_VAL_US=12us to 2298 us w/ DELAY_VAL_US=255 *** DELAY_US: nop ;1 nop ;1 nop ;1 nop ;1 nop ;1 nop ;1 decfsz DELAY_VAL_US, F ;DEC DELAY_VAL skip of zero ;1+1 goto DELAY_US ;2 retlw 0x01 ;2 ;******************************************************************************************** ;******************************** DELAY SUBROUTINE **************************************** ;**** DELAYS FROM ~1ms w/ DELAY_VAL_MS=1.1 to 255ms w/ DELAY_VAL_MS=~290 ******************* DELAY_MS: movlw 0x7D movwf DELAY_VAL_US ;set up 125 loops for ~1128 us DELAY_1000_US: nop nop nop nop nop nop decfsz DELAY_VAL_US, F goto DELAY_1000_US ; delay us decfsz DELAY_VAL_MS, F goto DELAY_MS ; delay ms retlw 0x01 ;******************************************************************************************** ;******************************** I2C_ERROR SUBROUTINE ************************************ I2C_ERROR: movlw 0x01 movwf READ_I2C_FLAG ;set flag movlw 0x10 movwf I2C_BIT_CNTR ;initilize number of times to blink NEXT_BLINK: call LED_ON movlw 0x10 movwf DELAY_VAL_MS call DELAY_MS call LED_OFF movlw 0x10 movwf DELAY_VAL_MS call DELAY_MS decfsz I2C_BIT_CNTR, F ;Decrement counter, if 0 skip goto NEXT_BLINK ;loop back retlw 0xFA ;******************************************************************************************** ;******************************************************************************************** ;******************************************************************************************** ;******************************************************************************************** ;******************************************************************************************** ;******************************************************************************************** INFINITE: ;****************** LOAD pot1/0 state from GP2 ******************************** clrwdt ;reset watchdog timer -optional movf I2C_ERROR_COUNT, F btfss STATUS, Z ;check if I2C_ERROR_COUNT has any non-zero value call I2C_ERROR ;an i2c error has occured. movlw 0x00 movwf I2C_ERROR_COUNT ;reset i2c error counter. btfss GPIO, POT ;LOAD pot1/0 switch state from GP2 goto POT0_SET POT1_SET: movlw 0xF8 movwf REG_ADD_BYTE ; Put F8 in command byte for POT1 movlw 0xFF movwf MAX_PSBL_POT ; Put FF in max position pot variable. movlw 0x01 movwf POT_SEL ;set POT_SEL =1 flag goto DONE_SETTING_POT POT0_SET: movlw 0xF9 movwf REG_ADD_BYTE ; Put F9 in command byte for POT0 movlw 0x63 movwf MAX_PSBL_POT ; Put 63 in max position pot variable. movlw 0x00 movwf POT_SEL ;set POT_SEL =0 flag DONE_SETTING_POT: ;************** Has POT0/1 Switch been changed? ******************************** movf POT_SEL, W subwf POT_SEL_OLD, W btfsc STATUS, Z ; POT_SEL_OLD ?= POT_SEL goto NO_POT_CHANGE ;NO POT_CHANGED: movlw 0x01 movwf READ_I2C_FLAG ;set flag NO_POT_CHANGE: movf POT_SEL, W movwf POT_SEL_OLD ; POT_SEL_OLD = POT_SEL ;************** read pot values and store them in POT_VAL if flag is set ******** btfss READ_I2C_FLAG, 0x00 ;if POT_VAL was changed in last itteration, then re-read goto NO_READ_POT call I2C_READ_WORD ;ADDRESS_BYTE_R/W, & REG_ADD_BYTE already set movf I2C_WORD, W ;move POT read value into POT_VAL movwf POT_VAL ;recall I2C_WORD and put into POT_VAL ; call I2C_RESET movlw 0x00 movwf READ_I2C_FLAG ;reset flag NO_READ_POT: ;************************ UP Pressed? ********************************************** btfsc GPIO, UP ;if UP is a 0 goto UP_NOT_PRESSED UP_PRESSED: call LED_ON ;LED on movf MAX_PSBL_POT, W subwf POT_VAL, W btfsc STATUS, Z ;test if POT value is at MAX already goto POT_AT_MAX movlw 0x01 movwf READ_I2C_FLAG ;set flag incf POT_VAL, F movf POT_VAL, W movwf I2C_WORD call I2C_WRITE_WORD ;ADDRESS_BYTE_R/W, & REG_ADD_BYTE already set movlw 0x80 movwf DELAY_VAL_MS call DELAY_MS movlw 0xFF movwf DELAY_VAL_MS call DELAY_MS ;delay ~400ms FAST_UP: clrwdt ;reset watchdog timer -optional btfsc GPIO, UP ;Is UP still pressed? goto UP_NOT_PRESSED movf MAX_PSBL_POT,W subwf POT_VAL, W btfsc STATUS, Z ;test if POT value is at MAX already goto POT_AT_MAX movlw 0x01 movwf READ_I2C_FLAG ;set flag incf POT_VAL, F movf POT_VAL, W movwf I2C_WORD call I2C_WRITE_WORD ;ADDRESS_BYTE_R/W, & REG_ADD_BYTE already set btfss GPIO, DWN ;DWN & UP are 0's? goto POT_AT_MAX movlw 0x20 movwf DELAY_VAL_MS call DELAY_MS ;delay 00ms goto FAST_UP POT_AT_MAX: movlw 0x01 movwf READ_I2C_FLAG ;set flag movf MAX_PSBL_POT,W movwf POT_VAL movwf I2C_WORD call I2C_WRITE_WORD ;ADDRESS_BYTE_R/W, & REG_ADD_BYTE already set UP_NOT_PRESSED: call LED_OFF ;UP Button is not pressed ;************************ DWN Pressed? ********************************************** btfsc GPIO, DWN ;if DWN is a 0 goto DWN_NOT_PRESSED DWN_PRESSED: call LED_ON ;LED on movlw 0x00 subwf POT_VAL, W btfsc STATUS, Z ;test if POT value is at MIN already goto POT_AT_MIN movlw 0x01 movwf READ_I2C_FLAG ;set flag decf POT_VAL, F movf POT_VAL, W movwf I2C_WORD call I2C_WRITE_WORD ;ADDRESS_BYTE_R/W, & REG_ADD_BYTE already set movlw 0x80 movwf DELAY_VAL_MS call DELAY_MS movlw 0xFF movwf DELAY_VAL_MS call DELAY_MS ;delay 300ms FAST_DWN: clrwdt ;reset watchdog timer -optional btfsc GPIO, DWN ;Is DWN still pressed? goto DWN_NOT_PRESSED movlw 0x00 subwf POT_VAL, W btfsc STATUS, Z ;test if POT value is at MIN already goto POT_AT_MIN movlw 0x01 movwf READ_I2C_FLAG ;set flag decf POT_VAL, F movf POT_VAL, W movwf I2C_WORD call I2C_WRITE_WORD ;ADDRESS_BYTE_R/W, & REG_ADD_BYTE already set btfss GPIO, UP ;UP & DWN are 0's? goto POT_AT_MIN movlw 0x40 movwf DELAY_VAL_MS call DELAY_MS ;delay 40ms goto FAST_DWN POT_AT_MIN: movlw 0x01 movwf READ_I2C_FLAG ;set flag movlw 0x00 movwf POT_VAL movwf I2C_WORD call I2C_WRITE_WORD ;ADDRESS_BYTE_R/W, & REG_ADD_BYTE already set DWN_NOT_PRESSED: call LED_OFF ;DWN Button is not pressed ;************************ MID Pressed? ********************************************** btfsc GPIO, MID ;if MID is a 0 goto MID_NOT_PRESSED MID_PRESSED: ;call LED_ON ;This is done in HW movlw 0x01 movwf READ_I2C_FLAG ;set flag movf MAX_PSBL_POT,W movwf TEMP rrf TEMP,F bcf TEMP,0x07 ;clear bit 7 ir C brought in a 1 movf TEMP, W ;MAX_PSBL_POT / 2 --> POT0/1 movwf I2C_WORD call I2C_WRITE_WORD ;ADDRESS_BYTE_R/W, & REG_ADD_BYTE already set movlw 0xFF movwf DELAY_VAL_MS call DELAY_MS ;delay so that there are not to many write cycles MID_NOT_PRESSED: ;movf GPIO, W ;read pin states for wake-up (comment out if WD enabled) ;sleep ; put PIC to sleep (comment out if WD enabled) goto INFINITE END ; directive 'end of program'