Shift registers

thunderace7

New Member
Hi. I want to find out a bit about shift registers and how they can be used for led sequencing. Does anyone know of a good tutorial? Or maybe there is a simple project I could take a look at?
Thanks.
 

thunderace7

New Member
There are a few issues with this program but I think I have most of them resolved to the extent that it passes the syntax check and will run in the editor. There is, however, one issue I would like some help with, please.
The program has a subroutine that contains an IF-THEN test. If this is fulfilled the program flow jumps out of the subroutine with a GOTO command before it gets to the RETURN.
Is this going to cause a stack overflow error of some kind or will all the unused return locations fall off the bottom of the stack when it gets full?
If I have to move the IF-THEN test out of the subroutine it will add another couple of hundred lines of code to the program.
Thanks.
 

inglewoodpete

Senior Member
There are a few issues with this program but I think I have most of them resolved to the extent that it passes the syntax check and will run in the editor. There is, however, one issue I would like some help with, please.
The program has a subroutine that contains an IF-THEN test. If this is fulfilled the program flow jumps out of the subroutine with a GOTO command before it gets to the RETURN.
Is this going to cause a stack overflow error of some kind or will all the unused return locations fall off the bottom of the stack when it gets full?
If I have to move the IF-THEN test out of the subroutine it will add another couple of hundred lines of code to the program.
Thanks.
Unfortunately, without seeing how your code is structured, members are unable offer much assistance. GoTo statements taking execution out of a subroutine and bypassing a return statement are usually fatal as far as programming goes. While it is possible to manage the use of GoTos, I avoid them like 'the' current virus! Please post your code.
 

Aries

New Member
If you want to "jump out" of a subroutine, it's better to set a "return value" in a variable and then RETURN, test the variable, and take whatever action you need.
Code:
...
gosub MySub
if b1 <> 0 then DoAJump
....

MySub:
b1 = 0
....
if .... then    ' need to "jump out"
  b1 = 1
  goto EndMySub
endif
......
EndMySub:
  return
 

thunderace7

New Member
Thanks but this seems much the same. there will need to be an IF...THEN test for every subroutine call, whether it be moving the one out of the subroutine or the one to test whether the one in the subroutine had a result. Either way I will need to add a lot of lines to the program. Unavoidable, it seems.
 

AllyCat

Senior Member
Hi,

Are you really calling the subroutine from (and thus returning to) "a hundred" different sections of the program? Perhaps you should be looking at a restructure?

