Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

comparing multiple button presses

Status
Not open for further replies.
MAKING SOME PROGRESS
it runs but it drops some of the leds on the challenge (not enabled)
got several MATCHES but I addd to the DELAYS and added CLS before writing to LCD.
changed the number of challenge helped but still not right. leds donot concide with the challenge. Seems to miss the 5th led/button combo
Code:
{
*****************************************************************************
*  Name    : UNTITLED.BAS                                                   *
*  Author  : [select VIEW...EDITOR OPTIONS]                                 *
*  Notice  : Copyright (c) 2021 [select VIEW...EDITOR OPTIONS]              *
*          : All Rights Reserved                                            *
*  Date    : 11/4/2021                                                      *
*  Version : 1.0                                                            *
*  Notes   :                                                                *
*          :                                                                *
*****************************************************************************
}

device = 18F43K22
clock = 16

// int osc and IO pin libraries
include "intosc.bas"
#option DIGITALIO_INIT = true       // automatically call setalldigital
include "setdigitalio.bas"

// lcd
#option LCD_DATA = PORTD.4
#option LCD_RS = PORTD.2
#option LCD_EN = PORTD.3
include "LCD.bas"

// hardware
dim SWT0 as PORTB.0
dim SWT1 as PORTB.1
dim SWT2 as PORTB.2
dim SWT3 as PORTB.3
dim SWT4 as PORTB.4
dim SWT5 as PORTB.5
dim SWT6 as PORTB.6
dim SWT7 as PORTB.7
dim SW_PORT as PORTB

dim LED0 as PORTC.0
dim LED1 as PORTC.1
dim LED2 as PORTC.2
dim LED3 as PORTC.3
dim LED4 as PORTC.4
dim LED5 as PORTC.5
dim LED6 as PORTC.6
dim LED7 as PORTC.7
dim LED_PORT as PORTC

// this option limits max numbers of PORTB keys, from 1-8
// if the ICSP programmer is connected, limit this to 6 (no RB6 or RB7)
#option MAX_NUM_KEYS = 4
#if not (MAX_NUM_KEYS in (1 to 4))
  #error MAX_NUM_KEYS, "MAX_NUM_KEYS must be 1-8"
#endif 
const MAX_KEY = MAX_NUM_KEYS
const KEY_MASKS(9) as byte = ($00, $01, $03, $07, $0F, $1F, $3F, $7F, $FF)

// this option controls the max length of the challenge sequence, from 1-23
#option MAX_SEQ_LENGTH = 4
#if not (MAX_SEQ_LENGTH in (1 to 23))
  #error MAX_SEQ_LENGTH, "MAX_SEQ_LENGTH must be 1 to 23"
#endif 
const MAX_SEQ = MAX_SEQ_LENGTH
  
//-------------------------------------
// subroutines and functions
//-------------------------------------

// wait for a keypress and return key number, 1-8
function GetKey() as byte
    // wait for a key press
    repeat
        if (SWT0 = 0) then
            result = 1
        elseif (SWT1 = 0) then
            result = 2
        elseif (SWT2 = 0) then
            result = 3
        elseif (SWT3 = 0) then
            result = 4
        elseif (SWT4 = 0) then
            result = 5
        elseif (SWT5 = 0) then
            result = 6
        elseif (SWT6 = 0) then
            result = 7
        elseif (SWT7 = 0) then
            result = 8
        else    // no valid key press           
            result = 0
        endif
        // limit response to max number of supported keys
        if (result > MAX_KEY) then
            result = 0
        endif           
    until (result <> 0)
    
    // now wait for any/all keys to be released
    while ((SW_PORT and KEY_MASKS(MAX_KEY)) <> KEY_MASKS(MAX_KEY))
        delayms(50)
    end while
    
    // wait a bit for release bounce (for next time)
    delayms(50)
end function

// given an ledno 1-8, turn it off (0) or on (>0)
// if ledno = 0 then set them all on or off
sub SetLED(ledno as byte, onoff as byte)
    select (ledno)
        case 0
            if (onoff > 0) then
                onoff = $FF
            endif               
            LED_PORT = onoff
        case 1
            LED0 = onoff
        case 2
            LED1 = onoff
        case 3
            LED2 = onoff
        case 4
            LED3 = onoff
        case 5
            LED4 = onoff
        case 6
            LED5 = onoff
        case 7
            LED6 = onoff
        case 8
            LED7 = onoff
    end select           
