OSDN Git Service

Merge branch 'master' of https://gitlab.com/Jean-Michel/FastForthForMSP430fr5xxx
[fast-forth/master.git] / MSP430-FORTH / UARTI2CS.f
1 \ -*- coding: utf-8 -*-
2 \
3 \ TARGET SELECTION ( = the name of \INC\target.pat file without extension)
4 \ MSP_EXP430FR5739  MSP_EXP430FR5969    MSP_EXP430FR5994    MSP_EXP430FR6989
5 \ MSP_EXP430FR4133 (can't use LED1 because wired on UART TX)
6 \ MSP_EXP430FR2433  CHIPSTICK_FR2433    MSP_EXP430FR2355
7 \ LP_MSP430FR2476   MY_MSP430FR5738_1
8 \
9 \ from scite editor : copy your target selection in (shift+F8) parameter 1:
10 \
11 \ OR
12 \
13 \ drag and drop this file onto SendSourceFileToTarget.bat
14 \ then select your TARGET when asked.
15 \
16 \
17 \ FastForth kernel compilation minimal options:
18 \ TERMINAL3WIRES, TERMINAL4WIRES
19 \ MSP430ASSEMBLER, CONDCOMP
20 \
21 \ ================================================================================
22 \ REGISTERS USAGE for embedded MSP430 ASSEMBLER
23 \ ================================================================================
24 \ don't use R2, R3,
25 \ R4, R5, R6, R7 must be PUSHed/POPed before/after use
26 \ scratch registers S to Y are free,
27 \ under interrupt, IP is free,
28 \ Apply FORTH rules for TOS, PSP, RSP registers use.
29 \
30 \ PUSHM order : PSP,TOS, IP, S , T , W , X , Y ,rDOVAR,rDOCON,rDODOES,rDOCOL, R3, SR,RSP, PC
31 \ PUSHM order : R15,R14,R13,R12,R11,R10, R9, R8,  R7  ,  R6  ,  R5   ,  R4  , R3, R2, R1, R0
32 \
33 \ example : PUSHM #6,IP pushes IP,S,T,W,X,Y registers to return stack
34 \
35 \ POPM  order :  PC,RSP, SR, R3, rDODOES,rDOCON,rDOVAR,rEXIT,  Y,  X,  W,  T,  S, IP,TOS,PSP
36 \ POPM  order :  R0, R1, R2, R3,   R4   ,  R5  ,  R6  ,  R7 , R8, R9,R10,R11,R12,R13,R14,R15
37 \
38 \ example : POPM #6,IP   pop Y,X,W,T,S,IP registers from return stack
39 \
40 \ ASSEMBLER conditionnal usage before IF UNTIL WHILE : S< S>= U< U>= 0= 0<> 0>=
41 \ ASSEMBLER conditionnal usage before          ?GOTO : S< S>= U< U>= 0= 0<> 0< 
42 \
43 \ ================================================================================
44 \ coupled to a PL2303HXD/TA cable, this driver enables a FastForth target to act as USB to I2C_Slave bridge,
45 \ thus, from TERATERM.exe you can take the entire control of up to 112 I2C_FastForth targets.
46 \ In addition, it simulates a full duplex communication while the I2C bus is only half duplex.
47 \ Don't forget to wire 3k3 pull up resistors on wires SDA SCL!
48 \ ================================================================================
49
50 \ driver test : MCLK=24MHz, PL2303HXD with shortened cable (20cm), WIFI off, all windows apps closed else Scite and TERATERM.
51 \ -----------
52 \                                                                                               /         ┌────────────────────────────────┐
53 \     notebook                                  USB to I2C_Slave bridge                        +-- I2C -->|  up to 112 I2C_Slave targets   |
54 \ ┌───────────────┐          ╔════════════════════════════════════════════════════════════╗   /         ┌───────────────────────────────┐  |
55 \ |               |          ║   PL2303HXD                target running UARTI2CS @ 24MHz ║  +-- I2C -->|    MSP430FR4133 @ 1 MHz       |  |
56 \ |               |          ║───────────────┐           ┌────────────────────────────────║ /        ┌───────────────────────────────┐  |──┘
57 \ |               |          ║               |  3 wires  |    MSP430FR2355 @ 24MHz        ║/         |    MSP430FR5738 @ 24 MHz      |  |
58 \ |   TERATERM   -o--> USB --o--> USB2UART --o--> UART --o--> FAST FORTH ---> UARTI2CS  --o--> I2C --o--> FAST FORTH with option     |──┘
59 \ |   terminal    |          ║               |   6 MBds  |                  (I2C MASTER)  ║          |    TERMINAL_I2C (I2C SLAVE)   | 
60 \ |               |          ║───────────────┘           └────────────────────────────────║          └───────────────────────────────┘
61 \ |               |          ║               |<- l=20cm->|                                ║ 
62 \ └───────────────┘          ╚════════════════════════════════════════════════════════════╝              
63 \
64 \ test results :
65 \ ------------
66 \
67 \ downloading (+ interpret + compile + execute) CORETEST.4TH to I2C Master target = 1016ms.
68 \ downloading (+ interpret + compile + execute) CORETEST.4TH to I2C Slave target = 1422ms.
69 \ the difference (406 ms) is the time of the I2C Half duplex exchange.
70 \ [(45906 chars * 9 bits) + (1533 * 31)] / 0,406 = 1,135 MHz (9 bits / char + (2*START + 2*STOP + 2*addr + CTRL_Char) / line) 
71 \ ==> 113 % of I2C Fast-mode Plus (Fm+)!
72
73 \ also connected to and tested with another I2C_FastForth target with MCLK = 1MHz (I2C CLK = MCLK ! ).
74 \
75 \ The I2C_Slave address is defined as 'MYSLAVEADR' in forthMSP430FR.asm source file of I2C_Slave target.
76 \ You can use any pin for SDA and SCL, preferably in the interval Px0...Px3.  
77 \ you will find SCA and SCL pin by searching 'SM_BUS' in your \inc\target.pat files (I2C_Master and I2C_Slave)
78 \ don't forget to add 3.3k pullup resitors on wires SDA and SCL.
79 \
80 \
81 \ the LEDs TX and RX work fine, comment/uncomment as you want.
82 \
83 \ Multi Master Mode works but is not tested in the real word.
84 \
85 \ how it works
86 \ ------------
87 \
88 \ 1- the I2C bus is Master to Slave oriented, the Slave does not decide anything.
89 \    This order of things allows in any case to establish the connection.
90 \    The I2C Master device is therefore placed on the control TERMINAL side and the FastForth target on the I2C Slave side.
91 \    But once the link is established, we have to find a trick to reverse the roles, 
92 \    so that the slave can take control of the data exchange.
93 \
94 \ 2- The I2C bus operates on half duplex. 
95 \    Another trick will be to simulate an I2C_Master TERMINAL in Full Duplex mode.
96 \
97 \ Solution: The slave "slavishly" sends control characters to the master,
98 \ and since this one obeys a bigger man than himself: the programmer..,
99 \ he makes it his "masterly" duty to obey the slave.
100 \
101 \ To take control of the master, the slave emits 1 of 6+1 CTRL-Char:
102 \   CTRL-Char $00 sent by ACCEPT (before falling asleep with SLEEP),
103 \   CTRL-Char $01 sent by KEY: request to send a single character entered on TERMINAL,
104 \   CTRL-Char $02 sent by ABORT": request to abort the file being downloaded if any,
105 \                                followed by a START RX for ABORT" message,
106 \   CTRL-Char $03 sent by WARM, to do a reSTART RX for WARM message,
107 \   CTRL-Char $04 sent by NOECHO, to switch the UART to half-duplex mode,
108 \   CTRL-Char $05 sent by ECHO, to switch the UART to full duplex mode.
109 \
110 \   Finally, if the master receives a $FF as data, he considers the link broken,
111 \   it performs ABORT which forces a START RX on a loop.
112 \
113 \ Once the slave sends the CTRL_Char $00, he falls asleep, 
114 \ On receipt of this CTRL_Char, the master also falls asleep, awaiting a UART RX interruption.
115 \ As long as the TERMINAL is silent, the master and the slave remain in SLEEP mode,
116 \ (a part the Tx0_INT interrupt every 1/2 s).
117 \ SLEEP mode is LPM0 for the master (UART does not work if LPMx > LPM0), LPM4 for the slave.
118 \
119 \ interruptions
120 \ -------------
121 \ Since the slave can't wake up the master with a dedicated interrupt, the master must generate one
122 \ cyclically to listen to the slave.
123 \ HALF_S_INT is used to generate a 1/2 second interrupt, obviously taken into account only when the master goes to sleep.
124 \ It performs a (re)START RX that enables the I2C link to be re-established following a RESET performed on I2C_Slave.
125 \
126 \ This interruption also allows the UARTI2CS program to exit when Teraterm sends a BREAK (Alt-B).
127 \
128 \ the other interruption U2I_TERM_INT is used to communicate with TERMINAL, by replacing of the TERM_INT one.
129 \
130 \  Software              +----------------------------------+       Hardware
131 \  I2C Master            |       +-------------------+      |       I2C Slave
132 \                        |       |                   |      |   
133 \ UART to I2C bridge    SCL     SDA   connected to: SDA    SCL    I2CFastForth target 
134 \ -------------------   ----    ----                ----   ----   ------------------  
135 \ MSP_EXP430FR5739      P4.1    P4.0                P1.6   P1.7   MSP_EXP430FR5739    
136 \ MSP_EXP430FR5969      P1.3    P1.2                P1.6   P1.7   MSP_EXP430FR5969          
137 \ MSP_EXP430FR5994      P8.1    P8.2                P7.0   P7.1   MSP_EXP430FR5994       
138 \ MSP_EXP430FR6989      P1.5    P1.3                P1.6   P1.7   MSP_EXP430FR6989    
139 \ MSP_EXP430FR4133      P8.3    P8.2                P5.2   P5.3   MSP_EXP430FR4133    
140 \ CHIPSTICK_FR2433      P2.2    P2.0                P1.2   P1.3   CHIPSTICK_FR2433       
141 \ MSP_EXP430FR2433      P3.1    P3.2                P1.2   P1.3   MSP_EXP430FR2433       
142 \ MSP_EXP430FR2355      P3.3    P3.2                P1.2   P1.3   MSP_EXP430FR2355    
143 \ LP_MSP430FR2476       P3.3    P3.2                P4.4   P4.3   LP_MSP430FR2476     
144 \
145 \ don't forget to link 3V3 and GND on each side and to add 3k3 pullup resistors on SDA and SCL.
146
147 ; ----------------------------------------------------------------------
148 ; UARTI2CS.f (Software I2C Master)
149 ; ----------------------------------------------------------------------
150
151 \ first, we do some tests before downloading application
152 CODE ABORT_UARTI2CS
153 SUB #4,PSP
154 MOV TOS,2(PSP)
155 MOV &KERNEL_ADDON,TOS
156 BIT #$7800,TOS
157 0<> IF MOV #0,TOS THEN  \ if TOS <> 0 (UART TERMINAL), set TOS = 0
158 MOV TOS,0(PSP)
159 MOV &VERSION,TOS
160 SUB #308,TOS            \ FastForth V3.8
161 COLON
162 $0D EMIT            \ return to column 1 without CR
163 ABORT" FastForth V3.8 please!"
164 ABORT" <-- Ouch! unexpected I2C_FastForth target!"
165 PWR_STATE           \ remove the ABORT_UARTI2CS definition before continuing the download.
166 ;
167
168 ABORT_UARTI2CS      \ abort test
169
170 [DEFINED] {UARTI2CS} [IF] {UARTI2CS} [THEN] \ remove {UARTI2CS} if already defined
171
172 MARKER {UARTI2CS}   \ {UARTI2CS}+8 = RET_ADR by default
173 8 ALLOT             \ {UARTI2CS}+10 <-- previous INI_APP
174 \                     {UARTI2CS}+12 <-- previous TERM_VEC
175 \                     {UARTI2CS}+14 <-- previous Tx0_x_VEC
176 \                     {UARTI2CS}+16 <-- Half_Duplex flag : 0=ECHO, <>0=NOECHO
177
178 [UNDEFINED] CONSTANT [IF]
179 \ https://forth-standard.org/standard/core/CONSTANT
180 \ CONSTANT <name>     n --                      define a Forth CONSTANT 
181 : CONSTANT 
182 CREATE
183 HI2LO
184 MOV TOS,-2(W)           \   PFA = n
185 MOV @PSP+,TOS
186 MOV @RSP+,IP
187 MOV @IP+,PC
188 ENDCODE
189 [THEN]
190
191 I2CSLA0 CONSTANT I2CS_ADR       \ I2CSLA0=$FFA2
192
193 \ note: HDNCODE definitions are HiDdeN and cannot be executed from TERMINAL
194 \---------------------------\
195 HDNCODE I2CSTOP                 \ sends a STOP on I2C_BUS
196 \---------------------------\     _
197 BIS.B #SM_SCL,&I2CSM_DIR    \ 3 h  v_   force SCL as output (low)
198 NOP3                        \ 3 l _
199 BIS.B #SM_SDA,&I2CSM_DIR    \ 3 l  v_   SDA as output ==> SDA low
200 NOP3                        \ 3 l   _
201 BIC.B #SM_SCL,&I2CSM_DIR    \ 3 l _^    release SCL (high)
202 NOP3                        \ 3 h   _
203 BIC.B #SM_SDA,&I2CSM_DIR    \ 3 h _^    relase SDA (high) when SCL is high = STOP
204 MOV @RSP+,PC                \
205 ENDCODE                      \
206 \---------------------------\
207
208 \---------------------------\
209 HDNCODE STOP_U2I                \ STOP_APP subroutine, the next of TERATERM(ALT+B)|SW2+RST|SYS_failures
210 \ --------------------------\ UARTI2CS can't be stopped by any other means.
211 BW1                         \ <-- I2C_MASTER_RX <-- TERATERM break (Alt+B)
212 CMP #RET_ADR,&{UARTI2CS}+8  \
213 0<> IF                      \ run STOP_U2I once, only if MARKER_DOES is already initialized
214 \    \ ----------------------\
215 \    BIC.B #LED2,&LED2_DIR   \ set RX green led OFF
216 \    BIC.B #LED2,&LED2_OUT   \ set RX green led OFF
217 \    BIC.B #LED1,&LED1_DIR   \ set TX red led OFF
218 \    BIC.B #LED1,&LED1_OUT   \ set TX red led OFF
219 \    \ ----------------------\
220     CALL #I2CSTOP           \ stop properly I2C_BUS
221     MOV #SM_BUS,W           \
222     BIC.B W,&I2CSM_DIR      \ restore I2C_BUS I/O as input 
223     BIS.B W,&I2CSM_OUT      \         with pull up resistors
224     BIS.B W,&I2CSM_REN      \
225 \    MOV #0,&TA0CTL          \ stop timer and clear its interrupt flags IE, IFG
226     MOV #0,&TB0CTL          \ stop timer and clear its interrupt flags IE, IFG
227 \ --------------------------\
228     MOV #{UARTI2CS}+10,W    \ W = addr of first saved param after MARKER_DOES
229     MOV #RET_ADR,-2(W)      \ don't forget: restore default MARKER_DOES call address !
230     MOV @W+,&WARM+2         \ restore previous (default) INI_APP address
231     MOV @W+,&TERM_VEC       \ restore previous (default) TERM_VEC value
232 \    MOV @W+,&TA0_X_VEC      \ restore previous (default) TB0_x_VEC value
233     MOV @W+,&TB0_X_VEC      \ restore previous (default) TB0_x_VEC value
234     MOV #1,TOS              \ to identify Alt+B|SW2+RST request in WARM message
235 THEN                        \
236 \ --------------------------\ when STOP_U2I is the next of:  TERATERM(ALT+B)|SW2+RESET|SYS_failures
237 MOV @RSP+,PC                \                       RET to:        WARM_BODY|WARM_BODY|WARM_BODY
238 ENDCODE                      \
239 \ --------------------------\
240
241
242 \ \ vvvvvvvMulti-Master-Modevvvvvv\
243 \ HDNCODE DO_IDLE                     \ 
244 \ MOV #4,W                        \ 1   wait bus idle time = 5 µs @ 16 MHz
245 \ BEGIN
246 \     BIT.B #SM_SCL,&I2CSM_IN     \ 3 
247 \     0= IF                       \ 2
248 \         MOV #4,W                \ 1 if SCL is LOW
249 \     THEN
250 \         BIT.B #SM_SDA,&I2CSM_IN \ 3
251 \     0= IF                       \ 2
252 \         MOV #4,W                \ 1 if SDA is LOW
253 \     THEN
254 \     SUB #1,W                    \ 1
255 \ 0= UNTIL                        \ 2
256 \ MOV @RSP+,PC
257 \ ENDCODE
258 \ \ ^^^^^^^Multi-Master-Mode^^^^^^\
259
260 \ **************************************\
261 HDNCODE U2I_TERM_INT                        \ UART RX interrupt starts on first char of each line sent by TERMINAL
262 \ **************************************\
263 ADD #4,RSP                              \ 1 remove unused PC_RET and SR_RET
264 \ --------------------------------------\
265 MOV &{UARTI2CS}+16,W                      \ 3 W = HALF_DUPLEX = 0 if ECHO, -1 if NOECHO
266 MOV #PAD_ORG,T                          \ 2 T = buffer pointer for UART_TERMINAL input
267 MOV #$0D,S                              \ 2 S = 'CR' = penultimate char of line to be RXed by UART
268 BEGIN                                   \
269     MOV.B &TERM_RXBUF,Y                 \ 3 move char from TERM_RXBUF...
270     ADD #1,T                            \ 1
271     MOV.B Y,-1(T)                       \ 3 ... to input buffer
272     CMP.B Y,S                           \ 1 char = CR ? (if yes goto next REPEAT)
273 0<> WHILE                               \ 2 if <>
274     CMP #0,W                            \ 1 HALF_DUPLEX = 0 ?
275     0= IF                               \ 2 yes, echo is ON
276         BEGIN                           \   )
277             BIT #2,&TERM_IFG            \ 3 > Test TX_Buf empty, mandatory for low baudrates
278         0<> UNTIL                       \ 2 )
279         MOV.B Y,&TERM_TXBUF             \ 3 echo char to UART_TERMINAL
280     THEN                                \
281     BEGIN                               \ 
282         BIT #1,&TERM_IFG                \ 3 wait for next char received
283     0<> UNTIL                           \ 2 
284 REPEAT                                  \ 2 31 cycles loop ==> up to UART 2.58 Mbds @ 8MHz
285 CALL #UART_RXOFF                        \ stops UART RX still char CR is received, the LF char is being transmitted.
286 BEGIN                                   \
287     BIT #1,&TERM_IFG                    \ 3 char LF received ?
288 0<> UNTIL                               \ 2
289 \ --------------------------------------\
290 BW2                                     \   <=== Ctrl_char $01 (KEY input)
291 \ --------------------------------------\
292 MOV.B &TERM_RXBUF,S                     \ 3 S = last char RXed by UART (LF|KEY)
293 MOV.B S,0(T)                            \ 4 store it into buffer
294 \ ======================================\
295 \ ======================================\
296 \ I2C MASTER TX                         \ now we transmit UART RX buffer (PAD) to I2C_Slave, S = LF|KEY = last char to transmit
297 \ ======================================\
298 \ ======================================\          
299 BW3                                     \   <=== multi master TX
300 \ --------------------------------------\
301 \ BIS.B #LED1,&LED1_DIR                   \ red led ON = I2C TX 
302 \ BIS.B #LED1,&LED1_OUT                   \ red led ON = I2C TX
303 \ --------------------------------------\
304 \ I2C_Master_TX_Start                   \ here, SDA and SCL must be in idle state
305 \ --------------------------------------\     _
306 BIS.B   #SM_SDA,&I2CSM_DIR              \ 3 l  v_ force SDA low when SCL is high = START
307 MOV.B   &I2CS_ADR,X                     \ 3 h     X = Slave_Address
308 MOV     #PAD_ORG,Y                      \ 2 h     Y = buffer pointer for I2C_Master TX
309 NOP3                                    \ 3 h _
310 BIS.B   #SM_SCL,&I2CSM_DIR              \ 3 h  v_ force SCL as output (low)
311 \ --------------------------------------\
312 BEGIN
313 \   ------------------------------------\
314 \   I2C_Master_TX address/Data          \
315 \   ------------------------------------\
316     MOV.B #8,W                          \ 1 l       prepare 8 bits address
317     BEGIN                               \
318         ADD.B X,X                       \ 1 l       shift one left
319         U>= IF                          \ 2 l       carry set ?
320             BIC.B #SM_SDA,&I2CSM_DIR    \ 3 l       yes : SDA as input  ==> SDA high because pull up resistor
321         ELSE                            \ 2 l
322             BIS.B #SM_SDA,&I2CSM_DIR    \ 3 l       no  : SDA as output ==> SDA low
323         THEN                            \   l   _
324         BIC.B #SM_SCL,&I2CSM_DIR        \ 3 l _^    release SCL (high)
325         BEGIN                           \           we must wait I2C_Slave software
326             BIT.B #SM_SCL,&I2CSM_IN     \ 3 h       by testing SCL released
327         0<> UNTIL                       \ 2 h       (because Slave can strech SCL low)
328 \ \       vvvvvvvvMulti-Master-Modevvvvvvv\
329 \         BIT.B #SM_SDA,&I2CSM_IN         \ 3 h       test SDA
330 \ \       ^^^^^^^^Multi-Master-Mode^^^^^^^\   _
331         BIS.B #SM_SCL,&I2CSM_DIR        \ 3 h  v_   SCL as output : force SCL low
332 \ \       vvvvvvvvvvvvMulti-Master-Modevvvvvvvvvvv\
333 \         0= IF                                   \ 2 l   SDA input low
334 \             BIT.B #SM_SDA,&I2CSM_DIR            \ 3 l + SDA command high
335 \             0= IF                               \ 2 l = collision detected
336 \                 BIS.B #SM_SCL,&I2CSM_DIR        \ 4 l release SCL first
337 \                 CALL #DO_IDLE                   \     wait stable idle state 
338 \                 GOTO BW3                        \ 2 l goto START TX
339 \             THEN                                \
340 \         THEN                                    \
341 \ \       ^^^^^^^^^^^^Multi-Master-Mode^^^^^^^^^^^\
342         SUB #1,W                        \ 1 l       bits count-1
343     0= UNTIL                            \ 2 l
344 \   ------------------------------------\
345     BIC.B #SM_SDA,&I2CSM_DIR            \ 3 l       after TX byte we must release SDA to read Ack/Nack from Slave
346 \   ------------------------------------\
347 \   I2C_Master_TX get Slave Ack/Nack    \
348 \   ------------------------------------\       _
349     BIC.B #SM_SCL,&I2CSM_DIR            \ 3 l _^    release SCL (high)
350 \    BEGIN                               \
351 \        BIT.B #SM_SCL,&I2CSM_IN         \ 3 h      testing SCL released is useless
352 \    0<> UNTIL                           \ 2 h      because no risk of Slave streching SCL low
353     NOP3                                \ 3 h       replaced by NOP3.
354     BIT.B #SM_SDA,&I2CSM_IN             \ 3 h _     get SDA state
355     BIS.B #SM_SCL,&I2CSM_DIR            \ 3 h  v_   SCL as output : force SCL low, to keep I2C_BUS until next I2C_MASTER START (RX|TX)
356 \   ------------------------------------\
357 0= WHILE \ 1- Slave Ack received        \ 2 l       out of loop if Nack (goto THEN next REPEAT) 
358 \   ------------------------------------\           
359 \   I2C_Master_TX_data_loop             \
360 \   ------------------------------------\
361     CMP S,T                             \ 1         last char TXed = last char RXed ? (when address is sent, T = 16bits <> S = 8bits)
362 \   ------------------------------------\
363 0<> WHILE \ 2- TXed char <> last char   \ 2         out of loop if TXed char T = last char S to be TXed (goto below REPEAT)
364 \   ------------------------------------\
365     MOV.B @Y+,X                         \ 2 l       get next RXed char
366     MOV X,T                             \ 1         T = last TX char for comparaison above, on next loop.
367 REPEAT                                  \           <-- WHILE2  search "Extended control-flow patterns"... 
368 THEN                                    \           <-- WHILE1  ...in https://forth-standard.org/standard/rationale
369 \ \ --------------------------------------\
370 \     BIC.B #LED1,&LED1_DIR               \   red led OFF = endof I2C TX 
371 \     BIC.B #LED1,&LED1_OUT               \   red led OFF = endof I2C TX
372 \ \ --------------------------------------\
373 GOTO FW1                                \   X > 4 ==> reSTART RX repeated every 1/2s 
374 \ ======================================\
375 \ END OF I2C MASTER TX                  \ SCL is kept low until START RX  --┐
376 \ ======================================\                                   |
377 ENDCODE                                 \                                   |
378 \ **************************************\                                   v
379
380
381 \ **************************************\
382 HDNCODE HALF_S_INT                          \ wakes up every 1/2s to listen I2C Slave or break from TERMINAL.
383 \ **************************************\
384 ADD #4,RSP                              \ 1 remove PC_RET and SR_RET        |
385 \ --------------------------------------\                                   |
386 FW1                                     \ <-- the next of TERM_INT above <--┘
387 BW3                                     \ <-- the next of INI_U2I below  <--┐
388 \ --------------------------------------\                                   |
389 CMP #0,&KERNEL_ADDON                    \ 3 KERNEL_ADDON(BIT15) = LF XTAL flag
390 0>= IF                                  \ if no LF XTAL
391 \  MOV #%0001_0101_0110,&TA0CTL          \ 3 (re)starts RX_timer,ACLK=VLO=8kHz,/2=4096Hz,up mode,clear timer,enable TA0 int, clear IFG
392   MOV #%0001_0101_0110,&TB0CTL          \ 3 (re)starts RX_timer,ACLK=VLO=8kHz,/2=4096Hz,up mode,clear timer,enable TB0 int, clear IFG
393 ELSE                                    \ if LF XTAL
394 \  MOV #%0001_1101_0110,&TA0CTL          \ 3 (re)starts RX_timer,ACLK=LFXTAL=32768,/8=4096Hz,up mode,clear timer,enable TA0 int, clear IFG
395   MOV #%0001_1101_0110,&TB0CTL          \ 3 (re)starts RX_timer,ACLK=LFXTAL=32738,/8=4096Hz,up mode,clear timer,enable TB0 int, clear IFG
396 THEN                                    \
397 \ ======================================\
398 \ I2C_MASTER RX                         \ le driver I2C_Master envoie START RX en boucle continue (X < 4) ou discontinue (X >= 4).
399 \ ======================================\ le test d'un break en provenance de l'UART est intégré dans cette boucle.
400 BEGIN \   I2C MASTER START RX           \ ABORT|WARM loop back
401 \   ------------------------------------\       _
402     BIC.B #SM_SCL,&I2CSM_DIR            \ 3 l _^    release SCL to enable ReSTART RX
403     BIT #8,&TERM_STATW                  \ 3         break (Alt+B) sent by TERATERM ?
404     0<> ?GOTO BW1                       \           goto STOP_U2I, exit to WARM+4.
405 \   ------------------------------------\
406 \   I2C_Master_RX_Start_Cond            \   here, SDA and SCL must be in idle state
407 \   ------------------------------------\     _
408     BIS.B   #SM_SDA,&I2CSM_DIR          \ 3 l  v_   force SDA as output (low)
409     MOV.B   &I2CS_ADR,Y                 \ 3 h       X = Slave_Address
410     BIS.B   #1,Y                        \ 1 h       set Master RX
411     NOP2                                \ 2   _
412     BIS.B   #SM_SCL,&I2CSM_DIR          \ 3 h  v_   force SCL as output (low)
413 \   ------------------------------------\
414 \   I2C_Master_RX_Send_address          \           may be SCL is held low by slave
415 \   ------------------------------------\
416     MOV.B #8,W                          \ 1 l       prepare 8 bits address
417     BEGIN                               \
418         ADD.B Y,Y                       \ 1 l       shift one left
419         U>= IF                          \ 2 l       carry set ?
420            BIC.B #SM_SDA,&I2CSM_DIR     \ 3 l yes : SDA as input  ==> SDA high because pull up resistor
421         ELSE                            \ 2 l
422            BIS.B #SM_SDA,&I2CSM_DIR     \ 3 l no  : SDA as output ==> SDA low
423         THEN                            \       _
424         BIC.B #SM_SCL,&I2CSM_DIR        \ 3 l _^    release SCL (high)
425 \        BEGIN                           \
426 \            BIT.B #SM_SCL,&I2CSM_IN     \ 3 h      testing SCL released is useless
427 \        0<> UNTIL                       \ 2 h      because no risk of Slave streching SCL low
428         NOP3                            \ 3         replaced by NOP3
429 \ \       vvvvvvMulti-Master-Modevvvvvvvvv\
430 \         BIT.B #SM_SDA,&I2CSM_IN         \ 3 h     test SDA
431 \ \       ^^^^^^Multi-Master-Mode^^^^^^^^^\   _
432         BIS.B #SM_SCL,&I2CSM_DIR        \ 3 h  v_  force SCL as output (low)
433 \ \       vvvvvvvvvvvvMulti-Master-Modevvvvvvvvvvv\
434 \         0= IF                                   \ 2 l   SDA input low
435 \             BIT.B #SM_SDA,&I2CSM_DIR            \ 3 l + SDA command high
436 \             0= IF                               \ 2 l = collision detected
437 \                 BIS.B #SM_SCL,&I2CSM_DIR        \ 4 l release SCL first
438 \                 CALL #DO_IDLE                   \     wait stable idle state 
439 \                 GOTO BW3                        \ 2 l goto START RX
440 \             THEN                                \
441 \         THEN                                    \
442 \ \       ^^^^^^^^^^^^Multi-Master-Mode^^^^^^^^^^^\
443         SUB #1,W                        \ 1 l       bits count - 1
444     0= UNTIL                            \ 2 l
445 \   ------------------------------------\
446 \   Wait Ack/Nack on address            \           
447 \   ------------------------------------\       _
448     BIC.B   #SM_SDA,&I2CSM_DIR          \ 3 l _^_   after TX address we must release SDA to read Ack/Nack from Slave
449     BIC.B   #SM_SCL,&I2CSM_DIR          \ 3 l _^    release SCL (high)
450     BEGIN                               \           we must wait I2C_Slave software
451         BIT.B #SM_SCL,&I2CSM_IN         \ 3 h       by testing SCL released
452     0<> UNTIL                           \ 2 h       (because Slave can strech SCL low)
453     BIT.B   #SM_SDA,&I2CSM_IN           \ 3 h _     get SDA
454     BIS.B   #SM_SCL,&I2CSM_DIR          \ 3 h  v_   SCL as output : force SCL low
455 \   ------------------------------------\  
456     0<> IF   \ Nack_On_Address          \ 2 l
457 \       --------------------------------\  
458 \       I2C_Master Send STOP            \
459 \       --------------------------------\
460         CALL #I2CSTOP                   \
461         MOV #SLEEP,PC                   \ 4         goto dodo for 1/2 s .. wake up by HALF_S_INT
462     THEN                                \ 2
463 \   ------------------------------------\
464 \   I2C_Master_RX_data                  \
465 \ \   ------------------------------------\
466 \     BIS.B #LED2,&LED2_DIR               \           green led ON = I2C RX
467 \     BIS.B #LED2,&LED2_OUT               \           green led ON = I2C RX
468 \ \   ------------------------------------\
469     BEGIN
470         BEGIN
471             BIC.B #SM_SDA,&I2CSM_DIR    \ 4 l       after Ack and before RX next byte, we must release SDA
472             MOV.B #8,W                  \ 1 l       prepare 8 bits transaction
473 \           ----------------------------\
474             BEGIN                       \
475 \              -------------------------\       _
476 \              do SCL pulse             \ SCL _| |_
477 \              -------------------------\       _
478                BIC.B #SM_SCL,&I2CSM_DIR \ 3 l _^    release SCL (high)
479 \               BEGIN                   \
480 \               BIT.B #SM_SCL,&I2CSM_IN \ 3 h       testing SCL released is useless
481 \               0<> UNTIL               \ 2 h       because no risk of Slave streching SCL low
482                NOP3                     \ 3         replaced by NOP3 
483                BIT.B #SM_SDA,&I2CSM_IN  \ 3 h _     get SDA
484                BIS.B #SM_SCL,&I2CSM_DIR \ 3 h  v_   SCL as output : force SCL low   13~
485                ADDC.B X,X               \ 1 l       C <--- X(7) ... X(0) <--- SDA
486                SUB #1,W                 \ 1 l       count down of bits
487             0= UNTIL                    \ 2 l       here, slave releases SDA
488 \           ----------------------------\
489 \           case of RX data $FF         \
490 \           ----------------------------\
491             CMP.B #-1,X                 \ 1
492             0= IF                       \ 2         received char $FF: let's consider that the slave is lost...
493                 MOV #2,X                \           to do ABORT action
494             THEN                        \
495 \           ----------------------------\
496             CMP.B #8,X                  \ 1 l       $08 = char BS
497         U>= WHILE                       \ 2 l       ASCII char received, from char 'BS' up to char $7F.
498 \           ----------------------------\
499             BEGIN                       \
500                 BIT #2,&TERM_IFG        \ 3 l       UART TX buffer empty ?
501             0<> UNTIL                   \ 2 l       loop if no
502 \           ----------------------------\   
503             BIS.B #SM_SDA,&I2CSM_DIR    \ 3 l       prepare Ack
504 \           ----------------------------\
505 \           I2C_Master_RX Send Ack      \           on ASCII char >= $08
506 \           ----------------------------\       _   
507             BIC.B #SM_SCL,&I2CSM_DIR    \ 3 l _^    release SCL (high)
508             BEGIN                       \           we must wait I2C_Slave software
509                 BIT.B #SM_SCL,&I2CSM_IN \ 3 h       by testing SCL released
510             0<> UNTIL                   \ 2 h       (because Slave can strech SCL low)
511 \           ----------------------------\
512             MOV.B X,&TERM_TXBUF         \ 3 h       send RXed ASCII char to UART TERMINAL
513 \           ----------------------------\     _
514             BIS.B #SM_SCL,&I2CSM_DIR    \ 3 h  v_   SCL as output : force SCL low
515         REPEAT                          \ 2 l       loop back to I2C_Master_RX_data for chars >= 8
516 \       --------------------------------\
517 \       case of RX CTRL_Chars < $08     \           here Master holds SCL low, Slave can test it: CMP #8,&TERM_STATW
518 \       --------------------------------\           see forthMSP430FR_TERM_I2C.asm
519         CMP.B #4,X                      \ 1         
520         U>= IF                          \ 2
521             MOV #0,&{UARTI2CS}+16         \           preset ECHO
522             0= IF                       \ 2
523                 MOV #-1,&{UARTI2CS}+16    \ 3         set NOECHO if char $04
524             THEN
525             BIS.B #SM_SDA,&I2CSM_DIR    \ 3 l       prepare Ack for Ctrl_Chars $04 $05
526         THEN
527 \       --------------------------------\
528 \       Master_RX send Ack/Nack on data \           Ack for $04, $05, Nack for $00, $01, $02, $03
529 \       --------------------------------\       _   
530         BIC.B #SM_SCL,&I2CSM_DIR        \ 3 l _^    release SCL (high)
531         BEGIN                           \           we must wait I2C_Slave software
532             BIT.B #SM_SCL,&I2CSM_IN     \ 3 h       by testing SCL released
533         0<> UNTIL                       \ 2 h       (because Slave can strech SCL low)
534         BIT.B #SM_SDA,&I2CSM_IN         \ 3 h _     get SDA as TX Ack/Nack state
535         BIS.B #SM_SCL,&I2CSM_DIR        \ 3 h  v_   SCL as output : force SCL low
536 \       --------------------------------\   l    
537     0<> UNTIL                           \           if Ack, loop back to Master_RX data for CTRL_Char $04,$05
538 \   ------------------------------------\   
539 \   Nack is sent by Master              \   l       case of CTRL-Char {$00|$01|$02|$03}
540 \   ------------------------------------\   
541     CMP.B #2,X                          \           $02 = ctrl_char for ABORT request
542 U>= WHILE                               \           $03 = Ctrl_Char for WARM request
543 \   ------------------------------------\   
544 \   CTRL_Char $02|$03                   \   l       if ABORT|WARM requests, SDA is high, SCL is low
545 \   ------------------------------------\
546     0= IF                               \           if ABORT request:
547         MOV #0,&{UARTI2CS}+16             \               set echo ON I2C_Master side
548         CALL #UART_RXON                 \               resume UART downloading source file
549         BEGIN                           \   
550             BIC #UCRXIFG,&TERM_IFG      \               clear UCRXIFG
551             MOV &FREQ_KHZ,Y             \               1000, 2000, 4000, 8000, 16000, 240000
552             BEGIN MOV #32,W             \           2~        <-------+ windows 10 seems very slow...
553                 BEGIN SUB #1,W          \           1~        <---+   | ==> ((32*3)+5)*1000 = 101ms delay
554                 0= UNTIL                \           2~ 3~ loop ---+   | to refill its USB buffer
555                 SUB #1,Y                \           1~                |
556             0= UNTIL                    \           2~ 101~ loop -----+
557 \           BEGIN MOV #65,W             \                  <-------+ linux with minicom seems very very slow...
558 \               BEGIN SUB #1,W          \                  <---+   | ==> ((65*3)+5)*1000 = 200ms delay
559 \               0= UNTIL                \           3~ loop ---+   | to refill its USB buffer
560 \               SUB #1,Y                \                          |
561 \           0= UNTIL                    \           200~ loop -----+
562             BIT #UCRXIFG,&TERM_IFG      \               4 new char in TERMRXBUF during this delay ?
563         0= UNTIL                        \               2 yes, the input stream may be still active: loop back
564     THEN                                \
565 REPEAT                                  \   l       loop back to reSTART RX
566 \ --------------------------------------\
567 \ I2C_Master_RX Send STOP               \   l       remainder: CTRL_Chars $00,$01
568 \ --------------------------------------\ 
569 CALL #I2CSTOP                           \
570 \ \ --------------------------------------\
571 \ BIC.B #LED2,&LED2_DIR                   \ green led OFF = endof I2C RX
572 \ BIC.B #LED2,&LED2_OUT                   \ green led OFF = endof I2C RX
573 \ ======================================\
574 \ END OF I2C MASTER RX                  \   here I2C_bus is freed, Nack on Ctrl_char $FF|$00|$01 remains to be processed.
575 \ ======================================\
576 \ I2C_Slave KEY ctl_char $01            \ I2C_Slave request for KEY input
577 \ --------------------------------------\
578 CMP.B #1,X                              \
579 \ Quand I2C_Master reçoit ce caractère de contrôle,
580 \ il attend un caractère en provenance de TERMINAL UART
581 \ et une fois ce caractère reçu reSTART TX pour l'envoyer à I2C_Slave
582 0= IF                                   \
583     MOV #PAD_ORG,T                      \ ready to store KEY char: MOV.B S,0(T)
584     CALL #UART_RXON                     \ enables TERMINAL to TX; use no registers
585     BEGIN                               \ wait for a char
586         BIT #UCRXIFG,&TERM_IFG          \ received char ?
587     0<> UNTIL                           \ 
588     CALL #UART_RXOFF                    \ stops UART RX then
589     GOTO BW2                            \ goto end of UART RX line input, for receiving last char
590 THEN                                    \                             
591 \ --------------------------------------\
592 \ I2C_Slave ACCEPT ctrl_char $00        \ I2C_Slave requests I2C_Master to stop RX and start TX
593 \ --------------------------------------\
594 \ en début de sa routine ACCEPT, I2C_Slave envoie sur le bus I2C le caractère de contrôle $00
595 \ avant de s'endormir avec SLEEP
596 \ I2C_Master envoie NACK + STOP pour signifier la fin de la transaction.
597 \ --------------------------------------\
598 \ et si I2C_Slave est sorti de son sommeil par un START RX, idem.
599 \ --------------------------------------\
600 MOV #SLEEP,PC                           \ executes RXON (that enables TERMINAL to TX) before LPM0 shut down.
601 \ --------------------------------------\
602 \ I2C_Master se réveillera au premier caractère saisi sur le TERMINAL ==> TERM_INT,
603 \ ou en fin du temps TxIFG ==> HALF_S_INT\
604 ENDCODE                                 \ 
605 \ **************************************\
606
607 \---------------------------\
608 HDNCODE INI_U2I             \ define INI_HARD_APP subroutine called by WARM
609 \ --------------------------\
610 CALL &{UARTI2CS}+10         \ previous INI_APP executing init TERM_UC, activates I/O and sets TOS = RSTIV_MEM.
611 \ --------------------------\ TOS = SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures 
612 CMP #$0E,TOS                \ SVSHIFG SVSH event ?
613 0<> IF                      \ if not
614     CMP #$0A,TOS            \   RSTIV_MEM >= violation memory protected areas ?
615     U>= ?GOTO BW1           \   execute STOP_U2I then RET to BODY of WARM
616 THEN                        \ RSTIV_MEM = {$00,$02,$04,$6,$0E} as: {WARM,PWR_ON,RST,COLD,SVSH_Threshold}
617 BIT.B #SW2,&SW2_IN          \ SW2 pressed ?
618 0= ?GOTO BW1                \   if yes execute STOP_U2I then RET to BODY of WARM
619 MOV #0,&RSTIV_MEM           \ clear RSTIV_MEM before next RST event!
620 \ --------------------------\ 
621 \ init HALF_S_INT           \ used to scan I2C_Slave hard RESET and to slow (re)START RX loop
622 \ --------------------------\ 
623 MOV #$800,&TB0CCR0          \ time = (2047+1)/4096 = 0.5s
624 \ MOV #$800,&TA0CCR0        \ time = (2047+1)/4096 = 0.5s
625 \ --------------------------\ 
626 \ init I2C_MASTER I/O       \ see \inc\your_target.pat to find I2C MASTER SDA & SCL pins (as SM_BUS)
627 \ --------------------------\ 
628 BIC.B #SM_BUS,&I2CSM_REN    \ remove internal pullup resistors to avoid pulling down resistors with next instruction:
629 BIC.B #SM_BUS,&I2CSM_OUT    \ preset SDA + SCL output LOW
630 \ --------------------------\ 
631 GOTO BW3                    \ goto I2C_Master START RX loop, with no other return than ALT+B|SW2+RST 
632 \ --------------------------\
633 ENDCODE                     \
634 \ --------------------------\
635 \
636 \ ==============================================================
637 \ Driver UART to I2CM which does the bridge USB to I2C_FastForth
638 \ ==============================================================
639
640 \ I2C address mini = 10h, maxi = 0EEh (I2C-bus specification and user manual V6)
641 \ type on TERMINAL "16 UARTI2CS" to link teraterm TERMINAL with FastForth I2C_Slave at address $10
642 \ you can also link with last known I2C_Slave address : "I2CS_ADR @ UARTI2CS"
643 \
644 : UARTI2CS                          \ I2C_Slave_Address_%0 --
645 CR I2CS_ADR !                       \ --        save I2C_Slave_Address_%0
646 HI2LO
647 CMP #RET_ADR,&{UARTI2CS}+8          \
648 0= IF                               \ save parameters only if MARKER_DOES is not initialized
649     MOV #STOP_U2I,&{UARTI2CS}+8     \ MARKER_DOES of {UARTI2CS} will do CALL &{UARTI2CS}+8 = CALL #STOP_U2I
650     MOV &WARM+2,&{UARTI2CS}+10      \ save previous INI_APP from WARM PFA to {UARTI2CS}+10
651     MOV &TERM_VEC,&{UARTI2CS}+12    \ save previous TERM_VEC value to {UARTI2CS}+12, see target.pat
652     MOV &TB0_X_VEC,&{UARTI2CS}+14   \ save previous TB0_X_VEC value to {UARTI2CS}+14
653     \ MOV &TA0_X_VEC,&{UARTI2CS}+14   \ save previous TA0_X_VEC value to {UARTI2CS}+14
654     MOV #0,&{UARTI2CS}+16           \ reset Half_Duplex variable (set ECHO ON)
655     MOV #INI_U2I,&WARM+2            \ replace INI_APP by new INI_U2I
656     MOV #U2I_TERM_INT,&TERM_VEC     \ set TERM_VEC with U2I_TERM_INT
657     MOV #HALF_S_INT,&TB0_X_VEC      \ set TB0_X_VEC with HALF_S_INT
658     \ MOV #HALF_S_INT,&TA0_X_VEC      \ set TA0_X_VEC with HALF_S_INT
659 THEN
660 MOV #WARM,PC                        \ execute INI_U2I then goto BW3; abort with Alt-B or SW2+RST.
661 ENDCODE           
662
663 RST_HERE ECHO
664 18 UARTI2CS     ; TERATERM(Alt-B) or I2C_Master(SW2+RST) to quit