📄 part3.asm
字号:
; COPYRIGHT 2002 BY DAVID EK. PERMISSION IS GRANTED TO FREELY USE
; THIS SOFTWARE FOR NONCOMMERCIAL PURPOSES.
;
; NOTE: USE AT YOUR OWN RISK. THE AUTHOR ASSUMES NO LIABILITY FOR DAMAGE
; INCURRED FROM THE USE OF THIS SOFTWARE.
;
; THIS IS THE CODE FOR INSTALLMENT 3 OF THE PIC WX ARTICLES IN
; THE DIGITAL QRP HOMEBREWING COLUMN OF QRP QUARTERLY.
;
; PIC16F84 code for the NK0E weather station project. The purpose
; of this project is to provide a hardware interface which measures
; temperature, wind speed, wind direction, humidity, and pressure
; and communicates it to a PC via serial communications when
; requested. Note that the data collected and transmitted must be
; processed by the PC in order to obtain the actual measurement
; values (i.e. no calibration is done in the PIC code).
;
; The hardware circuit makes use of two major components: the
; PIC16F84 itself and a MAX232 TTL-RS232 level converter.
; The MAX232 converts the TTL signals from the PIC to RS232 levels
; for the PC (and vice versa).
;
; The PIC16F84 pin assignments are as follows:
;
; RA1: serial out to PC
; RA2: clock out to SHT11 temp/humid sensor
; RA3: data in/out for SHT11 temp/humid sensor
; RB0: serial in from PC (generates interrupt)
;
; This software implements the following command set:
;
; Received Reply Description
;-----------------------------------
; 't' 12345 CR LF Get the temperature raw data. Five digits
; or e1 CR LF are always returned, with leading zeroes
; or e2 CR LF if necessary, followed by a carriage return
; and line feed. See the SHT11 data sheet
; for instructions on computing the temperature.
; A return of e1 indicates that the SHT11 didn't
; acknowledge the request. A return of e2
; indicates that no data was returned by the SHT11
; within the wait period (255 ms).
; 'h' 12345 CR LF Get the humidity raw data. Five digits
; or e1 CR LF are always returned, with leading zeroes
; or e2 CR LF if necessary, followed by a carriage return
; and line feed. See the SHT11 data sheet for
; instructions on computing the humidity.
; A return of e1 indicates that the SHT11 didn't
; acknowledge the request. A return of e2
; indicates that no data was returned by the SHT11
; within the wait period (255 ms).
; 'v' version info Gets the version number of the firmware.
; returns an ASCII string followed by a
; carriage return and line feed.
;
; Written using Microchip MPLAB v5.40
;
; Please direct any questions, comments, or suggestions to the
; author: David Ek
; nk0e@earthlink.net
;
; History:
;
; Rev 1 (23 Jul 2002):
; Creation. Returns dummy temperature data. This version used
; for installment 2 of the PIC WX articles in QQ.
; Rev 2 (26 Sep 2002):
; Added code for the Sensirion SHT11 temperature/humidity sensor
; for installment 3 of the PIC WX articles in QQ.
;
;----------------------------------------------------------------
list p=16f84
radix dec
__config _CP_OFF & _WDT_OFF & _XT_OSC
include "p16f84.inc"
; defines:
; These are used by the serial comm routines for timing. Note that
; slower speeds may not work well because the delays might be larger
; than 255, requiring the use of the prescalar.
_Clkspd equ 1000000 ;external clock / 4
_BaudRate equ 9600 ;desired baud rate
_Period equ (_Clkspd/_BaudRate) ;clock cycles / bit
_StartRxDelay equ (_Period/2 - 15)/3 ;this is how long to
;wait to get to the
;middle of the start
;bit. This is loops,
;not clock cycles.
_BitRxDelay equ 277 - _Period ;this is what to load
;into TMR0 for correct
;interval between bits
;on RX
_BitTxDelay equ 285 - _Period ;this is what to load
;into TMR0 for correct
;interval between bits
;on TX
_StopTxDelay equ 272 - _Period ;this is what to load
;into TMR0 for correct
;interval betweeen last
;bit and stop bit on TX
; defines for I/0 pins:
_SER_IN equ 0 ;Serial input line (PORTB)
_SER_OUT equ 1 ;Serial output line (PORTA)
_SHT11_SCK equ 2 ;clock line for SHT11 sensor
;(PORTA)
_SHT11_DAT equ 3 ;data line for SHT11 sensor
;(PORTA)
; macros for the bin-to-ascii conversion routines:
loadw macro arg1
movlw high(arg1)
movwf hi
movlw low(arg1)
movwf lo
endm
dodigit macro arg1
movlw high(arg1)
movwf shi
movlw low(arg1)
movwf slo
call dosub
endm
; memory locations:
cblock 0x0C
BitCount ;number of bits left to send or
;receive, not including start & stop
;bits
RXChar ;received character while being received
RXBuff ;most recently received character
TXChar ;character to transmit
SerialReg ;status register:
;bit 0: on if character has been
; received
;bit 1: on if busy with RX/TX
;bit 2: on if sending, off if receiving
;bit 3: on if next bit to send is stop bit
WSave ;copy of W register
SSave ;copy of the Status register
SHT11Byte ;a byte of data sent to or returned by the SHT11
counter ;generic counter register
hi ;hi byte for number to be converted to ascii
lo ;lo byte for number to be converted to ascii
shi ;hi byte for subtractor for conversion to ascii
slo ;lo byte for subtractor for conversion to ascii
digit ;ascii digit converted from binary. Also used as
;input to SendErrorCode
MSDelay ;register for WaitMS timing
endc
org 0x00
goto Main
org 0x04
;--------------------------------------------------------------------
;-----Serial Communication Routines----------------------------------
;--------------------------------------------------------------------
;
; The serial comm routines generate 1 start bit, 8 data bits, 1 stop bit,
; no parity. The baud rate is determined by the delay programmed
; into the onboard timer. The sending and receiving is interrupt driven,
; meaning other tasks can be carried on while the characters are being
; sent and received.
;
;-----Main Interrupt Routine-----------------------------------------
;
; for timing: 4 cycles from interrupt time to get here.
; Save the W and STATUS registers:
Int
movwf WSave
swapf STATUS,W ;use swapf to prevent any
movwf SSave ;status flags from being changed
; cycles so far since interrupt occurred: 7
; Okay, I'm doing something goofy here. Instead of actually checking
; the overflow bits themselves, I'm using the enable bits to determine
; which interrupt occurred. I can do this because there should only
; ever be one type of interrupt active at a time. The problem with
; checking the overflow bits is that the T0IF bit can get set even
; if that interrupt is disabled.
; Check first for a timer overflow interrupt. The overflow bit gets
; set even if the interrupt is disabled.
btfsc INTCON,T0IE
goto DoBit ;we're in the middle of sending or
;receiving
; 10 cycles executed on entry to DoBit
; If not a timer overflow interrupt, check for external interrupt:
btfsc INTCON,INTE ;RB0 is our receive line and it
goto StartRX ;generates an interrupt on a high-
;to-low transition
; 12 cycles executed on entry to StartRX
; Else, must be something we don't care about:
;do nothing for now
; Restore the W and STATUS registers:
Restore swapf SSave,W
movwf STATUS
swapf WSave,F
swapf WSave,W
retfie
;------end Main Interrupt Routine------------------------------------
;------Subroutine SerSetup-------------------------------------------
SerSetup
; set up the option register for internal counting, WDT disabled,
; no prescaler.
clrf TMR0
bsf STATUS,RP0
clrwdt ;set bits in OPTION_REG to
movlw b'10001000' ;enable internal clock counting,
movwf OPTION_REG ;disable watchdog timer.
bcf STATUS,RP0 ;switch to bank 0
; set the output line to idle (high)
bsf PORTA,_SER_OUT ;set the output line to idle
;(high) state
movlw b'00000001'
movwf PORTB
; enable the external interrupt via RB0
movlw b'10010000' ;set bits in INTCON to enable
movwf INTCON ;external interrupt
; initialize the SerialReg:
clrf SerialReg
return
;------end SetSetup--------------------------------------------------
;------Subroutine StartRX--------------------------------------------
;
; This subroutine is called by the main interrupt routine when an
; external interrupt on RB0 occurs. This means we're receiving the
; start bit for a character. We want to enable the external TMR0
; interrupt and prepare to receive the character.
StartRX
; wait halfway through the bit to see if it's real:
bcf INTCON,INTF ;clear the interrupt
movlw _StartRxDelay
movwf BitCount ;this is the 15th instruction since
;the interrupt. Note--we're using
;BitCount for this loop purely for
;convenience. Usually it's used to
;actually count the bits we TX/RX.
RXWait decfsz BitCount,F ;this loop takes 3 times the initial
goto RXWait ;value of BitCount clock cycles
; now we should be at the middle of the start bit. Is the input still
; low? If not, goto Restore and ignore this interrupt.
btfsc PORTB,_SER_IN
goto Restore
; if we get to here it must really be the start bit. Load TMR0,
; disable the external interrupt, and enable the TMR0 interrupt.
; load up the appropriate delay to get us to the middle of the
; first bit:
movlw _BitRxDelay
movwf TMR0 ;4 cycles from read of PORTB
movlw b'00100000'
movwf INTCON
; set the SerialReg to indicate that the routines are busy getting
; a character:
movlw b'00000010'
movwf SerialReg
; initialize BitCount:
movlw 8
movwf BitCount
; okay, now we return.
goto Restore
;------end StartRX---------------------------------------------------
;------DoBit---------------------------------------------------------
;
; sends or receives the next bit. Bits are sent/received from least
; to most significant bit.
DoBit
; clear the TMR0 overflow interrupt flag:
bcf INTCON,T0IF
; Are we receiving?
btfsc SerialReg,2
goto Sending
; check to see if we're receiving the stop bit:
movf BitCount,F
btfsc STATUS,Z
goto GetStopBit
; if we get to here, we're in the middle of receiving. Get the next
; bit: (16 cycles to get to the next instruction from the start of
; the interrupt).
rrf PORTB,W ;rrf PORTB into W. This sets
;the carry bit if RB0 was high.
rrf RXChar,F ;doing a rrf on RXChar brings
;in the carry bit to the MSB.
; Decrement the bit counter.
decf BitCount,F
; reload TMR0 for the next interrupt, and
; go to the end of the interrupt routine.
movlw _BitRxDelay
movwf TMR0 ;21 cycles from start of interrupt
goto Restore
; if we get to here it's because we need to check for the stop bit.
GetStopBit
btfss PORTB,_SER_IN ;is the RX line low? If so, it's not
goto Done ;the stop bit. Otherwise, set the
movlw b'00000001' ;SerialReg to show a character has
movwf SerialReg ;been received
movf RXChar,W ;copy the received character to RXBuff
movwf RXBuff
goto Done
; We got here because we're sending.
; check to see if we're finished sending the stop bit:
Sending
btfsc SerialReg,3
goto Done
; check to see if we need to send the stop bit:
movf BitCount,F
btfsc STATUS,Z ;18th cycle
goto SendStopBit
; if we get to here, we're in the middle of sending. Send the next
; bit: (16 cycles to get to the next instruction from the start of
; the interrupt).
rrf TXChar,F ;doing rrf on TXChar puts the
btfss STATUS,C ;least significant bit in the
goto SendZero ;carry flag.
nop
bsf PORTA,_SER_OUT ;if carry is set, send a one.
goto EndDoBit ;PORTA,1 is set on the 24th cycle
SendZero
bcf PORTA,_SER_OUT ;otherwise, send a zero. (24th cycle)
nop ;nop's are for taking the same time
nop ;to get to reloading TMR0 as for when
;a one is sent.
; Decrement the bit counter.
EndDoBit
decf BitCount,F
; reload TMR0 for the next interrupt, and
; go to the end of the interrupt routine.
movlw _BitTxDelay
movwf TMR0 ;29th cycle
goto Restore
; Here we need to send the stop bit, turn off the TMR0 interrupt,
; turn on the external interrupt, and set the SerStatus register
; flags appropriately.
SendStopBit
nop
nop
nop
bsf PORTA,_SER_OUT ;no. Send the stop bit. (24th cycle)
bsf SerialReg,3 ;set the "sending stop bit" flag
; reload TMR0 for the next interrupt, and
; go to the end of the interrupt routine.
movlw _StopTxDelay
movwf TMR0 ;27th cycle
goto Restore
; we're completely done sending or receiving. Clean up.
Done movlw b'00010000' ;set bits in INTCON to enable
movwf INTCON ;external interrupt
movlw b'00000001'
andwf SerialReg,F ;clear the busy bits in SerialReg
goto Restore
;------end DoBit-----------------------------------------------------
;------Subroutine SendChar-------------------------------------------
;
; This is not called by the interrupt handler. Rather, it activates
; the interrupts needed to send it. Put the character to be sent in
; the TXChar file register before calling this subroutine.
;
SendChar
; send the start bit:
bcf PORTA,_SER_OUT
; set the SerStatus to indicate that the routines are busy sending
; a character:
movlw b'00000110'
movwf SerialReg
; load up TMR0 so it overflows at the right time.
nop ;for timing
movlw _BitTxDelay
movwf TMR0 ;5th cycle after write to PORTA
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -