2 ; -----------------------------------
4 ; -----------------------------------
6 \ to see kernel options, download FastForthSpecs.f
7 \ FastForth kernel options: MSP430ASSEMBLER, CONDCOMP, FREQUENCY = 8/16/24 MHz
10 \ MSP_EXP430FR5739 MSP_EXP430FR5969 MSP_EXP430FR5994 MSP_EXP430FR6989
14 \ R4 to R7 must be saved before use and restored after
15 \ scratch registers Y to S are free for use
16 \ under interrupt, IP is free for use
17 \ interrupts reset SR register !
19 \ PUSHM order : PSP,TOS, IP, S, T, W, X, Y, rEXIT,rDOVAR,rDOCON, rDODOES, R3, SR,RSP, PC
20 \ PUSHM order : R15,R14,R13,R12,R11,R10, R9, R8, R7 , R6 , R5 , R4 , R3, R2, R1, R0
22 \ example : PUSHM #6,IP pushes IP,S,T,W,X,Y registers to return stack
24 \ POPM order : PC,RSP, SR, R3, rDODOES,rDOCON,rDOVAR,rEXIT, Y, X, W, T, S, IP,TOS,PSP
25 \ POPM order : R0, R1, R2, R3, R4 , R5 , R6 , R7 , R8, R9,R10,R11,R12,R13,R14,R15
27 \ example : POPM #6,IP pop Y,X,W,T,S,IP registers from return stack
29 \ ASSEMBLER conditionnal usage after IF UNTIL WHILE : S< S>= U< U>= 0= 0<> 0>=
30 \ ASSEMBLER conditionnal usage before ?JMP ?GOTO : S< S>= U< U>= 0= 0<> 0<
32 \ FORTH conditionnal : 0= 0< = < > U<
34 \ display on a LCD 2x20 CHAR the code sent by an IR remote under philips RC5 protocol
35 \ target : any TI MSP-EXP430FRxxxx launchpad (FRAM)
36 \ LPM_MODE = LPM0 because use SMCLK for LCDVo
38 \ DEMO : driver for IR remote compatible with the PHILIPS RC5 protocol
39 \ plus : driver for 5V LCD 2x20 characters display with 4 bits data interface
40 \ without usage of an auxiliary 5V to feed the LCD_Vo
41 \ and without potentiometer to adjust the LCD contrast :
42 \ to adjust LCD contrast, just press S1 (-) or S2 (+)
43 \ LCDVo current consumption ~ 500 uA.
45 \ ===================================================================================
46 \ notice : adjust WDT_TIM_EX0,LCD_TIM_CTL,LCD_TIM_EX0 and 20_us to the target frequency if <> 8MHz !
47 \ ===================================================================================
50 \ layout : I/O are defined in the launchpad.pat file (don't work with ChipStick_FR2433)
52 \ GND <-------o---0V0----------> 1 LCD_Vss
53 \ VCC >-------|---3V6-----o----> 2 LCD_Vdd
60 \ TB0.2 >---||--o--^/\/\/v--o----> 3 LCD_Vo (= 0V6 without modulation)
61 \ -------------------------> 4 LCD_RW
62 \ -------------------------> 5 LCD_RW
63 \ -------------------------> 6 LCD_EN
64 \ <------------------------> 11 LCD_DB4
65 \ <------------------------> 12 LCD_DB5
66 \ <------------------------> 13 LCD_DB5
67 \ <------------------------> 14 LCD_DB7
69 \ <----- LCD contrast + <--- Sw1 <--- (finger) :-)
70 \ <----- LCD contrast - <--- Sw2 <--- (finger) :-)
72 \ rc5 <--- OUT IR_Receiver (1 TSOP32236)
76 [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application
80 [UNDEFINED] MAX [IF] \ MAX and MIN are defined in {ANS_COMP}
82 CODE MAX \ n1 n2 -- n3 signed maximum
89 CODE MIN \ n1 n2 -- n3 signed minimum
98 [UNDEFINED] U.R [IF] \ defined in {UTILITY}
99 : U.R \ u n -- display u unsigned in n width (n >= 2)
101 R> OVER - 0 MAX SPACES TYPE
105 CODE 20_US \ n -- n * 20 us
106 BEGIN \ here we presume that LCD_TIM_IFG = 1...
108 BIT #1,&LCD_TIM_CTL \ 3
109 0<> UNTIL \ 2 loop until LCD_TIM_IFG set
110 BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG
112 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0=
117 CODE TOP_LCD \ LCD Sample
118 \ \ if write : %xxxxWWWW --
119 \ \ if read : -- %0000RRRR
120 BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1
121 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test
122 0= IF \ write LCD bits pattern
124 MOV.B TOS,&LCD_DB_OUT \ send LCD_Data
125 BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data
128 THEN \ read LCD bits pattern
131 BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data
132 MOV.B &LCD_DB_IN,TOS \ get LCD_Data
137 CODE LCD_W \ byte -- write byte to LCD
139 MOV TOS,0(PSP) \ -- %xxxxLLLL %HHHHLLLL
140 RRUM #4,TOS \ -- %xxxxLLLL %xxxxHHHH
141 BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0
142 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output
143 COLON \ high level word starts here
144 TOP_LCD 2 20_US \ write high nibble first
148 CODE LCD_WRC \ char -- Write Char
149 BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1
153 CODE LCD_WRF \ func -- Write Fonction
154 BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0
159 $01 LCD_WRF 100 20_us \ $01 LCD_WrF 80 20_us ==> bad init !
163 $02 LCD_WRF 100 20_us
168 \ https://forth-standard.org/standard/core/OR
169 \ C OR x1 x2 -- x3 logical OR
177 : LCD_ENTRY_SET $04 OR LCD_WrF ;
179 : LCD_DSP_CTRL $08 OR LCD_WrF ;
181 : LCD_DSP_SHIFT $10 OR LCD_WrF ;
183 : LCD_FN_SET $20 OR LCD_WrF ;
185 : LCD_CGRAM_SET $40 OR LCD_WrF ;
187 : LCD_GOTO $80 OR LCD_WrF ;
189 CODE LCD_R \ -- byte read byte from LCD
190 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput
191 BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1
192 COLON \ starts a FORTH word
193 TOP_LCD 2 20_us \ -- %0000HHHH
194 TOP_LCD 2 20_us \ -- %0000HHHH %0000LLLL
195 HI2LO \ switch from FORTH to assembler
196 RLAM #4,0(PSP) \ -- %HHHH0000 %0000LLLL
197 ADD.B @PSP+,TOS \ -- %HHHHLLLL
198 MOV @RSP+,IP \ restore IP saved by COLON
202 CODE LCD_RDS \ -- status Read Status
203 BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0
207 CODE LCD_RDC \ -- char Read Char
208 BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1
213 \ ******************************\
214 ASM WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable !
215 \ ******************************\
216 \ XOR.B #LED1,&LED1_OUT \ to visualise WDT
217 BIT.B #SW2,&SW2_IN \ test switch S2
218 0= IF \ case of switch S2 pressed
219 CMP #19,&LCD_TIM_CCR2 \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4
221 ADD #1,&LCD_TIM_CCR2 \ action for switch S2 (P2.5) : 150 mV / increment
224 BIT.B #SW1,&SW1_IN \ test switch S1 input
225 0= IF \ case of Switch S1 pressed
226 CMP #3,&LCD_TIM_CCR2 \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V
228 SUB #1,&LCD_TIM_CCR2 \ action for switch S1 (P2.6) : -150 mV / decrement
232 BW1 \ from quit on truncated RC5 message
233 BW2 \ from repeated RC5 command
234 BW3 \ from end of RC5_INT
235 BIC #$78,0(RSP) \ 4 SCG0,OSCOFF,CPUOFF and GIE are OFF in retiSR to force LPM0_LOOP despite pending interrupt
239 \ ******************************\
240 ASM RC5_INT \ wake up on Px.RC5 change interrupt
241 \ ******************************\
242 \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use
243 \ ******************************\
244 \ \ in : SR(9)=old Toggle bit memory (ADD on)
245 \ \ SMclock = 8|16|24 MHz
246 \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register
247 \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0
248 \ \ SR(9)=new Toggle bit memory (ADD on)
249 \ ******************************\
250 \ RC5_FirstStartBitHalfCycle: \
251 \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8)
252 \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ?
253 \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value
255 FREQ_KHZ @ 16000 = [IF] \ 16 MHz ?
256 MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register
258 FREQ_KHZ @ 24000 = [IF] \ 24 MHz ?
259 MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register
261 MOV #1778,X \ RC5_Period * 1us
262 MOV #14,W \ count of loop
264 \ ******************************\
265 \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period
266 \ ******************************\ |
267 MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R
268 \ RC5_Compute_3/4_Period: \ |
269 RRUM #1,X \ X=1/2 cycle |
272 ADD X,Y \ Y=3/4 cycle
273 BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles
275 \ ******************************\
276 \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first
277 \ ******************************\
278 BIT.B #RC5,&IR_IN \ C_flag = IR bit
279 ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag
280 MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG
281 BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change
282 SUB #1,W \ decrement count loop
283 \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6
284 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1
285 0<> WHILE \ ----> out of loop ----+
286 ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present
288 MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles
289 CMP Y,X \ 1 | cycle time out of bound ?
291 BIC #$30,&RC5_TIM_CTL \ | | stop timer
292 GOTO BW1 \ | | quit on truncated RC5 message
294 BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present
296 REPEAT \ ----> loop back --+ | with X = new RC5_period value
297 \ ******************************\ |
298 \ RC5_SampleEndOf: \ <---------------------+
299 \ ******************************\
300 BIC #$30,&RC5_TIM_CTL \ stop timer
301 \ ******************************\
302 \ RC5_ComputeNewRC5word \
303 \ ******************************\
304 RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0
305 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0
306 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0
307 \ ******************************\
309 \ ******************************\
310 BIT #BIT14,T \ test /C6 bit in T
311 0= IF BIS #BIT6,X \ set C6 bit in X
312 THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0
313 \ ******************************\
314 \ RC5_CommandByteIsDone \ -- BASE RC5_code
315 \ ******************************\
316 \ Only New_RC5_Command ADD_ON \ use SR(9) bit as toggle bit
317 \ ******************************\
318 RRUM #3,T \ new toggle bit = T(13) ==> T(10)
319 XOR @RSP,T \ (new XOR old) Toggle bits
320 BIT #UF10,T \ repeated RC5_command ?
321 0= ?GOTO BW2 \ yes, RETI without UF10 change and without action !
322 XOR #UF10,0(RSP) \ 5 toggle bit memory
323 \ ******************************\
324 \ Display IR_RC5 code \ X = RC5 code
325 \ ******************************\
327 MOV &BASE,2(PSP) \ save current base
328 MOV #$10,&BASE \ set hex base
329 MOV TOS,0(PSP) \ save TOS
331 LO2HI \ switch from assembler to FORTH
332 ['] LCD_CLEAR IS CR \ redirects CR to LCD
333 ['] LCD_WRC IS EMIT \ redirects EMIT to LCD
334 CR ." $" 2 U.R \ print IR_RC5 code to LCD
335 ['] CR >BODY IS CR \ restore CR
336 ['] EMIT >BODY IS EMIT \ restore EMIT
337 HI2LO \ switch from FORTH to assembler
338 MOV TOS,&BASE \ restore current BASE
340 \ ******************************\
342 \ ******************************\
345 \ ------------------------------\
347 \ ------------------------------\
349 \ ... \ insert here your background task
353 \ ******************************\
354 \ here start all interrupts \
355 \ ******************************\
356 \ here return all interrupts \
357 \ ******************************\
361 \ ------------------------------\
362 CODE STOP \ stops multitasking, must to be used before downloading app
363 \ ------------------------------\
364 \ restore default action of primary DEFERred word SLEEP (assembly version)
365 MOV #SLEEP,X \ the ASM word SLEEP is only visible in mode assembler.
366 ADD #4,X \ X = BODY of SLEEP, X-2 = PFA of SLEEP
367 MOV X,-2(X) \ restore the default background
369 \ restore default action of primary DEFERred word WARM (FORTH version)
370 ['] WARM >BODY IS WARM \ remove START from FORTH init process
372 ." RC5toLCD is removed." CR \ display message
373 ." type START to restart" \
374 COLD \ performs reset to reset all interrupt vectors.
377 \ ------------------------------\
378 CODE START \ this routine completes the init of system, i.e. FORTH + this app.
379 \ ------------------------------\
380 \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0
381 \ - - \CNTL Counter lentgh \ 00 = 16 bits
382 \ -- \TBSSEL TimerB clock select \ 10 = SMCLK
383 \ -- \ID input divider \ 10 = /4
384 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0
385 \ - \TBCLR TimerB Clear
388 \ -------------------------------\
389 \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E}
390 \ -- \CM Capture Mode
395 \ --- \OUTMOD \ 011 = set/reset
401 \ -------------------------------\
403 \ -------------------------------\
405 \ ------------------------------\
406 \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt
407 \ ------------------------------\
408 MOV #%1011010100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int
409 \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ)
410 FREQ_KHZ @ 16000 = [IF] \ if 16 MHz
411 MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ)
413 FREQ_KHZ @ 24000 = [IF] \ if 24 MHz
414 MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ)
416 MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us
417 \ ------------------------------\
418 \ set LCD_TIM_.2 to generate PWM for LCD_Vo
419 \ ------------------------------\
420 MOV #%01100000,&LCD_TIM_CCTL2 \ output mode = set/reset \ clear CCIFG
421 MOV #10,&LCD_TIM_CCR2 \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6)
422 \ MOV #12,&LCD_TIM_CCR2 \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3)
423 \ ------------------------------\
424 BIS.B #LCDVo,&LCDVo_DIR \
425 BIS.B #LCDVo,&LCDVo_SEL \ SEL.2
426 \ ------------------------------\
427 BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs
428 BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable
429 \ ------------------------------\
430 BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data
431 BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable
432 \ ******************************\
434 \ ******************************\
435 BIS.B #RC5,&IR_IE \ enable RC5_Int
436 BIC.B #RC5,&IR_IFG \ reset RC5_Int flag
437 MOV #RC5_INT,&IR_Vec \ init interrupt vector
438 \ ******************************\
439 \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_
440 \ ******************************\
441 \ %01 0001 0100 \ TAxCTL
442 \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz
443 \ -- \ ID divided by 1
444 \ -- \ MC MODE = up to TAxCCRn
445 \ - \ TACLR clear timer count
448 \ ------------------------------\
449 MOV #%0100010100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int,
450 \ ------------------------------\
452 \ --- \ TAIDEX pre divisor
453 \ ------------------------------\
454 \ %0000 0000 0000 0101 \ TAxCCR0
455 MOV ##1638,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 50ms
456 \ ------------------------------\
457 \ %0000 0000 0001 0000 \ TAxCCTL0
458 \ - \ CAP capture/compare mode = compare
461 MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0
462 \ ------------------------------\
463 MOV #WDT_INT,&WDT_TIM_0_Vec \ for only CCIFG0 int, this interrupt clears automatically CCIFG0
464 \ ------------------------------\
465 \ define LPM mode for ACCEPT \
466 \ ------------------------------\
467 \ MOV #LPM4,&LPM_MODE \ with MSP430FR59xx
468 \ MOV #LPM2,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2
469 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value
470 \ ------------------------------\
471 \ redirects to background task \
472 \ ------------------------------\
474 MOV #BACKGROUND,2(X) \
475 \ ------------------------------\
477 \ ------------------------------\
478 BIC #1,&PM5CTL0 \ activate all previous I/O settings; if not activated, nothing works after reset !
479 BIS.B #TERM_BUS,&TERM_SEL \ Configure pins TXD & RXD for TERM_UART use, otherwise no TERMINAL !
480 \ ------------------------------\
481 \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet
482 \ ------------------------------\
483 MOV &SAVE_SYSRSTIV,Y \ Y = SYSRSTIV register memory
484 \ CMP #2,Y \ Power_ON event
485 \ 0= ?JMP STOP \ uncomment if you want to loose application in this case...
487 0= ?JMP STOP \ hardware RESET performs STOP. Should be mandatory...
489 \ 0= ?JMP STOP \ COLD event performs STOP... uncomment if it's that you want.
491 \ 0= ?JMP STOP \ fault event (violation memory protected areas) performs STOP
493 \ U>= ?JMP STOP \ all other fault events + Deep Reset perform STOP
494 \ ------------------------------\
496 \ ------------------------------\
498 \ ------------------------------\
499 #1000 20_US \ 1- wait 20 ms
500 %011 TOP_LCD \ 2- send DB5=DB4=1
501 #205 20_US \ 3- wait 4,1 ms
502 %011 TOP_LCD \ 4- send again DB5=DB4=1
503 #5 20_US \ 5- wait 0,1 ms
504 %011 TOP_LCD \ 6- send again again DB5=DB4=1
505 #2 20_US \ wait 40 us = LCD cycle
506 %010 TOP_LCD \ 7- send DB5=1 DB4=0
507 #2 20_US \ wait 40 us = LCD cycle
508 %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal
509 %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off.
510 LCD_CLEAR \ 10- "LCD_Clear"
511 %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM
512 %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off.
513 LCD_CLEAR \ 10- "LCD_Clear"
514 ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME
515 ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC
516 CR ." I love you" \ display message on LCD
517 ['] CR >BODY IS CR \ CR executes its default value
518 ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value
519 ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal
520 \ ------------------------------\
521 \ START replaces WARM \
522 \ ------------------------------\
523 LIT RECURSE IS WARM \ START replaces WARM...
524 \ ['] WARM >BODY EXECUTE \ ...and end START with default WARM, no return.
525 ABORT \ ...and end START with ABORT, no return.
529 ; downloading RC5toLCD.4th is done
530 RST_HERE ; this app is protected against <reset>