end sub

// given a number n=1-8, convert it to a char and append it to the string s
sub AddToString(byref s as string, n as byte)
    dim ch as char
    
    ch = n + byte("0")
    s = s + ch
end sub

// given a string s and an index n=1-8, get the char value
function GetFromString(byref s as string, n as byte) as byte
    dim ch as char
    
    // convert n to 0-based index
    if (n > 0) then
        n = n - 1
    endif       
    // get nth char from string
    ch = s(n)
    // convert numeric char to value 0-9
    result = byte(ch) - byte("0")
end function

// random number generator
// based on https://www.sfcompiler.co.uk/wiki/pmwiki.php?n=SwordfishUser.PseudoRandomNumberGenerator
dim LCG,GLFSR as byte
sub initRND()
    GLFSR = 1
    LCG = 84
end sub

function GetRND() as byte
    // init rand (just in case)
    if (LCG = 0) or (GLFSR = 0) then
        initRND()
    endif
    
    'LCG
    LCG=(7*LCG+17)

    'Galios LFSR
    if (GLFSR and 1) = 1 then
        GLFSR = GLFSR xor 135 '135 is the tap
        GLFSR = (GLFSR >> 1) or $80
    else
        GLFSR = (GLFSR >> 1)
    endif
    result = GLFSR xor LCG

    // for our application we want a number from 1 to MAX_KEYS
    result = (result mod MAX_KEY) + 1
end function


//-------------------------------------
// main program variables
//-------------------------------------
dim challenge as string     // challenge pattern
dim user as string          // user input
dim i as byte
dim n as byte
    
//-------------------------------------
// start of main program
//-------------------------------------
main:
// init hdw
TRISC = 0               // LED port - all outputs
TRISB = $FF             // SW port - all inputs
WPUB = $FF              // PORTB pullup mask - all PORTB pullups
INTCON2.bits(7) = 0     // RBPU bit - turn on pullups

//turn off all LEDs
SetLED(0, 0)

// init random number generator
initRND()

// do forever
while true
    // start off with both strings empty
    challenge = ""
    user = ""
    cls
    // generate a random 8-char challenge string
    // as the string is generated, light the LEDs
    lcd.writeat(1,1,"challenge...")
    for i = 1 to MAX_SEQ
        n = getRND()                // get a new random number from 1 to 8
        AddToString(challenge, n)   // add it to the challenge string
        SetLED(n, 1)                // turn on the corresponding LED
        delayms(1000)                // wait a bit...
        SetLED(n, 0)                // and turn led off
    next
            
    // get user response
    cls
    lcd.writeat(1,1,"enter keys...")
    for i = 1 to MAX_SEQ
        n = GetKey()
        AddToString(user, n)        // add it to the user response
        SetLED(n, 1)                // turn on the corresponding LED
        delayms(500)                // wait a bit...
        SetLED(n, 0)                // and turn led off
    next
     cls
    // compare the two strings
    if (challenge = user) then
        lcd.writeat(1,1,"match")
        delayms(2000)
    else
        lcd.writeat(1,1,"NO MATCH")
        delayms(2000)
    endif
    delayms(2000)
     cls
    // show the two strings
    lcd.writeat(1,1,"challenge", challenge)
    lcd.writeat(2,1,"user", user)
    delayms(3000)
end while
            
end program
 
Seems to miss the 5th led/button combo
Do you think that might be because you have "#option MAX_SEQ_LENGTH = 4",
so it only ever generates and looks for 4 button presses?

Was there something confusing about
Code:
// this option controls the max length of the challenge sequence, from 1-23
#option MAX_SEQ_LENGTH = 4

also, just FYI, the sequence
Code:
    if (challenge = user) then
        lcd.writeat(1,1,"match")
        delayms(2000)
    else
        lcd.writeat(1,1,"NO MATCH")
        delayms(2000)
    endif
    delayms(2000)
could be/is the same as
Code:
    if (challenge = user) then
        lcd.writeat(1,1,"match")
    else
        lcd.writeat(1,1,"NO MATCH")
    endif
    delayms(4000)
 
I changed the #option MAX_SEQ_LENGTH = 5
but it still wants more keypress.
More than the challenge
 
I don't see how... both the challenge and response loops use
for i = 1 to MAX_SEQ
and MAX_SEQ is a constant.

