; I2C communication with CP2401 and HIH6130 by using DMA
#include "msp430.h"                     ; the actual CPU model is set in
                                        ; the project options in IAR

PWR   	EQU     BIT2			; CP2401 PWR pin (input)
INT	EQU	BIT3			; CP2401 INT pin (output)

        RSEG    CSTACK                  ; pre-declaration of segment
        RSEG    DATA16_N                ; data segment
i2cb:	DS 	4			; I2C data buffer
mode:  	DS	1			; 0: display humi, 1: display temp
cnt:    DS      1                       ; I2C sent bytes counter
ta_p:   DS      2                       ; Timer_A period for 1 sec wakeup
temp:	DS	2			; storage for temperature
humi:	DS	2			; storage for humidity
LCD_data:   DS  21                      ; LCD data

        RSEG    CODE                    ; code segment
reset:  mov     #SFE(CSTACK), SP        ; set up stack
        mov.w   #WDTPW+WDTHOLD, &WDTCTL ; stop watchdog timer
        bis.b   #REFTCOFF, &REFCTL0_L   ; disable temp sensor

        ; I/O ports setup
        mov.b   #0xC8, &P1OUT           ; init port  
        mov.b   #0xC0, &P1REN           ; enable pull-up resistors
        mov.b   #0x37, &P1DIR           ; output pins on P1      
        mov.b   #BIT6+BIT7, &P1SEL1	; activate special functions

        bis.b   #PWR, &P2OUT
        mov.b   #BIT2+BIT1+BIT0, &P2DIR ; configure P2 for output

        mov.w   #0x0F, &PJDIR
        clr.w   &PJOUT
        bis.w   #BIT5+BIT4, &PJSEL0     ; enable Xtal oscillator pins

	clr.w	R11			; power-up delay
	dec.w	R11
	jnz	$-2

        ; clock setup     
	mov.w   #0xFFFF, &ta_p
	mov.b   #0xA5, &CSCTL0_H        ; unlock the clock interface 
        mov.b   #XT1DRIVE_1, &CSCTL4_L  ; crystal drive level
        clr.w   R5     			; initialize cycle counter                
wco1:   bic.b   #XT1OFFG, &CSCTL5_L     ; clear XT1 fault flag
        bic.b   #OFIFG, &SFRIFG1_L              
        bit.b   #OFIFG, &SFRIFG1_L	; test oscillator fault flag
        jz	wco2                    ; wait for crystal osc. to start
        inc.w   R5                      ; wait for at most 1 second for the
        jnz     wco1                    ; oscillator to start 
        mov.w   #8192, &ta_p            ; Timer_A0 period for 1 sec
wco2:   clr.w   &CSCTL3                 ; set 8 MHz MCLK, SMCLK

        ; DMA_1 setup for I2C write operation
        mov.w   #DMASRCINCR_3+DMASRCBYTE, &DMA1CTL
        mov.w   #UCB0TXBUF, &DMA1DAL    ; DMA_1 destination address
        ; DMA_2 setup for I2C read operation
        mov.w   #DMA0TSEL_18, &DMACTL1  ; triggers for DMA_2
        mov.w   #DMADSTINCR_3+DMADSTBYTE, &DMA2CTL
        mov.w   #UCB0RXBUF, &DMA2SAL    ; DMA_2 source address
        mov.w   #i2cb, &DMA2DAL         ; DMA_2 destination address

        ; I2C setup for eUSCI_B
i2c1:	bit.b	#BIT6, &P1IN		; toggle SCK pin till the data
	jnz	i2c2			; line goes high
	bic.b	#BIT7, &P1OUT
	nop
	nop
	bis.b	#BIT7, &P1OUT
	jmp	i2c1

i2c2:	bis.b   #UCSWRST, &UCB0CTL1     ; keep I2C module in reset	
        bis.w   #UCMODE_3+UCMST+UCSYNC, &UCB0CTLW0 ; I2C master mode 
	mov.w   #UCASTP_2, &UCB0CTLW1   ; automatic stop generation
        mov.w   #64, &UCB0BRW          	; baudrate = 8MHz / 64
	call	#Init_CP2401

	; Timer_A0 setup for 1 sec periodic wakeups
      	mov.w   #TASSEL_1+TACLR+TAIE, &TA0CTL ; ACLK, int. enable, clear
        mov.w   &ta_p, &TA0CCR0         ; Timer_A0 period
	bis.w   #MC_1, &TA0CTL          ; start Timer_A0 in Up mode

	clr.b	&mode			; start with displayhing temp
	clr.b	&cnt
	jmp	lp1			; request first conversion
	
loop:   bis.w   #LPM3+GIE, SR           ; MAIN LOOP
        nop				; enter LPM3 sleep mode

	inc.b	&mode			; inc mode mod 2
	and.b	#0x01, &mode		; if mode=1 then display temp
	jnz	lp2
		
	call	#Format_Humi		; display humi
	call	#Display_Value
