; I2C communication with CP2401 and Si7005 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	1			; storage for temperature
humi:	DS	1			; 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_0 setup for LCD interface
        mov.w   #DMA0TSEL_15+DMA1TSEL_19, &DMACTL0  ; triggers for DMA_0,1                 
        mov.w   #UCA0TXBUF, &DMA0DAL    ; DMA_0 destination address        
        ; 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   #DMA2TSEL_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

        ; SPI setup for eUSCI_A        
        bis.b   #UCSWRST, &UCA0CTL1     ; keep SPI module in reset  
        bis.w   #UCCKPL+UCMSB+UCMST+UCSYNC+UCSSEL_2, &UCA0CTLW0
        mov.b   #1, &UCA0BR0            ; clock division factor (1:1)

        ; 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		; init LCD driver

	; 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

	xor.b	#0x01, &mode		; inc mode mod 2
	jnz	lp2			; if mode=1 then display temp

	call	#Get_Temp_Humi		; read new humi value
	call	#Format_Humi		; compute humi
	call	#Display_Value		; display humi value
lp1:	mov.w	#0x1103, &i2cb		; request temp conversion
	call	#Request_Conversion	
	jmp	loop

lp2:	call	#Get_Temp_Humi		; read new temp value
	call	#Format_Temp		; compute temp
	call	#Display_Value		; display tenp
	mov.w	#0x0103, &i2cb		; request humi conversion
	call	#Request_Conversion
	jmp	loop
        
;===PROCEDURES==============================================================
Format_Humi:				; convert humi value into BCD
	mov.w	&i2cb, R6
	add.w	#0x0080, R6		; rounding off
	swpb	R6
	mov.b	R6, R6			; get rid of the lower-order byte	
	sub.w	#24, R6			; subtract offset R11 = RH
	
	mov.w   R6, &MPY          	; linearize RH
        mov.w   #393, &OP2            	; 16x16 bit multiplication
        nop                         
        mov.w   &RES0, R10              ; R10 = RH*393
	clr.w	R11
	add.w	#59920, R10		; R11:R10 = 59920 + R10*393
	addc.w	#0, R11

	mov.w   R10, &MPY32L          	; compute RH*(59920 - RH*393)
	mov.w	R11, &MPY32H
        mov.w   R6, &OP2            	; 16x16 bit multiplication
        nop  				; wait for operation is done
	nop
	nop
	mov.w   &RES0, R10
	mov.w	&RES1, R11		; R11:R10 = product

	add.w	#LWRD(478440), R10	; add 47844*10 to the result
	addc.w	#HWRD(478440), R11	; R11:R10 = linearized RH*10^5	

	mov.w	R10, &MPY32L		; thermo-compensation
	mov.w	R11, &MPY32H
	mov.w	#39762, &OP2		; multiply by Q_1
	nop				; result is at most 32 bits with
	nop				; 24 fractional bits
	nop
	mov.w   &RES1, R12		; getting rid of 16 fractional bits
	mov.w	&RES2, R13		; integer part has at most 8 bits 	

	mov.b	&temp, R6
	sub.w	#30, R6			; R6 = temp-30
	mov.w	R12, &MPYS32L		; signed multiplication by temp-30	
	mov.w	R13, &MPYS32H
	mov.w	R6,  &OP2
	nop
	nop
	nop
	mov.w	&RES0, R12
	add.w	#0x80, R12		; rounding off
	mov.w	&RES1, R13
	adc.w	R13
	mov.w	&RES2, R14
	adc.w	R14

	swpb	R12			; get rid of 8 fraction bits
	mov.b	R12, R12
	mov.b	R13, R6
	swpb	R6
	add.w	R6, R12
	swpb	R13
	mov.b	R13, R13
	mov.b	R14, R14
	swpb	R14
	add.w	R14, R13		; R13:R12 = (temp compensation)*10^5

	add.w	R12, R10
	addc.w	R13, R11		; R11:R10 = final RH*10^5
fh0:	add.w	#50000, R10
	adc.w	R11			; rounding off

	clr.b	R6
	mov.w	#LWRD(1000000), R12
	mov.w	#HWRD(1000000), R13
fh1:	sub.w	R12, R10
	subc.w	R13, R11
	jl	fh2
	add.b	#16, R6			; accumulate the 1st digit of humi
	jmp	fh1

fh2:	cmp.b	#160, R6		; truncate humi to [0-99] range
	jlo	fh3
	mov.b	#0x99, R6
	jmp	fh5

fh3:	add.w	R12, R10		; restore the subtrahend
	addc.w	R13, R11
	mov.w	#LWRD(100000), R12
	mov.w	#HWRD(100000), R13