However, it is "possible" to jump out of a subroutine with a GOTO (but it's certainly not good practice), because the "stack" (in both the PIC hardware and PICaxe Basic) is circular. So you can push addresses onto the stack as many times as you wish without any problems, BUT what you must NOT do is then to attempt any subsequent RETURN (because it will very probably be to the "wrong" location). Therefore, the only time that it might be justifiable (to use a GOTO within any subroutine) is if you are effectively jumping to a "Restart" or even a "Reset" location, for example if a calculation produced a "useless" result and you we returning to get some new data. In which case you might consider whether you could/should exit the subsroutine with a RESET command.

Cheers, Alan.
 

thunderace7

New Member
Hi, Alleycat. It's not really my program but one I have adapted from the link further up the thread.
There is an interrupt subroutine that sets a flag and this routine checks for the flag being set and takes appropriate action. Therefore the subroutine needs to be run very frequently to detect the interrupt as early as possible. As it happens, I have had to reduce the number of subroutine calls because I ran out of program space (08M2).
 

AllyCat

Senior Member
Hi,

An interrupt is NOT the same as a subroutine and jumping out of an interrupt routine (with a GOTO) is normally a total "No No". One reason is that the interrupt is only re-enabled by (i.e. after) a RETURN. I've not read through all the code you linked to because it looks like one of the "horrible" programs produced by Blockly. It does look as if it's jumping out of a subroutine, but effectively just to a restart, since "main:" appears to be the first line of the program.

Actually, this is one case where I would use a "GOTO", but for a completely different reason. That long string of IF varG = n THEN : GOTO cell_M is very inefficient and could be replaced by an ON varG GOTO cell_0, cell_1,...... instruction to reduce both program code size and execution time. Note that the "jump index" starts from zero (which can be easily accommodated) and the labels are sequential integers (which the program appears to do anyway). A useful feature is that if the index exceeds the number of address labels in the list, the program just falls into the next line.

Or, I suspect the whole program could be reduced in size very considerably by simply passing a few parameters (variables) to some generalised routines.

Cheers, Alan.
 

hippy

Technical Support
Staff member
There is an interrupt subroutine that sets a flag and this routine checks for the flag being set and takes appropriate action.
It is probably worth posting the code you have created and describing exactly what you need the program to do from the 'big picture' perspective because, as said; there is probably some easier way to achieve what you want to do.

A 'bale out and do something else' program, as this appears to be, can usually be coded in a different way.
 

thunderace7

New Member
Hi, Alan. Thanks for the reply. I'm not familiar with Blockly so I am unable to comment but it took me a while to get my head around the program. It looks like 2 programs spliced together and there are a few things that don't fit very well.
There is a push switch that initiates an interrupt routine. This increments a variable, re-enables the interrupt and Returns. At intervals throughout the program there is a test for the incremented variable with an appropriate action if true. This test used to be within a subroutine so only took up a couple of lines of code (actually, I think it was 4). Because I took the test out of the subroutine I had to insert it in the main program at every position where the subroutine is called. This is a lot of extra lines of code.
I like the idea of the On-Goto command and will probably use it.
 

thunderace7

New Member
Hi, Hippy. My problem, I think, is getting the interrupt to work efficiently. The purpose of the program is to help me understand how shift registers work. It contains a number of patterns, one of which is sent to the shift register and displayed on a row of 16 leds. The pattern to be sent is chosen by operating a push switch that causes an interrupt. The interrupt routine increments a variable, re-enables the interrupt and exits. Throughout the program are tests for the incremented variable and, if true, a jump to the part of the program that reads the variable, selects the pattern and sends it to the shift register. On the original program the test was within the subroutine that sends serial data to the shift register so it was performed for every step in the pattern. I took it out of the subroutine because I didn't think it was ok to jump out of a subroutine leaving the RETURN command unused. I thought the stack would eventually fill up with unused Returns and cause an overflow error. Perhaps I am getting confused with older types of Basic language that I used over 30 years ago.
How are interrupts typically used to initiate an action?
 

thunderace7

New Member
Currently, the code looks like this:

Code:
initialise:
    #picaxe 08m2
    Setfreq M4
    Symbol patternselect=b0
    Symbol currentpattern=b1
    Symbol i=b2
    Symbol delay=b3
    Symbol k=b4
    Symbol val=w3
    Symbol lsb=w4
    Symbol pausetime=w5
    Symbol sequence=w6
    
    Let dirs=%00010101
    Symbol dataline=C.0
    Symbol clockline=C.4
    Symbol latch=C.2
    Symbol switch1=pinC.3
    Symbol potentiometer=C.1

    Let sequence=%0000000000000000
    Gosub outsequence
    Pause 400
    Let patternselect=0
    Setint %00001000,%00001000

main:
    Readadc potentiometer,delay
    pausetime=delay*2
        If patternselect =1 Then
            Goto pattern1
        end if
        If patternselect =2 Then
            Goto pattern2
        end if
        If patternselect =3 Then
            Goto pattern3
        end if
        If patternselect =4 Then
            Goto pattern4
        end if
        If patternselect =5 Then
            Goto pattern5
        end if
        If patternselect =6 Then
            Goto pattern6
        end if
        If patternselect =7 Then
            Goto pattern7
        end if
        If patternselect=8 Then
            Goto pattern8
        end if
        If patternselect=9 Then
            Goto pattern9
        end if
        If patternselect=10 Then
            Goto pattern10
        end if
        If patternselect=11 Then
            Goto pattern11
        end if
        If patternselect=12 Then
            Goto pattern12
        end if
        If patternselect>=13 Then
            Goto patternblank
        end if
    Goto main

outsequence:
    Let val=sequence
        For i=1 To 16
            Let lsb=val&%000000000000001
                If lsb=1 Then High dataline
                Else Low dataline
                endif
            Pulsout clockline,1
            Let val=val/2
        Next i
    Pulsout latch,1
    Return

interrupt:
    debounce1:
        If switch1=1 Then
            Goto debounce2
            end if
        Goto debounce1
    debounce2:
        If switch1=0 Then
            Goto patterncount
            end if
        Goto debounce2
    patterncount:
        Inc patternselect
        Setint %00001000,%00001000
    Return

pattern1:
    Let currentpattern=patternselect
    Let sequence=%1000000110000001
    Pause pausetime
        Gosub outsequence
    Let sequence=%1100001111000011
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%1110011111100111
    Pause pausetime
        Gosub outsequence
    Let sequence=%1111111111111111
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%0111111001111110
    Pause pausetime
        Gosub outsequence
    Let sequence=%0011110000111100
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%0001100000011000
    Pause pausetime
        Gosub outsequence
    Let sequence=%0000000000000000
    Pause pausetime
        Gosub outsequence
    Goto main

pattern2:
    Let currentpattern=patternselect
    Let sequence=%1111000011110000
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%0000111100001111
    Pause pausetime
        Gosub outsequence
    Goto main

pattern3:
     ; code deleted to avoid breaking 10000 character limit
    Goto main

pattern4:
    Let currentpattern=patternselect
    Let sequence=%1100110011001100
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%0011001100110011
    Pause pausetime
        Gosub outsequence
    Goto main

pattern5:
    Let currentpattern=patternselect
    Let sequence=%1000000000000010
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%0100000000000001
    Pause pausetime
        Gosub outsequence
    Goto main

pattern6:
    Let currentpattern=patternselect
    Let sequence=%0000000000000000
        Gosub outsequence
    Pause pausetime
    Let sequence=%0000000000000001
        Gosub outsequence
    Pause pausetime
    For k=1 To 15
        If patternselect <> currentpattern Then
            Goto main
        endif
        Let sequence=sequence * 2
        Gosub outsequence
        If patternselect <> currentpattern Then
            Goto main
        endif
        Pause pausetime
    Next k
    Let sequence=%0000000000000000
        Gosub outsequence
    Pause pausetime
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%1000000000000000
        Gosub outsequence
    Pause pausetime
    For k=1 To 15
        If patternselect <> currentpattern Then
            Goto main
        endif
        Let sequence=sequence/2
        Gosub outsequence
        Pause pausetime
    Next k
    Pause pausetime
        Goto main

pattern7:
    Let currentpattern=patternselect
    Let sequence=%1110011111100111
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%0001100000011000
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%1110011001100111
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%1111111111111111
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%0111111001111110
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%0011110000111100
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%0001100000011000
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%0000000000000000
    Pause pausetime
        Gosub outsequence
    Goto main

pattern8:
      ; code deleted to avoid breaking 10000 character limit
    Goto main

pattern9:
    Let currentpattern=patternselect
    Let sequence=%0000000000000000
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%1111111111111111
    Pause pausetime
        Gosub outsequence
    Goto main

pattern10:
    ; code deleted to avoid breaking 10000 character limit
    Goto main

pattern11:
    Let currentpattern=patternselect
    Let sequence=%0010001000100010
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%0001000100010001
    Pause pausetime
        Gosub outsequence
    Goto main

pattern12:
    Let currentpattern=patternselect
    Let sequence=%0100010001000100
    Pause pausetime
        Gosub outsequence
    If patternselect <> currentpattern Then
        Goto main
    endif
    Let sequence=%1000100010001000
    Pause pausetime
        Gosub outsequence
    Goto main

patternblank:
    Let sequence=%0000000000000000
        Gosub outsequence
    Let patternselect=0
    Goto main
The outsequence subroutine used to look like this before I took the pattern test out.

Code:
outsequence:
    if patternselect <> currentpattern Then
        Goto main
    endif
    Let val=sequence
        For i=1 To 16
            Let lsb=val&%000000000000001
                If lsb=1 Then High dataline
                Else Low dataline
                endif
            Pulsout clockline,1
            Let val=val/2
        Next i
    Pulsout latch,1
    Return
As you can see, if the interrupt has incremented Patternselect, program flow will exit the subroutine prematurely.
 

thunderace7

New Member
I now have a question regarding switch bounce. The interrupt subroutine contains switch bounce code:

Code:
interrupt:
debounce1:
        If switch1=1 Then
            Goto debounce2
            end if
        Goto debounce1
debounce2:
        If switch1=0 Then
            Goto patterncount
            end if
        Goto debounce2
patterncount:
        Inc patternselect
        Setint %00001000,%00001000
    Return
My question is how long do switches normally bounce for? Is it all likely to be finished by the time the code reaches the patterncount label? If it is still bouncing, another interrupt will be generated. It is running on an 08M2 at 4MHz. By howmuch can I increase the speed before the code is faster than the switchbounce?

Thanks.
 

hippy

Technical Support
Staff member
Here's one way to switch between sequences, abort sequences and choose another half way through, without using interrupts at all.

It would be worth running this through PE6 simulation. It doesn't output to shift registers, merely updates Port B, and switches between patterns when C.0 is set high. There are three patterns implemented here.

Responsiveness to C.0 going high is a little slow because it's being simulated but should be fine on a real PICAXE chip.
Code:
#Picaxe 20M2
#No_Data

Symbol END_OF_PATTERN = $FF
Symbol PAUSE_TIME_MS  = 500

Symbol PUSH_BUTTON    = pinC.0

Symbol reserveW0      = w0 ; b1:b0
Symbol pauseTimer     = w1 ; b3:b2
Symbol patternSelect  = b4
Symbol stepSelect     = b5
Symbol sequence       = b6
Symbol buttonState    = b7

MainLoop:
  Do
    Do
      If stepSelect = END_OF_PATTERN Then
        stepSelect = 0
      End If
      Select Case patternSelect
        Case 0 : Gosub Pattern0
        Case 1 : Gosub Pattern1
        Case 2 : Gosub Pattern2
        Else   : Gosub NoMorePatterns
      End Select
    Loop While stepSelect = END_OF_PATTERN
    Gosub OutputSequence
    stepSelect = stepSelect + 1
    Gosub PauseAndCheckButtonPush
  Loop

NoMorePatterns:
  patternSelect  = 0
NoMoreSteps:
  stepSelect     = END_OF_PATTERN
  Return

Pattern0:
  Select Case stepSelect
    Case 0 : sequence = %01010101
    Case 1 : sequence = %10101010
    Else   : Gosub NoMoreSteps
  End Select
  Return

Pattern1:
  Select Case stepSelect
    Case 0 : sequence = %00001111
    Case 1 : sequence = %11110000
    Else   : Gosub NoMoreSteps
  End Select
  Return

Pattern2:
  Select Case stepSelect
    Case 0 : sequence = %00000001
    Case 1 : sequence = %00000010
    Case 2 : sequence = %00000100
    Case 3 : sequence = %00001000
    Case 4 : sequence = %00010000
    Case 5 : sequence = %00100000
    Case 6 : sequence = %01000000
    Case 7 : sequence = %10000000
    Else   : Gosub NoMoreSteps
  End Select
  Return

PauseAndCheckButtonPush:
  pauseTimer = 0
  Do
    If PUSH_BUTTON <> buttonState Then
      buttonState = buttonState ^ 1
      If buttonState = 1 Then
        patternSelect = patternSelect + 1
        stepSelect    = 0
        Return
      End If
    End If
    #IfDef SIMULATING
       Pause PAUSE_TIME_MS
       Return
    #EndIf
    Pause 10
    pauseTimer = PauseTimer + 10
  Loop Until pauseTimer >= PAUSE_TIME_MS
  Return

Outputsequence:
  dirsB = $FF
  pinsB = sequence
  Return
 

premelec

Senior Member
Switch bounce is usually less than a few milliseconds depending on type of switch and how much coffee operator has had... ;-o
 

thunderace7

New Member
That's the trouble when you get people involved. They are unreliable. A simple button press can be anything from a quick stab to a deliberate press and release that could total up to half a second. In the case of the latter, the debounce functionality would take care of the button press but a further interrupt would be generated by bouncing during button release.
Debouncing within normal programming would work ok because the program would have moved on and no longer be looking at what the button is doing. Interrupts, however, are a different matter and more susceptible to the inconsistencies of human behaviour.
 

thunderace7

New Member
Hippy, it will take me a while to fully understand your program. There are a number of commands with which I am not familiar, i.e. Select Case, Do Loop, Loop While, #IfDef. I will need to spend some time with the manuals.
 

lbenson

Senior Member
Interrupts, however, are a different matter and more susceptible to the inconsistencies of human behaviour.
True enough, but an interrupt will not be triggered within an interrupt unless you have again executed the SETINT command. That is why you don't do that until immediately before the RETURN. Pausing within an interrupt is usually discouraged, but in this case if you are getting bounce retriggerings when you leave the interrupt, you might inside the interrupt loop until the button is released (PAUSE 200: DO WHILE switch=1: LOOP) and then pause say 200ms to get past any release bounce.
 
Last edited:

thunderace7

New Member
I had Stack Underflow error today which was a new experience for me. I can't remember exactly what I was doing but I think it was caused by a Return without a Gosub.
 

Buzby

Senior Member
Try this in the simulator, then on a real chip.

Rich (BB code):
main: 

do
      pause 1000
      inc b0
      sertxd("Entered loop ",#b0,cr,lf)

      sertxd("Gosub main ",#b0,cr,lf)
      gosub main

      sertxd("Return",#b0,cr,lf)
      return

      sertxd("Loop end",#b0,cr,lf)
loop
PE should just flag a warning, not stop the simulation completely.

Cheers,

Buzby
 

Eng460

Well-known member
It is easy, worthwhile and interesting to do a simple breadboard experiment to see how long switch bounce interupts complete switch closure. Base it on which ever chip you have handy. Try different toggle switches, push buttons and slide switches, old and new, just whatever you have in your parts box.

Just use the count command, and an LED to tell you when it is ready to start counting and you must push the switch. Then a sertxd command can report the number of bounces. If you keep reducing the count period, it will soon be too short for you to respond, but you will have an idea of how many bounces. The result is most interesting.

Mechanically they all occur in the time between when the contacts are close enough to register as connected, and the time they are fully pressed home, so very quickly indeed.

You will need to be more creative with your programming to make the count period start at the first bounce to remove your reaction time from the equation, but personally, I found it a fascinating and informative experiment early in my Picaxe adventures. I wonder if pulsin to detect the first contact to start the timing would work? At some stage it might be worth increasing the chip speed, to see if you get even more counts.

Eng460
 

lbenson

Senior Member
It is easy, worthwhile and interesting to do a simple breadboard experiment to see how long switch bounce interrupts complete switch closure
Great idea. Something like
Code:
setfreq m32
w1=1000 ' initial count period
For b4 = 1 to 20
  do while switch=0: loop
  count pinx.y,w1,w0
  sertxd("Period: ",#w1," bounces: ",#w0)
  do while switch=1: loop
  w1=w1-50
next b4
Maybe adjust down below 50ms. Does w1 (count period) need to be adjusted for the clock speed?
 

Eng460

Well-known member
Hi Ibenson, that code looks to me like it would do it. Might have to go down in smaller steps to see when you start missing a few.

The other issue is the time taken to move on to Count after the first high. certainly higher speed might help there.

My interpretation of the section 2 instructions for count is that the speed does indeed affect the count period.

I suspect that it is necessary to do a few in manual first, that is a diode to tell you when to flick the switch manually, with a long count period to allow for your reaction time. This will allow collecting some data on how many bounces occur with a given switch, and how much this varies with successive operations.

Then your idea looks like a good one with a long period initially to see if you are missing any before the count starts, and progressively shorter to see when you start missing the last ones.

I must admit I never thought of trying to time them when I did the experiment, I was just fascinated to be able to count them. I did not even play with the speed to see if that resulted in a higher count.

Eng460
 

Eng460

Well-known member
Thinking a bit further, that code might be better if it included a pause 50 or similar after the sertxd just to make sure the serout and bounces are complete, so to be sure the denounce routine is properly included Before assuming the switch signal really is high.

Eng460
 

thunderace7

New Member
Every day is a school day!
Tried the bounce count program and all I got from the terminal was gibberish. Changed the processor speed and it became readable so I then realised that I had to change the baud rate when I changed the speed. With the terminal working I could not count any bounces until I realised that the circuit contains hardware antibounce components. I disconnected these and started seeing bounces. Had to increase the Period to 5000 with a step of -250 to see the full picture. Bouncing seems to stop at 1500.
I found that if I pressed and held the button I got no bounces at all but a more normal press and release produced up to 50 bounces (more commonly less than 15).
At least I now know that the hardware debounce works and bouncing is not an issue in this circuit.

Thanks to all.
 

thunderace7

New Member
The processor speed didnt seem to make any difference. Odd, i know but could the Count command be immune to speed changes?
 

Eng460

Well-known member
May I suggest that The count command is not immune, but the number of bounces is, as is the time Period for which the command looks for bounces.

If the slowest processor speed is fast enough to detect the bounces, faster won’t make any difference. However, if a slow Processor speed is missing bounces, a faster speed may pick them up As long as the count period is long enough to see the last one.

Hard to see exactly what is going on, but it seems to me that bouncing for 200 mS indicates a faulty switch that is not making reliable contact. Looking for Switch bounce, I think we are looking for that intermittent contact that occurs during sliding together between the contacts first meeting and when contact is firmly established. Even a push button action on this time scale has a microscopic degree of sliding as the contacts come together. Something detected at the speed of the microprocessor, but totally obscured by persistence of vision, inductance, capacitance and mechanical Inertia in more traditional circuits, where the response of the circuit is sufficiently slow that by the time it reaches it’s “on“ condition, the Period of bouncing is ancient history (relatively speaking).

Eng460
 

thunderace7

New Member
I think it might be due to the type of switch I am using and it is bouncing on the release of the switch. If I used a microswitch type the operation would be much crisper and more predictable. I am using a cheap switch and release bounces would occur within the increase period I was using and be dependent upon the timing of my index finger.
This is the switch I am using.
 

Attachments

premelec

Senior Member
Snap action helps - and small capacitor in parallel with contacts and Schmitt input... or software debounce...
 
Top