Virtual Shift Register

Chris Kelly

Active member
Hi everyone

This is my first post. I'm new to programming, my background is diy electronics, and my latest project uses 8No shift registers to make a 64-step 'loop'. The data is input at the first step and gets fed back into the register from another of the steps (which therefore sets the length of the loop). This idea takes up alot of real estate and I was hoping to use a Picaxe chip instead.

I'm willing to learn the code myself but I was hoping I could describe what I think is needed so that someone could tell me if I'm approaching it correctly?:

- For an 8 step loop this would need 1 byte.
- on the rising edge of a clock pulse, check whether the input pin is high
- if so, add 1 to the byte (e.g it becomes 00000001)
- check if the MSB is a 1
- if so, make a temporary variable = 1
- double the byte value to shift the data to the left
- add the temporary variable to the byte
- reset the temporary variable to 0 again

This should keep the data looping around, and i could expand this idea up to 8 bytes to get a 64 bit register.

Is this the correct idea? My way of setting the loop length works via a binary counter which counts while a button is pressed, and uses a 16 gate switch to select a position on the shift register to 'tap' into the data for feedback. This is obviously more complex so for now I just wanted to see if using a number of bytes is the right approach.

Sorry for the long winded explanation!

Chris
 

hippy

Technical Support
Staff member
Welcome to the PICAXE forum.

You should be able to do what you want and this is my variation of what you suggest, using four 16-bit registers rather than eight 8-bit registers -

Code:
#Picaxe 08M2
#Terminal 4800

Symbol DAT_IN     = pinC.4
Symbol CLK_IN     = pinC.3

Symbol reserveW0  = w0

Symbol bits63to48 = w1
Symbol bits47to32 = w2
Symbol bits31to16 = w3
Symbol bits15to00 = w4

#Macro ShiftTheRegister( register, dummy, inputBit )
  register = inputBit Max 1 + register + register
#EndMacro

Main:
  Do
    Gosub ShowShiftRegisterBits
    Do : Loop Until CLK_IN = 0
    Do : Loop Until CLK_IN = 1
    ShiftTheRegister( bits63to48, "<-", bits47to32 & $8000 )
    ShiftTheRegister( bits47to32, "<-", bits31to16 & $8000 )
    ShiftTheRegister( bits31to16, "<-", bits15to00 & $8000 )
    ShiftTheRegister( bits15to00, "<-", DAT_IN             )
  Loop

ShowShiftRegisterBits:
  w0 = bits63to48 : Gosub ShowW0bits : SerTxd( " "    )
  w0 = bits47to32 : Gosub ShowW0bits : SerTxd( " "    )
  w0 = bits31to16 : Gosub ShowW0bits : SerTxd( " "    )
  w0 = bits15to00 : Gosub ShowW0bits : SerTxd( CR, LF )
  Return  