lp1:	call	#Request_Conversion	; request new temp and humi measurement
	jmp	loop

lp2:	call	#Get_Temp_Humi		; read new temp and humi values
	call	#Format_Temp		; display temp
	call	#Display_Value
	jmp	loop
        
;===PROCEDURES==============================================================
Format_Humi:				; convert humi value into BCD
	mov.w	&humi, R5
	mov.w   R5, &MPY          	; multiply humi sensor value by 100
        mov.w   #100, &OP2            
        nop                         
        nop
	nop
        mov.w   &RES0, R10              ; R11:R10 = humi*100
	mov.w	&RES1, R11
	
	add.w	#8192, R10		; rounding off (add 2^13)
	addc.w	#0, R11

	clrc
	rlc.w	R10
	rlc.w	R11
	clrc
	rlc.w	R10
	rlc.w	R11			; R11 = (humi*100+2^13)/2^14

fh1:	cmp.w	#100, R11		; trancate R11 to [0-99] range
	jlo	fh2
	mov.b	#99, R11	

fh2:	clr.w  	R6                      ; storage for 4-digit BCD
        mov.b   #8, R7                  ; loop cnt = # of bits
                
fh3:    rla.b   R11                     ; use decimal addition
        dadd.w  R6, R6                  ; operation to convert R4
        dec.b   R7                      ; into BCD
        jnz     fh3 
	ret				; BCD is in R6

;------------------------------------------------------------------------
Format_Temp:				; convert temp value into BCD
	mov.w	&temp, R5		; display temp
	mov.w	R5, R6	
	rram.a  #2, R6			; get rid of 2 lsb's
	mov.w   R6, &MPY          	; multiply temp sensor value by 165
        mov.w   #165, &OP2            
        nop                         
        nop
	nop
        mov.w   &RES0, R10              ; R11:R10 = temp*165
	mov.w	&RES1, R11
	
	add.w	#8192, R10		; rounding off (add 2^13)
	addc.w	#0, R11

	clrc
	rlc.w	R10
	rlc.w	R11
	clrc
	rlc.w	R10
	rlc.w	R11			; R11 = (temp*165+2^13)/2^14

	sub.w	#40, R11		; subtract offset (=40)
	jge	fh1
	clr.w	R11
	jmp	fh1			; do BCD conversion
	
;------------------------------------------------------------------------
Display_Value:				; display value in R6
	clr.b	&LCD_data+17		; blank
	clr.b	&LCD_data+8

	mov.b	R6, R7			; R7=12
	mov.b	R7, R8
	rram.w  #4, R8			; r8 = 0001
	and.b	#0x0F, R8		; r8 = 01
	add.b	R8, R8			
	add.w	#Table_14_seg, R8	; r8 = data address
	mov.w	@R8, R10
	mov.b	R10, &LCD_data+18	; compose digit 6
	swpb	R10
	mov.b	R10, &LCD_data+7

	and.b	#0x0F, R7
	add.b	R7, R7
	add.w	#Table_14_seg, R7	; r7 = data address
	mov.w	@R7, R10
	mov.b	R10, &LCD_data+19	; compose digit 7
	swpb	R10
	mov.b	R10, &LCD_data+6

	bit.b	#0x01, &mode
	jnz	deg_s
hum_s:	mov.w	#Table_14_seg+(2*17), R8   ; H
	mov.w	#Table_14_seg+(2*30), R9   ; U
	mov.w	#Table_14_seg+(2*22), R10  ; M
	mov.w	#Table_14_seg+(2*18), R11  ; I
	mov.w	#Table_14_seg+(2*38), R12  ; %	
	jmp	load

deg_s:	mov.w	#Table_14_seg+(2*29), R8   ; T
	mov.w	#Table_14_seg+(2*14), R9   ; E
	mov.w	#Table_14_seg+(2*22), R10  ; M
	mov.w	#Table_14_seg+(2*25), R11  ; P
	mov.w	#Table_14_seg+(2*39), R12  ; *
		
load:	mov.w	@R8, R7
	mov.b	R7, &LCD_data+13	; compose symbol
	swpb	R7
	mov.b	R7, &LCD_data+12
	mov.w	@R9, R7
	mov.b	R7, &LCD_data+14	; compose symbol
	swpb	R7
	mov.b	R7, &LCD_data+11
	mov.w	@R10, R7
	mov.b	R7, &LCD_data+15	; compose symbol
	swpb	R7
	mov.b	R7, &LCD_data+10
	mov.w	@R11, R7
	mov.b	R7, &LCD_data+16	; compose symbol
	swpb	R7
	mov.b	R7, &LCD_data+9	
 	mov.w	@R12, R7
	mov.b	R7, &LCD_data+20	; compose symbol
	swpb	R7
	mov.b	R7, &LCD_data+5

