;; IR Thermometer for MSP430F2012
;;
;; Copyright 2012 - By Michael Kohn
;; http://www.mikekohn.net/
;; mike to mikekohn.net
;;
;; Use DS18B20 thermometer to sense the temperature and send to
;; a another device using Sony SIRC IR protocol

.include "msp430g2x31.inc"

; 2.4 ms = 192 interrupts
; 1.2 ms = 96 interrupts
; 0.6 ms = 48 interrupts

; 7 bit command
; 5 bit address
; Lsb first
;
; address is 7
; function is mapped:  C reading - bit 7 (sign) bits 6 to 0 (temp)

RAM equ 0x0200
HEADER equ 192
LONG equ 96
SHORT equ 48

DS18B20_DATA equ 0x0210
DS18B20_CMD equ 0x0220
DS18B20_CMD_LEN equ 0x0221
DS18B20_BIT equ 0x0222
DEBUG_STATE equ 0x0223

;  r4 =
;  r5 = interupt count
;  r6 = pointer to next byte going out
;  r7 = current byte
;  r8 = bit count
;  r9 = bit time len
; r10 = DS18B20
; r11 = DS18B20
; r12 = temp in main
; r13 = interrupt routine
; r14 =
; r15 =

  .org 0xf800
start:
  ;; Turn off watchdog
  mov.w #(WDTPW|WDTHOLD), &WDTCTL

  ;; Please don't interrupt me
  dint

  ;; r13 points to which interrupt routine should be called
  mov.w #led_off, r13

  ;; Set up stack pointer
  mov.w #0x0280, SP

  ;; Set MCLK to 16 MHz with DCO 
  mov.b #DCO_4, &DCOCTL
  mov.b #RSEL_15, &BCSCTL1
  mov.b #0, &BCSCTL2

.if 0
  ;; Set MCLK to 16 MHz external crystal
  bic.w #OSCOFF, SR
  bis.b #XTS, &BCSCTL1
  mov.b #LFXT1S_3, &BCSCTL3
  ;mov.b #LFXT1S_3|XCAP_1, &BCSCTL3
test_osc:
  bic.b #OFIFG, &IFG1
  mov.w #0x00ff, r15
dec_again:
  dec r15
  jnz dec_again
  bit.b #(OFIFG), &IFG1
  jnz test_osc
  mov.b #(SELM_3|SELS), &BCSCTL2
.endif

  ;; Set up output pins
  ;; P1.0 = Data In/Out on DS18B20
  ;; P1.1 = On button (input)
  ;; P1.2 = Data Out on IR LED
  ;; P1.7 = Data Out on LED  (debug)
  mov.b #0x85, &P1DIR        ; P1.0, P1.7
  mov.b #0x03, &P1OUT
  mov.b #2, &P1REN

  ;; Set up Timer
  mov.w #200, &TACCR0
  mov.w #(TASSEL_2|MC_1), &TACTL ; SMCLK, DIV1, COUNT to TACCR0
  mov.w #CCIE, &TACCTL0
  mov.w #0, &TACCTL1

  mov.b #0, &RAM+0   ; command (should be temp)
  mov.b #7, &RAM+1   ; command len (always 7)
  mov.b #7, &RAM+2   ; address (let's make it 7)
  mov.b #5, &RAM+3   ; address len (always 5)

  ;; Okay, I can be interrupted now
  eint

main:
  ;bit.b #2, &P1IN
  ;jnz main

  call #read_temp
  mov.w &DS18B20_DATA, r12
  rra.w r12                  ; if this is a DS18B20, shift 4 bits
  rra.w r12                  ; for DS18S20, shift only once
  rra.w r12
  rra.w r12
  mov.b r12, &RAM
  call #send_command
  mov.w #led_off, r13

  mov.w #60000, &TACCR0
  mov.w #0, r5
main_delay:
  cmp.w #16000, r5           ; 1 min delay
  jl main_delay
  jmp main

; ----- send IR

send_command:
  mov.w #200, &TACCR0
  bis.b #128, &P1OUT
  mov.w #0, r5
  mov.w #led_on, r13
wait_header_on:
  cmp.w #HEADER, r5
  jl wait_header_on

  mov.w #0, r5
  mov.w #led_off, r13
wait_header_off:
  cmp.w #SHORT, r5
  jl wait_header_off

  mov.w #RAM, r6
send_next_byte:
  mov.b @r6+, r7     ; bits to shift left out
  mov.b @r6+, r8     ; bit len
ir_send_next_bit:
  mov.w #LONG, r9
  rra.b r7
  jc it_was_a_1
  mov.w #SHORT, r9
it_was_a_1:

  mov.w #led_on, r13
  mov.w #0, r5
wait_on_first_half:
  cmp.w r9, r5
  jl wait_on_first_half

  mov.w #led_off, r13
  mov.w #0, r5
wait_on_second_half:
  cmp.w #SHORT, r5
  jl wait_on_second_half

  dec.b r8
  jnz ir_send_next_bit

  cmp.w #RAM+4, r6
  jne send_next_byte

  mov.w #0, r5
