OSDN Git Service

V205
[fast-forth/master.git] / MSP430-FORTH / lib / I2C_Slave.f
1 \ name : MSP430FR5xxx_I2C_Slave.f
2 RST_STATE
3 \ NOECHO
4
5 \ Copyright (C) <2015>  <J.M. THOORENS>
6 \
7 \ This program is free software: you can redistribute it and/or modify
8 \ it under the terms of the GNU General Public License as published by
9 \ the Free Software Foundation, either version 3 of the License, or
10 \ (at your option) any later version.
11 \
12 \ This program is distributed in the hope that it will be useful,
13 \ but WITHOUT ANY WARRANTY\ without even the implied warranty of
14 \ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 \ GNU General Public License for more details.
16 \
17 \ You should have received a copy of the GNU General Public License
18 \ along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 \
20 \ driver I2C SLAVE with START interrupt only, for any I2C_Slave addresses
21 \ Target: MSP430FR5xxx @ 8,16,24 MHz
22 \ version 1.0 2015-03-17
23
24 \ ---------------------------------------------------------------------------------------------------------------------;
25 \ SCL clock generation, timing, and test of data(s) number are made by I2C_Master.
26 \ slave can strech SCL low after Start Condition and after any bit.
27 \
28 \ address Ack/Nack is generated by the slave on SDA line (released by the master)
29 \ Two groups of eight addresses (000$xxy and 1111xxxy) are not allowed (reserved)
30 \ after address or data is sent, the transmitter (Master or Slave) must release SDA line to allow (N)Ack by the receiver
31 \ data Ack/Nack are generated by the receiver (master or slave) on SDA line
32 \ a master receiver must signal the end of data to the slave transmitter by sending a Nack bit
33 \ Stop or restart conditions must be generated by master after a Nack bit.
34 \ after Ack bit is sent, Slave must release SDA line to allow master to do stop or restart conditions
35 \
36 \     __      _____ _____ _..._ _____ _____ _NACK _____ _____ _..._ _____ _____ _NACK     _
37 \ SDA   \____/_MSB_X_____X_..._X_LSB_X_R/W_x_ACK_x_MSB_X_____X_..._X_____X_LSB_X_ACK_X___/
38 \     _____     _     _           _     _     _     _     _           _     _     _     ___
39 \ SCL      \___/1\___/2\___...___/7\___/8\___/9\___/1\___/2\___...___/7\___/8\___/9\___/
40 \       ^   ^                             ^     ^                             ^     ^    ^
41 \       |   |Slave Stretch Low            |SSL  |SSL                          |SSL  |SSL |
42 \       |                                                                                |
43 \       |Start Condition                                                                 |stoP Condition
44 \
45 \     __      _____ _____ _..._ _____ _____ _NACK _____ _____ _..._ _____ _____ _NACK ___
46 \ SDA   \____/_MSB_X_____X_..._X_LSB_X_R/W_x_ACK_x_MSB_X_____X_..._X_____X_LSB_X_ACK_X   \____...
47 \     _____     _     _           _     _     _     _     _           _     _     _     ____
48 \ SCL      \___/1\___/2\___...___/7\___/8\___/9\___/1\___/2\___...___/7\___/8\___/9\___/    \_...
49 \       ^   ^                             ^     ^                             ^     ^    ^
50 \       |   |Slave Stretch Low            |SSL  |SSL                          |SSL  |SSL |
51 \       |                                                                                |
52 \       |Start Condition                                                                 |reStart Condition
53 \
54 \ tHIGH : SCL high time
55 \ tLOW : SCL low time
56 \ tBUF : SDA high time between Stop and Start conditions
57 \ tHD:STA : Start_Condition SCL high time after SDA is low
58 \ tSU:STO : Stop_Condition SCL high time before SDA rise
59 \ tSU:STA : Start_Condition SCL high time before SDA fall
60 \ tHD:DAT : SDA data change time after SCL is low
61 \ the SDA line must be strobe just after SCL is high
62 \ the SDA data must be change just after SCL is low
63 \ standard mode (up to 100 kHz) :   tHIGH   =   tHD:STA =   tSU:STO =   4µs
64 \                                   tLOW    =   tSU:STA =   tBUF    =   4,7µs
65 \                                   tHD:DAT <=  3,45 µs
66 \
67 \ fast mode     (up to 400 kHz) :   tHIGH   =   tHD:STA =   tSU:STO =   0,6µs
68 \                                   tLOW    =   tSU:STA =   tBUF    =   1,3µs
69 \                                   tHD:DAT <=  0,9 µs
70 \ -------------------------------------------------------------------------------------------------------------------;
71
72
73
74 VARIABLE I2CS_OWN       \ slave I2C address without RW flag (low byte) + DATA0 input (HIGH byte)
75 \ 2 ALLOT               \ next the low byte of I2CS_OWN word, it is the input buffer
76 VARIABLE I2CS_BUF       \ buffer output, lentgh (low byte),DATA0 output (HIGH byte)
77 \ 2 ALLOT               \ this byte lentgh is shared by input and output buffers
78
79 \ ******************************\
80 ASM I2CS_TX                     \ TX part of I2C_Slave
81 \ ******************************\
82 \                               \       T = TX buffer_org
83 \                               \ use   W = TX buffer_ptr
84 \                               \ out : low(I2CS_BUF) = RX or TX lentgh
85 \ ------------------------------\
86 MOV T,W                         \ W = TX_buf_ptr -1
87 BEGIN                           \
88     ADD #1,W                    \ first reserve one byte for length then inc
89     MOV.B @W,&UCB0TXBUF         \ +[W] --> UCB0TXBUF
90 \   ----------------------------\
91 \   slave send byte             \
92 \   ----------------------------\
93     BEGIN                       \
94         BIT #$0C,&UCB0IFG       \ UCB0IFG(STP,STT) = 1 ?
95         0<> IF                  \
96 \           --------------------\
97 \           stop or restart received
98 \           --------------------\
99             SUB   T,W           \
100             SUB.B #1,W          \ sub #1, because char +[W] is not sent
101             MOV.B W,0(T)        \ store length in first byte of buffer output
102             BIC #UCTR,&UCB0CTLW0 \ reset UCTR R/W bit for next START
103             MOV @RSP+,PC        \ ===> ret
104 \           --------------------\
105         THEN                    \
106         BIT #2,&UCB0IFG         \ UCB0IFG(TX0) = 1 ?
107     0<> UNTIL                   \
108 AGAIN                           \
109 ENDASM                          \
110     \
111
112 \ -------------+------+------+------+------++---+---+---+---+---------+
113 \ SR(low byte) | SCG1 | SCG0 |OSCOFF|CPUOFF||GIE| N | Z | C | current |
114 \ -------------+------+------+------+------++---+---+---+---+---------+
115 \ LPM0 = $18  |  0   |  0   |  0   |  1   || 1 | x | x | x |  180uA  | default mode
116 \ LPM1 = $58  |  0   |  1   |  0   |  1   || 1 | x | x | x |         | same mode as LPM0
117 \ LPM2 = $98  |  1   |  0   |  0   |  1   || 1 | x | x | x |   60uA  |
118 \ LPM3 = $D8  |  1   |  1   |  0   |  1   || 1 | x | x | x |   10uA  | 32768Hz XTAL is running
119 \ LPM4 = $F8  |  1   |  1   |  1   |  1   || 1 | x | x | x |    6uA  |
120 \ -------------+------+------+------+------++---+---+---+---+---------+
121
122 \ **************************************\
123 ASM I2C_S                               \ <== eUSCIB0 interrupt vector : i2c_addres&R/w sent by master is received
124 \ **************************************\
125 BIC #$F8,0(RSP)                         \ SCG1,SCG0,OSCOFF,CPUOFF and GIE are OFF in retiSR to force LPM0_LOOP with pending interrupt
126 MOV #$98,&LPM_MODE                      \ to reenter in LPM2 mode. LPM3-4 don't work with MSP430FR57xx.
127 \ --------------------------------------\
128 MOV #I2CS_BUF,T                         \ T = buffer output address -1
129 MOV #I2CS_OWN,S                         \ S = buffer input address -1
130 MOV #0,&UCB0IFG                         \ write UCB0IFG to clear all int flags (STTIFG,TXIFG,..)
131 CMP.B &UCB0I2COA0,&UCB0ADDRX            \ UCB0ADDRX = own address ?
132 0<> IF                                   \ 
133 BIC #UCTXACK,&UCB0CTLW0                 \ send Nack address
134 RETI                                    \
135 ELSE
136 \ --------------------------------------\
137 \ It's my own address                   \
138 \ --------------------------------------\
139     BIS #UCTXACK,&UCB0CTLW0             \ send software Ack address
140     BIT #UCTR,&UCB0CTLW0                \ test UCB0CTLW0(UCTR) R/W bit
141     0= IF                               \ I2C_Master Write
142         \ ------------------------------\
143         \ slave receive datas           \ yes
144         \ ------------------------------\
145         MOV S,W                         \ W = input buffer address - 1
146         BEGIN                           \
147             \ --------------------------\
148             \ slave receive one byte    \
149             \ --------------------------\
150             BEGIN                       \
151                 BIT #$8C,&UCB0IFG       \ UCB0IFG(STP,STT,CLTO) = 1 ? (STOP, START, SCL low timeout)
152                 0<> IF                  \
153                     \ ------------------\
154                     \ TX stop or restart\ from master
155                     \ ------------------\
156                     SUB   S,W           \ W = Adr_end - Adr_start = length
157                     MOV.B W,0(T)        \ store length in first byte of buffer output
158                     \ --------------------\
159                     \ here your RX process\
160                     \ --------------------\
161
162
163                     \ --------------------\
164                     \ end of RX process   \
165                     \ --------------------\
166                     RETI                \
167                 THEN                    \ if not (stop, restart, CLTO)
168                 BIT #1,&UCB0IFG         \ UCB0IFG(RX0) = 1 ?
169             0<> UNTIL                   \ 
170             ADD #1,W                    \ reserve one byte for length first, then preincrement
171             MOV.B &UCB0RXBUF,0(W)       \ [UCB0RXBUF] = data --> +[W]
172         AGAIN                           \ loop for new received data if any
173     THEN                                \ end of I2C_Master read
174 \   ------------------------------------\
175 \   slave transmit datas variant 1      \ to loop back after bad_own_address code
176 \   ------------------------------------\
177     CALL #I2CS_TX                       \ 
178     RETI                                \
179 \   ------------------------------------\
180 \   slave transmit datas variant 2      \
181 \   ------------------------------------\
182 \         MOV T,W                         \ W = output buffer address -1
183 \         BEGIN                           \
184 \             ADD     #1,W                \ first reserve one byte for length
185 \             MOV.B   @W,&UCB0TXBUF       \ +[W] --> UCB0TXBUF
186 \ \           ----------------------------\
187 \ \           slave send byte             \
188 \ \           ----------------------------\
189 \             BEGIN                       \
190 \                 BIT.B #$0C,&UCB0IFG     \ UCB0IFG(STP,STT) = 1 ?
191 \                 0<> IF                  \
192 \ \               ------------------------\
193 \ \               RX stop or restart      \ from master
194 \ \               ------------------------\
195 \                     SUB   T,W           \ W = Adr_end - Adr_start = length
196 \                     SUB.B #1,W          \ sub #1, because char +[W] is not sent
197 \                     MOV.B W,0(T)        \ store length in first byte of buffer output
198 \                     BIC.B #UCTR,&UCB0CTLW0  \ reset UCTR R/W bit for next START
199 \                     RETI                \
200 \                 THEN                    \
201 \                 BIT.B #$02,&UCB0IFG     \ UCB0IFG(TX0) = 1 ?
202 \             0<> UNTIL                   \
203 \         AGAIN                           \
204 \   ------------------------------------\
205 \   End of slave transmit datas variants\
206 \   ------------------------------------\
207 THEN                                    \ if bad I2C address
208 \ --------------------------------------\
209 \ BAD I2C ADDRESS CaseOf                \
210 \ --------------------------------------\
211 \ insert here post BAD I2C address code \
212 \ ...that can loop back to I2CS_TX...   \
213 \ --------------------------------------\
214 ENDASM
215     \
216
217 \ --------------------------------------\
218 CODE START                              \ init I2C_slave 
219 \ --------------------------------------\
220 \ UCB0CTLW0 = %0000 0111 1100 0001     $640
221 \                    -                  UCMST = 0 : I2C_Slave
222 \                     --                UCMODE = %11 = I2C
223 \                       _               USYNC=1 (always 1)
224 \                         --            UCSSEL=SMCLK (don't care in slave mode)
225 \                           -           UCTXACK=0 not auto ACK slave address
226 \                            -          UCTR=0 : RX (for RX address)
227 \                                 -     UCSWRST=1
228 \ UCB0CTLW1 = %0000 0000 1101 0000     $642
229 \                       -               UCETXINT=0 : UCTXIFG0 set address match UCxI2COAx and TX mode
230 \                         --            UCCLTO=%11 : SCL low time out = 34 ms
231 \                            -          UCSWACK=1 : UCTXACK must be written to continue
232 \ UCB0RXBUF                             $64C
233 \ UCB0TXBUF                             $64E
234 \ UCB0I2COA0                            $654 must be written ? enabled ?
235 \ UCB0ADDRX                             $65C
236 \ UCB0ADDMSK                            $65E
237 \ UCB0IE    = %0000 0000 0000 0100     $66A
238 \                               -       UCSTTIE : StartCond Interrupt only
239 \ UCB0IFG                               $66C
240 \ UCB0IV                                $66E : write it to clear all IFG 
241
242 \ ------------------------------\
243 \ init I2C_slave                \
244 MOV #1,&UCB0CTLW0               \ set eUSCI_B in reset state, clear UCB0IE & UCB0IFG all flags
245 BIS #$07A0,&UCB0CTLW0           \
246 BIS #$10,&UCB0CTLW1             \ set software ack address (UCSWACK=1)
247 MOV #%1010,&UCB0I2COA0          \ set my own address
248 BIS #$0400,&UCB0I2COA0          \ UCOAEN=1 enable UCB0I2COA0 with address slave
249 \ MOV #0,&UCB0ADDMSK              \ enable address mask for all addresses i.e. software address 
250 BIC #1,&UCB0CTLW0               \ activate eUSCI_B (UCSWACK release)
251 MOV #4,&UCB0IE                  \ enable StartCond interrupt; WARNING ! UCB0IE must be set only after UCSWACK release !
252 \ ------------------------------\
253 \ init interrupt vectors
254 MOV #I2C_S,&$FFEE               \ eUSCIB0 interrupt vector
255 \ ------------------------------\
256 \ init PORTA (P2:P1) (complement)
257 \ notice : UCB0 I2C driver seems to control only DIR register !!!
258 BIC.B #S_BUS,&I2CS_REN          \ SDA + SCL pullup/down disable
259 BIC.B #S_BUS,&I2CS_OUT          \ OUT0 : preset output low
260 BIS.B #S_BUS,&I2CS_SEL1         \ enable I2C I/O
261 \ ------------------------------\
262 COLON
263 ." I2C_Slave is running. Type STOP to quit"
264 LIT recurse is WARM         \ insert this routine between COLD and WARM...
265 (WARM) ;                    \ ...and continue with WARM
266     \
267
268 : STOP                  \ stops multitasking, must to be used before downloading app
269     ['] (WARM) IS WARM  \ remove START app from FORTH init process
270     ECHO COLD           \ reset CPU, interrupt vectors, and start FORTH
271 ;
272     \
273
274 ECHO
275 PWR_HERE
276 \ START