;=======CP2401 INTERFACE=============================================
Write_LCD:
	bic.b	#PWR, &P2OUT		; wake-up CP2401
wd:	bit.b	#INT, &P1IN		; and wait for wake-up
	jnz	wd

	mov.w	#Disable_Int, R11	; disable CP2400 interrupts
	call	#Send_CP2401
	mov.w	#Config_Ports, R11	; reconfigure all ports for analog mode
	call	#Send_CP2401
	mov.w	#LCD_data, R11		; send data
	call	#Send_CP2401

Set_ULP:
	mov.w	#ULP_mode, R11		; put LCD into ultra low-power mode
	call	#Send_CP2401
	bis.b	#PWR, &P2OUT		; turn on low-power mode
	ret
	
;------------------------------------------------------------------------	
Init_CP2401:
	mov.b	#20, &LCD_data		; DMA amount to transfer
	mov.b	#4,  &LCD_data+1	; block write command
	mov.b   #0,  &LCD_data+2	; LCD memory address
	mov.b	#0x80, &LCD_data+3	;    (2 bytes)
	clr.b	&LCD_data+4		; no blinking
	mov.b	#16, R10
	mov.w	#LCD_data+5, R11	; init LCD data area	
ic1:	mov.b	#0xFF, 0(R11)
	inc.w	R11
	dec.b	R10
	jnz	ic1
	
	bic.b	#PWR, &P2OUT
ic2:	bit.b	#INT, &P1IN		; wait for reset pin is up
	jnz	ic2

	mov.w	#Disable_Int, R11	; disable CP2400 interrupts
	call	#Send_CP2401	
	mov.w	#Unlock_RTC, R11	; unlock SmaRTClock interface
	call	#Send_CP2401
	mov.w	#Load_CAPS, R11		; load caps value
	call	#Send_CP2401
	mov.w	#Start_RTC, R11		; start SmaRTClock oscillator
	call	#Send_CP2401
	mov.w	#Config_Ports, R11	; set analog mode for all ports
	call	#Send_CP2401	
	mov.w	#Config_LCD, R11	; LCD configuration
	call	#Send_CP2401
	mov.w	#LCD_data, R11		; set all segments off
	call	#Send_CP2401
	mov.w	#Enable_LCD, R11	; start LCD operation
	call	#Send_CP2401
	jmp	Set_ULP			; enter low-power mode

;------------------------------------------------------------------------
Send_CP2401:
	mov.b	@R11+, R10
	mov.w   R10, &UCB0TBCNT         ; I2C byte counter
	mov.w   R10, &DMA1SZ            ; amount of DMA transfer 
	mov.w   R11, &DMA1SAL         	; DMA_1 source address	
	mov.w   #0x3A, &UCB0I2CSA       ; CP2401 slave address	
        bic.w   #UCSWRST, &UCB0CTLW0    ; clear I2C reset 
        bis.w   #UCSTPIE, &UCB0IE       ; enable I2C stop interrupt
        bis.w   #DMAEN, &DMA1CTL        ; enable DMA transfer
        bis.w   #UCTR+UCTXSTT, &UCB0CTLW0   ; send start condition and write  
        bis.w   #LPM0+GIE, SR           ; enter LPM0 
        nop                             ; remain in LPM0  
        bic.w   #DMAEN, &DMA1CTL        ; disable DMA channel 1        
        bis.w   #UCSWRST, &UCB0CTLW0    ; I2C reset 
	ret
 
;=======HIH6130 INTERFACE================================================      
Request_Conversion:
	mov.w   #0x27, &UCB0I2CSA       ; HIH6130 slave address	
        bic.w   #UCSWRST, &UCB0CTLW0    ; clear I2C reset 
        bis.w   #UCSTPIE, &UCB0IE       ; enable I2C stop interrupt
        bis.w   #UCTR+UCTXSTT+UCTXSTP, &UCB0CTLW0   ; send start condition and write  
        bis.w   #LPM0+GIE, SR           ; enter LPM0 
        nop                             ; remain in LPM0         
        bis.w   #UCSWRST, &UCB0CTLW0    ; I2C reset 
	ret

