there has got to be a better way...
hey all, i have some code here that polls six buttons, then spits out a midi code that corresponds with the button pressed. After the serial string is sent, it lights up one of 6 led's also corresponding to the last button pressed.
the code works great, but it's rather unevolved and longwinded. is there a better way to do this perhaps with a for next loop?
thanks in advance!
Post Edited (skynugget) : 10/20/2009 12:26:23 PM GMT
the code works great, but it's rather unevolved and longwinded. is there a better way to do this perhaps with a for next loop?
thanks in advance!
Btn1 PIN RB.0 input PULLUP SCHMITT
Btn2 PIN RB.1 input PULLUP SCHMITT
Btn3 PIN RB.2 input PULLUP SCHMITT
Btn4 PIN RB.3 input PULLUP SCHMITT
Btn5 PIN RB.4 input PULLUP SCHMITT
Btn6 PIN RB.5 input PULLUP SCHMITT
SW1 PIN RB.6 input PULLUP SCHMITT
LEDS PIN RC
LED1 PIN RC.0
LED2 PIN RC.1
LED3 PIN RC.2
LED4 PIN RC.3
LED5 PIN RC.4
LED6 PIN RC.5
' -------------------------------------------------------------------------
' Constants
' -------------------------------------------------------------------------
open con 1
closed con 0
Baud CON "OT31250" ' for MIDI
Channel CON 0
CtrChange CON $C0 | Channel
' -------------------------------------------------------------------------
' Variables
' -------------------------------------------------------------------------
idx var byte
tmpB1 VAR Byte ' for subs/funcs
tmpB2 VAR Byte
tmpW1 VAR Word
' =========================================================================
PROGRAM Start
' =========================================================================
' -------------------------------------------------------------------------
' Subroutine / Function Declarations
' -------------------------------------------------------------------------
WAIT_MS SUB 1, 2 ' replaces PAUSE
TX_BYTE SUB 1 ' transmit a byte
TX_STR SUB 2 ' transmit a string
CNG_PGM SUB 1 ' Change MIDI program
' -------------------------------------------------------------------------
' Program Code
' -------------------------------------------------------------------------
Start:
Main:
IF btn1 = closed THEN
WAIT_MS 20 'debounce
IF btn1 = closed THEN
LOW LEDS
CNG_PGM 0
WAIT_MS 1000 'allow for program load time
HIGH LED1
DO WHILE btn1 = closed 'wait for button release
WAIT_MS 20
LOOP
ENDIF
ENDIF
IF btn2 = closed THEN
WAIT_MS 20 'debounce
IF btn2 = closed THEN
LOW LEDS
CNG_PGM 1
WAIT_MS 1000 'allow for program load time
HIGH LED2
DO WHILE btn2 = closed 'wait for button release
WAIT_MS 20
LOOP
ENDIF
ENDIF
IF btn3 = closed THEN
WAIT_MS 20 'debounce
IF btn3 = closed THEN
LOW LEDS
CNG_PGM 2
WAIT_MS 1000 'allow for program load time
HIGH LED3
DO WHILE btn3 = closed 'wait for button release
WAIT_MS 20
LOOP
ENDIF
ENDIF
IF btn4 = closed THEN
WAIT_MS 20 'debounce
IF btn4 = closed THEN
LOW LEDS
CNG_PGM 3
WAIT_MS 1000 'allow for program load time
HIGH LED4
DO WHILE btn4 = closed 'wait for button release
WAIT_MS 20
LOOP
ENDIF
ENDIF
IF btn5 = closed THEN
WAIT_MS 20 'debounce
IF btn5 = closed THEN
LOW LEDS
CNG_PGM 4
WAIT_MS 1000 'allow for program load time
HIGH LED5
DO WHILE btn5 = closed 'wait for button release
WAIT_MS 20
LOOP
ENDIF
ENDIF
IF btn6 = closed THEN
WAIT_MS 20 'debounce
IF btn6 = closed THEN
LOW LEDS
CNG_PGM 5
WAIT_MS 1000 'allow for program load time
HIGH LED6
DO WHILE btn6 = closed 'wait for button release
WAIT_MS 20
LOOP
ENDIF
ENDIF
GOTO Main ' loop
Post Edited (skynugget) : 10/20/2009 12:26:23 PM GMT