With MAX_SEQ_LENGTH = 5 how many keypresses does it take?
 
I ran the code at a slower pace and the challenge asks for 3 buttons but I need to enter 4 buttons to finish the sequence.
It is missing a digit/led for the challenge.
to help from getting wrong code version I now am using a USB drive extender so now each project goes on a different memory stick.
seems to help with locating different code versions.
have orginal, tumbleweed2 and tumbleweed3 on one stick
 
I ran a slightly modified version on some hdw and it seems to work ok for me, but I didn't use an LCD or LEDs.
I made some changes so that the numbers start off a little more random,
and it lights up the LEDs while you press the buttons (for better feedback)

When it starts up it'll ask you to press a key to start playing

There are two different things you can change...
// this option limits the max numbers of PORTB keys used, from 1-8
// in case you don't want to use all 8 buttons
#option MAX_NUM_KEYS = 4

// this option controls the max length of the challenge sequence,
// from 1-23 chars (number of button presses)
#option MAX_SEQ_LENGTH = 4


Try this version and see if it works any better for you...
Code:
program simple_simon

device = 18F43K22
clock = 16

// int osc and IO pin libraries
include "intosc.bas"
#option DIGITALIO_INIT = true       // automatically call setalldigital
include "setdigitalio.bas"

// lcd
#option LCD_DATA = PORTD.4
#option LCD_RS = PORTD.2
#option LCD_EN = PORTD.3
include "LCD.bas"
include "convert.bas"

// hardware
dim SWT0 as PORTB.0
dim SWT1 as PORTB.1
dim SWT2 as PORTB.2
dim SWT3 as PORTB.3
dim SWT4 as PORTB.4
dim SWT5 as PORTB.5
dim SWT6 as PORTB.6
dim SWT7 as PORTB.7
dim SW_PORT as PORTB

dim LED0 as PORTC.0
dim LED1 as PORTC.1
dim LED2 as PORTC.2
dim LED3 as PORTC.3
dim LED4 as PORTC.4
dim LED5 as PORTC.5
dim LED6 as PORTC.6
dim LED7 as PORTC.7
dim LED_PORT as PORTC

// this option limits the max numbers of PORTB keys used, from 1-8
// if the ICSP programmer is connected, limit this to 6 (no RB6 or RB7)
#option MAX_NUM_KEYS = 4
#if not (MAX_NUM_KEYS in (1 to 8))
  #error MAX_NUM_KEYS, "MAX_NUM_KEYS must be 1-8"
#endif 
const MAX_KEY = MAX_NUM_KEYS
const KEY_MASKS(9) as byte = ($00, $01, $03, $07, $0F, $1F, $3F, $7F, $FF)

// this option controls the max length of the challenge sequence, 
// from 1-23 chars (button presses)
#option MAX_SEQ_LENGTH = 4
#if not (MAX_SEQ_LENGTH in (1 to 23))
  #error MAX_SEQ_LENGTH, "MAX_SEQ_LENGTH must be 1 to 23"
#endif 
const MAX_SEQ = MAX_SEQ_LENGTH
  
//-------------------------------------
// subroutines and functions
//-------------------------------------

// given an ledno 1-8, turn it off (0) or on (>0)
// if ledno = 0 then set them all on or off
sub SetLED(ledno as byte, onoff as byte)
    select (ledno)
        case 0
            if (onoff > 0) then
                onoff = $FF
            endif               
            LED_PORT = onoff
        case 1
            LED0 = onoff
        case 2
            LED1 = onoff
        case 3
            LED2 = onoff
        case 4
            LED3 = onoff
        case 5
            LED4 = onoff
        case 6
            LED5 = onoff
        case 7
            LED6 = onoff
        case 8
            LED7 = onoff
    end select           
end sub

// flash an LED on-off with delay
sub FlashLED(ledno as byte)
    const FLASH_DELAY = 500
    
    SetLED(ledno, 1)            // turn on the corresponding LED
    delayms(FLASH_DELAY)        // wait a bit...
    SetLED(ledno, 0)            // and turn led off
    delayms(FLASH_DELAY)
end sub