;----------------------------------------------------------------------
Get_Temp_Humi:                          ; read temp from sensor
	mov.w   #4, &UCB0TBCNT          ; prepare to receive 4 bytes via I2C
        mov.w   #4, &DMA2SZ             ; amount to transfer
	mov.w   #0x27, &UCB0I2CSA       ; HIH6130 slave address        
	bic.w   #UCTR, &UCB0CTLW0 	; set receiver mode
	bic.w   #UCSWRST, &UCB0CTLW0    ; clear I2C reset 
        bis.w   #UCSTPIE, &UCB0IE       ; enable I2C stop interrupt 
        bis.w   #DMAEN, &DMA2CTL        ; enable DMA_2 transfer  
        bis.w   #UCTXSTT, &UCB0CTLW0    ; send start condition 
        bis.w   #LPM0+GIE, SR           ; enter LPM0 
        nop                             ; remain in LPM0
        bic.w   #DMAEN, &DMA2CTL        ; disable DMA channel 2
        bis.w   #UCSWRST, &UCB0CTLW0    ; I2C reset   
        
	swpb	&i2cb			; reverse the bytes order and
	mov.w	&i2cb, &humi		; save received values
	swpb	&i2cb+2			; in variables humi and temp
	mov.w	&i2cb+2, &temp
	ret
	
;======================================================================
I2C_ISR:
        add     &UCB0IV,PC              ; add offset to PC
        reti                            ; no interrupt
        reti                            ; ALIFG break
        reti                   		; NACKIFG break
        reti                     	; STTIFG break
        jmp     I2end                   ; STPIFG break
        reti                            ; RXIFG3 break
        reti                            ; TXIFG3 break
        reti                            ; RXIFG2 break
        reti                            ; TXIFG2 break
        reti                            ; RXIFG1 break
        reti                            ; TXIFG1 break
        reti                            ; RXIFG0 break
        reti                    	; TXIFG0 break
      	reti                   		; BCNTIFG break
        reti                            ; clock low timeout break
        reti                            ; 9th bit break
        
I2end:  bic.w   #LPM0,0(SP)             ; wake-up CPU       
        reti

;----------------------------------------------------------------------
TA0_ISR:                                ; timestamp for every 0.5 sec
        bic.w   #TAIFG, &TA0CTL         ; clear Timer_A0 interrupt flag
        bic.w   #LPM3, 0(SP)       	; wake-up CPU
        reti
        
;===========================================================================
Disable_Int:
	DB	0x05, 0x04, 0x00, 0x30, 0x00, 0x00 
Unlock_RTC:
	DB	0x05, 0x03, 0x00, 0x0A, 0xA5, 0xF1	
Load_CAPS:
	DB	0x05, 0x04, 0x00, 0x0B, 0x06, 0x00
Start_RTC:
	DB	0x05, 0x04, 0x00, 0x0B, 0x04, 0x80	
Config_Ports:
	DB	0x08, 0x04, 0x00, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00
Config_LCD:
	DB	0x0A, 0x04, 0x00, 0x95, 0x0F, 0x0F, 0xAF, 0x70, 0x00, 0x06, 0x10
Enable_LCD:
	DB	0x04, 0x03, 0x00, 0xA0, 0x01
ULP_mode:
	DB	0x05, 0x04, 0x00, 0xA1, 0x80, 0x08

EVEN
Table_14_seg: ;	def`lkji.cbamngh	
	DW	1110000001110000b	; 0
	DW	0000001001100000b	; 1
	DW	1100010000110010b	; 2
	DW	1000010001110000b	; 3
	DW	0010010001100010b	; 4
	DW	1010010001010010b	; 5
	DW	1110010001010010b	; 6
	DW	0000001000011000b	; 7
	DW	1110010001110010b	; 8
	DW	1010010001110010b	; 9
	DW	0110010001110010b	; A
	DW	1000010101111000b	; B
	DW	1110000000010000b	; C
	DW	1000000101111000b	; D
	DW	1110000000010010b	; E
	DW	0110000000010010b	; F	
	DW	1110010001010000b	; G
	DW	0110010001100010b	; H
	DW	1000000100011000b	; I
	DW	1100000001100000b	; J
	DW	0110101000000010b	; K
	DW	1110000000000000b	; L
	DW	0110001001100001b	; M
	DW	0110100001100001b	; N
	DW	1110000001110000b	; O
	DW	0110010000110010b	; P
	DW	1110100001110000b	; Q
	DW	0110110000110010b	; R
	DW	1000010001010001b	; S
	DW	0000000100011000b	; T
	DW	1110000001100000b	; U
	DW	0110001000000100b	; V
	DW	0110100001100100b	; W
	DW	0000101000000101b	; X
	DW	0000001000001001b	; Y
	DW	1000001000010100b	; Z
	DW	0000010100001010b	; +
	DW	0000010000000010b	; -
	DW	0010001001000100b	; %
	DW	0010010000010010b	; *

;---------------------------------------------------------------------------
        COMMON  INTVEC                  ; MSP430 interrupt vectors

        ORG     RESET_VECTOR
        DW      reset                   ; POR, ext. Reset, Watchdog
        
        ORG     USCI_B0_VECTOR          ; USCI_B vector
        DW      I2C_ISR        

        ORG     TIMER0_A1_VECTOR        ; Timer_A0 vector
        DW      TA0_ISR

        END

