38400baud transfers with PICAXE-08M

vttom

Senior Member
I have a data-logging application where I collect ASCII-formatted data, stuff it into an outboard SPI memory, and then transfer it to a PC on-demand. By formatting the data with a stopbit/startbit preamble when I write it to the memory, I can drive the RS232 RX line of the PC directly from the SPI memory data out and get much higher data rates than I could otherwise achieve with a PICAXE-08M.

Here's a diagram of how the PICAXE interfaces to the SPI memory:
Code:
+----------------+    VDD +---------------+
|   PICAXE-08M   |    -+- |RAMTRON FM25640|
|                |     | 3|               |
|                |     +--+/WP            |
|                |     | 7|               |
|                |     +--+/HOLD          |
|                |6      5|               |2
|        IN1/OUT1+--------+SI           SO+-+->RS232RX
|                |5      6|               | |
|        IN2/OUT2+--------+SCK            | /
|                |2      1|               | \ 10k
|        IN4/OUT4+--------+/CS            | /
|                |        |               | |
+----------------+        +---------------+ V
Here's the code associated with writing the memory...

Code:
writetospi:
	' Kick off a WRITE operation
	low 1		' Idle SPI SI
	low 2		' Idle SPI CLK
	low 4		' Assert SPI /CS low

	b1 = 0x06	' WREN opcode
	gosub spiout

	low 1		' Idle SPI SI
	pulsout 4, 1	' Strobe /CS

	b1 = 0x02	' WRITE opcode
	gosub spiout

	w0 = w2		' Next avail address is in w2
	gosub spiout
	gosub spiout

	' Loop over contents of buffer
	for b9 = 80 to b10

		' Fetch a byte from buffer
		peek b9, b0

		' Writing an RS232-compatible bitstream here, so:
		' 1) Invert the data
		' 2) Shift the data LSB-first
		' 3) Prepend a stopbit(0)/startbit(1) preamble

		b1 = b0 NOR 0	' b1 = NOT(b0 OR 0) = NOT(b0)
		b0 = 0x80	' Preamble

		' Shift out 16bits
		for b2 = 0 to 15
			pin1 = bit0	' Set SPI SI TO LSB
			pulsout 2, 1	' Strobe SPI CLK
			w0 = w0 / 2	' Shift data right
		next b2

		' Increment the SPI addr pointer
		' (remember that we're writing 2 bytes to the memory
		' for every byte of data in the buffer)

		w2 = w2 + 2

	next b9

	low 1	' Idle SPI SI
	high 4	' De-assert SPI /CS high

	return

' This subroutine simply bangs out the 8 bits stored in b1, MSB first
' To transmit the entire contents of w0, just call it twice in a row
' outpin2 is clk
' outpin1 is data
' Note that this routine will corrupt the contents of b0 and b1

spiout:	for b2 = 0 to 7
		pin1 = bit15	' Set the output pin
		pulsout 2, 1	' Strobe clock low then high
		w0 = w0 * 2	' Shift data left
	next b2
	return
And here's how I read it out (note that a "setfreq m8" is assumed in the pwmout and pause statements)...

Code:
readfromspi:
	' Initialize a READ starting @ address 0
	low 1		' Idle SPI SI
	low 2		' Idle SPI CLK
	low 4		' Assert SPI /CS low

	b1 = 0x03	' READ opcode
	gosub spiout

	w0 = 0		' Address = 0
	gosub spiout
	gosub spiout

	' Blast the data to the PC
	pwmout 2, 51, 104	' Fire up the SPICLK @ 38400Hz
	pause	3413		' 2 * 1000 * 8192 * 8 / 38400 
	pwmout 2,  0,   0	' Shut down SPICLK

	low 1		' Idle SPI SI
	high 4		' De-assert SPI /CS high

	return
How do I get data into the PICAXE-08M to begin with? Well, IN3 and SEROUT/OUT0 are not used in the above circuit diagram, so those are available for interfacing with an external data source. Also, IN1/OUT1 and IN2/OUT2 can also be used when the SPI memory is not being written or read because it will ignore the SCK and SI inputs so long as /CS is held high.

Enjoy!
 

MPep

Senior Member
Thanks for sharing this idea with us all.
I am sure that more than a few people will be employing this technique, especially when a 8M is more than enough to do the job.
 

vttom

Senior Member
Oh. I forgot to mention that this example, as-posted, streams the contents of the entire memory. So, you'll want to make sure it's blank before you start writing to it. Here's some code that uses a similar technique to blank the memory in < 1 second...

Code:
wipespimem:
		low 1, 2		' Idle SPI CLK and SI
		low 4			' Assert SPI /CS low
		b1 = 0x06		' WREN opcode
		gosub spiout
		pulsout 4, 1		' Strobe /CS
		w0 = 0x0200		' WRITE opcode
		gosub spiout
		gosub spiout		' Address will be 0x0000
		gosub spiout
		pwmout 2, 19, 40	' Fire up SPICLK @ 100kHz
		pause 1310		' 2 * 1000 * 8192 * 8 / 100000
		pwmout 2,  0,   0	' Shut down SPICLK
		high 4			' De-assert /CS
		return
 

Jeremy Leach

Senior Member
I haven't studied this, but I was wondering if it could cycle round the memory continously, and offer the possibility of outputting a waveform? Bit of a long shot, but I was toying with doing something using your solution.
 

vttom

Senior Member
I haven't studied this, but I was wondering if it could cycle round the memory continously, and offer the possibility of outputting a waveform? Bit of a long shot, but I was toying with doing something using your solution.
According to the datasheet for the RAMTRON FRAM, once you issue a READ opcode with a starting address and then keep hitting the clock, it'll keep incrementing the internal address counter after every 8bits, wrapping back to zero when the end of the address space has been reached. Problem is, it will cycle through the entire memory. There's no way to tell it to loop over only a small section of the address range.
 

Jeremy Leach

Senior Member
well at least it loops. There's the possibility then of making the waveform fit the RAM (ie x number of repeats of the waveform lining up precisely with the RAM boundaries) then choosing different clock rates to blast it out (using delta mod). Trouble is ...I haven't got an application in mind :)
 

hippy

Technical Support
Staff member
There are a number of possible ways to get looping of just a section of memory - none would be perfect but could be acceptable ...

1) Use a pause as in the current application then stop the PWM and restart the output.

2) It may be possible to use an internal counter incremented in sync with PWM so you can tell how many pulses have been output, check when the counter exceeds a value.

3) Use a second SPI FRAM in parallel which is loaded with an 'end of data' pulse.

By pre-triggering before the end of data actually occurs the PICAXE could be ready to stop and restart the output at pretty close to when the output actually needs to be restarted.

With a little bit of extra electronics it should be possible to have a pair of output streamers, and switch between the two when an end of data signal occurs. The PICAXE would hopefully have enough time to restart and prime the other to start outputting as soon as the switch over to it occurs.

As to applications ... Function generator springs to mind. Using the output waveform as PWM you could create arbitrary voltage levels via an RC and adjust the level as output proceeds.

It's really massaging a solution good for one application to be another though. It would perhaps be simpler to go for a clocked binary counter and RAM with 'address comparator' so arbitrary repeat is seamless although the circuit would be more complicated.
 

papaof2

Senior Member
Possible application: musical instrument tuner

Store a pattern that can reproduce a (near) sine wave, clock it out at various speeds to get the notes wanted and filter the output as needed.

John
 
Top