pause:
  cmp.w #20000, r5
  jl pause
  bic.b #0x04, &P1OUT
  bic.b #128, &P1OUT
  ret

; ----- read temp

read_temp:
  mov.w #2000, &TACCR0
  mov.w #ds18b20_signal_interrupt, r13
  ;; First do a convert
  call #init_ds18b20
  mov.b #0xcc, &DS18B20_CMD
  call #send_ds18b20_cmd
  mov.b #0x44, &DS18B20_CMD
  call #send_ds18b20_cmd
  call #wait_ds18b20_convert

  ;; Second fetch the scratch-pad
  call #init_ds18b20
  mov.b #0xcc, &DS18B20_CMD
  call #send_ds18b20_cmd
  mov.b #0xbe, &DS18B20_CMD
  call #send_ds18b20_cmd
  call #fetch_ds18b20_scratch_pad

  mov.w #led_off, r13
  ret

;; init DS18B20
init_ds18b20:
  bic.b #1, P1OUT
  mov.w #7680, &TACCR0   ; 480us
  mov.w #0, TAR
  mov.w #0, r11
master_reset_pulse:
  cmp.w #1, r11
  jne master_reset_pulse
  bis.b #1, P1OUT
  bic.b #1, P1DIR
wait_slave_low:
  bit.b #1, P1IN
  jnz wait_slave_low
wait_slave_hi:
  bit.b #1, P1IN
  jz wait_slave_hi
  bis.b #1, P1DIR
  mov.w #960, &TACCR0   ; 60us
  ret

;; send_ds18b20_cmd
send_ds18b20_cmd:
  mov.b #8, &DS18B20_CMD_LEN
send_next_bit:
  bit.b #1, &DS18B20_CMD
  jz cmd_bit_0
  call #write_ds18b20_1
  jmp cmd_bit_done
cmd_bit_0:
  call #write_ds18b20_0
cmd_bit_done:
  mov.w #6, r10
cmd_wait_1us:
  dec r10
  jnz cmd_wait_1us
  rra.b &DS18B20_CMD
  dec.b &DS18B20_CMD_LEN
  jnz send_next_bit
  ret

;; wait_ds18b20_convert
wait_ds18b20_convert:
  mov.w #6, r10
convert_wait_1us:
  dec r10
  jnz convert_wait_1us
  call #read_ds18b20
  cmp.w #0, r10
  jeq wait_ds18b20_convert
  ret

;; fetch_ds18b20_scratch_pad
fetch_ds18b20_scratch_pad:
  mov.w #DS18B20_DATA, r14
fetch_next_byte:
  mov.w #8, &DS18B20_BIT

fetch_next_bit:
  mov.w #6, r10
scratch_wait_1us:
  dec r10
  jnz scratch_wait_1us

  call #read_ds18b20
  rra.b 0(r14)
  cmp.w #0, r10
  jeq fetched_0
  bis.b #128, 0(r14)
  jmp done_set_fetch
fetched_0:
  bic.b #128, 0(r14)
done_set_fetch:
  dec &DS18B20_BIT
  jnz fetch_next_bit

  inc r14
  cmp.w #DS18B20_DATA+9, r14
  jne fetch_next_byte
  ret

;; write 1 to DS18B20
write_ds18b20_1:
  bic.b #1, P1OUT
  mov.w #0, TAR
  mov.w #0, r11
  mov.w #6, r10
wait_at_least_1us_write_1:
  dec r10
  jnz wait_at_least_1us_write_1
  bis.b #1, P1OUT
master_write_1:
  cmp.w #1, r11
  jne master_write_1
  ret

;; write 0 to DS18B20
write_ds18b20_0:
  bic.b #1, P1OUT
  mov.w #0, TAR
  mov.w #0, r11
master_write_0:
  cmp.w #1, r11
  jne master_write_0
  bis.b #1, P1OUT
  ret

;; read from DS18B20 (returns 0 in r10 for a 0, return 1 for a 1)
read_ds18b20:
  bic.b #1, P1OUT
  mov.w #6, r10
wait_at_least_1us_read:
  dec r10
  jnz wait_at_least_1us_read
  bis.b #1, P1OUT
  bic.b #1, P1DIR
  mov.w #0, TAR
  mov.w #0, r11
wait_read_bit:
  bit.w #1, P1IN
  jnz read_bit_not_zero
  inc r10
read_bit_not_zero:
  cmp.w #1, r11
  jne wait_read_bit
  bis.b #1, P1DIR

  cmp.w #0, r10
  jeq return_1
  mov.w #0, r10
  ret
return_1:
  mov.w #1, r10
  ret

; ----- interrupts

timer_interrupt:
  br r13

led_on:
  xor.b #4, &P1OUT
  inc.w r5
  reti

led_off:
  bic.b #4, &P1OUT
  inc.w r5
  reti

ds18b20_signal_interrupt:
  mov.w #1, r11
  reti

  org 0xffe8
vectors:
  dw 0
  dw 0
  dw 0
  dw 0
  dw 0
  dw timer_interrupt       ; Timer_A2 TACCR0, CCIFG
  dw 0
  dw 0
  dw 0
  dw 0
  dw 0
  dw start                 ; Reset