Comments
You COULD do some fancy stuff with bit masks and such, but it would make it alot less readable.
Bean.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Does that byte of memory hold "A", 65, $41 or %01000001 ?
Yes it does...
·
Here's the Main section:
Main: tmpBtns = GET_BTNS IF tmpBtns = %00000000 THEN Main ' wait for button press FOR idx = 0 TO 5 ' loop through six buttons mask = 1 << idx ' create mask for input check = tmpBtns & mask ' test the button IF check > 0 THEN ' was button pressed? Leds = mask ' yes, light its LED CNG_PGM idx ' change selected program DO tmpBtns = GET_BTNS LOOP UNTIL tmpBtns = %00000000 ' force release of all buttons GOTO Main ENDIF NEXT GOTO MainAs you can see, this calls a function called GET_BTNS which handles the input debouncing:
FUNC GET_BTNS gbIdx VAR tmpB1 gbResult VAR tmpB2 gbReseult = %00111111 ' assume pressed FOR gbIdx = 1 TO 25 ' 25ms debounce DELAY_MS 1 gbResult = gbResult &~ RB ' scan inputs NEXT RETURN gbResult ENDFUNCJust a note here: You're using active-low inputs. The GET_BTNS routine deals with that and then returns a value with "1" indicating a button press -- it's a little easier to work with in the main code. What I'm saying is that you don't have to change your hardware; this routine will work fine. Just remember that if button 1 is pressed the fuctnion will return %00000001.
Once you get your project working save a new version and try this to see how you like it. Even if you don't use it here, masking tricks are handy to have up one's sleeve.
Post Edited (JonnyMac) : 10/21/2009 7:48:07 PM GMT
I have always had problems with bit masks till now. I really wasn't concerned about space for this stage of the project, i just need to get it built and working. As I have time I would like to incorporate the ISR code from the Mo MIDI project to get status from the device, so it puts me a leg up. (If i can ever get the manual)
Here's the updated code that does the exact same thing above. Hope someone learns something about those fancy bitmasks!
DEVICE SX28, OSCXT2, TURBO, STACKX, OPTIONX, BOR42 FREQ 20_000_000 ID "BTN2MIDI" ' ------------------------------------------------------------------------- ' I/O Pins ' ------------------------------------------------------------------------- TX PIN RA.1 INPUT Btns PIN RB input PULLUP SCHMITT LEDS PIN RC output ' ------------------------------------------------------------------------- ' Constants ' ------------------------------------------------------------------------- Baud CON "OT31250" ' for MIDI Channel CON 0 CtrChange CON $C0 | Channel ' ------------------------------------------------------------------------- ' Variables ' ------------------------------------------------------------------------- tmpB1 VAR Byte ' for subs/funcs tmpB2 VAR Byte tmpW1 VAR Word tmpBtns VAR Byte idx VAR Byte mask VAR Byte check VAR Byte ' ========================================================================= PROGRAM Start ' ========================================================================= ' ------------------------------------------------------------------------- ' Subroutine / Function Declarations ' ------------------------------------------------------------------------- WAIT_MS SUB 1, 2 ' replaces PAUSE TX_BYTE SUB 1 ' transmit a byte TX_STR SUB 2 ' transmit a string CNG_PGM SUB 1 ' Change MIDI program GET_BTNS FUNC 1, 0, 0 ' Poll for button press ' ------------------------------------------------------------------------- ' Program Code ' ------------------------------------------------------------------------- Start: Main: tmpBtns = GET_BTNS IF tmpBtns = %00000000 THEN Main ' wait for button press FOR idx = 0 TO 5 ' loop through six buttons mask = 1 << idx ' create mask for input check = tmpBtns & mask ' test the button IF check > 0 THEN ' was button pressed? CNG_PGM idx ' yes, change selected program wait_ms 1000 ' pause for load time on device leds = mask ' light its LED DO tmpBtns = GET_BTNS LOOP UNTIL tmpBtns = %00000000 ' force release of all buttons GOTO Main ENDIF NEXT GOTO Main ' loop ' ------------------------------------------------------------------------- ' Subroutine / Function Code ' ------------------------------------------------------------------------- ' Use: WAIT_MS duration SUB WAIT_MS IF __PARAMCNT = 1 THEN tmpW1 = __PARAM1 ELSE tmpW1 = __WPARAM12 ENDIF PAUSE tmpW1 ENDSUB ' ------------------------------------------------------------------------- ' Use: TX_BYTE aByte SUB TX_BYTE SEROUT TX, Baud, __PARAM1 ENDSUB ' ------------------------------------------------------------------------- ' Use: TX_STR [noparse][[/noparse]String | Label] ' -- moves z-String to tx buffer ' -- "String" is an embedded string ' -- "Label" is a DATA label with z-String SUB TX_STR tmpW1 = __WPARAM12 ' get address of start DO READINC tmpW1, tmpB1 ' read a character IF tmpB1 = 0 THEN EXIT ' done? TX_BYTE tmpB1 ' no, transmit the char LOOP ENDSUB ' ------------------------------------------------------------------------- ' Use: CNG_PGM program# SUB CNG_PGM tmpB1 = __PARAM1 TX_BYTE CtrChange ' Control Change TX_BYTE tmpB1 ' Program Number ENDSUB ' ------------------------------------------------------------------------- ' Use: var = GET_BTNS FUNC GET_BTNS gbIdx VAR tmpB1 gbResult VAR tmpB2 gbResult = %11000000 ' bit 6,7 not used, assume pressed FOR gbIdx = 1 TO 25 ' 25ms debounce WAIT_MS 1 gbResult = gbResult | RB ' scan inputs NEXT GbResult = gbResult ^ $FF ' invert (1 = pressed) RETURN gbResult ENDFUNC ' -------------------------------------------------------------------------Post Edited (skynugget) : 10/21/2009 9:19:58 PM GMT
Hint: If one SUB/FUNC A is called by SUB/FUNC B then A should appear further down the listing -- this lets you use conditional compilation of routines to preserve space as your projects get bigger. Note, too, how with SX/B 2.0 the WAIT_MS subroutine is simplified to one [noparse][[/noparse]working] line. You'll also note that I replaced the FOR-NEXT loop with manual code; this is a personal style thing as I think one should limit the amount of code enclosed in any loop structure.
DEVICE SX28, OSCXT2, BOR42 FREQ 20_000_000 ID "BTN2MIDI" ' ------------------------------------------------------------------------- ' I/O Pins ' ------------------------------------------------------------------------- TX PIN RA.1 Btns PIN RB INPUT PULLUP SCHMITT LEDS PIN RC OUTPUT ' ------------------------------------------------------------------------- ' Constants ' ------------------------------------------------------------------------- Baud CON "OT31250" ' for MIDI Channel CON 0 CtrChange CON $C0 | Channel ' ------------------------------------------------------------------------- ' Variables ' ------------------------------------------------------------------------- tmpBtns VAR Byte chan VAR Byte mask VAR Byte check VAR Byte tmpB1 VAR Byte ' for subs/funcs tmpB2 VAR Byte tmpW1 VAR Word ' ------------------------------------------------------------------------- ' Subroutine / Function Declarations ' ------------------------------------------------------------------------- GET_BTNS FUNC 1, 0, 0, Byte ' Poll for button press CNG_PGM SUB 1, 1, Byte ' Change MIDI program TX_STR SUB 2, 2, Word ' transmit a string TX_BYTE SUB 1, 1, Byte ' transmit a byte WAIT_MS SUB 2, 2, Word ' replaces PAUSE ' ========================================================================= PROGRAM Main ' ========================================================================= Main: tmpBtns = GET_BTNS IF tmpBtns = %00000000 THEN Main ' wait for button press chan = 0 ' start with Button 1 Check_ChBtn: mask = 1 << chan ' create mask for input check = tmpBtns & mask ' test the button IF check > 0 THEN ' was button pressed? CNG_PGM chan ' yes, change selected program WAIT_MS 1000 ' pause for load time on device Leds = mask ' light its LED DO tmpBtns = GET_BTNS LOOP UNTIL tmpBtns = %00000000 ' force release of all buttons GOTO Main ENDIF INC chan ' next channel IF chan < 6 THEN Check_ChBtn ' more? GOTO Main ' ------------------------------------------------------------------------- ' Subroutine / Function Code ' ------------------------------------------------------------------------- ' Use: var = GET_BTNS FUNC GET_BTNS '{$IFUSED GET_BTNS} gbIdx VAR tmpB1 gbResult VAR tmpB2 gbResult = %00111111 ' bit 6,7 not used, assume pressed FOR gbIdx = 1 TO 25 ' 25ms debounce WAIT_MS 1 gbResult = gbResult &~ RB ' scan inputs NEXT RETURN gbResult '{$ENDIF} ENDFUNC ' ------------------------------------------------------------------------- ' Use: CNG_PGM program# SUB CNG_PGM '{$IFUSED CNG_PGM} newPgm VAR tmpB1 newPgm = __PARAM1 TX_BYTE CtrChange ' Control Change TX_BYTE newPgm ' Program Number '{$ENDIF} ENDSUB ' ------------------------------------------------------------------------- ' Use: TX_STR [noparse][[/noparse]String | Label] ' -- "String" is an embedded string ' -- "Label" is a DATA label with z-String SUB TX_STR '{$IFUSED TX_STR} tsAddr VAR tmpW1 tsChar VAR __PARAM1 tsAddr = __WPARAM12 ' get string address DO READINC tsAddr, tsChar ' get a character IF tsChar = 0 THEN EXIT ' abort on 0 TX_BYTE tsChar LOOP '{$ENDIF} ENDSUB ' ------------------------------------------------------------------------- ' Use: TX_BYTE aByte ' -- shell for SEROUT SUB TX_BYTE '{$IFUSED TX_BYTE} SEROUT TX, Baud, __PARAM1 '{$ENDIF} ENDSUB ' ------------------------------------------------------------------------- ' Use: WAIT_MS milliseconds ' -- replaces PAUSE SUB WAIT_MS '{$IFUSED WAIT_MS} PAUSE __WPARAM12 '{$ENDIF} ENDSUB ' -------------------------------------------------------------------------Post Edited (JonnyMac) : 10/23/2009 7:41:52 AM GMT
i don't quite understand how the compile time directive saves space? the way i understand it $ifused will not compile the code enclosed if its not called? which has seemed a little odd to me. why write code you're not going to use? got time to set me straight?