OSDN Git Service

V3.5 ABORT messages display I2C address
[fast-forth/master.git] / MSP430-FORTH / UARTI2CS.f
1 \ -*- coding: utf-8 -*-
2
3 \ Fast Forth For Texas Instrument MSP430FRxxxx FRAM devices
4 \ Copyright (C) <2019>  <J.M. THOORENS>
5 \
6 \ This program is free software: you can redistribute it and/or modify
7 \ it under the terms of the GNU General Public License as published by
8 \ the Free Software Foundation, either version 3 of the License, or
9 \ (at your option) any later version.
10 \
11 \ This program is distributed in the hope that it will be useful,
12 \ but WITHOUT ANY WARRANTY\ without even the implied warranty of
13 \ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 \ GNU General Public License for more details.
15 \
16 \ You should have received a copy of the GNU General Public License
17 \ along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 \
19 ; ----------------------------------------------------------------------
20 ; UARTI2CS.f
21 ; ----------------------------------------------------------------------
22 \
23 \ TARGET SELECTION
24 \ LP_MSP430FR2476
25 \ MSP_EXP430FR5739  MSP_EXP430FR5969    MSP_EXP430FR5994    MSP_EXP430FR6989
26 \ MSP_EXP430FR4133 (can't use LED1 because wired on UART TX)
27 \ MSP_EXP430FR2433  MSP_EXP430FR2355    CHIPSTICK_FR2433
28 \
29 \ software I2C MASTER, you can use any pin for SDA and SCL,
30 \ Preferably use a couple of pins in the interval Px0...Px3.
31 \ don't forget to wire 3.3k pullup resitors on pin SDA and SCL.
32 \
33 \ FastForth kernel compilation minimal options:
34 \ TERMINAL3WIRES, TERMINAL4WIRES
35 \ MSP430ASSEMBLER, CONDCOMP
36 \
37 \ driver test @ speed maxi: MCLK=24MHz
38 \ ------------------------------------
39 \
40 \     notebook                                  USB to I2C_Slave bridge                                     any I2C_slave
41 \ +---------------+          +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +            +-------------------------------+
42 \ |               |          i    CP2102                  master running UARTI2CS @ 24MHz i           +-------------------------------+|
43 \ |               |          +---------------+           +--------------------------------+          +-------------------------------+||
44 \ |               |          |               |           |                                |RX:1.15MHz|                               |||
45 \ |   TERATERM   -o--> USB --o--> USB2UART --o--> UART --o--> FAST FORTH ---> UARTI2CS  --o--> I2C --o--> FAST FORTH @ 24MHz with    ||+
46 \ |   terminal    |          |               | 2457600Bds|                                |TX:692kHz |   kernel option TERMINAL_I2C  |+
47 \ |               |          +---------------+           +--------------------------------+          +-------------------------------+
48 \ |               |          i                                                            i 
49 \ +---------------+          +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
50 \
51 \ I2C frequency = (TX+RX)/2 = 921 kHz @ MCLK=24 MHz, without any error...
52 \ downloading (+ interpret + compile + execute) CORETEST.4TH to I2C Slave in 859ms.
53 \ downloading (+ interpret + compile + execute) CORETEST.4TH to I2C Master in 625ms.
54 \
55 \
56 \ the LEDs TX and RX work fine, uncomment if you want.
57 \
58 \ Multi Master Mode works but is not tested in the real word.
59 \
60 \ les arcanes du bus I2C
61 \ ----------------------
62 \
63 \ le bus I2C est orienté Maître vers esclave, l'esclave ne décide de rien.
64 \ Cet ordre des choses permet en tout cas d'établir la liaison.
65 \ On placera donc le maître du côté du TERMINAL de commande et la cible FastForth côté esclave.
66 \ Mais une fois que la liaison est établie il faut trouver une astuce pour renverser
67 \ les rôles, pour que l'esclave puisse prendre le contrôle de la liaison.
68 \
69 \ Pour ce faire l'esclave envoie "servilement" des caractères de contrôle au maître,
70 \ et comme celui-ci obéit à un plus grand que lui, le programmeur,
71 \ il se fait un devoir "magistral" d'obéir à l'esclave.
72 \
73 \ Pour prendre le contrôle du maître, l'esclave émet donc 1 parmi 6 CTRL-Char:
74 \   CTRL-Char $00 envoyé par ACCEPT (1ère partie, avant SLEEP),
75 \   CTRL-Char $01 envoyé par KEY: demande d'envoi d'un caractère unique saisi sur TERMINAL,
76 \   CTRL-Char $02 envoyé par ABORT: demande d'abandon du fichier en cours de transmission le cas échéant,
77 \                                   suivi de la réception du message envoyé par ABORT,
78 \   CTRL-Char $03 envoyé par COLD, pour que le maître relance la connexion I2C,
79 \   CTRL-Char $04 envoyé par NOECHO, pour qu'il passe l'UART en mode half duplex,
80 \   CTRL-Char $05 envoyé par ECHO, pour qu'il rétablisse l'UART en mode full duplex.
81 \
82 \ Enfin, si le maître reçoit un caractère $FF, il considère que la liaison est coupée,
83 \ il envoie le reste du fichier en cours de téléchargment à la poubelle, quitte le driver
84 \ puis exécute COLD.
85 \
86 \ Une fois que l'esclave a envoyé le CTRL_Char $00, il s'endort, 
87 \ à la reception de ce CTRL_Char, le maître s'endort aussi, dans l'attente d'une nouvelle entrée TERMINAL.
88 \ Tant que le TERMINAL n'envoie pas de nouvelle ligne, le maître et l'esclave sont en mode SLEEP,
89 \ LPM0 pour le maître, LPM4 pour l'esclave.
90 \
91 \
92 \ le timer TB0 sert à générer une interruption 1/2 seconde
93 \ pour détecter un hard RESET effectué sur I2C_Slave, quand I2C_Master fait dodo, 
94 \ ainsi que pour effectuer le couplage dans la boucle I2C_Master RX: 
95 \   si X U>= 4 (I2C_WARM state) ==> un START RX à chaque 1/2s, 
96 \   si X U< 4 (I2C_Slave COLD|RESET|ABORT) ==> continuous repeated START RX 
97
98 PWR_STATE
99
100 [DEFINED] {FF_I2C} [IF]  {FF_I2C} [THEN]
101
102 MARKER {FF_I2C}
103
104 [UNDEFINED] @ [IF]
105 \ https://forth-standard.org/standard/core/Fetch
106 \ @     c-addr -- char   fetch char from memory
107 CODE @
108 MOV @TOS,TOS
109 MOV @IP+,PC
110 ENDCODE
111 [THEN]
112
113 [UNDEFINED] CONSTANT [IF]
114 \ https://forth-standard.org/standard/core/CONSTANT
115 \ CONSTANT <name>     n --                      define a Forth CONSTANT 
116 : CONSTANT 
117 CREATE
118 HI2LO
119 MOV TOS,-2(W)           \   PFA = n
120 MOV @PSP+,TOS
121 MOV @RSP+,IP
122 MOV @IP+,PC
123 ENDCODE
124 [THEN]
125
126 $1820 CONSTANT SLAVE_ADR    \ CONSTANT = I2C_Slave address in FRAM
127
128 ASM QUIT_I2C                    \ as ASM word, QUIT_I2C is hidden.
129 \ ------------------------------\
130 BW1                             \
131 \ \ ------------------------------\
132 \     BIC.B #LED2,&LED2_DIR       \ RX green led OFF
133 \     BIC.B #LED2,&LED2_OUT       \ RX green led OFF
134 \     BIC.B #LED1,&LED1_DIR       \ TX red led OFF
135 \     BIC.B #LED1,&LED1_OUT       \ TX red led OFF
136 \ \ ------------------------------\
137     BIS.B #SM_BUS,&I2CSM_REN    \ reset I/O as reset state
138     BIC.B #SM_BUS,&I2CSM_DIR    \
139     BIS.B #SM_BUS,&I2CSM_OUT    \
140     MOV #$5A88,&WDTCTL          \ stop WDT
141     BIC #1,&SFRIE1              \ disable WDT int
142     MOV #COLD,&WDT_VEC          \ restore default WDT_VEC value
143 \    MOV #0,&TA0CTL              \ stop timer
144 \    MOV #COLD,&TA0_x_VEC        \ restore default TA0_x_VEC value
145     MOV #0,&TB0CTL              \ stop timer
146     MOV #COLD,&TB0_x_VEC        \ restore default TB0_x_VEC value
147     MOV &TERMINAL_INT,&TERM_VEC \ restore default TERM_VEC value
148     MOV #WARM,X                 \ X = CFA of WARM
149     ADD #4,X                    \ X = BODY of WARM
150     MOV X,-2(X)                 \ restore default WARM: BODY of WARM --> PFA of WARM
151     MOV #COLD,PC                \ explicit return with COLD
152 ENDASM
153
154 ASM WDT_INT                     \ to enable Alt+B when I2C_Master is sleeping
155 BIT #8,&TERM_STATW              \ UART break sent by TERATERM ?
156 0<> IF
157     ADD #4,RSP                  \ remove RETI
158     GOTO BW1                    \
159 THEN
160 RETI
161 ENDASM
162
163 \ \ vvvvvvvMulti-Master-Modevvvvvv\
164 \ ASM DO_IDLE                     \ 
165 \ MOV #4,W                        \ 1   wait bus idle time = 5 µs @ 16 MHz
166 \ BEGIN
167 \     BIT #8,&TERM_STATW          \ 3         break sent by TERATERM ?
168 \     0<> IF                      \ 2
169 \         ADD #2,RSP              \           remove RET
170 \         MOV #QUIT_I2C,PC        \           STOP I2C
171 \     THEN  
172 \     BIT.B #SM_SCL,&I2CSM_IN     \ 3 
173 \     0= IF                       \ 2
174 \         MOV #4,W                \ 1 if SCL is LOW
175 \     THEN
176 \         BIT.B #SM_SDA,&I2CSM_IN \ 3
177 \     0= IF                       \ 2
178 \         MOV #4,W                \ 1 if SDA is LOW
179 \     THEN
180 \     SUB #1,W                    \ 1
181 \ 0= UNTIL                        \ 2
182 \ MOV @RSP+,PC
183 \ ENDASM
184 \ \ ^^^^^^^Multi-Master-Mode^^^^^^\
185
186 \ wake up from LPM0 is longer with MSP30FR2xxx devices than with MSP430FR5xxx devices:
187 \ @ MCLK = 24MHz baudrate max = 5MBds with FR2xxx, and 6MBds with FR57xx. 
188 \ **************************************\
189 ASM TERM_INT                            \  interrupt starts on RX first char of line input from TERMINAL
190 \ **************************************\
191 ADD #4,RSP                              \ 1 remove RET and SR
192 \ --------------------------------------\
193 MOV &PAD_I2CADR,W                       \ 3 W = 0 if ECHO, 1 if NOECHO
194 MOV #PAD_ORG,T                          \ 2 T = input buffer for I2C_Master TX
195 MOV #$0D,S                              \ 2 S = last char to be RXed by UART
196 BEGIN                                   \
197     MOV.B &TERM_RXBUF,Y                 \ 3 move char from TERM_RXBUF...
198     ADD #1,T                            \ 1
199     MOV.B Y,-1(T)                       \ 3 ... to buffer (PAD)
200     CMP.B Y,S                           \ 1 char = CR ?
201 0<> WHILE                               \ 2 28 cycles loop ==> up to 2.96 Mbds @ 8MHz
202     CMP #0,W                            \ 1
203     0= IF                               \ 2 if echo ON requested by I2C_Slave
204         BEGIN                           \   )
205             BIT #2,&TERM_IFG            \ 3 > mandatory for low baudrates
206         0<> UNTIL                       \ 2 )
207         MOV.B Y,&TERM_TXBUF             \ 3
208     THEN
209     BEGIN                               \
210         BIT #1,&TERM_IFG                \ 3 received char ?
211     0<> UNTIL                           \ 2 
212 REPEAT                                  \ 2
213 CALL &RXOFF                             \ stops UART RX still char CR is received and before receiving char LF
214 BEGIN                                   \
215     BIT #1,&TERM_IFG                    \ 3 char $0A received ?
216 0<> UNTIL                               \ 2 RX_int flag is cleared
217 \ --------------------------------------\
218 BW1                                     \   <=== Ctrl_char $01 (KEY input)
219 \ --------------------------------------\
220 MOV.B &TERM_RXBUF,S                     \ 3 S = last char (LF|KEY) ...
221 MOV.B S,0(T)                            \ 4 store it into buffer
222 \ ======================================\
223 \ ======================================\ S = last char to be transmitted
224 \ I2C MASTER TX                         \ T = last char transmitted
225 \ ======================================\ W = bits count
226 \ ======================================\ X = I2C_Address / I2C_Data           
227 \ \ --------------------------------------\
228 \ BIS.B #LED1,&LED1_DIR                   \ red led ON = I2C TX 
229 \ BIS.B #LED1,&LED1_OUT                   \ red led ON = I2C TX
230 \ \ --------------------------------------\
231 \ \     vvvvvvvvvvMulti-Master-Modevvvvvvv\
232 BW2 \ I2C_Master TX Start               \ here, SDA and SCL must be in idle state
233 \ \     ^^^^^^^^^^Multi-Master-Mode^^^^^^^\
234 BIS.B   #SM_SDA,&I2CSM_DIR              \ 3 l   force SDA as output (low)
235 MOV.B   &SLAVE_ADR,X                    \ 3 h   X = Slave_Address
236 MOV     #PAD_ORG,Y                      \ 2 h   Y = input buffer for I2C_Master TX
237 NOP3                                    \ 3 h
238 BIS.B   #SM_SCL,&I2CSM_DIR              \ 3 h   force SCL as output (low)
239 \ --------------------------------------\
240 BEGIN
241 \   ------------------------------------\
242 \   I2C Master TX address/Data          \
243 \   ------------------------------------\
244     MOV.B #8,W                          \ 1 l       prepare 8 bits address
245     BEGIN                               \
246         ADD.B X,X                       \ 1 l       shift one left
247         U>= IF                          \ 2 l       carry set ?
248             BIC.B #SM_SDA,&I2CSM_DIR    \ 3 l       yes : SDA as input  ==> SDA high because pull up resistor
249         ELSE                            \ 2 l
250             BIS.B #SM_SDA,&I2CSM_DIR    \ 3 l       no  : SDA as output ==> SDA low
251             NOP2                        \ 2
252         THEN                            \   l   _
253         BIC.B #SM_SCL,&I2CSM_DIR        \ 3 l _^    release SCL (high)
254         BEGIN                           \           9~h/16~l
255             BIT.B #SM_SCL,&I2CSM_IN     \ 3 h       test if SCL is released (Slave RX addr/data)
256         0<> UNTIL                       \ 2 h 
257 \ \       vvvvvvvvMulti-Master-Modevvvvvvv\
258 \         BIT.B #SM_SDA,&I2CSM_IN         \ 3 h _     test SDA
259 \ \       ^^^^^^^^Multi-Master-Mode^^^^^^^\
260         BIS.B #SM_SCL,&I2CSM_DIR        \ 3 h  v_   SCL as output : force SCL low
261 \ \       vvvvvvvvvvvvMulti-Master-Modevvvvvvvvvvv\
262 \         0= IF                                   \ 2 l
263 \             BIT.B #SM_SDA,&I2CSM_DIR            \ 3 l
264 \             0= IF                               \ 2 l
265 \ \               --------------------------------\
266 \ \               collision detected              \   l collision if SDA(IN)=0 AND SDA(DIR)=0
267 \ \               --------------------------------\
268 \                 BIS.B #SM_SCL,&I2CSM_DIR        \ 4 l release SCL first
269 \                 CALL #DO_IDLE                   \     wait stable idle state 
270 \                 GOTO BW2                        \ 2 l goto START TX
271 \             THEN                                \
272 \         THEN                                    \
273 \ \       ^^^^^^^^^^^^Multi-Master-Mode^^^^^^^^^^^\
274         SUB #1,W                        \ 1 l       bits count-1
275     0= UNTIL                            \ 2 l
276 \   ------------------------------------\
277     BIC.B #SM_SDA,&I2CSM_DIR            \ 3 l       after TX byte we must release SDA to read Ack/Nack from Slave
278 \   ------------------------------------\
279 \   I2C Master get Slave Ack/Nack       \
280 \   ------------------------------------\       _
281     BIC.B #SM_SCL,&I2CSM_DIR            \ 3 l _^    release SCL (high)
282 \    BEGIN                               \
283 \        BIT.B #SM_SCL,&I2CSM_IN         \ 3 h      useless test if SCL is released (SLAVE ACK Addr/data) 
284 \    0<> UNTIL                           \ 2 h
285     NOP3                                \ 3 h
286     BIT.B #SM_SDA,&I2CSM_IN             \ 3 h _     get SDA state
287     BIS.B #SM_SCL,&I2CSM_DIR            \ 3 h  v_   SCL as output : force SCL low
288 \   ------------------------------------\
289 0= WHILE \ 1 Slave Ack received         \ 2 l       goto THEN; out of loop if Nack
290 \   ------------------------------------\           
291 \   I2C_Master_TX_data_loop             \
292 \   ------------------------------------\
293     CMP S,T                             \ 1         T = S = last char to transmit ? after address is sent, T = PAD_ORG <> S = any char
294 0<> WHILE \ 2                           \ 2         out of loop if yes
295     MOV.B @Y+,X                         \ 2 l       get next byte to TX
296     MOV X,T                             \ 1         T = last char TX for comparaison above
297 REPEAT                                  \           <-- WHILE2  search "Extended control-flow patterns"... 
298 THEN                                    \           <-- WHILE1  ...in https://forth-standard.org/standard/rationale
299 \   ------------------------------------\
300 \ Nack or Ack on last char              \           Nack = I2C_Slave request or I2C_Slave RESET, Ack = last char has been TX
301 \   ------------------------------------\
302     NOP3                                \ 3 l   _   delay to reach I2C tLO
303     BIC.B #SM_SCL,&I2CSM_DIR            \ 3 l _^    release SCL to enable reSTART
304 \ \ --------------------------------------\
305 \     BIC.B #LED1,&LED1_DIR               \   red led OFF = endof I2C TX 
306 \     BIC.B #LED1,&LED1_OUT               \   red led OFF = endof I2C TX
307 \ \ --------------------------------------\
308 GOTO FW1                                \   X > 4 ==> continuous repeated START RX below
309 \ ======================================\
310 \ END OF I2C MASTER TX                  \
311 \ ======================================\
312 ENDASM
313
314 ASM RX_INT
315 \ **************************************\
316 \ I2C MASTER RX                         \
317 \ **************************************\
318 ADD #4,RSP                              \ 1 remove RET and SR
319 \ --------------------------------------\
320 FW1                                     \   from TERM_INT above
321 \ --------------------------------------\
322 BW3                                     \   from I2C_WARM below
323 \ --------------------------------------\
324 \ I2C_Master START RX                   \
325 \ --------------------------------------\
326 CMP #0,&KERNEL_ADDON                    \ 3
327 0>= IF                                  \ 2 if LF XTAL present
328     MOV #%0001_0101_0110,&TB0CTL        \ 3 (re)starts RX_timer,ACLK=VLO=8kHz,/2=4096Hz,up mode,clear timer,enable TB0 int, clear IFG
329 \    MOV #%0001_0101_0110,&TA0CTL        \ 3 (re)starts RX_timer,ACLK=VLO=8kHz,/2=4096Hz,up mode,clear timer,enable TB0 int, clear IFG
330 ELSE                                    \ 2
331     MOV #%0001_1101_0110,&TB0CTL        \ 3 (re)starts RX_timer,ACLK=LFXTAL=32738,/8=4096Hz,up mode,clear timer,enable TB0 int, clear IFG
332 \    MOV #%0001_1101_0110,&TA0CTL        \ 3 (re)starts RX_timer,ACLKLFXTAL=32768,/8=4096Hz,up mode,clear timer,enable TB0 int, clear IFG
333 THEN
334 \ --------------------------------------\
335 \ le driver I2C_Master envoie START RX en boucle continue (X < 4) ou discontinue (X >= 4).
336 \ le test d'un break en provenance de l'UART est intégré dans cette boucle.
337 \ --------------------------------------\
338 BEGIN                                   \   I2C MASTER RX
339 \ --------------------------------------\
340     BEGIN                               \   I2C MASTER START RX
341 \   ------------------------------------\
342         BIT #8,&TERM_STATW              \ 3 break sent by TERATERM ?
343         0<> IF                          \ 2
344             MOV #QUIT_I2C,PC            \ 2 STOP I2C
345         THEN
346 \       --------------------------------\
347 \       I2C_Master_Start_Cond           \   here, SDA and SCL must be in idle state
348 \       --------------------------------\
349         BIS.B   #SM_SDA,&I2CSM_DIR      \ 3 l       force SDA as output (low)
350         MOV.B   &SLAVE_ADR,Y            \ 3 h       X = Slave_Address
351         BIS.B   #1,Y                    \ 1 h       Master RX
352         NOP2                            \ 2
353         BIS.B   #SM_SCL,&I2CSM_DIR      \ 3 h       force SCL as output (low)
354 \       --------------------------------\
355 \       I2C_Master_Send_address         \           may be SCL is held low by slave
356 \       --------------------------------\
357         MOV.B   #8,W                    \ 1 l       prepare 8 bits address
358         BEGIN                           \
359             ADD.B Y,Y                   \ 1 l       shift one left
360             U>= IF                      \ 2 l       carry set ?
361                BIC.B #SM_SDA,&I2CSM_DIR \ 3 l yes : SDA as input  ==> SDA high because pull up resistor
362             ELSE                        \ 2 l
363                BIS.B #SM_SDA,&I2CSM_DIR \ 3 l no  : SDA as output ==> SDA low
364                NOP2                     \ 2 l
365             THEN                        \       _
366             BIC.B #SM_SCL,&I2CSM_DIR    \ 3 l _^    release SCL (high)
367 \            BEGIN                       \
368 \                BIT.B #SM_SCL,&I2CSM_IN \ 3 h       useless test if SCL is released (SLAVE RX Addr)
369 \            0<> UNTIL                   \ 2 h
370             NOP3                        \ 3
371 \ \           vvvvvvMulti-Master-Modevvvvv\
372 \             BIT.B #SM_SDA,&I2CSM_IN     \ 3 h _     test SDA
373 \ \           ^^^^^^Multi-Master-Mode^^^^^\
374             BIS.B #SM_SCL,&I2CSM_DIR     \ 3 h  v_   SCL as output : force SCL low
375 \ \           vvvvvvvvvvvvMulti-Master-Modevvvvvvvvvvv\
376 \             0= IF                                   \ 2 l
377 \                 BIT.B #SM_SDA,&I2CSM_DIR            \ 3 l
378 \                 0= IF                               \ 2 l
379 \ \               ------------------------------------\
380 \ \               collision detection                 \   l collision if SDA(IN)=0 AND SDA(DIR)=0
381 \ \               ------------------------------------\
382 \                     BIS.B #SM_SCL,&I2CSM_DIR        \ 4 l release SCL first
383 \                     CALL #DO_IDLE                   \     wait stable idle state 
384 \                     GOTO BW3                        \ 2 l goto START RX
385 \                 THEN                                \
386 \             THEN                                    \
387 \ \           ^^^^^^^^^^^^Multi-Master-Mode^^^^^^^^^^^\
388             SUB #1,W                    \ 1 l       bits count - 1
389         0= UNTIL                        \ 2 l
390 \       --------------------------------\
391 \       Wait Ack/Nack on address        \
392 \       --------------------------------\
393         BIC.B   #SM_SDA,&I2CSM_DIR      \ 3 l   _   after TX address we must release SDA to read Ack/Nack from Slave
394         BIC.B   #SM_SCL,&I2CSM_DIR      \ 3 l _^    release SCL (high)
395         BEGIN                           \
396             BIT.B #SM_SCL,&I2CSM_IN     \ 3 h       test if SCL is released (SLAVE TX ACK_ON_Addr)
397         0<> UNTIL                       \ 2 h
398         BIT.B   #SM_SDA,&I2CSM_IN       \ 3 h _     get SDA
399         BIS.B   #SM_SCL,&I2CSM_DIR      \ 3 h  v_   SCL as output : force SCL low
400 \       --------------------------------\  
401     0<> WHILE   \ Nack_On_Address       \ 2 l
402 \       --------------------------------\  
403         NOP3                            \ 3 l       delay to reach tLO
404 \       --------------------------------\
405 \       I2C_Master Send STOP            \           after Nack_On_Address
406 \       --------------------------------\     _
407         BIS.B #SM_SDA,&I2CSM_DIR        \ 3 l  v_   SDA as output ==> SDA low
408         NOP3                            \ 3 l   _
409         BIC.B #SM_SCL,&I2CSM_DIR        \ 3 l _^    release SCL (high)
410         NOP3                            \ 3 h
411         NOP3                            \ 3 h   _
412         BIC.B #SM_SDA,&I2CSM_DIR        \ 3 h _^    SDA as input  ==> SDA high with pull up resistor
413         CMP.B #4,X                      \           last CTRL_char <> ABORT ?
414         U>= IF                          \
415             MOV #SLEEP,PC               \ 4          if yes goto dodo
416         THEN
417     REPEAT                              \ 2
418 \ \   ------------------------------------\
419 \     BIS.B #LED2,&LED2_DIR               \           green led ON = I2C RX
420 \     BIS.B #LED2,&LED2_OUT               \           green led ON = I2C RX
421 \ \   ------------------------------------\
422 \   I2C_Master_RX_data                  \
423 \   ------------------------------------\
424     BEGIN
425         BEGIN
426             BIC.B #SM_SDA,&I2CSM_DIR    \ 4 l       after Ack and before RX next byte, we must release SDA
427             MOV.B #8,W                  \ 1 l       prepare 8 bits transaction
428 \           ----------------------------\
429             BEGIN                       \
430 \              -------------------------\       _
431 \              do SCL pulse             \ SCL _| |_
432 \              -------------------------\       _
433                BIC.B #SM_SCL,&I2CSM_DIR \ 3 l _^    release SCL (high)
434 \               BEGIN                   \           9/16~l
435 \               BIT.B #SM_SCL,&I2CSM_IN \ 3 h       useless test if SCL is released (SLAVE TX Data)
436 \               0<> UNTIL               \ 2 h
437                NOP3
438                BIT.B #SM_SDA,&I2CSM_IN  \ 3 h _     get SDA
439                BIS.B #SM_SCL,&I2CSM_DIR \ 3 h  v_   SCL as output : force SCL low   13~
440                ADDC.B X,X               \ 1 l       C <--- X(7) ... X(0) <--- SDA
441                SUB #1,W                 \ 1 l       count down of bits
442             0= UNTIL                    \ 2 l
443 \       --------------------------------\
444         CMP.B #-1,X                     \ 1
445         0= IF                           \ 2         received char $FF: let's consider that the slave is lost...
446             MOV #2,X                    \           to do ABORT action
447         THEN                            \
448 \       --------------------------------\
449             CMP.B #8,X                  \ 1 l       $08 = char BS
450         U>= WHILE                       \ 2 l       ASCII char received, from char 'BS' up to char $7F.
451 \           ----------------------------\
452             BEGIN                       \
453                 BIT #2,&TERM_IFG        \ 3 l       UART TX buffer empty ?
454             0<> UNTIL                   \ 2 l       loop if no
455 \           ----------------------------\   
456             BIS.B #SM_SDA,&I2CSM_DIR    \ 3 l       prepare Ack
457 \           ----------------------------\       _   
458             BIC.B #SM_SCL,&I2CSM_DIR    \ 3 l _^    release SCL (high)
459             BEGIN                       \
460                 BIT.B #SM_SCL,&I2CSM_IN \ 3 h       test if SCL is released (SLAVE RX Ack)
461             0<> UNTIL                   \ 2 h
462             MOV.B X,&TERM_TXBUF         \ 3 h _     send RX char to UART TERMINAL
463             BIS.B #SM_SCL,&I2CSM_DIR    \ 3 h  v_   SCL as output : force SCL low
464         REPEAT                          \ 2 l       loop back to I2C_Master_RX_data
465 \       --------------------------------\
466 \       case of Ctrl_char received      \           here Master holds SCL low, Slave can test it: CMP #8,&TERM_STATW
467 \       --------------------------------\ 
468         CMP.B #4,X                      \ 1         
469         U>= IF                          \ 2
470             0= IF                       \ 2
471                 MOV #1,&PAD_I2CADR      \ 3         set NOECHO if char $04
472             ELSE                        \ 
473                 MOV #0,&PAD_I2CADR      \           set ECHO if char >$04
474             THEN
475             BIS.B #SM_SDA,&I2CSM_DIR    \ 3 l       prepare Ack
476         THEN
477 \       --------------------------------\       _   
478         BIC.B #SM_SCL,&I2CSM_DIR        \ 3 l _^    release SCL (high)
479         BEGIN                           \
480             BIT.B #SM_SCL,&I2CSM_IN     \ 3 h       test if SCL is released (SLAVE RX Ack)
481         0<> UNTIL                       \ 2 h
482         BIT.B #SM_SDA,&I2CSM_IN         \ 3 h _     get SDA
483         BIS.B #SM_SCL,&I2CSM_DIR        \ 3 h  v_   SCL as output : force SCL low
484 \       --------------------------------\       
485     0<> UNTIL                           \ 2 l       until Nack sent by Master for CTRL-Char {$00|$01|$02|$03} 
486 \   ------------------------------------\   
487 \   Nack is sent by Master              \
488 \   ------------------------------------\   
489     CMP.B #2,X                          \ 1 l       $02 = ctrl_char for ABORT request
490 U>= WHILE                               \ 2 l
491 \   ------------------------------------\   
492 \   CTRL_Char $02|$03                   \           if ABORT|COLD requests
493 \   ------------------------------------\
494     0= IF                               \           if ctrl_char $02 = ABORT request
495         MOV #0,&PAD_I2CADR              \           set echo ON I2C_Master side (I use the useless address PAD_I2CADR)
496         CALL &RXON                      \           resume UART downloading source file
497         BEGIN                           \
498             BIC #UCRXIFG,&TERM_IFG      \           clear UCRXIFG
499             MOV &FREQ_KHZ,Y             \           1000, 2000, 4000, 8000, 16000, 240000
500             BEGIN MOV #32,W             \           2~        <-------+ windows 10 seems very slow...
501                 BEGIN SUB #1,W          \           1~        <---+   | ==> ((32*3)+5)*1000 = 101ms delay
502                 0= UNTIL                \           2~ 3~ loop ---+   | to refill its USB buffer
503                 SUB #1,Y                \           1~                |
504             0= UNTIL                    \           2~ 101~ loop -----+
505 \           BEGIN MOV #65,W             \                  <-------+ linux with minicom seems very very slow...
506 \               BEGIN SUB #1,W          \                  <---+   | ==> ((65*3)+5)*1000 = 200ms delay
507 \               0= UNTIL                \           3~ loop ---+   | to refill its USB buffer
508 \               SUB #1,Y                \                          |
509 \           0= UNTIL                    \           200~ loop -----+
510             BIT #UCRXIFG,&TERM_IFG      \           4 new char in TERMRXBUF during this delay ?
511         0= UNTIL                        \           2 yes, the input stream may be still active: loop back
512 \    ELSE                                \           do nothing if Ctrl_char = $03
513     THEN
514 REPEAT                                  \           loop back to I2C MASTER reSTART RX
515 \ --------------------------------------\   
516 \ CTRL_Char $00|$01                     \           if ACCEPT|KEY requests
517 \ --------------------------------------\
518 \ I2C_Master RX Send STOP               \
519 \ --------------------------------------\       
520 BIS.B #SM_SDA,&I2CSM_DIR                \ 3         before STOP, we must pull SDA low
521 \   ------------------------------------\       _
522 BIC.B #SM_SCL,&I2CSM_DIR                \ 3 l _^    release SCL (high)
523 NOP3                                    \ 3 h
524 MOV #PAD_ORG,T                          \ 2 h   _   ready to store KEY char: MOV.B S,0(T)
525 BIC.B #SM_SDA,&I2CSM_DIR                \ 3 h _^    SDA as input  ==> SDA high with pull up resistor
526 \ \ --------------------------------------\
527 \ BIC.B #LED2,&LED2_DIR                   \ 4 l green led OFF = endof I2C RX
528 \ BIC.B #LED2,&LED2_OUT                   \ 4 l green led OFF = endof I2C RX
529 \ \ --------------------------------------\
530 \ ======================================\
531 \ ======================================\
532 \ END OF I2C MASTER RX                  \   here I2C_bus is freed and Nack on Ctrl_char $FF|$00|$01 remains to be processed.
533 \ ======================================\
534 \ ======================================\
535 \ TERMINAL TX --> UART RX               \
536 \ --------------------------------------\
537 \ I2C_Slave KEY ctl_char $01            \ I2C_Slave request for KEY input
538 \ --------------------------------------\
539 CMP.B #1,X                              \ 1 l
540 \ Quand I2C_Master reçoit ce caractère de contrôle,
541 \ il attend un caractère en provenance de TERMINAL UART
542 \ et une fois ce caractère reçu reSTART TX pour l'envoyer à I2C_Slave
543 0= IF                                   \ 2 l
544     CALL &RXON                          \ 4 l  to enable UART RX; use no registers
545     BEGIN                               \   wait for a char or for a break
546         BIT #UCRXIFG,&TERM_IFG          \ 3 received char ?
547     0<> UNTIL                           \ 2 
548     CALL &RXOFF                         \ stops UART RX
549     GOTO BW1                            \ goto end of TERMINAL line input to RX KEY char to 0(T) with T = 
550 THEN                                    \                             
551 \ --------------------------------------\
552 \ I2C_Slave ACCEPT ctrl_char $00        \ I2C_Slave requests I2C_Master to stop RX and start TX
553 \ --------------------------------------\
554 \ en début de sa routine ACCEPT, I2C_Slave envoie sur le bus I2C le caractère de contrôle $00
555 \ avant de s'endormir avec SLEEP
556 \ I2C_Master envoie NACK + STOP pour signifier la fin de la transaction.
557 \ --------------------------------------\
558 \ et si I2C_Slave est sorti de son sommeil par un START RX, idem.
559 \ --------------------------------------\
560 MOV #SLEEP,PC                           \ execute RXON then goto dodo
561 \ --------------------------------------\
562 \ I2C_Master se réveillera au premier caractère saisi sur le TERMINAL ==> TERM_INT,
563 \ ou en fin du temps TxyCCR0 ==> RX_INT,
564 \ ou par un break opérateur ==> WDT_INT. 
565 ENDASM                                  \ 
566 \ --------------------------------------\
567
568
569 \ --------------------------------------\
570 ASM I2C_WARM                            \           replace default WARM
571 \ --------------------------------------\
572 CMP #4,&SAVE_SYSRSTIV                   \           hard RESET ?
573 0= IF                                   \           yes
574     BIT.B #SW2,&SW2_IN                  \           SW2 pressed ? ( SW2 <> SW1 = Deep RESET)
575     0= IF                               \           yes
576         MOV #QUIT_I2C,PC                \           quit I2C only if SW2+RESET
577     THEN                                \
578 THEN                                    \
579 CMP #$10,&SAVE_SYSRSTIV                 \
580 U>= IF                                  \           if SYS failure >= $10 then STOP I2C
581     MOV #QUIT_I2C,PC                    \
582 THEN                                    \
583 MOV #0,&SAVE_SYSRSTIV                   \           clear SAVE_SYSRSTIV after use
584 \ --------------------------------------\
585 \ init WDT_INT                          \
586 \ --------------------------------------\
587 MOV #%0101_1010_0101_1111,&WDTCTL       \           start Watchdog Timer : XDTPW, WDTSSEL=VLOCLK, WDTCNTCL=1, WDTIS=2^6 (8ms)
588 BIS #1,&SFRIE1                          \           enable WDT
589 MOV #WDT_INT,&WDT_VEC                   \           replace WDT_VEC default value (COLD) by WDT_INT
590 \ --------------------------------------\
591 \ init RX_INT                           \           used to scan I2C_Slave hard RESET during SLEEP and to slow START RX loop
592 \ --------------------------------------\
593 MOV #$800,&TB0CCR0                      \           be careful:  RX_Int time = (2047+1)/4096 = 0.5s must be >> COLD time !
594 MOV #RX_INT,&TB0_x_VEC                  \
595 \ MOV #$800,&TA0CCR0                      \           be careful:  RX_Int time = (2047+1)/4096 = 0.5s must be >> COLD time !
596 \ MOV #RX_INT,&TA0_x_VEC                  \
597 \ --------------------------------------\
598 \ init UART_INT                         \
599 \ --------------------------------------\
600 MOV #TERM_INT,&TERM_VEC                 \           replace TERM_VEC default value (TERMINAL_INT) by TERM_INT
601 \ --------------------------------------\
602 \ init I2C_MASTER I/O                   \           reset state: I2CSM_DIR(SM_BUS) = 0
603 \ --------------------------------------\
604 BIC.B #SM_BUS,&I2CSM_REN                \           remove internal pullup resistors because of external 3.3k pullup resistor
605 BIC.B #SM_BUS,&I2CSM_OUT                \           preset SDA + SCL output LOW
606 \ --------------------------------------\
607 \ activate I/O                          \           SYSRSTIV = $02 | $0E = POWER ON | SVSH threshold
608 \ --------------------------------------\
609 BIC #1,&PM5CTL0                         \           activate all previous I/O settings; if not activated, nothing works after reset !
610 \ --------------------------------------\
611 MOV.B #4,X                              \           to enable RX_INT sleep
612 GOTO BW3                                \           goto I2C_Master START RX loop
613 ENDASM
614
615 \ ================================================================================
616 \ Driver UART to I2CM: this FastForth launchpad becomes an USB to I2C_Slave bridge
617 \ ================================================================================
618 \ type on TERMINAL "$10 UARTI2CS" to link TERMINAL with FastForth I2C_Slave at address hex $10
619 \
620 : UARTI2CS              \ SlaveAddress --
621 CR                      \ to compensate the lack of one INTERPRET
622 HI2LO
623 MOV @RSP+,IP
624 MOV TOS,&SLAVE_ADR      \ save in FRAM
625 MOV @PSP+,TOS
626 MOV #WARM,X
627 MOV #I2C_WARM,2(X)      \ replace WARM by I2C_WARM, so POR falls down to I2C_WARM
628 MOV X,PC                \ execute I2C_WARM
629 ENDCODE
630
631 RST_HERE ECHO
632 #16 UARTI2CS    ; Alt-B (TERATERM) or S2+RESET (I2C_Master) to quit
633
634 ; Since there is no difference in behaviour whether the TERMINAL is connected to the Master
635 ; or bridged to any Slave, WARM is the convenient way to check which target is connected to,
636 ; because, as any ABORT message, WARM displays first the decimal I2C address if applicable:
637 WARM