Einzelnen Beitrag anzeigen
Alt Heute, 17:00:37   #10
wekltf
Erfahrener Benutzer
 
Registriert seit: 23.03.2017
Ort: Worms
Beiträge: 140
Standard

Finales Update:

Hab noch ein ganz klein wenig an der Software rumgefrickelt, hier der finale Stand, damit es ggf. jemand bei Interesse nachmachen kann:







;************************************************* *****************************
; *
; Refer to the MPASM User's Guide for additional information on the *
; features of the assembler. *
; *
; Refer to the PIC16F677 Data Sheet for additional *
; information on the architecture and instruction set. *
; *
;************************************************* *****************************
; *
; Filename: I2C677.asm *
; Date: 06.08.2024 *
; File Version: 2.0 *
; *
; Author: Wolfgang *
; *
;************************************************* *****************************
; *
; Files required: P16F677.INC *
; *
;************************************************* *****************************

LIST P=PIC16F677 ;directive to define processor
#include <P16F677.INC> ;processor specific variable definitions

;************************************************* *****************************

;Motorbike comp to show gear, battery and temperature for GS500

;Configuration bits

; CONFIG
; 0x2007h
__CONFIG _INTOSCIO & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _BOREN_ON

; Program variables

PVVARS UDATA 0x40 ;variables in access RAM (3+10 bytes) udata_acs for PIC18

WREG_TEMP RES 1 ;variable used for context saving
STATUS_TEMP RES 1 ;variable used for context saving
PCLATH_TEMP RES 1 ;variable used for context saving


PVVARS1 UDATA 0x44

mcounter RES 1 ;counter, incremented each blink period (0,26s), not reset
modestat RES 1 ;status, bit0=oil alarm, bit1=batt alarm,
;bit3=show seq initiated, bit4=show seq active,
;bit5=overtime, bit7=start
Hunds RES 1 ;variables for hex to decimal conversion
Tens RES 1
Ones RES 1
binin RES 1
count RES 1
buf RES 1
oldvolt RES 1 ;for averaging
avgvolt RES 1 ;result of average

TIMER_VAR UDATA 0x50 ; could be banked

timeri RES 1 ; variable for inner loop
timero RES 1 ; timer one ms
timerx RES 1 ; timer x ms


; The TM1637 uses an I2C 'like' signalling method with two caveats:
; 1. It does not use an I2C address so you can't have any other device on the bus.
; 2. I2C sends data MSB first, the TM1637 expects data LSB first.
; Since this code uses the hardware MSSP module in the PIC, the data to be sent
; must be reversed before loading into the PICs MSSP buffer register.


#define WriteDispAddrAuto 0x02 ;//0100 0000 = 40h
#define WriteDispAddrFix 0x22 ;//0100 0100 = 44h
#define AddressZero 0x03 ;//1100 0000 = C0h

; See TM1637 datasheet page 5
; Display control table Data Byte Reversed
#define DisplayOn1 0x11 ;10001000 00010001
#define DisplayOn2 0x91 ;10001001 10010001
#define DisplayOn4 0x51 ;10001010 01010001
#define DisplayOn10 0xD1 ;10001011 11010001
#define DisplayOn11 0x31 ;10001100 00110001
#define DisplayOn12 0xB1 ;10001101 10110001
#define DisplayOn13 0x71 ;10001110 01110001
#define DisplayOn14 0xF1 ;10001111 11110001
#define DisplayOff 0x01 ;10000000 00000001

#define battery PORTA,0 ;battery voltage
#define oilswitch PORTC,2 ;temp switch instead of NTC
#define alarmpin PORTx,x ;alarm LED
#define gear1 PORTB,7 ;low=gear 1
#define gear2 PORTB,6 ;
#define gear3 PORTB,5 ;
#define gear4 PORTB,4 ;
#define gear5 PORTC,4 ;
#define gear6 PORTC,5 ;
#define i2csda PORTC,0 ;software i2c; (PORTC,1 reserved)
#define i2cscl PORTA,1 ;(wired to PORTA,4; PORTA,4 must be input!)
#define neutral PORTA,3 ;input for neutral switch
#define lifebit PORTC,3 ;life-bit, is toggled periodically (no MCLR)
#define relay PORTA,5 ;drives a relay


; table in RAM with values to be displayed on LED

LEDTAB_VAR UDATA 0x58 ; values to be sent to LED via I2C
LEDTAB
Voltl RES 3
Oill RES 3
Gearl RES 1
Resvl RES 1

;table for 7-Segment Display

org 0x0300

Tab7Seg: andlw 0x0F ;range 0-15
addwf PCL,f
retlw 0xFC ;0
retlw 0x60 ;1
retlw 0xDA ;2
retlw 0xF2 ;3
retlw 0x66 ;4
retlw 0xB6 ;5
retlw 0xBE ;6
retlw 0xE0 ;7
retlw 0xFE ;8
retlw 0xF6 ;9
retlw 0x0 ;blank
retlw 0x1C ;L
retlw 0xFC ;O
retlw 0x7C ;U
retlw 0x2 ;-
retlw 0x2 ;-

;colon is the 8th segment on grid2/display2 from left (TM1637)
;i.e. LSB must be set because of the reversed order ( 1 = 0x60 1: = 0x61)


; battery: 25,5V = 5V at Uin = 255 dec. ADC out
; batt --9.064k--+--2,2K-- 0V
; I I = input Pin PIC


;************************************************* *****************************
;Reset vector
; This code will start executing when a reset occurs.


ORG 0x0

goto Main ;go to start of main code

;************************************************* *****************************
; interrupt vector
; This code will start executing when an interrupt occurs


ORG 0x0004

goto interrupt ;goto interrupt main code



;************************************************* *****************************
; Start of main program
; The main program code is placed here.

Main:

; *** main code goes here ***

; internal oscillator setup
banksel OSCCON
movlw 0x71 ; IRCF = 111 (8 MHz)
movwf OSCCON

; port initialization
movlw b'11011101' ; set PORTA 1,5 to output, rest is input
movwf TRISA ; TRISA/B/C are in same bank as OSCCON
movlw b'11111111' ; set PORTB to input
movwf TRISB
movlw b'11110111' ; PORTC 3 output, lifebit
movwf TRISC ; rest of PORTC is input
banksel WPUB
movlw b'11111111'
movwf WPUB ; weak pull-ups on PORTB (4-7)

; watchdog init
clrwdt
banksel OPTION_REG
movlw b'00000000' ; pull-ups enabled
movwf OPTION_REG ; prescaler assigned to timer
banksel WDTCON
movlw b'00010110' ; prescaler WDT 1:65536
movwf WDTCON ; WDT period appr. 470ms
clrwdt

; A/D initialization
banksel ADCON1
movlw b'01010000' ; 16Tosc conversion clock
movwf ADCON1
banksel ANSEL
movlw b'00000001'
movwf ANSEL ; set RA0 to analog input
clrf ANSELH ; all other ports digital I/O
banksel ADCON0
movlw b'00000001' ; ADC enabled, left justified, VDD ref.
movwf ADCON0
nop

; timer and interrupt initialization
; timer 1 used, general and peripheral interrupt must be enabled
; timer 1: 8 MHz / 4 / 8 = 250 kHz -> 0.004 ms clock for 16bit timer
; overflow every 262 ms -> interrupt
movlw b'00110001' ; FOSC/4, 1:8 prescaler, Timer 1 on
banksel T1CON
movwf T1CON
movlw b'11000000' ; interrupt GIE and PEIE enabled
movwf INTCON
banksel PIE1
movlw b'00000001' ; enable TMR1IE
movwf PIE1
bcf STATUS,RP0
bcf STATUS,RP1 ; select bank 0, i.e. PORTA/B/C

; initialisation PIC done

; init variables
clrf mcounter
clrf modestat
bsf relay ; power relay to check battery voltage

; Memory init for LED values

PINIT: clrw ; clear w
movwf Voltl ; fill 7 adresses with "0"
movwf Voltl+1
movwf Voltl+2
movwf Voltl+3
movwf Voltl+4
movwf Voltl+5
movwf Voltl+6

call i2c_present
call init_LED
movlw .250
call xms ; wait 750ms after init
clrwdt
movlw .250
call xms
clrwdt
movlw .250
call xms
clrwdt

goto mainstart


; initialisation PIC done
; loop for sending data to RS232 / I2C

mainstart: nop

movlw .100
call xms ; wait 100ms (loop update time)
clrwdt

alarmLED: bcf modestat,0 ;reset oil alarm
btfsc oilswitch ;test temperature switch
bsf modestat,0 ;set oil alarm
movf modestat,w ;test for alarm
andlw b'00000011' ;only check alarm bits 0,1
btfss STATUS,Z
goto alarm1
;bcf alarmpin ;status=0, no alarm, go on
goto checkgear
alarm1: andlw b'00000010' ;test battery alarm
btfsc STATUS,Z
goto alarm3
btfsc lifebit ;battery alarm!
nop ;bsf alarmpin
btfss lifebit
nop ;bcf alarmpin
goto checkgear ;blink alarm
alarm3: btfsc modestat,0
;bsf alarmpin ;bit0, oil alarm


