; 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
25
Embed
PIC16F84 Code for the NK0E Weather Station Project
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
; 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; [email protected];; 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=16f84radix 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.
BitCount ;number of bits left to send or;receive, not including start & stop;bits
RXChar ;received character while being receivedRXBuff ;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 asciilo ;lo byte for number to be converted to asciishi ;hi byte for subtractor for conversion to asciislo ;lo byte for subtractor for conversion to asciidigit ;ascii digit converted from binary. Also used as
;input to SendErrorCode
MSDelay ;register for WaitMS timing
endc
org 0x00goto 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:
Intmovwf WSave
swapf STATUS,W ;use swapf to prevent anymovwf 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,T0IEgoto 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 itgoto StartRX ;generates an interrupt on a high-
;------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 _StartRxDelaymovwf 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_INgoto 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 _BitRxDelaymovwf TMR0 ;4 cycles from read of PORTBmovlw b'00100000'movwf INTCON
; set the SerialReg to indicate that the routines are busy getting; a character:
; 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,2goto Sending
; check to see if we're receiving the stop bit:
movf BitCount,Fbtfsc STATUS,Zgoto 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 _BitRxDelaymovwf TMR0 ;21 cycles from start of interruptgoto Restore
; if we get to here it's because we need to check for the stop bit.
GetStopBitbtfss PORTB,_SER_IN ;is the RX line low? If so, it's notgoto Done ;the stop bit. Otherwise, set themovlw b'00000001' ;SerialReg to show a character hasmovwf SerialReg ;been receivedmovf RXChar,W ;copy the received character to RXBuff
movwf RXBuffgoto Done
; We got here because we're sending.; check to see if we're finished sending the stop bit:
; 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 thebtfss STATUS,C ;least significant bit in thegoto SendZero ;carry flag.nopbsf PORTA,_SER_OUT ;if carry is set, send a one.goto EndDoBit ;PORTA,1 is set on the 24th cycle
SendZerobcf PORTA,_SER_OUT ;otherwise, send a zero. (24th cycle)nop ;nop's are for taking the same timenop ;to get to reloading TMR0 as for when
;a one is sent.
; Decrement the bit counter.
EndDoBitdecf BitCount,F
; reload TMR0 for the next interrupt, and; go to the end of the interrupt routine.
; 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.
SendStopBitnopnopnopbsf 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.
;------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 timingmovlw _BitTxDelay
movwf TMR0 ;5th cycle after write to PORTA
; clear the external interrupt flag, disable the external interrupt,; and enable the TMR0 interrupt.
;------Subroutine WaitMS---------------------------------------------;; WaitMS is an approximate millisecond delay. It assumes a 4 MHz
; oscillator, meaning instructions are executed at a rate of 1 MHz.; I got the timing info (number of cycles per instruction) from the; Microchip PIC16F84 data sheet.
; the call to this subroutine takes 2 cycles to execute.
WaitMSmovlw 248 ;1 cyclemovwf MSDelay ;1 cyclenop ;1 cycle--these nops are added to nop ;1 cycle make the total number ofnop ;1 cycle instructions executed in
; the routine to be 1000.;the nop instruction simply does ;nothing except take time to execute.
; The loop below takes four cycles for every time through except the; last, when it takes five (including the time needed to execute the; return). So, the total number of instructions executed in getting; to and returning from this subroutine is:;; 2 to get here; + 2 to set the MSDelay value; + 3 for the nops; + 247*4 for the first 247 times through the loop; + 5 for the last time through the loop and to return; --------; = 1000
RepeatWaitMSnop ;1 cycledecfsz MSDelay,F ;1 cycle if not zero, 2 if zerogoto RepeatWaitMS ;2 cyclesreturn ;2 cycles
;------begin SHT11TXRX-----------------------------------------------;; Sends a byte command to the SHT11 temp/humidity sensor and retrieves; a two-byte response. Sends the response back to the PC as an ASCII; string representation of the number.;
; Put the byte to send in SHT11Byte before calling this routine.
SHT11TXRX
;make _SHT11_DAT an output
bsf STATUS,RP0 ;switch to bank 1bcf TRISA,_SHT11_DAT ;make Port A data line an outputbcf STATUS,RP0 ;switch back to bank 0
;send the Transmission Start sequence:
bsf PORTA,_SHT11_DAT ;set the data line highbsf PORTA,_SHT11_SCK ;take the clock line highbcf PORTA,_SHT11_DAT ;take the data line lowbcf PORTA,_SHT11_SCK ;take the clock line lowbsf PORTA,_SHT11_SCK ;take the clock line high againbsf PORTA,_SHT11_DAT ;set the data line high again
;load up the counter to loop through the eight bits to send:
movlw 8movwf counter
SHT11SendBitLoopbcf PORTA,_SHT11_SCK ;take the clock line low
btfss SHT11Byte,7 ;is the next bit to send a one?goto SHT11SendZero ;nope. Go send a zero.bsf PORTA,_SHT11_DAT ;if it's a one, send it.goto SHT11SendBit
SHT11SendZerobcf PORTA,_SHT11_DAT ;set the data line to zero
SHT11SendBitbsf PORTA,_SHT11_SCK ;take the clock line high to send
rlf SHT11Byte,F ;move the next bit into MSB
decfsz counter,F ;dec the counter and check for zero.goto SHT11SendBitLoop ;if not zero, more bits to send
bcf PORTA,_SHT11_SCK ;take the clock line low
;no more bits to send. Set the data line to be an input and;wait for the ack from the SHT11:
bsf STATUS,RP0 ;switch to bank 1bsf TRISA,_SHT11_DAT ;make Port A data line an inputbcf STATUS,RP0 ;switch back to bank 0
; now look for an ack (SHT11 pulls data line low--should; happen on the next rise of the SCK line). If it doesn't; happen, return an 'e' and quit.
; we got an Ack. Get ready for the data to be returned. take; the clock line low, and then wait for the data line to be; pulled low again.
SHT11GotAckbcf PORTA,_SHT11_SCK
; now wait for the data. It takes approximately 210 ms for; the temperature measurement, or 55 ms for the humidity; measurement, so we'll wait up to 255 ms before giving up.
;------begin SHT11GetByte---------------------------------------------;; Gets a byte of data from the SHT11. Assumes that the data; is ready to be sent by the SHT11. Also assumes that _SHT11_DAT has; been set to input. Also assumes that _SHT11_SCK has been set to low.; Returns the byte in SHT11Byte.
SHT11GetByte; clear SHT11Byte:
clrf SHT11Byte
; set counter to get eight bits
movlw 8movwf counter
SHT11GetByteLoopbsf PORTA,_SHT11_SCK ;set the clock high to get the next
bitbtfss PORTA,_SHT11_DAT ;is the next bit a one?goto SHT11GetZeroBit ;no--it's a zerobsf SHT11Byte,0 ;if it's a one, set the LSB in
SHT11Byte
goto SHT11GotBit
SHT11GetZeroBitbcf SHT11Byte,0 ;set the LSB to zero in SHT11Byte
SHT11GotBitbcf PORTA,_SHT11_SCK ;set the clock line low again.decfsz counter,Fgoto SHT11GetNextBitgoto SHT11GetByteDone
SHT11GetNextBitrlf SHT11Byte,F ;move the bits over to get the next
; "dosub" is called by the "dodigit" macro defined above.; Subtract the number in shi/slo from hi/lo until the result; is negative, incrementing the ascii equivelent each time.
dosub movlw '0'-1movwf digit
moresub incf digit,F ; increment ASCII charactermovf slo,w ; subtract current power of 10subwf lo,fmovf shi,wbtfss STATUS,Caddlw 1subwf hi,fbtfsc STATUS,C ; any carry?goto moresub ; no, keep subtracting
movf slo,w ; reverse the last subtractionaddwf lo,fmovf shi,wbtfsc STATUS,Caddlw 1addwf hi,freturn
; send a string with the version in it. The string comes from EEPROM; memory and is null-terminated. The null terminator is not sent. The; protocol dictates that the string sent is terminated by a CR, which; is sent. This subroutine is called when the 'v' command is received.
ReportVersionbcf STATUS,RP0clrf EEADR ;the string we want starts at the
;beginning of EEPROM memory.
GetNextVersionCharbsf STATUS,RP0bsf EECON1,RDbcf STATUS,RP0movf EEDATA,Wbtfsc STATUS,Z ;if the character in W is null, don't
;------begin Idle----------------------------------------------------;; Idle should be called whenever the chip is waiting for something; to happen (waiting for a character to be sent or received, for; example). Here it's not doing anything.