Shop OBEX P1 Docs P2 Docs Learn Events
QuickStart and Touch Button demo — Parallax Forums

QuickStart and Touch Button demo

ErNaErNa Posts: 1,752
edited 2014-11-27 19:08 in Propeller 1
Did anybody make use of the touch buttons on the quickstart board? In my case, there is obviously a heavy bouncing and I can not measure the pin voltage: the moment I touch the button with a scope probe, the switch is activated and so I do not have any information what really happens. The demo software (Touch Buttons LED Demo v1.0) starts sampling 100 Hz and then loops 32 times, free running and not synchronized to the sampling.

Comments

  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-11-23 10:01
    I've collected links to various QuickStart demos and projects in this thread.

    I thought the touchpads worked reasonably well with my QuickStart servo tester but there just so much overhead to monitor the pads, I don't use them often in my own projects. Buttons are really cheap and IMO work much better than the touchpads.
  • cavelambcavelamb Posts: 720
    edited 2014-11-23 10:07
    While they can be made to work well enough, I'd have to agree with Duane.
    Kind of a shame there isn't a connector site to hook external switches to them...
  • ErNaErNa Posts: 1,752
    edited 2014-11-23 11:02
    OK, my idea is to create a demo to show objects like my buttons object and the communication between propellers on an environment that is likely on the table of propeller heads. But I see, running the touchpads savely needs some more work to do. Hopefully I can finish that.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-11-23 11:24
    I'm not sure how much experience you have with the touchpads but I made a mini-tutorial for Jose in post #17 of his QuickStart BOE-Bot thread. Jose seemed pleased with how the touchpads worked.
  • ErNaErNa Posts: 1,752
    edited 2014-11-23 12:12
    I made some measurements and could see, that 50Hz mains are relevant to the behavior of the input voltage, I can see the input is triggered with 50Hz and a duty-cycle of about 20%. This is a result I didn't expect. Next I will see, how the decay takes place and why the scope has such an impact to the signal.
  • ErNaErNa Posts: 1,752
    edited 2014-11-23 13:23
    Playing around with different parameters:
    When bridging the pads with a conductor, that generates a clear signal, so a switch is the best solution, but not implemented.
    But the strength of the propeller is to do things differently.
    I now changed the decay time to half a ms, which gives a clear decay from 2.9 to .5V when measured with the scope (as mentioned: the scope "destroys" the signal, so this is not what happens when not measured. But now I replace the wait by a loop and the input read I output on another pin, so I can see, when the input detects low or high. It turns out: when I touch the central pad, the signal is active for 4,7ms in a 20ms period. So during about 8 decay periods of 40 the signal is measured active. That means: I have to measure not faster than mains frequency and should have more than 10 % duty cycle.
    That I will implement now and see, if it works stable.
    So two effects are combined: EMI from mains and resistive load to the pin. The moment I wet my finger, the electrical contact is dominant and the signal comes continuously. It takes rather long to dry the finger again.
    The signal definitely is pressure sensitive, the harder I push, the longer the period is. From the scope is visible, that the speed of touch can be detected! So a nice sensing device can be made this way.
    But there has to be an electrical contact from the skin to the pad, the moment I insulate the pad with paper or film tape, no signal can be generated.
  • ErNaErNa Posts: 1,752
    edited 2014-11-24 11:54
    In the meantime I have seen, a lot of work was done with the touch buttons, so I am in a good company. As usual I run into a problem with signed integers
    Loop
                  or        dira,     ToBuPins              ' set TestPins as outputs (high)
                  or        outa,     ToBuPins              ' set TestPins high, but keep as inputs
                  andn      dira,     ToBuPins              ' set TestPins as inputs (floating)           Test-Puls ist ausgegeben
    
                  mov       LoopCntr, #8                    ' 8 buttons to check
                  mov       RegstrPtr, #Registers           ' Set up pointer to first accumulator register
                  mov       OutValue, #0                    ' reset outputregister
    hLoop                                                   ' a little more than 1µs per loop
                  or  outa, SignalPin
                  mov       Increment, Reading              '
                  shr       Reading,   #1        wc         ' lsbit ins carry
                  and       Increment, #1                   ' isolate Bit 0
                  shl       Increment, #3                   ' times 8
                  sub       Increment, #1                   ' -1:  -1 or +7
    
                  movs      srce,      RegstrPtr            ' adds register to increment
                  movd      dest,      RegstrPtr            ' adjust destination
                  add       RegstrPtr, #1                   ' advance pointer
    
    srce          adds      Increment, 0_0                  ' add register to increment
    
                  adds      Increment, #15  wc              ' add offset lower limit 
    if_c          mov       Increment, #0                   ' limit if less -15
                  cmps      Increment, #32   wc
    if_nc         mov       Increment, #32                  ' limit to +15
                  subs      Increment, #15                  ' remove offset lower limit
                 andn  outa,SignalPin
    
    dest          mov       0_0,       Increment            ' mov Increment to register
    
                  shl       Increment, #1   wc              ' sign bit of increment to Carry
    
                  rcr       OutValue,  #1                   ' shift in carry to result
    
                  djnz      LoopCntr,  #hLoop
    
                  shr       OutValue,  #24                  ' shift Outvalue to the right
                  xor       OutValue,  ToBuPins             ' invert
                  wrlong    OutValue,  ToBuRegiPtr          ' write the result to RAM
                  wrlong    Registers, ToBuDbgPtr           ' write the result to RAM
    
    

    The outer loop emits the test pulse and Reading gets the response, that is a high or low bit for every button pushed.
    The first strange appearance: one expects to have a LOW read back, when ever the finger touches the pad. But the opposite is true.

    The input pins are 0-7, so 8 least significant bits have to be processed. That is done by the inner hloop.
    I copy reading to Increment and mask lsBit., reading is shifted to right to prepare for the next turn.
    My measurements have shown, that the result of delay varies over time with a 50Hz period, and a touched button can have a
    duty cycle low/high reading from about 10 to 100%.
    I am integrating the input to filter the signal and to count up/down with the same speed, high input has to count more. So I multiply the bit by 8 and subtract 1 to have now +7 for a high input and -1 for a low.

    Via a RegistrPtr I address the appropriate accumulated value and add this value to the new read. Then I limit the accumulator to +-15, and that is not working properly: lower limit is not detected
    srce          adds      Increment, 0_0                  ' add register to increment
    
                  adds      Increment, #15  wc              ' add offset lower limit 
    if_c          mov       Increment, #0                   ' limit if less -15
                  cmps      Increment, #32   wc
    if_nc         mov       Increment, #32                  ' limit to +15
                  subs      Increment, #15                  ' remove offset lower limit
                 andn  outa,SignalPin
    
    
    The first line adds to the current read the old accumulator value. The result can be any value from -16 to plus 23:
    If the accumulator was at the lower limit, -15, adding -1 should result in -16. If the upper limit was reached, adding +7 gives 23.
    I now add 15, so carry should be set when the value was -16 and now is -1. Then I set the value to 0.
    Cmps checks for the upper limit and sets value to 32. So increment should be positive and =< 32.
    In a next step I substract 15 to come back to +- value range. This value is stored to the accumulator register.

    The last step isolates the sign bit and writes it to an output register, as the final result will be only a bit per pin.

    In principle, that works nicely.
    There is one weak point: the lower limit is not detected.
    Any idea what is going wrong?
  • kuronekokuroneko Posts: 3,623
    edited 2014-11-24 12:11
    ErNa wrote: »
    I now add 15, so carry should be set when the value was -16 and now is -1. Then I set the value to 0.
    Why would the transition from -16 to -1 (by adding 15) result in carry being set? There is no overflow of any kind here. Can you elaborate?
  • ErNaErNa Posts: 1,752
    edited 2014-11-24 13:47
    Kuroneko, you are just gold ;-)
                  adds      Increment, #15                  ' add offset lower limit 
                  cmps      Increment, #0   wc
    if_c          mov       Increment, #0                   ' limit if less -15
    
    did the job. It's certainly not perfect, but the buttons now work nicely.
  • kuronekokuroneko Posts: 3,623
    edited 2014-11-24 21:14
    Couldn't you just use this:
    add     Increment, #15
            mins    Increment, #0
    
    or simply:
    srce    add     Increment, 0-0
            mins    Increment, m15    ' lower bound -15
            maxs    Increment, #15    ' upper bound +15
    
    m15     long    -15
    
  • ErNaErNa Posts: 1,752
    edited 2014-11-27 12:46
    OK, didn't come back until now: Yes, it looks good, do not see a difference but much better solution. The buttons work very reliable and it looks nice. Now it would make sense to check if this solutions also works in other places of the world. The results should be even better, if the time constants are adjusted to mains frequency and implementation of a hysteresis would be nice to have.

    I think a common BUTTON module is a nice feature to have and more functionality could be implemented. I will now integrate the latest BUTTONS version into my shutter controller. I've already seen, that the Button state, as counting up and down creates a high load to the network and it might be better to count with less resolution, related to the fingers time slice ;-)

    I could upload the latest version for Quickstart if there is interest
  • jazzedjazzed Posts: 11,803
    edited 2014-11-27 19:08
    Here's a button/led game I wrote just after the first Quickstart game contest ended.

    Rewrite it in Spin if you like.


    [php]
    /**
    * This is a QuickStart Whack-a-Mole game.
    *
    * Copyright 2011 by Steve Denson.
    * Rights MIT Licensed. See end of file.
    */

    /**
    * The Blue LED moles are eating up your QuickStart yard!
    * You must get rid of them!
    *
    * Gameplay:
    *
    * All living moles will run around the QuickStart burrow during
    * the game and you'll know they're alive by the dimly lit LEDs.
    *
    * Sometimes a mole will pop up as brighly lit LED and you must
    * wack the mole pad to knock it out.
    *
    * When the moles are gone, your QuickStart yard will celebrate!
    *
    */
    #include <stdlib.h> // for rand() and seed()
    #include <propeller.h> // for propeller functions.

    /* game utilities */
    void celebrate(void); // no more moles
    int rotateLeft(int mask);
    int rotateRight(int mask);

    /* simple utilities */
    void LEDOUT(int val); // set leds to val
    void msleep(int t); // sleep for t ms
    int getButton(int n); // used to get button states
    int getButtons(); // used to get button states

    /*
    * Main program entry point
    * Start with 8 moles and flick LEDs via rotating mask.
    * Choose a random mole to light for fraction of a second.
    * If the mole button is pushed while the LED is lit, kill the mole.
    * When all moles are dead celebrate - check button press for restart.
    */
    int main(void)
    {
    int molemask = 0xff; // live moles

    int ontime = 1; // flicker on time in ms
    int offtime = 80; // flicker off time

    int whacked = 0; // it's whacked?
    int direction = 1; // keep direction of LED mole run

    int popcount = 6; // initial popcount
    int countmask = 0x0700; // allow up to 7 mole run steps
    int popmask = 0x7; // only 8 LEDs, choose from rand

    int popupspot = 0; // show popup position
    int popuptime = 500; // popup on time

    /* mole run forever loop */
    for(;;)
    {
    /* nothing whacked */
    whacked = 0;
    /* seed random function with Propeller CNT */
    srand(CNT);

    /* blank moles */
    LEDOUT(0);
    /* sleep a little */
    msleep(offtime);

    /* if all moles gone, celebrate */
    while(!molemask) {
    /* make LEDs dance */
    celebrate();
    /* if button press, restart game */
    if(getButtons()) {
    molemask = 0xff;
    }
    }

    /* get popup spot */
    if(popcount-- == 0) {
    /* get random number */
    popupspot = rand();
    /* use upper bits for popup count */
    popcount = (popupspot & countmask) >> 8;
    /* use lower popmask bits for popup mole LED */
    popupspot &= popmask;

    /* show popup and check button */
    if(molemask & (1<<popupspot)) {
    /* set LED to show a mole to whack */
    LEDOUT((1<<popupspot) & molemask);
    /* single thread limit. sample twice */
    msleep(popuptime>>1);
    whacked = getButton(popupspot);
    msleep(popuptime>>1);
    whacked |= getButton(popupspot);
    /* set back to mole mask */
    LEDOUT(molemask);
    }
    }

    /* mole whacked ? */
    if(whacked) {
    molemask &= ~(1<<popupspot);
    }

    /* if a random bit is set switch direction */
    if(rand() & 0x20) {
    direction ^= 1;
    }

    /* set new mole run spots */
    if(direction) {
    molemask = rotateRight(molemask);
    }
    else {
    molemask = rotateLeft(molemask);
    }

    /* show mole run */
    LEDOUT(molemask);
    /* sleep a little so we can see the mole run */
    msleep(ontime);

    }
    return 0;
    }

    /*
    * make LEDs dance with a checker board pattern
    */
    void celebrate(void)
    {
    int n = 16;
    while(n-- > -1)
    {
    LEDOUT(0x55);
    msleep(50);
    LEDOUT(0xAA);
    msleep(50);
    }
    LEDOUT(0);
    msleep(400);
    }

    /*
    * rotate mask left
    */
    int rotateLeft(int mask)
    {
    int temp = mask << 1;
    mask = (temp & 0x100) ? 1 : 0;
    mask |= temp & 0xff;
    return mask;
    }

    /*
    * rotate mask right
    */
    int rotateRight(int mask)
    {
    int temp = mask >> 1;
    mask &= 0x01;
    mask <<= 7;
    mask |= temp;
    return mask;
    }

    /*
    * Set the LEDs to val
    */
    void LEDOUT(int val)
    {
    DIRA |= 0xff << 16;
    OUTA = (OUTA & ~(0xff<<16)) | (val<<16);
    }

    /*
    * msleep makes the cog wait for about t milliseconds.
    */
    void msleep(int t)
    {
    waitcnt((CLKFREQ/1000)*t+CNT);
    }

    /*
    * Get a specific button number.
    * buttons are enumerated from 0 to 7.
    */
    int getButton(int n)
    {
    return (getButtons() & (1<<n));
    }

    /*
    * Get all buttons at once
    * Run this function from HUB if compiled for LMM.
    */
    int getButtons()
    {
    int n = 16;
    int result = -1;
    int reading = 0;
    int pins = 0xFF;
    OUTA |= pins; //output high to buttons

    while(n--)
    {
    DIRA |= pins; //output high to buttons
    DIRA ^= pins; //switch to input
    reading = pins;
    msleep(2); //wait for RC time
    reading &= INA; //return button states
    result &= reading;
    }
    result = ~result & 0xFF;
    return result;
    }

    /*
    +
    | TERMS OF USE: MIT License
    +
    Permission is hereby granted, free of charge, to any person obtaining
    a copy of this software and associated documentation files
    (the "Software"), to deal in the Software without restriction,
    including without limitation the rights to use, copy, modify, merge,
    publish, distribute, sublicense, and/or sell copies of the Software,
    and to permit persons to whom the Software is furnished to do so,
    subject to the following conditions:

    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    +
    */[/php]
Sign In or Register to comment.