fh4:	sub.w	R12, R10
	subc.w	R13, R11
	jl	fh5
	inc.b	R6
	jmp	fh4

fh5:	ret				; R6 = BCD(humi)

;------------------------------------------------------------------------
Format_Temp:				; convert temp value into BCD
	mov.w	&i2cb, R11		; display temp
	add.w	#0x0040, R11		; rounding off
	rrum.w	#4, R11
	rrum.w	#3, R11			; R11 = temp/32
	sub.w	#50, R11		; subtract offset 
	jge	ft1
	clr.w	R11
	
ft1:	cmp.w	#100, R11		; trancate R11 to [0-99] range
	jlo	ft2
	mov.b	#99, R11	

ft2:	mov.b	R11, &temp		; save temp value for humi compensation
	mov.w	R11, &BIN2BCD		; BCD conversion
	mov.w	&BIN2BCD, R6
	ret				; R6 = BCD(temp)	
	
;------------------------------------------------------------------------
Display_Value:				; display hex value in R5 and decimal 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
 
;------------------------------------------------------------------------ 
Read_CP2401:
	bic.b	#PWR, &P2OUT		; wake-up CP2401
rd:	bit.b	#INT, &P1IN		; and wait for wake-up
	jnz	rd

	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   #0x0203, &i2cb          ; read reg
	mov.w	R5,   &i2cb+2		; ADDRH:ADDRL
	mov	#i2cb, R11
	call	#Send_CP2401

        mov.w   #3, &UCB0TBCNT          ; prepare to receive 3 bytes via I2C
        mov.w   #3, &DMA2SZ             ; amount to transfer	
        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 
	
	jmp	Set_ULP
	ret

;----------------------------------------------------------------------
Test_Font:
	mov.b	&cnt, R11
	add.w	R11, R11
	add.w	#Table_14_seg, R11
	mov.w	@R11, R10
	
	mov.b	#8, R12
	mov.w	#LCD_data+13, R11	; init LCD data area	
tf1:	mov.b	R10, 0(R11)
	inc.w	R11
	dec.b	R12
	jnz	tf1

	swpb	R10
	mov.b	#8, R12
	mov.w	#LCD_data+5, R11	; init LCD data area	
tf2:	mov.b	R10, 0(R11)
	inc.w	R11
	dec.b	R12
	jnz	tf2
	
	inc.b	&cnt
	cmp.b	#39, &cnt
	jne	tf3
	clr.b	&cnt
tf3:	call	#Write_LCD	
	ret

;=======Si7005 INTERFACE================================================      
Request_Conversion:			; send 2 bytes from i2cb buffer
	mov.w   #0x40, &UCB0I2CSA       ; Si7005 slave address
	mov.w   #2, &UCB0TBCNT         	; I2C byte counter
	mov.w   #2, &DMA1SZ            	; amount of DMA transfer 
	mov.w   #i2cb, &DMA1SAL         ; DMA_1 source address		
        bic.w   #UCSWRST, &UCB0CTLW0    ; clear I2C reset 
        bis.w   #UCSTPIE, &UCB0IE       ; enable I2C stop interrupt
        bis.w   #DMAEN, &DMA1CTL        ; enable DMA_1 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

;----------------------------------------------------------------------
Get_Temp_Humi:                          ; read temp from sensor
        mov.b   #1, &cnt
	mov.b	#1, &i2cb		; register number
	clr.w   &UCB0TBCNT	
	mov.w   #0x40, &UCB0I2CSA       ; Si7005 slave address	
        bic.w   #UCSWRST, &UCB0CTLW0    ; clear I2C reset
        bis.w   #UCTXIE0+UCNACKIE, &UCB0IE  ; enable I2C TX interrupt
        bis.w   #UCTR+UCTXSTT, &UCB0CTLW0   ; send start condition and write  
        bis.w   #LPM0+GIE, SR           ; enter LPM0 
        nop                             ; remain in LPM0      

	mov.w   #2, &UCB0TBCNT          ; prepare to receive 2 bytes via I2C
        mov.w   #2, &DMA2SZ             ; amount to transfer        
	bic.w   #UCTR, &UCB0CTLW0 	; set receiver mode
        mov.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
	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
        jmp	I2TX                    ; TXIFG0 break
      	reti                   		; BCNTIFG break
        reti                            ; clock low timeout break
        reti                            ; 9th bit break

I2TX:   cmp.b   #0, &cnt
        jeq     I2end
        mov.b   &i2cb, R10		; byte to send
        mov.w   R10, &UCB0TXBUF         ; Load TX buffer
        dec.b   &cnt                    ; Decrement TX byte counter
        reti

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