// wait for a keypress and return key number, 1-8
// turns on LED while the key is pressed
function GetKey() as byte
    const KEY_BOUNCE_DELAY = 50     // msec delay for key bounce
    
    // wait for a key press
    repeat
        if (SWT0 = 0) then
            result = 1
        elseif (SWT1 = 0) then
            result = 2
        elseif (SWT2 = 0) then
            result = 3
        elseif (SWT3 = 0) then
            result = 4
        elseif (SWT4 = 0) then
            result = 5
        elseif (SWT5 = 0) then
            result = 6
        elseif (SWT6 = 0) then
            result = 7
        elseif (SWT7 = 0) then
            result = 8
        else    // no valid key press
            result = 0
        endif
        // limit response to the max number of supported keys
        if (result > MAX_KEY) then
            result = 0
        endif
    until (result <> 0)
    
    // turn on the corresponding LED
    SetLED(result, 1)
    
    // now wait for all keys to be released
    while ((SW_PORT and KEY_MASKS(MAX_KEY)) <> KEY_MASKS(MAX_KEY))
        delayms(KEY_BOUNCE_DELAY)
    end while
    
    // wait a bit for any release bounce
    delayms(KEY_BOUNCE_DELAY)

    // turn off the LED
    SetLED(result, 0)
end function

// given a number n=1-8, convert it to a char and append it to the string s
sub AddToString(byref s as string, n as byte)
    dim ch as char
    
    ch = n + byte("0")
    s = s + ch
end sub

// given a string s and an index n=1-8, get the char value
function GetFromString(byref s as string, n as byte) as byte
    dim ch as char
    
    // convert n to 0-based index
    if (n > 0) then
        n = n - 1
    endif       
    // get nth char from string
    ch = s(n)
    // convert numeric char to value 0-9
    result = byte(ch) - byte("0")
end function

// random number generator
// based on https://www.sfcompiler.co.uk/wiki/pmwiki.php?n=SwordfishUser.PseudoRandomNumberGenerator
dim LCG,GLFSR as byte
sub initRND(reseed as byte=0)
    if (reseed = 0) then
        GLFSR = 1
        LCG = 84
    endif
    LCG = reseed
    GLFSR = LCG xor $55     // just making the start values very different - not really important
    if (GLFSR = 0) then     // except that GLFSR must not be zero
        GLFSR=1 
    endif
end sub

function GetRND() as byte
    // init rand (just in case)
    if (LCG = 0) or (GLFSR = 0) then
        initRND()
    endif
    
    // LCG
    LCG=(7*LCG+17)

    // Galios LFSR
    if (GLFSR and 1) = 1 then
        GLFSR = GLFSR xor 135 '135 is the tap
        GLFSR = (GLFSR >> 1) or $80
    else
        GLFSR = (GLFSR >> 1)
    endif
    result = GLFSR xor LCG

    // for our application we want a number from 1 to MAX_KEY
    result = (result mod MAX_KEY) + 1
end function


//-------------------------------------
// main program variables
//-------------------------------------
dim challenge as string     // challenge pattern
dim user as string          // user input
dim i as byte
dim n as byte
    
//-------------------------------------
// start of main program
//-------------------------------------
main:
// init hdw
TRISC = 0               // LED port - all outputs
TRISB = $FF             // SW port - all inputs
WPUB = $FF              // PORTB pullup mask - all PORTB pullups
INTCON2.bits(7) = 0     // RBPU bit - turn on pullups

//turn off all LEDs
SetLED(0, 0)

// wait for user to press a key
// use the response time to seed the random number generator
LCG = 0
lcd.writeat(1,1,"press key 1-", MAX_KEY+byte("0"), "...")
while ((SW_PORT and KEY_MASKS(MAX_KEY)) = KEY_MASKS(MAX_KEY))
    LCG = LCG + 1
end while
initRND(LCG)

// do forever
while true
    // start off with both strings empty
    challenge = ""
    user = ""
    
    // generate a random challenge string (up to MAX_SEQ chars)
    // as the string is generated, light the LEDs
    lcd.cls
    lcd.writeat(1,1,"challenge...")
    for i = 1 to MAX_SEQ
        n = getRND()                // get a new random number from 1 to MAX_KEY
        AddToString(challenge, n)   // add it to the challenge string
        FlashLED(n)                 // show rand number using LEDs
    next
            
    // get user response
    lcd.cls
    lcd.writeat(1,1,"enter ", dectostr(MAX_SEQ), " keys...")
    for i = 1 to MAX_SEQ
        n = GetKey()                // get user keypress (shown on LEDs)
        AddToString(user, n)        // add it to the user response
    next

    // compare the two strings
    lcd.cls
    if (challenge = user) then
        lcd.writeat(1,1,"match")
    else
        lcd.writeat(1,1,"NO MATCH")
    endif
    delayms(2000)

    // show the strings (up to 10 chars on a 16x LCD)
    lcd.cls
    lcd.writeat(1,1,"gen : ", challenge)
    lcd.writeat(2,1,"user: ", user)
    delayms(2000)