ShowW0bits:
  SerTxd( #bit15, #bit14, #bit13, #bit12     )
  SerTxd( #bit11, #bit10, #bit9,  #bit8, " " )
  SerTxd( #bit7,  #bit6,  #bit5,  #bit4      )
  SerTxd( #bit3,  #bit2,  #bit1,  #bit0      )
  Return
You can run that in simulation under PE6. Set C.4 as your data input, set C.3 high then low to clock the data into the shift register.

The shift register is implemented so bit 63 is on the left, bit 0 on the right, so the bits shift towards the left, clocked on C.3 going high.
 

BESQUEUT

Senior Member
Hi everyone

This is my first post. I'm new to programming, my background is diy electronics, and my latest project uses 8No shift registers to make a 64-step 'loop'. The data is input at the first step and gets fed back into the register from another of the steps (which therefore sets the length of the loop). This idea takes up alot of real estate and I was hoping to use a Picaxe chip instead.

I'm willing to learn the code myself but I was hoping I could describe what I think is needed so that someone could tell me if I'm approaching it correctly?:

- For an 8 step loop this would need 1 byte.
- on the rising edge of a clock pulse, check whether the input pin is high
- if so, add 1 to the byte (e.g it becomes 00000001)
- check if the MSB is a 1
- if so, make a temporary variable = 1
- double the byte value to shift the data to the left
- add the temporary variable to the byte
- reset the temporary variable to 0 again

This should keep the data looping around, and i could expand this idea up to 8 bytes to get a 64 bit register.

Is this the correct idea? My way of setting the loop length works via a binary counter which counts while a button is pressed, and uses a 16 gate switch to select a position on the shift register to 'tap' into the data for feedback. This is obviously more complex so for now I just wanted to see if using a number of bytes is the right approach.

Sorry for the long winded explanation!

Chris
I think that SHIFTIN Command will do the job...
But hippy answer is probably better...
 

Buzby

Senior Member
Hi Chris,

This is my take on your problem.

By using 1 byte to hold just 1 bit, it makes adjusting the length of the buffer easier. This will give a maximum buffer of about 100, or more if you give up some variables. I'd stick with starting at 32, which gives a max length of 96.

The one-shot technique only runs this code on the rising edge of the clock pulse, so the rest of the time the PICAXE can be doing something else. This could be, say, driving a display to show the buffer length ( or even the buffer contents ), or adjusting the length with an analogue pot ( easy ) or rotary encoder ( a bit tricky). It all really depends on what your full application needs.

Cheers,

Buzby

Rich (BB code):
#picaxe 08m2
#no_data

' Circular buffer

symbol ClkPin           = pinC.1    ' Clock pin
symbol DatPin           = pinC.2    ' Data in pin
symbol OutPin           = C.4       ' Data out pin

symbol BufLen           = b4        ' Used to hold length of buffer
symbol BufEnd           = b5        ' Used to hold address of last byte of buffer
symbol OneShot1         = b6        ' Used to detect rising edge of clock pulse
symbol MyPtr            = b7        ' Not necessary, see later.


symbol BufStart         = 32        ' Buffer Start address 
                                    ' ( Set as low as you want, but leave room for varables ! )

' Code starts here
' ----------------

BufLen = 5                          ' Buffer length ( Don't go past RAM end, 127 on 08M2 )
BufEnd = BufStart + BufLen - 1      ' Calculate the end of the buffer
bptr = BufStart                     ' Set byte ptr to start of buffer


do ' Main loop
      
      if ClkPin = 1 then                  ' If clock pin is high ...    
            if OneShot1 = 0 then          '  and one-shot memory low  
                  OneShot1 = 1                  ' set one-shot memory
                  @bptr = DatPin                '     Save data at current bptr address
                  inc bptr                      '     Increment bptr address
                  if bptr > BufEnd then         '     If past last byte ...
                        bptr = BufStart         '           ... reset ptr
                  endif
      
      MyPtr = bptr ' Not really necessary, 
                   ' but lets you see value of bptr without switching tabs
                  
            
                  if @bptr <> 0 then            ' Output data is at current bptr address, 
                        high OutPin             ' because we incremented after storing input data.
                  else
                        low OutPin
                  endif
            endif
      endif
      oneshot1 = ClkPin             ' Update one-shot memory
      

      '
      '
      ' Put code here to manipulate 'BufLen'
      '
      ' ( Or other functions you might think of,
      '   once you discover the power of PICAXE )  
      '

loop 'Main loop
 

hippy

Technical Support
Staff member
By using 1 byte to hold just 1 bit, it makes adjusting the length of the buffer easier.
I did think about using @bPtr to actually hold bits per byte but you beat me to it.

One trick when using bPtr RAM as a buffer is not to set the end of buffer, but to make that so the last plus one wraps round to zero, and calculate the start -

Code:
Symbol ITEMS_NEEDED = 4
Symbol BUFFER_START = $1000 - ITEMS_NEEDED

bPtr = BUFFER_START
SerTxd( "Start of buffer = ", #bPtr, CR, LF )
That automagically works for any M2 or X2 PICAXE chip, for that example - 08M2 = 124, 14M2/18M2/20M2 = 508, 20X2 =124, 28X2/40X2 = 252.

Though note you have to move the BUFFER_START value into 'bPtr' before it becomes the correct, valid, value.

Once done, that can simplify determining when one has gone past the end of buffer, for example -

Code:
bPtr = BUFFER_START
w0  = 0
Do
  w0 = w0 + 1
  SerTxd( "Item ", #w0, " is at ", #bPtr, CR, LF )
  bPtr = bPtr + 1
Loop Until bPtr = 0
And it can really simplify code which happens to include an @bPtrInc -

Code:
bPtr = BUFFER_START
Do
  SerTxd( "RAM[", #bPtr, "] = ", #@bPtrInc, CR, LF )
Loop Until bPtr = 0
Using 'bPtr = -1' will also always set it to the last address as well, so going the other way is fairly easy too, but one does need to set a word variable to hold the BUFFER START value after moving it to 'bPtr', because "bPtr < BUFFER_START" is always true.

Code:
bPtr = BUFFER_START : bufferStart = bPtr
bPtr = -1
w0  = ITEMS_NEEDED
Do
  SerTxd( "Item ", #w0, " is at ", #bPtr, CR, LF )
  w0   = w0   - 1
  bPtr = bPtr - 1
Loop Until bPtr < bufferStart
Code:
bPtr = BUFFER_START : bufferStart = bPtr
bPtr = -1
Do
  SerTxd( "RAM[", #bPtr, "] = ", #@bPtrDec, CR, LF )
Loop Until bPtr < bufferStart
One can set 'bufferStart' once at the beginning of the program and then use that in 'bPtr = bufferStart' where needed.
 

Chris Kelly

Active member
Wow I've got a lot of reading up to do :D

I ran hippy's original code in PE6 and it compiles. Is there a simple way I can monitor the data at the bit where data is input?
 

Buzby

Senior Member
Hi hippy,

I did think of using wrap-around, but seeing as Chris is a beginner I chose to make it obvious how the code worked.

I'm sure that bit-twiddling will get a shorter, smarter solution, but it would need a lot more explaining, and be more difficult to adapt.

There's always more than one way to skin a cat.
 

hippy

Technical Support
Staff member
Is there a simple way I can monitor the data at the bit where data is input?
That really depends on how you mean by monitoring the bit input.

You can add the following in the 'ShowShiftRegisterBits' routine to show what the new input data bit clocked in was -

Code:
  w0 = bits15to00 
  SerTxd( "Data clocked in was ", #bit0, CR, LF )
You could replace the entire 'ShowShiftRegisterBits' routine with that.
 

AllyCat

Senior Member
Hi,

Hmm, I refrained from contributing to this thread yesterday because I "disagreed" with ALL the answers to a newcomer and thought I might be having a Bad Hair Day (I do still have some). :) But SHIFTIN is an instruction only for X2 PICaxes (as are the "direct" shift-left and shift-right instructions <<1 and >>1); also using 64 bytes to represent bits in a shift register will be S-L-O-W. And whilst it is useful to show what advanced features are available to a newcomer, I would also have avoided using a MACRO as being an unnecessary complication for a "first" program.

I was going to recommend using Words in place of Bytes, but even here we have the complication of the bytes being in the "wrong" order: i.e. b3,b2,b5,b4,b7,b6,,b9,b8 in hippy's example. I don't believe that even the PE6 simulator window can (directly) display all the bits of any Word variable? So I would have organised the "long word" as w4:w3:w2:w1 with an explanation that the component bytes need to be read left-to right but "from the bottom up" (i.e. b9. ... b2). So this is my "take" for the OP. :

Welcome to the forum. ;) Yes, your ideas are basically sound, but can be refined a little, specifically for the PICaxe. Nearly all PICaxe "internal operations" are actually 16 bits so it's more efficient to work with just 4 Words. But a useful trick is to start development with just (4) byte variables (i.e. 32 bits in total) for simpler simulation/analysis and then you can easily change a few "B"s to "W"s later on for the full 64 bits. Beware that even using words, the "shift register" will be quite slow compared with "real hardware", or even another microcontroller (if not using an interpreter or high level programming language).

PICaxe has only 4 "bit-addressable" bytes b0 - b3 (aka w0 and w1) so you need another method to read and write bits within a larger range of bytes or words. The LSB end is easy to Read or Write, just use AND 1 or OR 1 (or + 1 as you suggested) respectively. For the MSB end you need to use the "weight" of the bit, i.e. 128 (or $80) for a byte or 32768 ($8000) for a word. It's worth defining (e.g.) SYMBOL MSbit = 128 , particlarly if planning to move up from bytes to words at a later stage.

To read the MSB (as a 0 or 1), Hippy has used AND $8000 followed by MAX 1 , but a simple divide by (/) 32768 can work directly on the complete word. Normally it would be daft to use a full 16-bit divison in place of two simple "logic" operations, but there's not much time difference with a PICaxe (and there is no other option but / 2 for right-shifing in M2 chips). For left-shifting you can use (e.g.) w1 = w1 * 2 , but w1 = w1 + w1 is slightly faster and may be needed because of PICaxe Basic's strict left to right calculation priority (again as in hippy's example).

Cheers, Alan.
 

Chris Kelly

Active member
Hi Alan

Thanks for the input. Looks like there is definitely more than one approach!

I hadn't given enough consideration to the processing time, but this could definitely be a factor:

A 64 step sequence would actually be 16 beats split into 4 parts per step. At 120bpm (I'm building a drum machine) then each clock 'step' would need to complete in 500ms or less. At higher bpm's this would reduce accordingly
 

hippy

Technical Support
Staff member
500ms is an eternity, even for a PICAXE, especially if you go for a 20X2 which can run at 64MHz. I calculate something more in the region of 1ms for clocking in anew bit into the register. A few thousand beats per minute ought to be good enough for even the most hardcore techno music lover :)

For a drum machine there are usually multiple tracks so perhaps a byte per step might be better which can hold 8 tracks. A PICAXE should be able to handle 8 buttons for the tracks and an 'add that' button. Or maybe a push button keypad.

The PICAXE can also do MIDI output if you happen to have a MIDI drum machine or keyboard lying around. MIDI could even be used for input.

What might be an idea is to sit down and have a think about what you'd like. Give that as a specification, and members here can pull that apart, suggest what will or won't, might or might not, work and suggest the best way of doing it.
 

Chris Kelly

Active member
Thanks Hippy :)
Alot of the things I'm planning to use the Picaxe for should be simple (e.g/ using it for ADC's). But the main aim is to get the shift register looper to work.

I've built it using ic's, but there are a ton of chips. At the risk of boring you guys to death, here is how it works:
- press and hold button #1
- this resets a binary counter and starts counting again until the button is released. Max count = 16.
- Each count closes 1 of 16 analog switches on a 74HC4067 ic. This selects the feedback location from the shift register chain. Each switch connects to a 'tapping' position along the chain. For a 64 step register this means 1 tap every 4 steps. n.b/ there is no tap at the first position, to prevent a loop of only 1 beat in length
- the length of the loop is determined by how long you hold down button #1
- The shift register clock runs at 4x the rate of the counter clock.
- another button (#2) allows me to add data into the register 'in real time'.
- pressing #1 quickly will reset the counter and clear the loop since there is no feedback path

If any of this seems feasible to replicate in code I'd be grateful for any help you can offer!
 

Buzby

Senior Member
You could replicate these functions with some switches and a PICAXE, but seeing as you're using a PICAXE I would go for a more intuitive approach.

An analogue pot to set the length, and a 2 digit digital display to show the length. Once the length is set, pressing a button would start the sequence, and the display would show the current step count.

If you wanted a more sophisticated display, then a 20x4 OLED would offer the possibility of showing the full memory in real-time, with markers for 'occupied' slots. This could then let you edit the pattern visually, so making it easy to correct bum notes.

You could also store multiple patterns, with an easy method of switching between them, opening the opportunity for live performances.

Once you've got a PICAXE in there the options are endless !
 

hippy

Technical Support
Staff member
What you have seems entirely possible to implement in a PICAXE.

It is a bit difficult to figure out how it is used in practice and what the musical results are from the hardware implementation details, that is, what it does rather than how it currently achieves it.

I get, if I've read it right, how the first push can create one to four bars of 16 beats, but I am a bit lost, once that's done, as to how you can add live beats into the shift register without that shortening the shift cycle.

I am also somewhat lost on how shifting the shift register and the counter which closes analogue switches interact.
 

Chris Kelly

Active member
In practice I use button #1 as a 'record' button - I press it down and then use button #2 to add 'beats' into the 'loop'. When i release button #1 this ends the loop, and the rhythm plays back with the same order and timing as it was first entered during the recording phase. Extra 'beats' can be added while the loop continues playing by pressing #2 when i need to.

CD4027BE Shift register ic's have 8 output pins which go from low to high as data is shifted along with each clock pulse. Pin 5 of the ic is the 'first' output in the register. This connects to the base of an NPN transistor which triggers sound circuitry elsewhere.

Data is added to the ic via pin 7, and so this is where button #2 connects, and also where the feedback is sent to from the analog switch ic (74HC4067). This ic has 16 switches with one common i/o pin. It is a multiplexer and so it closes 1 of the 16 possible switches depending on the state of 4 control pins (s0, s1, s2, s4), so a binary input of say s0=1, s1=1, s2=0, s3=1 would close switch #15. The binary inputs to these control pins come from another ic which counts in binary while button #1 is pressed and stops when released. This then keeps one of the 16 switches closed and sets the length of the 'loop'. It's probably simpler to imagine you have a shift register of just 16 stages. Stage 1 connects to analog switch 1, stage 2 to switch 2 etc. Lets say i press and hold button #1 for 4 beats then release it. This means switch 4 is closed, and therefore data from shift register stage 4 gets sent through switch 4 and gets fed back into the data pin of the register. You then get a 4 beat 'loop'.

The difference in my setup is just that i built 64 stages in the register, and so only every 4 stages has a corresponding switch. This was just to add more flexibility with the beat timing.

Hope that explains it better 😁
 

Buzby

Senior Member
Wow !.

CD4000's, HC74's, acres of veroboard, dozens of chips, hundreds of interconnects. That brings back memories of the kind of things I built for fun back in the day.

Then I discovered microcontrollers. First it was 8060, now PICAXE. Now the 'what it does' is not limited by 'how it's built'.

If you've got the kind of mind that can design a logic circuit using chips from scratch, then you will have no problem programming.

What PICAXE hardware have you got ?
 

Buzby

Senior Member
08M2 is cheap and powerful, but severely limited for IO, and I think your going to need plenty of that.

I'd go for 14M2 which has more IO, or 20X2 which definately gives the most 'bangs per buck'.

There is no difference in downloading to any of the PICAXE chips, just press the 'Program' button and PE takes care of it, so there's no need to 'practice'.

This is an interesting project, please keep us informed of your progress.

And if you get stuck you will always find a helpful voice here on the forum.

Cheers,

Buzby
 

techElder

Well-known member
Just my thoughts on the PICAXE to use. For battery powered equipment I tend to go with the 20X2, but the workhorse PICAXE that I've settled on is the 28X2. Its size is manageable in a pin & socket package and it has a surface mount size, too. Lots of I/O available, lots of memory and program space. And lots of speed/frequency options, too.
 

Chris Kelly

Active member
Just my thoughts on the PICAXE to use. For battery powered equipment I tend to go with the 20X2, but the workhorse PICAXE that I've settled on is the 28X2. Its size is manageable in a pin & socket package and it has a surface mount size, too. Lots of I/O available, lots of memory and program space. And lots of speed/frequency options, too.
Tjanks for the recommendation!
 

hippy

Technical Support
Staff member
I think I have now got my head around what it's doing and what it achieves.

An 08M2 would be good enough for handling two input buttons, a trigger and LCD status outputs, but there may be advantages in an M2 which has more RAM variables or an X2 which has Scratchpad and potentially easier timing control if using an on-chip timer for accuracy.

Additional LED displays may also be useful for indicating recording or beats while being played back.

The reasoning for RAM or Scratchpad is that implementation may be better done by shifting multiple bytes rather than shifting bits. Though it is not necessary to actually shift bytes which would, as noted, be slower. The effect of shifting bytes can be replicated by using a circular buffer. Rather than bytes being shifted and the current output picked form a particular 'fixed bit', an index can cycle through all the bytes picking out the 'bit' it points to. @bPtrInc and @ptrInc are well suited to that.

Both seem equally feasible so it probably doesn't matter here, but bytes do open the door to having multiple channels as suggested earlier which might be useful in similar extended projects.

My suggestion would be for a 20X2 because that has SETTIMER which can generate a consistent interrupt when a new tick and shift is required. Timing is core to the entire project.
 

AllyCat

Senior Member
Hi,

Oh well, I may as well "disagree" with "everybody" again (except maybe hippy who has just posted). :)

Your original hardware might have lots of connections and have a very "fast" capability, but your application seems to be quite "slow" and need few (I/O) connections (a few switches and an output pin)?

Personally, I started with 20M2s as having the most "pins per Buck" and pinout compatibility with the 20X2 if additional speed or functions proved to be needed (which never have been for me). But in practice now, I nearly always use 08M2s for the "challenge" of how much can be done with a tiny chip at minimal power. And with the I2C bus it can have an almost "infinite" I/O capability if required.

However, I wouldn't normally recommend the 08M2 to a newcomer (except where it's particularly appropriate for the application) but a 14M2 or 20M2. Particularly as your application is "Audio-Related", take a look at the 14M2 + MP3 Player Kit (plus an AA battery box) which IMHO is an excellent way to get a 14M2 chip on a PCB with LEDS and a switch, whether the MP3 Audio output is ultimately applicable or not. Also, the USB Programming Cable is rather a "Luxury" (i.e. there are cheaper options) but may save you a lot of hassle in the long term.

Cheers, Alan.
 

hippy

Technical Support
Staff member
Personally, I started with 20M2s as having the most "pins per Buck" and pinout compatibility with the 20X2 if additional speed or functions proved to be needed (which never have been for me). But in practice now, I nearly always use 08M2s for the "challenge" of how much can be done with a tiny chip at minimal power. And with the I2C bus it can have an almost "infinite" I/O capability if required.
I loved the 18X for what could be done if one were prepared to jump through hoops, but the M2's and X2's made that unnecessary - why hamper oneself and slow things down with a battle to make things work when it 'just works' on a more capable PICAXE ?

I did however miss the hoop jumping and that morphed into enjoying the challenge of making things work on the smallest 08M2. But, beyond having that fun, having a project done can outweigh the challenge of any self-imposed limitation. As you say ...

However, I wouldn't normally recommend the 08M2 to a newcomer (except where it's particularly appropriate for the application) but a 14M2 or 20M2.
The 08M2 is a good chip to start with if new to programming or PICAXE devices, but not necessarily the best chip to start with if one has a specific project in mind, except where it is clearly suitable for the task or smallest size is essential.

I favour the 20X2 these days. The main advantage being that, with careful design, a 20X2 can usually be swapped with a 20M2 and vice-versa, or an 08M2 or 14M2 can be dropped in the same socket at the end of the day if desired.

So, for me, it's usually design for a 20X2 ( or 20M2 for multi-tasking ), keeping in mind being able to port it towards something smaller when done. And, if it can't be, or there's no inclination to do so, it still works using what it was designed for.

That suits my "get it working, and then optimise" philosophy.
 

Chris Kelly

Active member
Hi Buzby
I tried your circular buffer code in PE6 and I think it could be a better option for me than using shift registers. Can I just ask though - sometimes when I manually clock the program with the C.1 pin, it takes two full high states to advance the pointer i.e - high, low,high.
Is this just a quirk of using pe6?
 

Buzby

Senior Member
Hi Chris,

There was a slight bug in my code, sorry !.

Here is a new version, with a working 1-shot.

It should not miss any counts this time.

Cheers,

Buzby

Rich (BB code):
#picaxe 14m2
#no_data

' Circular buffer

symbol ClkPin           = pinC.1    ' Clock pin
symbol DatPin           = pinC.2    ' Data in pin
symbol OutPin           = C.4       ' Data out pin

symbol BufLen           = b4        ' Used to hold length of buffer
symbol BufEnd           = b5        ' Used to hold address of last byte of buffer

symbol MyPtr            = b6        ' Not necessary, see later.

symbol pincopy          = bit29
symbol pinmem           = bit30     ' Pin memory
symbol OneShot1         = bit31     ' Used to detect rising edge of clock pulse


symbol BufStart         = 32        ' Buffer Start address 
                                    ' ( Set as low as you want, but leave room for varables ! )

' Code starts here
' ----------------

BufLen = 5                          ' Buffer length ( Don't go past RAM end, 127 on 08M2 )
BufEnd = BufStart + BufLen - 1      ' Calculate the end of the buffer
bptr = BufStart                     ' Set byte ptr to start of buffer


do ' Main loop
            
      PinCopy  = ClkPin                   ' Only read pin once, to avoid glitches
      OneShot1 = PinCopy &/ PinMem        ' Make one-shot
      PinMem   = PinCopy                  ' Update pin memory
            
      if OneShot1 = 1 then                ' If one-shot is high ...     
            @bptr = DatPin                '     Save data at current bptr address
            inc bptr                      '     Increment bptr address
            if bptr > BufEnd then         '     If past last byte ...
                  bptr = BufStart         '           ... reset ptr
            endif
            
            MyPtr = bptr ' Not really necessary, 
                         ' but lets you see value of bptr without switching tabs
                  

            if @bptr <> 0 then            ' Output data is at current bptr address, 
                  high OutPin             ' because we incremented after storing input data.
            else
                  low OutPin
            endif
      endif
      '
      '
      ' Put code here to manipulate 'BufLen'
      '
      ' ( Or other functions you might think of,
      '   once you discover the power of PICAXE ) 
      '

loop 'Main loop
 
Last edited:

Buzby

Senior Member
ANDNOT, does a bitwise 'and' with the inverse of the second argument.

Not very clear in the help system, but see manual 2 page 15.
 

Chris Kelly

Active member
Hi Buzby
I've made some progress using your code. I had to make one change so that the data is held once added to the buffer (it was just clearing before when the pointer reached an address).
I also added a 'delete' pin so that I can manually remove data at the pointer location.
And I've made a pin which when held high, increases the buffer length for every clock cycle ('record' - pin C.0)
The output pin C.4 can only go high if the buffer length is >1. This mimics the hardware set-up I had in place.

One thing I cannot figure out yet, is how to 'reset' the buffer length back to 1 again for every time the pin C.0 is pressed. The idea being that a momentary button will be pressed and held to set the buffer length in real time. Once released, the buffer length is set, until the button is again pressed to re-record the buffer length.

Should I just use a one-shot approach similar to the way you have created the clock?


Code:
#picaxe 14m2
#no_data

' Circular buffer

symbol record        = pinC.0    ' When high this wil increment the buffer length every clock cycle
symbol ClkPin           = pinC.1    ' Clock pin
symbol DatPin           = pinC.2    ' Data in pin
symbol DelPin        = pinC.3    ' Delete Data
symbol OutPin           = C.4       ' Data out pin
symbol resetloop        = pinC.5    ' Resets loop when high

symbol BufLen           = b4        ' Used to hold length of buffer
symbol BufEnd           = b5        ' Used to hold address of last byte of buffer

symbol MyPtr            = b6        ' Not necessary, see later.
symbol delete        = b7        ' Used to delete memory from bit

symbol pincopy          = bit29
symbol pinmem           = bit30     ' Pin memory
symbol OneShot1         = bit31     ' Used to detect rising edge of clock pulse


symbol BufStart         = 32        ' Buffer Start address
                                    ' ( Set as low as you want, but leave room for varables ! )

' Code starts here
' ----------------

BufLen = 1                          ' Buffer length ( Don't go past RAM end, 127 on 08M2 )
BufEnd = BufStart + BufLen - 1      ' Calculate the end of the buffer
bptr = BufStart                     ' Set byte ptr to start of buffer


do ' Main loop
      
    if     resetloop = 1 then
        bptr = BufStart
    end if
        
      PinCopy  = ClkPin                   ' Only read pin once, to avoid glitches
      OneShot1 = PinCopy &/ PinMem        ' Make one-shot
      PinMem   = PinCopy                  ' Update pin memory
                
      if OneShot1 = 1 then                ' If one-shot is high ...
        if    record = 1 then
        
        BufLen = Buflen + 1
        BufEnd = BufStart + BufLen - 1
        end if
    
        if DatPin = 1 then
            @bptr = DatPin                '     Save data at current bptr address
        endif
    
        if DelPin = 1 then        '    Delete data from current bptr address
        @bptr = 0
        endif
    
        inc bptr                      '     Increment bptr address
            if bptr > BufEnd then         '     If past last byte ...
                  bptr = BufStart         '           ... reset ptr
            endif
            
            MyPtr = bptr ' Not really necessary,
                         ' but lets you see value of bptr without switching tabs
                  

            if BufLen > 1 then
            
            if @bptr <> 0 then            ' Output data is at current bptr address,
                      high OutPin             ' because we incremented after storing input data.
                else
                      low OutPin
                endif
        endif
    endif
      '
      '
      ' Put code here to manipulate 'BufLen'
      '
      ' ( Or other functions you might think of,
      '   once you discover the power of PICAXE )
      '

loop 'Main loop
 

Buzby

Senior Member
Hi Chris,

Here is your code, but with 8 one-shots !.

The bytewide instructions make it possible to process multiple pins at once.

I'm a bit busy this weekend, so I've not studied the rest of your code, but it does look like you are getting the hang of it. I'm sure you will progress well.

Cheers,

Buzby

Code:
#picaxe 28X2
#no_data

' Circular buffer

symbol record           = pinC.0    ' When high this wil increment the buffer length every clock cycle
symbol ClkPin           = pinC.1    ' Clock pin
symbol DatPin           = pinC.2    ' Data in pin
symbol DelPin           = pinC.3    ' Delete Data
symbol OutPin           = C.4       ' Data out pin
symbol resetloop        = pinC.5    ' Resets loop when high


symbol pincopies         = b8        ' copy of full byte of port C 
symbol pinsmem           = b1        ' memory for pins
symbol oneshots          = b2        ' oneshots for all pins i9n bits 16 to 23

symbol os_C0        = bit16    ' Oneshot from C.0
symbol os_C1        = bit17    ' Oneshot from C.1
symbol os_C2        = bit18    ' Oneshot from C.2
symbol os_C3        = bit19    ' Oneshot from C.3
symbol os_C4        = bit20    ' Oneshot from C.4
symbol os_C5        = bit21    ' Oneshot from C.5
symbol os_C6        = bit20    ' Oneshot from C.6
symbol os_C7        = bit21    ' Oneshot from C.7


symbol BufLen           = b4        ' Used to hold length of buffer
symbol BufEnd           = b5        ' Used to hold address of last byte of buffer

symbol MyPtr            = b6        ' Not necessary, see later.
symbol delete            = b7        ' Used to delete memory from bit



symbol BufStart         = 32        ' Buffer Start address
                                    ' ( Set as low as you want, but leave room for varables ! )

' Code starts here
' ----------------

BufLen = 1                          ' Buffer length ( Don't go past RAM end, 127 on 08M2 )
BufEnd = BufStart + BufLen - 1      ' Calculate the end of the buffer
bptr = BufStart                     ' Set byte ptr to start of buffer


do ' Main loop
      
    if resetloop = 1 then
        bptr = BufStart
    end if
        
    ' Make oneshots for all 8 pins of port C  
      pincopies = pinsC                   ' Only read pins once, to avoid glitches
      oneshots  = pincopies &/ pinsmem    ' Make one-shots
      pinsmem   = pincopies               ' Update pins memory
                
      if os_C0 = 1 then                    ' If one-shot for C.0 is high ...
 
      if  record = 1 then
            BufLen = Buflen + 1
            BufEnd = BufStart + BufLen - 1
        end if
    
        if DatPin = 1 then
            @bptr = DatPin                '     Save data at current bptr address
        endif
    
        if DelPin = 1 then        '    Delete data from current bptr address
            @bptr = 0
        endif
    
        inc bptr                      '     Increment bptr address
            if bptr > BufEnd then         '     If past last byte ...
                  bptr = BufStart         '           ... reset ptr
            endif
            
            MyPtr = bptr ' Not really necessary,
                         ' but lets you see value of bptr without switching tabs
                  

            if BufLen > 1 then
            
            if @bptr <> 0 then            ' Output data is at current bptr address,
                      high OutPin             ' because we incremented after storing input data.
                else
                      low OutPin
                endif
        endif
    endif
      
    '
    '
      ' Put code here to manipulate 'BufLen'
      '
      ' ( Or other functions you might think of,
      '   once you discover the power of PICAXE )
      '

loop 'Main loop
[\code]
 
Top