checkgear: nop
clrf Gearl ;check gear
incf Gearl,f
btfss gear1
goto setgear ;yes gear 1, so stop
incf Gearl,f
btfss gear2
goto setgear
incf Gearl,f
btfss gear3
goto setgear
incf Gearl,f
btfss gear4
goto setgear
incf Gearl,f
btfss gear5
goto setgear
incf Gearl,f
btfss gear6
goto setgear
clrf Gearl
btfss neutral
goto setgear ;yes neutral
movlw 0xFF ;ungültig
movwf Gearl ;writing status to Gearl finished

setgear: nop

LEDseq: call writeLED ;display on LED

endloop: goto mainstart

;code for LED is here
;write gear if no alarm
;write battery value for 1/2 minute after boot
;write Oil Temperature if oil alarm
;write battery if battery alarm

writeLED: nop
btfss mcounter,7 ;means: appr. 32s elapsed
goto writeLED1
bsf modestat,7
writeLED1: btfss modestat,7 ;if less than 32s after boot
goto showbatt ;jump to showbatt

LEDalmtest: movf modestat,w
andlw 0x03 ;mask for alarm bits 0 and 1
btfsc STATUS,Z
goto showgear ;if no alarm goto showgear

btfsc modestat,1 ;test for battery alarm
goto showbatt


showgear: call i2c_on
movlw AddressZero
call i2c_tx
movlw 0x0

testoilsw: btfsc oilswitch ;display "L" if oil temp low
movlw 0x1C

call i2c_tx ;write 7 Segment to LED
clrw ; blanks
call i2c_tx ;write 7 Segment to LED
clrw
call i2c_tx ;write 7 Segment to LED
lastdigit: movlw 0x2
btfsc Gearl,7 ;test for invalid
goto lastdigitend
movlw HIGH Tab7Seg
movwf PCLATH ;put adress of pointer to PCL
movf Gearl,w ;value*2 to w
call Tab7Seg ;this causes the jump into table
lastdigitend: call i2c_tx ;display gear
call i2c_off
goto LEDon

showbatt: nop
call i2c_on
movlw AddressZero
call i2c_tx

movlw HIGH Tab7Seg
movwf PCLATH ;put adress of pointer to PCL
movf Voltl,w ;value to w
call Tab7Seg ;this causes the jump into table
call i2c_tx ;write 7 Segment to LED

movlw HIGH Tab7Seg
movwf PCLATH ;put adress of pointer to PCL
movf Voltl+1,w ;value to w
call Tab7Seg ;this causes the jump into table
addlw .1 ;show colon
call i2c_tx ;write 7 Segment to LED

movlw HIGH Tab7Seg
movwf PCLATH ;put adress of pointer to PCL
movf Voltl+2,w ;value to w
call Tab7Seg ;this causes the jump into table
call i2c_tx ;write 7 Segment to LED

movlw 0x0 ;last digit blank
call i2c_tx
call i2c_off

resetseq: btfsc mcounter,7 ;if sequence was active and time expired
bcf modestat,4 ;reset sequence active bit
goto LEDon


LEDon: call i2c_on ;turn display on
movlw DisplayOn14
call i2c_tx
call i2c_off
return


; is called by interrupt
; each 0.262 s
; following conditions of mcounter0 apply:
; 0 = start ADC, ch.0 1 = read ADC, ch.0
interrupt:
banksel ADCON0 ; also PORTC, ADRESH and PIR1 (bank 0)
movwf WREG_TEMP ;save w
swapf STATUS,w
movwf STATUS_TEMP ;save STATUS register
movf PCLATH,w
movwf PCLATH_TEMP ;save PCLATH register

movlw b'00001000' ;mask bit 3
xorwf PORTC,f ; toggle lifebit
btfsc mcounter,0
goto readadc ;bit 0 of mcounter set, so read ADC
startadc: bsf ADCON0,GO ;set GO-bit of ADC
goto iend
readadc: movf ADRESH,w ;read ADC
addwf oldvolt,f
rrf oldvolt,w
movwf avgvolt
movf ADRESH,w
movwf oldvolt
movf avgvolt,w
bsf modestat,1 ;set battery alarm
sublw .120
btfsc STATUS,C
goto batt1 ;batt < 12V, go on with alarm
movf avgvolt,w
sublw .148
btfsc STATUS,C ;batt > 14.8V, go on with alarm
bcf modestat,1 ;else reset battery alarm
batt1: movf ADRESH,w
call binary_to_bcd ;convert to 0.0V - 25.5V
movf Hunds,w
movwf Voltl
movf Tens,w
movwf Voltl+1
movf Ones,w
movwf Voltl+2