end while
            
end program
 
this code works good. I changed the MAX_NUM_KEYS = 4
to 5 and seems to work well.
going to mount pcboard in an enclosure with batteries and change the MAX_NUM_KEYS TO 8
should work
I want to add a buzzer and mix in the TONE.BAS code.
every time an LED is enabled or when a button is pressed, the buzzer emits a tone coresponding to the button/led.
not going to use the RTTL version, just the simple TONE.bas code.
 
MAKING PROGRESS
its not pretty but it needs work
the CASE SELECT needs some work.
the buzzer sounds 2x instead of 1 thinking of a counter to count only 1 beep?
Code:
{
*****************************************************************************
*  Name    : UNTITLED.BAS                                                   *
*  Author  : [select VIEW...EDITOR OPTIONS]                                 *
*  Notice  : Copyright (c) 2021 [select VIEW...EDITOR OPTIONS]              *
*          : All Rights Reserved                                            *
*  Date    : 11/6/2021                                                      *
*  Version : 1.0                                                            *
*  Notes   :                                                                *
*          :                                                                *
*****************************************************************************
}

Device = 18F43K22
Clock = 16

// int osc and IO pin libraries
Include "intosc.bas"
#option DIGITALIO_INIT = true       // automatically call setalldigital
Include "setdigitalio.bas"

// lcd
#option LCD_DATA = PORTD.4
#option LCD_RS = PORTD.2
#option LCD_EN = PORTD.3
#OPTION TONE_OUTPUT_PIN = PORTA.0
Include "LCD.bas"
Include "convert.bas"
INCLUDE "TONE1.BAS"

// hardware
Dim SWT0 As PORTB.0
Dim SWT1 As PORTB.1
Dim SWT2 As PORTB.2
Dim SWT3 As PORTB.3
Dim SWT4 As PORTB.4
Dim SWT5 As PORTB.5
Dim SWT6 As PORTB.6
Dim SWT7 As PORTB.7
Dim SW_PORT As PORTB

Dim LED0 As PORTC.0
Dim LED1 As PORTC.1
Dim LED2 As PORTC.2
Dim LED3 As PORTC.3
Dim LED4 As PORTC.4
Dim LED5 As PORTC.5
Dim LED6 As PORTC.6
Dim LED7 As PORTC.7
Dim LED_PORT As PORTC
DIM ix as byte
dim note as word

// this option limits the max numbers of PORTB keys used, from 1-8
// if the ICSP programmer is connected, limit this to 6 (no RB6 or RB7)
#option MAX_NUM_KEYS = 4
#if Not (MAX_NUM_KEYS in (1 to 8))
  #error MAX_NUM_KEYS, "MAX_NUM_KEYS must be 1-8"
#endif
Const MAX_KEY = MAX_NUM_KEYS
Const KEY_MASKS(9) As Byte = ($00, $01, $03, $07, $0F, $1F, $3F, $7F, $FF)

// this option controls the max length of the challenge sequence,
// from 1-23 chars (button presses)
#option MAX_SEQ_LENGTH = 4
#if Not (MAX_SEQ_LENGTH in (1 to 23))
  #error MAX_SEQ_LENGTH, "MAX_SEQ_LENGTH must be 1 to 23"
#endif
Const MAX_SEQ = MAX_SEQ_LENGTH
 
//-------------------------------------
// subroutines and functions
//-------------------------------------

