\ -*- coding: utf-8 -*- ; ----------------------------------- ; PROG100k.f = 76 x RC5toLCD.f ; ----------------------------------- ; download source file sized to compile 100 kbytes ; ----------------------------------- \ TARGET SELECTION ( = the name of \INC\target.pat file without the extension) \ MSP_EXP430FR5739 MSP_EXP430FR5969 MSP_EXP430FR5994 MSP_EXP430FR6989 \ MSP_EXP430FR2355 \ LP_MSP430FR2476 \ \ from scite editor : copy your target selection in (shift+F8) parameter 1: \ \ OR \ \ drag and drop this file onto SendSourceFileToTarget.bat \ then select your TARGET when asked. \ \ \ REGISTERS USAGE \ R4 to R7 must be saved before use and restored after \ scratch registers Y to S are free for use \ under interrupt, IP is free for use \ interrupts reset SR register ! \ \ PUSHM order : PSP,TOS, IP, S, T, W, X, Y, rEXIT,rDOVAR,rDOCON, rDODOES, R3, SR,RSP, PC \ PUSHM order : R15,R14,R13,R12,R11,R10, R9, R8, R7 , R6 , R5 , R4 , R3, R2, R1, R0 \ \ example : PUSHM #6,IP pushes IP,S,T,W,X,Y registers to return stack \ \ POPM order : PC,RSP, SR, R3, rDODOES,rDOCON,rDOVAR,rEXIT, Y, X, W, T, S, IP,TOS,PSP \ POPM order : R0, R1, R2, R3, R4 , R5 , R6 , R7 , R8, R9,R10,R11,R12,R13,R14,R15 \ \ example : POPM #6,IP pop Y,X,W,T,S,IP registers from return stack \ \ ASSEMBLER conditionnal usage after IF UNTIL WHILE : S< S>= U< U>= 0= 0<> 0>= \ ASSEMBLER conditionnal usage before ?JMP ?GOTO : S< S>= U< U>= 0= 0<> 0< \ \ FORTH conditionnal : 0= 0< = < > U< \ \ display on a LCD 2x20 CHAR the code sent by an IR remote under philips RC5 protocol \ target : any TI MSP-EXP430FRxxxx launchpad (FRAM) \ LPM_MODE = LPM0 because use SMCLK for LCDVo \ \ DEMO : driver for IR remote compatible with the PHILIPS RC5 protocol \ plus : driver for 5V LCD 2x20 characters display with 4 bits data interface \ without usage of an auxiliary 5V to feed the LCD_Vo \ and without potentiometer to adjust the LCD contrast : \ to adjust LCD contrast, just press S1 (-) or S2 (+) \ LCDVo current consumption ~ 500 uA. \ \ =================================================================================== \ notice : adjust WDT_TIM_EX0,LCD_TIM_CTL,LCD_TIM_EX0 and 20_us to the target frequency if <> 8MHz ! \ =================================================================================== \ \ \ layout : I/O are defined in the launchpad.pat file (don't work with ChipStick_FR2433) \ \ GND <-------+---0V0----------> 1 LCD_Vss \ VCC >------ | --3V6-----+----> 2 LCD_Vdd \ | | \ ___ 470n --- \ ^ --- \ / \ 1N4148 | \ --- | \ 100n | 2k2 | \ LCD_TIM_.2 >---||--+--^/\/\/v--+----> 3 LCD_Vo (= 0V6 without modulation) \ -------------------------> 4 LCD_RW \ -------------------------> 5 LCD_RW \ -------------------------> 6 LCD_EN \ <------------------------> 11 LCD_DB4 \ <------------------------> 12 LCD_DB5 \ <------------------------> 13 LCD_DB5 \ <------------------------> 14 LCD_DB7 \ \ <----- LCD contrast + <--- Sw1 <--- (finger) :-) \ <----- LCD contrast - <--- Sw2 <--- (finger) :-) \ \ rc5 <--- OUT IR_Receiver (1 TSOP32236) \ first, we test for downloading driver only if UART TERMINAL target CODE ABORT_RC5TOLCD SUB #2,PSP MOV TOS,0(PSP) MOV &VERSION,TOS SUB #308,TOS \ FastForth V3.8 COLON 'CR' EMIT \ return to column 1 without 'LF' ABORT" FastForth V3.8 please!" PWR_STATE \ remove ABORT_UARTI2CS definition before resuming ; ABORT_RC5TOLCD [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ [DEFINED] {RC5TOLCD} [IF] {RC5TOLCD} [THEN] \ remove application MARKER {RC5TOLCD} \ restore the state before MARKER definition \ \ {UARTI2CS}+8 = RET_ADR: by default MARKER_DOES does CALL #RET_ADR 6 ALLOT \ {UARTI2CS}+10: make room to save previous INI_APP address \ {RC5TOLCD}+12: make room to save previous WDT_TIM_0_VEC \ {RC5TOLCD}+14: make room to save previous IR_VEC [UNDEFINED] CONSTANT [IF] \ https://forth-standard.org/standard/core/CONSTANT \ CONSTANT n -- define a Forth CONSTANT : CONSTANT CREATE HI2LO MOV TOS,-2(W) \ PFA = n MOV @PSP+,TOS MOV @RSP+,IP MOV @IP+,PC ENDCODE [THEN] [UNDEFINED] STATE [IF] \ https://forth-standard.org/standard/core/STATE \ STATE -- a-addr holds compiler state STATEADR CONSTANT STATE [THEN] [UNDEFINED] = [IF] \ https://forth-standard.org/standard/core/Equal \ = x1 x2 -- flag test x1=x2 CODE = SUB @PSP+,TOS \ 2 0<> IF \ 2 AND #0,TOS \ 1 MOV @IP+,PC \ 4 THEN XOR #-1,TOS \ 1 flag Z = 1 MOV @IP+,PC \ 4 ENDCODE [THEN] [UNDEFINED] IF [IF] \ define IF and THEN \ https://forth-standard.org/standard/core/IF \ IF -- IFadr initialize conditional forward branch CODE IF \ immediate SUB #2,PSP \ MOV TOS,0(PSP) \ MOV &DP,TOS \ -- HERE ADD #4,&DP \ compile one word, reserve one word MOV #QFBRAN,0(TOS) \ -- HERE compile QFBRAN ADD #2,TOS \ -- HERE+2=IFadr MOV @IP+,PC ENDCODE IMMEDIATE \ https://forth-standard.org/standard/core/THEN \ THEN IFadr -- resolve forward branch CODE THEN \ immediate MOV &DP,0(TOS) \ -- IFadr MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] ELSE [IF] \ https://forth-standard.org/standard/core/ELSE \ ELSE IFadr -- ELSEadr resolve forward IF branch, leave ELSEadr on stack CODE ELSE \ immediate ADD #4,&DP \ make room to compile two words MOV &DP,W \ W=HERE+4 MOV #BRAN,-4(W) MOV W,0(TOS) \ HERE+4 ==> [IFadr] SUB #2,W \ HERE+2 MOV W,TOS \ -- ELSEadr MOV @IP+,PC ENDCODE IMMEDIATE [THEN] [UNDEFINED] IS [IF] \ define DEFER! and IS \ https://forth-standard.org/standard/core/DEFERStore \ Set the word xt1 to execute xt2. An ambiguous condition exists if xt1 is not for a word defined by DEFER. CODE DEFER! \ xt2 xt1 -- MOV @PSP+,2(TOS) \ -- xt1=CFA_DEFER xt2 --> [CFA_DEFER+2] MOV @PSP+,TOS \ -- MOV @IP+,PC ENDCODE \ https://forth-standard.org/standard/core/IS \ IS xt -- \ used as is : \ DEFER DISPLAY create a "do nothing" definition (2 CELLS) \ inline command : ' U. IS DISPLAY U. becomes the runtime of the word DISPLAY \ or in a definition : ... ['] U. IS DISPLAY ... \ KEY, EMIT, CR, ACCEPT and WARM are examples of DEFERred words \ \ as IS replaces the PFA value of any word, it's a TO alias for VARIABLE and CONSTANT words... : IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE [THEN] [UNDEFINED] >BODY [IF] \ https://forth-standard.org/standard/core/toBODY \ >BODY -- addr leave BODY of a CREATEd word\ also leave default ACTION-OF primary DEFERred word CODE >BODY ADD #4,TOS MOV @IP+,PC ENDCODE [THEN] \ CODE 20uS \ n -- 8MHz version \ BEGIN \ 4 + 16 ~ loop \ MOV #39,rDOCON \ 39 \ BEGIN \ 4 ~ loop \ NOP \ SUB #1,rDOCON \ 0= UNTIL \ SUB #1,TOS \ 1 \ 0= UNTIL \ MOV #XDOCON,rDOCON \ 2 \ MOV @PSP+,TOS \ MOV @RSP+,IP \ \ ENDCODE CODE 20_US \ n -- n * 20 us BEGIN \ here we presume that LCD_TIM_IFG = 1... BEGIN BIT #1,&LCD_TIM_CTL \ 3 0<> UNTIL \ 2 loop until LCD_TIM_IFG set BIC #1,&LCD_TIM_CTL \ 3 clear LCD_TIM_IFG SUB #1,TOS \ 1 U< UNTIL \ 2 ...so add a dummy loop with U< instead of 0= MOV @PSP+,TOS \ 2 MOV @IP+,PC \ 4 ENDCODE CODE TOP_LCD \ LCD Sample \ \ if write : %xxxx_WWWW -- \ \ if read : -- %0000_RRRR BIS.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 0-->1 BIT.B #LCD_RW,&LCD_CMD_IN \ lcd_rw test 0= IF \ write LCD bits pattern AND.B #LCD_DB,TOS \ MOV.B TOS,&LCD_DB_OUT \ send LCD_Data BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV @PSP+,TOS \ MOV @IP+,PC THEN \ read LCD bits pattern SUB #2,PSP MOV TOS,0(PSP) BIC.B #LCD_EN,&LCD_CMD_OUT \ lcd_en 1-->0 ==> strobe data MOV.B &LCD_DB_IN,TOS \ get LCD_Data AND.B #LCD_DB,TOS \ MOV @IP+,PC ENDCODE CODE LCD_WRC \ char -- Write Char BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 BW1 SUB #2,PSP \ MOV TOS,0(PSP) \ -- %xxxx_LLLL %HHHH_LLLL RRUM #4,TOS \ -- %xxxx_LLLL %xxxx_HHHH BIC.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=0 BIS.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as output COLON \ high level word starts here TOP_LCD 2 20_US \ write high nibble first TOP_LCD 2 20_US ; CODE LCD_WRF \ func -- Write Fonction BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 GOTO BW1 ENDCODE : LCD_CLEAR $01 LCD_WRF 100 20_us ; \ $01 LCD_WrF 80 20_us ==> bad init ! : LCD_HOME $02 LCD_WRF 100 20_us ; \ [UNDEFINED] OR [IF] \ \ \ https://forth-standard.org/standard/core/OR \ \ C OR x1 x2 -- x3 logical OR \ CODE OR \ BIS @PSP+,TOS \ MOV @IP+,PC \ ENDCODE \ \ [THEN] \ \ : LCD_ENTRY_SET $04 OR LCD_WrF ; \ : LCD_DSP_CTRL $08 OR LCD_WrF ; \ : LCD_DSP_SHIFT $10 OR LCD_WrF ; \ : LCD_FN_SET $20 OR LCD_WrF ; \ : LCD_CGRAM_SET $40 OR LCD_WrF ; \ : LCD_GOTO $80 OR LCD_WrF ; \ \ \ CODE LCD_RDS \ -- status Read Status \ BIC.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=0 \ BW1 BIC.B #LCD_DB,&LCD_DB_DIR \ LCD_Data as intput \ BIS.B #LCD_RW,&LCD_CMD_OUT \ lcd_rw=1 \ COLON \ starts a FORTH word \ TOP_LCD 2 20_us \ -- %0000_HHHH \ TOP_LCD 2 20_us \ -- %0000_HHHH %0000_LLLL \ HI2LO \ switch from FORTH to assembler \ RLAM #4,0(PSP) \ -- %HHHH_0000 %0000_LLLL \ ADD.B @PSP+,TOS \ -- %HHHH_LLLL \ MOV @RSP+,IP \ restore IP saved by COLON \ MOV @IP+,PC \ \ ENDCODE \ \ CODE LCD_RDC \ -- char Read Char \ BIS.B #LCD_RS,&LCD_CMD_OUT \ lcd_rs=1 \ GOTO BW1 \ ENDCODE \ ******************************\ HDNCODE WDT_INT \ Watchdog interrupt routine, warning : not FORTH executable ! \ ******************************\ \ XOR.B #LED1,&LED1_OUT \ to visualise WDT BIT.B #SW2,&SW2_IN \ test switch S2 0= IF \ case of switch S2 pressed CMP #19,&LCD_TIM_CCRn \ maxi Ton = 19/20 & VDD=3V6 ==> LCD_Vo = -1V4 U< IF ADD #1,&LCD_TIM_CCRn \ action for switch S2 (P2.5) : 150 mV / increment THEN ELSE BIT.B #SW1,&SW1_IN \ test switch S1 input 0= IF \ case of Switch S1 pressed CMP #3,&LCD_TIM_CCRn \ mini Ton = 3/20 & VDD=3V6 ==> LCD_Vo = 0V U>= IF \ SUB #1,&LCD_TIM_CCRn \ action for switch S1 (P2.6) : -150 mV / decrement THEN \ THEN \ THEN \ RETI \ 5 ENDCODE \ ******************************\ HDNCODE RC5_INT \ wake up on Px.RC5 change interrupt \ ******************************\ \ IR_RC5 driver \ IP,S,T,W,X,Y registers are free for use \ ******************************\ \ \ in : SR(9)=old Toggle bit memory (ADD on) \ \ SMclock = 8|16|24 MHz \ \ use : T,W,X,Y, RC5_TIM_ timer, RC5_TIM_R register \ \ out : X = 0 C6 C5 C4 C3 C2 C1 C0 \ \ SR(9)=new Toggle bit memory (ADD on) \ ******************************\ \ RC5_FirstStartBitHalfCycle: \ \ ******************************\ division in RC5_TIM_CTL (SMCLK/1|SMCLK/1|SMCLK/2|SMCLK/4|SMCLK/8) \ FREQ_KHZ @ 8000 = [IF] \ 8 MHz ? \ MOV #0,&RC5_TIM_EX0 \ predivide by 1 in RC5_TIM_EX0 register, reset value \ [THEN] FREQ_KHZ @ 16000 = [IF] \ 16 MHz ? MOV #1,&RC5_TIM_EX0 \ predivide by 2 in RC5_TIM_EX0 register [THEN] FREQ_KHZ @ 24000 = [IF] \ 24 MHz ? MOV #2,&RC5_TIM_EX0 \ predivide by 3 in RC5_TIM_EX0 register [THEN] MOV #1778,X \ RC5_Period * 1us MOV #14,W \ count of loop BEGIN \ \ ******************************\ \ RC5_HalfCycle \ <--- loop back ---+ with readjusted RC5_Period \ ******************************\ | MOV #%1011100100,&RC5_TIM_CTL \ (re)start timer_A | SMCLK/8 time interval,free running,clear RC5_TIM__IFG and RC5_TIM_R \ RC5_Compute_3/4_Period: \ | RRUM #1,X \ X=1/2 cycle | MOV X,Y \ ^ RRUM #1,Y \ Y=1/4 ADD X,Y \ Y=3/4 cycle BEGIN CMP Y,&RC5_TIM_R \ 3 wait 1/2 + 3/4 cycle = n+1/4 cycles U>= UNTIL \ 2 \ ******************************\ \ RC5_SampleOnFirstQuarter \ at n+1/4 cycles, we sample RC5_input, ST2/C6 bit first \ ******************************\ BIT.B #RC5,&IR_IN \ C_flag = IR bit ADDC T,T \ C_flag <-- T(15):T(0) <-- C_flag MOV.B &IR_IN,&IR_IES \ preset Px_IES.y state for next IFG BIC.B #RC5,&IR_IFG \ clear Px_IFG.y after 4/4 cycle pin change SUB #1,W \ decrement count loop \ \ count = 13 ==> T = x x x x x x x x |x x x x x x x /C6 \ \ count = 0 ==> T = x x /C6 Tg A4 A3 A2 A1|A0 C5 C4 C3 C2 C1 C0 1 0<> WHILE \ ----> out of loop ----+ ADD X,Y \ | Y = n+3/4 cycles = time out because n+1/2 cycles edge is always present BEGIN \ | MOV &RC5_TIM_R,X \ 3 | X grows from n+1/4 up to n+3/4 cycles CMP Y,X \ 1 | cycle time out of bound ? U>= IF \ 2 ^ | yes: BIC #$30,&RC5_TIM_CTL \ | | stop timer GOTO FW1 \ | | quit on truncated RC5 message THEN \ | | BIT.B #RC5,&IR_IFG \ 3 | | n+1/2 cycles edge is always present 0<> UNTIL \ 2 | | REPEAT \ ----> loop back --+ | with X = new RC5_period value \ ******************************\ | \ RC5_SampleEndOf: \ <---------------------+ \ ******************************\ BIC #$30,&RC5_TIM_CTL \ stop timer \ ******************************\ \ RC5_ComputeNewRC5word \ \ ******************************\ RLAM #1,T \ T = x /C6 Tg A4 A3 A2 A1 A0|C5 C4 C3 C2 C1 C0 1 0 MOV.B T,X \ X = C5 C4 C3 C2 C1 C0 1 0 RRUM #2,X \ X = 0 0 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_ComputeC6bit \ \ ******************************\ BIT #BIT14,T \ test /C6 bit in T 0= IF BIS #BIT6,X \ set C6 bit in X THEN \ X = 0 C6 C5 C4 C3 C2 C1 C0 \ ******************************\ \ RC5_CommandByteIsDone \ -- BASE RC5_code \ ******************************\ \ Only New_RC5_Command ADD_ON \ use SR(10) bit as toggle bit \ ******************************\ RRUM #3,T \ new toggle bit = T(13) ==> T(10) XOR @RSP,T \ (new XOR old) Toggle bits BIT #UF10,T \ repeated RC5_command ? 0= ?GOTO FW2 \ yes, RETI without UF10 change and without action ! XOR #UF10,0(RSP) \ 5 toggle bit memory \ ******************************\ \ Display IR_RC5 code \ \ ******************************\ SUB #8,PSP \ TOS -- x x x x TOS MOV TOS,6(PSP) \ -- Save_TOS x x x TOS MOV &BASEADR,4(PSP) \ -- Save_TOS Save_Base x x TOS MOV #$10,&BASEADR \ set hexadecimal base MOV X,0(PSP) \ -- Save_TOS Save_Base x RC5_code TOS convert number to ascii low word = RC5 byte MOV #0,TOS \ -- Save_TOS Save_Base x RC5_code 0 convert number to ascii high word = 0 LO2HI \ switch from assembler to FORTH LCD_CLEAR \ set LCD cursor at home <# # #S #36 HOLD #> \ 32 bits conversion as "$xx" ['] LCD_WRC IS EMIT \ redirect EMIT to LCD TYPE \ -- Save_TOS Save_Base x adr cnt display "$xx" on LCD ['] EMIT >BODY IS EMIT \ -- Save_TOS Save_Base TOS restore EMIT HI2LO \ -- switch from FORTH to assembler MOV @PSP+,&BASEADR \ -- Save_TOS TOS restore current BASE MOV @PSP+,TOS \ -- TOS FW1 FW2 MOV @RSP+,SR \ restore SR flags BIC #%1111_1000,SR \ but force CPU Active Mode RET \ (instead of RETI) ENDCODE \ ------------------------------\ HDNCODE STOP_R2L \ define new STOP_APP \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ 0<> IF \ if previous START executing BIC.B #RC5,&IR_IE \ clear I/O RC5_Int BIC.B #RC5,&IR_IFG \ clear I/O RC5_Int flag MOV #0,&LCD_TIM_CTL \ stop LCD_TIMER MOV #0,&WDT_TIM_CTL \ stop WDT_TIMER MOV #0,&WDT_TIM_CCTL0 \ clear CCIFG0 disable CCIE0 MOV #RET_ADR,&{RC5TOLCD}+8 \ clear MARKER_DOES call MOV &{RC5TOLCD}+10,&WARM+2 \ restore previous ini_APP MOV &{RC5TOLCD}+12,&WDT_TIM_0_VEC \ restore Vector previous value MOV &{RC5TOLCD}+14,&IR_VEC \ restore Vector previous value MOV &{RC5TOLCD}+10,PC \ run previous INI_APP, then RET THEN MOV @RSP+,PC \ RET ENDCODE \ ------------------------------\ CODE STOP \ \ ------------------------------\ BW1 \ <-- INI_R2L for some events CALL #STOP_R2L COLON \ restore default action of primary DEFERred word WARM (FORTH version) ECHO \ ." RC5toLCD is removed," ." type START to restart" ABORT" " ; \ ------------------------------\ \ ------------------------------\ HDNCODE INI_R2L \ this routine completes the init of system, i.e. FORTH + this app. \ ------------------------------\ \ activate I/O \ \ ------------------------------\ BIC #1,&PM5CTL0 \ activate I/O to enable SW2 test \ ------------------------------\ \ RESET events handling \ search "SYSRSTIV" in your MSP430FRxxxx datasheet to get listing \ ------------------------------\ MOV &RSTIV_MEM,TOS \ SYSRSTIV = $00|$02|$04|$0E|$xx = POWER_ON|RST|SVSH_threshold|SYS_failures CMP #$0E,TOS \ RSTIV_MEM = SVSHIFG SVSH event ? 0<> IF \ if not CMP #$0A,TOS \ RSTIV_MEM >= violation memory protected areas ? U>= ?GOTO BW1 \ execute STOP_R2L then RET to BODY of WARM THEN \ BIT.B #SW2,&SW2_IN \ hardware SW2+RST ? 0= ?GOTO BW1 \ hardware SW2+RST execute STOP_U2I then RET to BODY of WARM \ CMP #4,TOS \ hardware RST \ 0= ?GOTO BW1 \ hardware RST performs STOP. \ CMP #2,TOS \ Power_ON event \ 0= ?GOTO BW1 \ uncomment if you want to loose application in this case... \ CMP #6,TOS \ \ 0= ?GOTO BW1 \ COLD event performs STOP... uncomment if it's that you want. \ CMP #$0A,TOS \ \ 0= ?GOTO BW1 \ fault event (violation memory protected areas) performs STOP \ CMP #$16,TOS \ \ U>= ?GOTO BW1 \ all other fault events + Deep Reset perform STOP MOV #0,&RSTIV_MEM \ clear RSTIV_MEM after use and before next RST event! \ ------------------------------\ \ LCD_TIM_CTL = %0000 0010 1001 0100\$3C0 \ - - \CNTL Counter lentgh \ 00 = 16 bits \ -- \TBSSEL TimerB clock select \ 10 = SMCLK \ -- \ID input divider \ 10 = /4 \ -- \MC Mode Control \ 01 = up to LCD_TIM_CCR0 \ - \TBCLR TimerB Clear \ - \TBIE \ -\TBIFG \ -------------------------------\ \ LCD_TIM_CCTLx = %0000 0000 0110 0000\$3C{2,4,6,8,A,C,E} \ -- \CM Capture Mode \ -- \CCIS \ - \SCS \ -- \CLLD \ - \CAP \ --- \OUTMOD \ 011 = set/reset \ - \CCIE \ - \CCI \ - \OUT \ - \COV \ -\CCIFG \ -------------------------------\ \ LCD_TIM_CCRx \ \ -------------------------------\ \ LCD_TIM_EX0 \ \ ------------------------------\ \ set LCD_TIM_ to make 50kHz PWM \ for LCD_Vo; works without interrupt \ ------------------------------\ MOV #%10_1101_0100,&LCD_TIM_CTL \ SMCLK/8, up mode, clear timer, no int \ MOV #0,&LCD_TIM_EX0 \ predivide by 1 in LCD_TIM_EX0 register (8 MHZ) FREQ_KHZ @ 16000 = [IF] \ if 16 MHz MOV #1,&LCD_TIM_EX0 \ predivide by 2 in LCD_TIM_EX0 register (16 MHZ) [THEN] FREQ_KHZ @ 24000 = [IF] \ if 24 MHz MOV #2,&LCD_TIM_EX0 \ predivide by 3 in LCD_TIM_EX0 register (24 MHZ) [THEN] MOV #19,&LCD_TIM_CCR0 \ 19+1=20*1us=20us \ ------------------------------\ \ set LCD_TIM_.2 to generate PWM for LCD_Vo \ ------------------------------\ MOV #%0110_0000,&LCD_TIM_CCTLn \ output mode = set/reset \ clear CCIFG MOV #10,&LCD_TIM_CCRn \ contrast adjust : 10/20 ==> LCD_Vo = -0V6|+3V6 (Vcc=3V6) \ MOV #12,&LCD_TIM_CCRn \ contrast adjust : 12/20 ==> LCD_Vo = -1V4|+3V3 (Vcc=3V3) \ ------------------------------\ BIS.B #LCDVo,&LCDVo_DIR \ BIS.B #LCDVo,&LCDVo_SEL \ SEL.2 \ ------------------------------\ BIS.B #LCD_CMD,&LCD_CMD_DIR \ lcd_cmd as outputs BIC.B #LCD_CMD,&LCD_CMD_REN \ lcd_cmd pullup/down disable \ ------------------------------\ BIS.B #LCD_DB,&LCD_DB_DIR \ as output, wired to DB(4-7) LCD_Data BIC.B #LCD_DB,&LCD_DB_REN \ LCD_Data pullup/down disable \ ******************************\ \ init RC5_Int \ \ ******************************\ BIS.B #RC5,&IR_IE \ enable RC5_Int BIC.B #RC5,&IR_IFG \ reset RC5_Int flag \ ******************************\ \ init WatchDog WDT_TIM_ \ eUSCI_A0 (FORTH terminal) has higher priority than WDT_TIM_ \ ******************************\ \ %01 0001 0100 \ TAxCTL \ -- \ TASSEL CLK = ACLK = LFXT = 32768 Hz \ -- \ ID divided by 1 \ -- \ MC MODE = up to TAxCCRn \ - \ TACLR clear timer count \ - \ TAIE \ - \ TAIFG \ ------------------------------\ MOV #%01_0001_0100,&WDT_TIM_CTL \ start WDT_TIM_, ACLK, up mode, disable int, \ ------------------------------\ \ 000 \ TAxEX0 \ --- \ TAIDEX pre divisor \ ------------------------------\ \ %0000 0000 0000 0101 \ TAxCCR0 MOV ##3276,&WDT_TIM_CCR0 \ else init WDT_TIM_ for LFXT: 32768/20=1638 ==> 100ms \ ------------------------------\ \ %0000 0000 0001 0000 \ TAxCCTL0 \ - \ CAP capture/compare mode = compare \ - \ CCIEn \ - \ CCIFGn MOV #%10000,&WDT_TIM_CCTL0 \ enable compare interrupt, clear CCIFG0 \ ------------------------------\ \ define LPM mode for ACCEPT \ \ ------------------------------\ \ MOV #LPM4+GIE,&LPM_MODE \ with MSP430FR59xx \ MOV #LPM2+GIE,&LPM_MODE \ with MSP430FR57xx, terminal input don't work for LPMx > 2 \ \ with MSP430FR2xxx, terminal input don't work for LPMx > 0 ; LPM0 is the default value \ ------------------------------\ COLON \ \ ------------------------------\ \ Init LCD 2x20 \ \ ------------------------------\ #1000 20_US \ 1- wait 20 ms %011 TOP_LCD \ 2- send DB5=DB4=1 #205 20_US \ 3- wait 4,1 ms %011 TOP_LCD \ 4- send again DB5=DB4=1 #5 20_US \ 5- wait 0,1 ms %011 TOP_LCD \ 6- send again again DB5=DB4=1 #2 20_US \ wait 40 us = LCD cycle %010 TOP_LCD \ 7- send DB5=1 DB4=0 #2 20_US \ wait 40 us = LCD cycle %00101000 LCD_WRF \ 8- %001DNFxx "FonctionSet" D=8/4 DataBus width, Number of lines=2/1, Font bold/normal %1000 LCD_WRF \ 9- %1DCB "DisplayControl" : Display off, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" %0110 LCD_WRF \ 11- %01xx "LCD_EntrySet" : address and cursor shift after writing in RAM %1100 LCD_WRF \ 12- %1DCB "DisplayControl" : Display on, Cursor off, Blink off. LCD_CLEAR \ 10- "LCD_Clear" ['] LCD_HOME IS CR \ ' CR redirected to LCD_HOME ['] LCD_WRC IS EMIT \ ' EMIT redirected to LCD_WrC CR ." I love you" \ display message on LCD ['] CR >BODY IS CR \ CR executes its default value ['] EMIT >BODY IS EMIT \ EMIT executes its defaulte value ." RC5toLCD is running. Type STOP to quit" \ display message on FastForth Terminal ABORT" " \ ; \ \ ------------------------------\ \ ------------------------------\ CODE START \ this routine replaces WARM and COLD default values by these of this application. \ ------------------------------\ CMP #RET_ADR,&{RC5TOLCD}+8 \ init R2L once, only if MARKER_DOES is not initialized 0= IF \ if not done, customizes MARKER_DOES MOV #STOP_R2L,&{RC5TOLCD}+8 \ execution of {RC5TOLCD} will perform STOP_R2L. MOV &WARM+2,&{RC5TOLCD}+10 \ save previous INI_APP subroutine MOV #INI_R2L,&WARM+2 \ replace it by RC5toLCD INI_APP MOV &WDT_TIM_0_VEC,&{RC5TOLCD}+12 \ save Vector previous value MOV #WDT_INT,&WDT_TIM_0_VEC \ for only CCIFG0 int, this interrupt clears automatically CCIFG0 MOV &IR_VEC,&{RC5TOLCD}+14 \ save Vector previous value MOV #RC5_INT,&IR_VEC \ init interrupt vector MOV #INI_R2L,PC \ then execute new INI_APP, without return THEN MOV @IP+,PC ENDCODE \ ------------------------------\ ECHO ; downloading PROG100k.4th is done RST_HERE ; this app is protected against \ START