; clear interrupt flag and return from interrupt
iend: incf mcounter,f
bcf PIR1,TMR1IF


movf PCLATH_TEMP,w
movwf PCLATH ;restore PCLATH register
swapf STATUS_TEMP,w
movwf STATUS ;restore working register
swapf WREG_TEMP,f
swapf WREG_TEMP,w
retfie

binary_to_bcd: ; convert 8 bit number to 3 decimal digits
movwf binin
clrf Hunds
swapf binin, W ; swap the nibbles
addwf binin, W ; so we can add the upper to the lower
andlw B'00001111' ; and lose the upper nibble (W is in BCD from now on)
btfsc STATUS, DC ; if we carried a one (upper + lower > 16)
addlw 0x16 ; add 16 (the place value) (1s + 16 * 10s)
btfsc STATUS, DC ; if we carried a one (upper + lower > 16)
addlw 0x06
addlw 0x06
btfss STATUS, DC
addlw -0x06

btfsc binin, 4 ; 16's place
addlw 0x16 - 1 + 0x06 ; add 16 - 1
btfss STATUS, DC
addlw -0x06

btfsc binin, 5 ; 32nd's place
addlw 0x30 ; add 32 - 2

btfsc binin, 6 ; 64th's place
addlw 0x60 ; add 64 - 4

btfsc binin, 7 ; 128th's place
addlw 0x20 ; add 128 - 8 % 100

addlw 0x60 ; check for digit overflows
rlf Hunds, F ; pop carry in hundreds' LSB
btfss Hunds, 0
addlw -0x60

movwf Tens
btfsc binin,7 ; remember adding 28 - 8 for 128?
incf Hunds, F ; add the missing 100 if bit 7 is set
andlw 0x0F
movwf Ones
swapf Tens,f
movlw 0x0F
andwf Tens,f
return

; I2C subroutines
; I2C Takt ist 100kHz, d.h. Taktlänge ist 10us, 1 Puls hat 5us
; 5us entsprechen bei 20Mhz 100 Takten, d.h. 25 Cyclen/Befehle
; 5us entsprechen bei 8Mhz 40 Takten, d.h. 10 Cyclen/Befehle

i2c_present:
nop ; return with 0 if slave present, otherwise 1
call onems ; don't check
retlw 0x0 ; assume everything is okay


i2c_on: ; SDA changes to low, when SCL is high
banksel TRISC
bsf TRISC,0 ; bring SDA high (=PORTC 0 to input)
bcf STATUS,RP0
bcf STATUS,RP1 ; select bank 0, i.e. PORTA/B/C
i2c_o1: call fiveus
call fiveus ; wait one I2C cycle
btfss i2csda ; port is input, level should be high
goto i2c_o1 ; no, so wait
bsf i2cscl ; set SCL high
call fiveus ; wait, just to make sure
call fiveus ; SDA and SCL are now high
; now begin with ON condition
banksel TRISC
bcf TRISC,0 ; set PORTC 0 to output (i2csda)
bcf STATUS,RP0
bcf STATUS,RP1 ; select bank 0, i.e. PORTA/B/C
bcf i2csda ; PORTC0/SDA is now output and low
call fiveus
call fiveus
bcf i2cscl ; SDA and SCL are low
call fiveus ; wait a bit
call fiveus
return

; subroutine ein Byte aus W senden
i2c_tx: nop ; -> zum I2C-Slave übertragen
call WrI2cW ; 8 Bit aus W nach I2C
banksel TRISC ; set PORTC 0 to input (i2csda)
bsf TRISC,0 ; d.h. SDA high
bcf STATUS,RP0
bcf STATUS,RP1 ; select bank 0, i.e. PORTA/B/C
call fiveus
bsf i2cscl ; SCL high
i2c_t2: ;btfsc i2csda ; ACK schon empfangen?
;goto i2c_t2 ; nein, noch nicht
nop ; ja, Daten sind im Slave
call fiveus ; prüfe nicht ACK, warte einfach etwas
bcf i2cscl ; SCL low again, transmission completed
call fiveus
call fiveus
return

;************************************************* ****
; I2C-Periode ist 10 µs

; schiebt das Byte aus W in den I2C
; MSB zuerst

WrI2cW:
; Takt unten, Daten unten
; Datenbyte in w
movwf buf
movlw .8
movwf count ; 8 Bits
banksel TRISC ; needs 2 instructions
bcf TRISC,0 ; set PORTC 0 output (i2csda)
bcf STATUS,RP0
bcf STATUS,RP1 ; select bank 0, i.e. PORTA/B/C
WrI2cW1:
bcf i2csda ; set SDA low (Datenleitung)
rlf buf,f
btfsc STATUS,C ; 0?
bsf i2csda ; set SDA high
; SDA ist set to the right data level
nop ; SDA setting time
bsf i2cscl ; SCL high (Data bit now valid)
call fiveus ; SCL high halten für 1/2 Periode
bcf i2cscl ; SCL output = low
nop ; one more cycle so that appr. 5 us since last SCL change
decfsz count,f ; 8 Bits raus?
goto WrI2cW1 ; nein
return ; ja

; I2C-Bus wieder freigeben (SDA wird high, wenn SCL bereits high ist)
i2c_off:
bcf i2cscl ; make sure, SCL is low
call fiveus
call fiveus ; wait
banksel TRISC
bcf TRISC,0 ; SDA is output
bcf STATUS,RP0
bcf STATUS,RP1 ; select bank 0, i.e. PORTA/B/C
bcf i2csda ; make SDA low (PORTC,0)
call fiveus ; wait while SCL and SDA are low
call fiveus
bsf i2cscl ; SCL high (PORTA)
call fiveus ; wait a I2C cycle
call fiveus
banksel TRISC ; set now SDA high
bsf TRISC,0 ; set PORTB 0 to input (i2csda)
bcf STATUS,RP0
bcf STATUS,RP1 ; select bank 0, i.e. PORTA/B/C, SDA should be high now
call fiveus
call fiveus
return

; reset I2C bus
i2c_res:
banksel TRISC ; release SDA
bsf TRISC,0 ; set PORTC 0 to input (i2csda)
bcf STATUS,RP0
bcf STATUS,RP1 ; select bank 0, i.e. PORTA/B/C, SDA should be high now
movlw .9
movwf count ; 9 repeats
i2c_r1: bcf i2cscl ; SCL low
call fiveus ; SCL low halten für 1/2 Periode
nop
bsf i2cscl ; SCL high
call fiveus
decfsz count,f ; 9 mal?
goto i2c_r1 ; nein
call i2c_off
return

; Init LED Display
; Send data to TM6137 module using Standard Ports
; See TM1637 datasheet Page 4
; Write SRAM data in address auto increment 1 mode.
init_LED:
call i2c_res
call onems
call i2c_on
movlw WriteDispAddrAuto
call i2c_tx
call i2c_off

call onems
call i2c_on
movlw AddressZero
call i2c_tx
movlw 0xFF ;all segments
call i2c_tx ;write 7 Segment to LED
movlw 0xFF
call i2c_tx ;write 7 Segment to LED
movlw 0xFF
call i2c_tx ;write 7 Segment to LED
movlw 0xFF
call i2c_tx
call i2c_off
call LEDon
return


; wait 5 microseconds (100 cycles at 20 MHZ = 25 instructions of 4 cycles each)
; (80 cycles at 16 MHz = 20 instructions)
fiveus: ; loop with 16/20 instructions plus 5 more for call up
;movlw .5 ; for 20MHz
;movlw .4 ; for 16 MHz
movlw .2 ; for 8 MHz
movwf timero
loop5us:
nop
decfsz timero,f ; 4 instructions loop
goto loop5us

return

; wait one millisecond (5000 instr. at 20 MHz)
onems:
movlw .100
banksel timero
movwf timero
loopms1:
;movlw .16 ;loop f0r 10 us = 50 instructions
;movlw .13
movlw .6
movwf timeri
loopms2:
decfsz timeri,f ; loop = 3 instr.
goto loopms2
decfsz timero,f
goto loopms1

return

; wait "w" ms
xms:
banksel timerx
movwf timerx
loopxms:
movlw .100 ;loop for onems embedded to save calls
movwf timero
xloopms1:
;movlw .16 ;loop f0r 10 us = 50 instr.
;movlw .13
movlw .6
movwf timeri
xloopms2:
decfsz timeri,f ; loop = 3 instr.
goto xloopms2
decfsz timero,f
goto xloopms1

decfsz timerx,f
goto loopxms
return

;************************************************* *****************************
;End of program

END
wekltf ist offline   Mit Zitat antworten