// given an ledno 1-8, turn it off (0) or on (>0)
// if ledno = 0 then set them all on or off
Sub SetLED(ledno As Byte, onoff As Byte)
    Select (ledno)
        Case 0
            If (onoff > 0) Then
                onoff = $FF
            EndIf               
            LED_PORT = onoff
        Case 1
            LED0 = onoff
             TONE1.play(NOTE_A4)
             DELAYMS(500)
             TONE1.STOP()   
        Case 2
            LED1 = onoff
             TONE1.play(NOTE_D4)
             DELAYMS(500)
             TONE1.STOP()   
        Case 3
            LED2 = onoff
            TONE1.play(NOTE_C5)
             DELAYMS(500)
             TONE1.STOP()   
        Case 4
            LED3 = onoff
            TONE1.play(NOTE_F4)
             DELAYMS(500)
             TONE1.STOP()   
        Case 5
            LED4 = onoff
            TONE1.play(NOTE_A6)
             DELAYMS(500)
             TONE1.STOP()   
        Case 6
            LED5 = onoff
            TONE1.play(NOTE_B4)
             DELAYMS(500)
             TONE1.STOP()   
        Case 7
            LED6 = onoff
            TONE1.play(NOTE_A4)
             DELAYMS(500)
             TONE1.STOP()   
        Case 8
            LED7 = onoff
            TONE1.play(NOTE_D4)
             DELAYMS(500)
             TONE1.STOP()   
    End Select           
End Sub

// flash an LED on-off with delay
Sub FlashLED(ledno As Byte)
    Const FLASH_DELAY = 500
    
    SetLED(ledno, 1)            // turn on the corresponding LED
    DelayMS(FLASH_DELAY)        // wait a bit...
    SetLED(ledno, 0)            // and turn led off
    DelayMS(FLASH_DELAY)
End Sub

// wait for a keypress and return key number, 1-8
// turns on LED while the key is pressed
Function GetKey() As Byte
    Const KEY_BOUNCE_DELAY = 50     // msec delay for key bounce
    
    // wait for a key press
    Repeat
        If (SWT0 = 0) Then
            result = 1
        ElseIf (SWT1 = 0) Then
            result = 2
        ElseIf (SWT2 = 0) Then
            result = 3
        ElseIf (SWT3 = 0) Then
            result = 4
        ElseIf (SWT4 = 0) Then
            result = 5
        ElseIf (SWT5 = 0) Then
            result = 6
        ElseIf (SWT6 = 0) Then
            result = 7
        ElseIf (SWT7 = 0) Then
            result = 8
        Else    // no valid key press
            result = 0
        EndIf
        // limit response to the max number of supported keys
        If (result > MAX_KEY) Then
            result = 0
        EndIf
    Until (result <> 0)
    
    // turn on the corresponding LED
    SetLED(result, 1)
    
    // now wait for all keys to be released
    While ((SW_PORT And KEY_MASKS(MAX_KEY)) <> KEY_MASKS(MAX_KEY))
        DelayMS(KEY_BOUNCE_DELAY)
    End While
    
    // wait a bit for any release bounce
    DelayMS(KEY_BOUNCE_DELAY)

    // turn off the LED
    SetLED(result, 0)
End Function

// given a number n=1-8, convert it to a char and append it to the string s
Sub AddToString(ByRef s As String, n As Byte)
    Dim ch As Char
    
    ch = n + Byte("0")
    s = s + ch
End Sub

// given a string s and an index n=1-8, get the char value
Function GetFromString(ByRef s As String, n As Byte) As Byte
    Dim ch As Char
    
    // convert n to 0-based index
    If (n > 0) Then
        n = n - 1
    EndIf       
    // get nth char from string
    ch = s(n)
    // convert numeric char to value 0-9
    result = Byte(ch) - Byte("0")
End Function

// random number generator
// based on https://www.sfcompiler.co.uk/wiki/pmwiki.php?n=SwordfishUser.PseudoRandomNumberGenerator
Dim LCG,GLFSR As Byte
Sub initRND(reseed As Byte=0)
    If (reseed = 0) Then
        GLFSR = 1
        LCG = 84
    EndIf
    LCG = reseed
    GLFSR = LCG Xor $55     // just making the start values very different - not really important
    If (GLFSR = 0) Then     // except that GLFSR must not be zero
        GLFSR=1
    EndIf
End Sub

Function GetRND() As Byte
    // init rand (just in case)
    If (LCG = 0) Or (GLFSR = 0) Then
        initRND()
    EndIf
    
    // LCG
    LCG=(7*LCG+17)

    // Galios LFSR
    If (GLFSR And 1) = 1 Then
        GLFSR = GLFSR Xor 135 '135 is the tap
        GLFSR = (GLFSR >> 1) Or $80
    Else
        GLFSR = (GLFSR >> 1)
    EndIf
    result = GLFSR Xor LCG

    // for our application we want a number from 1 to MAX_KEY
    result = (result Mod MAX_KEY) + 1
End Function


//-------------------------------------
// main program variables
//-------------------------------------
Dim challenge As String     // challenge pattern
Dim user As String          // user input
Dim i As Byte
Dim n As Byte
    
//-------------------------------------
// start of main program
//-------------------------------------
main:
// init hdw
TRISC = 0               // LED port - all outputs
TRISB = $FF             // SW port - all inputs
WPUB = $FF              // PORTB pullup mask - all PORTB pullups
INTCON2.bits(7) = 0     // RBPU bit - turn on pullups

//turn off all LEDs
SetLED(0, 0)

// wait for user to press a key
// use the response time to seed the random number generator
LCG = 0
LCD.WriteAt(1,1,"press key 1-", MAX_KEY+Byte("0"), "...")
While ((SW_PORT And KEY_MASKS(MAX_KEY)) = KEY_MASKS(MAX_KEY))
    LCG = LCG + 1
End While
initRND(LCG)
TONE1.INIT()
// do forever
While true
    // start off with both strings empty
    challenge = ""
    user = ""
     //PUT A NOTE FOR EACH LED
    // generate a random challenge string (up to MAX_SEQ chars)
    // as the string is generated, light the LEDs
    LCD.Cls
    LCD.WriteAt(1,1,"challenge...")
    For i = 1 To MAX_SEQ
        n = GetRND()                // get a new random number from 1 to MAX_KEY
        AddToString(challenge, n)   // add it to the challenge string
        FlashLED(n)                 // show rand number using LEDs
    Next
        
    // get user response
    LCD.Cls
    LCD.WriteAt(1,1,"enter ", DecToStr(MAX_SEQ), " keys...")
    For i = 1 To MAX_SEQ
        n = GetKey()                // get user keypress (shown on LEDs)
        AddToString(user, n)        // add it to the user response
    Next

    // compare the two strings
    LCD.Cls
    If (challenge = user) Then
        LCD.WriteAt(1,1,"match")
    Else
        LCD.WriteAt(1,1,"NO MATCH")
    EndIf
    DelayMS(2000)

    // show the strings (up to 10 chars on a 16x LCD)
    LCD.Cls
    LCD.WriteAt(1,1,"gen : ", challenge)
    LCD.WriteAt(2,1,"user: ", user)
    DelayMS(2000)
End While
            
End Program
 
The tone plays twice because it plays when you turn on the led and then again when it turns off.
Don't add a counter, just add code to only play the note when the led turns on.
Here's a replacement SetLED routine to do just that (using the original tone module, not TONE1)
Code:
// given an ledno 1-8, turn it off (0) or on (>0)
// if ledno = 0 then set them all on or off
// added: play a note when an led is turned on
Sub SetLED(ledno As Byte, onoff As Byte)
    dim note as word

    Select (ledno)
        Case 0
            If (onoff > 0) Then
                onoff = $FF
            EndIf               
            LED_PORT = onoff
            note = NOTE_PAUSE
        Case 1
            LED0 = onoff
            note = NOTE_A4
        Case 2
            LED1 = onoff
            note = NOTE_D4
        Case 3
            LED2 = onoff
            note = NOTE_C5
        Case 4
            LED3 = onoff
            note = NOTE_F4
        Case 5
            LED4 = onoff
            note = NOTE_A6
        Case 6
            LED5 = onoff
            note = NOTE_B4
        Case 7
            LED6 = onoff
            note = NOTE_A4
        Case 8
            LED7 = onoff
            note = NOTE_D4
    End Select           

    // if led is turned on then play the note for 500ms
    if (onoff > 0) then
        tone.play(note, 500)
        tone.wait()
    endif
End Sub

I've also attached another version of the game that works a little more like the original simon game,
where it does one challenge, then two, then three, etc
 

Attachments

  • simon.bas
    7.9 KB · Views: 187
thanks
I ran the SIMON code and it is pretty good
going to work on the orginal code with your revision suggestions
 
Why not study and try to understand the working code. You may learn something.

Mike.
P.S. tumbleweed, your patience is commendable.
 
I inserted Tumbleweeds suggestion but could not locate the proper tone.bas to use and found that if I change/ add this sub route all is good so far.

// flash an LED on-off with delay
Sub FlashLED(ledno As Byte)
Const FLASH_DELAY = 500

SetLED(ledno, 1) // turn on the corresponding LED
note = (ledno)
DelayMS(FLASH_DELAY) // wait a bit...
SetLED(ledno, 0) // and turn led off
DelayMS(FLASH_DELAY)
End Sub

this seems to work as it turns an LED on and tone plays.
going to clean up my little mess and do some editing to the LCD writeat code.
Organially wanted to time the user but ?
This is for my 11 year old grandson and calling it WICKED 5
If the player is really slow or can't get a match then the LCD displays an insult like "hope you wern't this slow in school"
Something funny but not dirty.
Have some congragulator sayings as well.





















i
 
but could not locate the proper tone.bas to use
That's because it looks like at some point you changed/copied/renamed the file tone.bas to tone1.bas and changed the
module name as well, why I don't know.

There's nothing in that module you needed to change, and now it's "tone1", so none of the example code works.
You just make life difficult.
 
SORRY
I have so many different versions of different code. Thats why I am using 4 USB sticks for all my codes. easier to find.
I changed it to tone1 so I can have several tone.bas versions in same file.
Just uses tone1 or tone2 or TONE3.
I only changed the module name.
didn't want to interfer with the RTTTL tone
 
I changed it to tone1 so I can have several tone.bas versions in same file.
Just uses tone1 or tone2 or TONE3.
That's my point. You should only need one version, tone.bas.

What's the difference between tone1, tone2, and tone3?
 
I got mixed up with the rtttl and tone modules. if in same file then they don't work as intended.
I am getting things sorted out with the USB extender
 
I got mixed up with the rtttl and tone modules. if in same file then they don't work as intended.
I wrote them so that they DO work together.

If they don't work together for you, you're using them incorrectly, or using the wrong files.
You can get the files from the swordfish website here: ToneLibraryAndRingtoneRTTTLPlayer

You shouldn't need to change them... they can be setup using various #options before including the modules.
Just ask.
 
I didn't change anything in the two different tone files. I just renamed then so I can include them in the same folder.
Attempting to change the #option code
tried using INDEX ect. but?
With correct matches the num of keys increases/
no match num of keys decreases.

Code:
// this option limits the max numbers of PORTB keys used, from 1-8
// if the ICSP programmer is connected, limit this to 6 (no RB6 or RB7)
#option MAX_NUM_KEYS = 1            //INCREASE THIS W/ CORRECT MATCHES tried using a variable such as INDEX
#if Not (MAX_NUM_KEYS in (1 to 8))
  #error MAX_NUM_KEYS, "MAX_NUM_KEYS must be 1-8"
#endif
Const MAX_KEY = MAX_NUM_KEYS
Const KEY_MASKS(9) As Byte = ($00, $01, $03, $07, $0F, $1F, $3F, $7F, $FF)

// this option controls the max length of the challenge sequence,
// from 1-23 chars (button presses)
#option MAX_SEQ_LENGTH = 3
#if Not (MAX_SEQ_LENGTH in (1 to 23))
  #error MAX_SEQ_LENGTH, "MAX_SEQ_LENGTH must be 1 to 23"
#endif
Const MAX_SEQ = MAX_SEQ_LENGTH
 
'#option MAX_NUM_KEYS' sets the number of buttons you want to use.
You have 8 buttons on your board, which would make the game VERY difficult (#option MAX_NUM_KEYS = 8).

The original SIMON game had 4 buttons (#option MAX_NUM_KEYS = 4).
This doesn't have anything to do with how many button presses there are, just WHICH buttons.
If you set it to 4 then it only uses buttons 1, 2, 3, and 4. If you set it to 2 then it only uses buttons 1 and 2.

How MANY button presses (ie the number of challenges before the game ends) is set by '#option MAX_SEQ_LENGTH'

With correct matches the num of keys increases/no match num of keys decreases.
This would be the code in the simon.bas file attached to post #30. If there isn't a match all the LEDs turn on and the game ends
(actually the number of keys don't decrease, it starts over with a new game)

That's the way I remember the original simon game to work. It gives you one challenge, then two, then three,
etc. If you get it wrong, game over. It does this until it reaches MAX_SEQ_LENGTH.

If you don't want it to work that way, then use the code in post #27 with the SetLED() routine from post #30.
 
Last edited:
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top