|
|
| Author |
Message |
twesthoff
Joined: 17 Mar 2006
Posts: 192
Location: Fredericksburg, VA
|
|
Posted: 19 January 2012, 15:02 PM Post subject: ShiftOut/ShiftOutEx |
|
|
I want to shift out a 26 bit or larger value, maybe up to 78 bits. ShiftOutEx appears to be limited to 16bits.
Is my only option to write code to do it? That is not hard to do, but each bit is 10us long so speed may be an issue.
Any thoughts before I dive into it?
Tom W |
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2514
Location: Portland, OR
|
|
Posted: 19 January 2012, 18:14 PM Post subject: Re: ShiftOut/ShiftOutEx |
|
|
| twesthoff wrote: | | I want to shift out a 26 bit or larger value, maybe up to 78 bits. | You can shift in or out an arbitrary number of bits by using multiple calls to ShiftIn() or ShiftOut(). For example, for 26 bits you could make two calls shifting 16 and then 10 bits. There will, of course, be a "gap" between the sets of bits but this generally will not matter given that this is a synchronous serial operation, the clock being provided by the ZX. |
|
| Back to top |
|
 |
twesthoff
Joined: 17 Mar 2006
Posts: 192
Location: Fredericksburg, VA
|
|
Posted: 20 January 2012, 4:09 AM Post subject: |
|
|
In this case I am trying to produce asynchronous NRZ data much like serial rs-232 but more bits, so the gap would mess it up. The clock is not used in this case. I ShiftOutEx would work on long integers or on an array of bytes it would work perfect.
I haven't tried writing code to do it yet. An XMega native device may be fast enough. |
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2514
Location: Portland, OR
|
|
Posted: 20 January 2012, 15:52 PM Post subject: |
|
|
| twesthoff wrote: | | I haven't tried writing code to do it yet. An XMega native device may be fast enough. | The coding will need to be done very carefully to get the timing as accurate as required. Interrupts will need to be disabled for the entire transmission, I would guess.
One issue to consider is that the larger the number of bits being sent, the greater timing accuracy is required because the error accumulates over the entire message frame.
One other thought: it may be possible to use OutputCapture to produce the NRZ bitstream. |
|
| Back to top |
|
 |
twesthoff
Joined: 17 Mar 2006
Posts: 192
Location: Fredericksburg, VA
|
|
Posted: 20 January 2012, 22:00 PM Post subject: |
|
|
Thanks Don,
Correct me if I am wrong, but Outputcapture always toggles the output pin for each value in the array. This would make it difficult to easily change the value of the bits being sent. I could easily setup an array for a particular bit pattern, but then changing a bit from 1 to a 0 (or 0 to 1) would involve a different number of transitions since it is NRZ.
I could just set or reset the bits I needed in an integer and use the bits to write an array of custom values and then call OutPutCapture to send the waveform. Hmmn...
If I were to do it with code and wrote a sub for it, what do I have to do to disable interrupts and then enable them again? I plan to use a ZX-24x as it is the fastest chip available.
The total length of the bit pattern is always 820us, each bit is 30us, broken into a 10us and 20us piece which are reversed in position for a 1 or 0. So the whole thing is: 10us (start) + 26 30us bits (780us) + 30us (stop), for a total of 820us.
Tom W |
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2514
Location: Portland, OR
|
|
Posted: 21 January 2012, 0:25 AM Post subject: |
|
|
| twesthoff wrote: | | Outputcapture always toggles the output pin for each value in the array. | True. As you realized, for each bit pattern to be transmitted, you would need to compute the successive high and low times.
| twesthoff wrote: | | If I were to do it with code and wrote a sub for it, what do I have to do to disable interrupts and then enable them again? | See DisableInt and EnableInt.
| twesthoff wrote: | | each bit is 30us, broken into a 10us and 20us piece | For an xmega-based ZX, running at 29.5Mhz, each main clock cycle is about 33.9nS and 295 cycles spans 10.003uS. Is that close enough? |
|
| Back to top |
|
 |
twesthoff
Joined: 17 Mar 2006
Posts: 192
Location: Fredericksburg, VA
|
|
Posted: 21 January 2012, 3:25 AM Post subject: |
|
|
| twesthoff wrote: | | each bit is 30us, broken into a 10us and 20us piece | For an xmega-based ZX, running at 29.5Mhz, each main clock cycle is about 33.9nS and 295 cycles spans 10.003uS. Is that close enough?[/quote]
I believe it should be fairly tolerant, so I think that will be close enough.
Here are two Subs I wrote tonight, and some comments for information.
I haven't tried them yet as I have no hardware or a scope at home. I'll probably test it on Monday. I can tweek some of the values to make the timing closer after looking at it with the scope.
I get a warning: "Warning(11): For loop may never terminate" for both of the For-Next loops which seems odd to me. I don't know why they wouldn't terminate since I am not changing the value of the loop variable.
I'm going to see which works the best/fastest etc. It needs to be able to work on multiple I/O pins (one at a time)
| Code: |
'For ZBasic Native mode XMega chip devices (ZX-24x)
'Output is:
' 1 - 10us Start bit
' 6 - 30us Bulb address bits
' 8 - 30us Brightness bits
'12 - Color bits (4-Red, 4-Blue, 4-Green)
' 1 - 30us Stop bit
' Total length 28 bits, 820us
Sub Send_G35_Shift(Out_pin as Byte, Value as Long)
'Send a single G-35 bulb pulse stream using "OutputCaptureEx"
Const bt10 as Integer = 90 'Approx 10us
Const bt20 as Integer = 90 'Approx 20us
Dim Vals(1 to 54) as Integer 'Array to store delay values in
Dim Count as Byte
'Calculate the array values for OutputCaptureEx
Count = 1
Vals(Count) = bt10 'Start bit, 10us high
'Do the 26 data bits
'Bits are 30us and always start low, and end high, but low of 10us = 1, and 20us = 0)
For Bit_Position = 25 to 0 Step -1 'MSbit first
If GetBit(v, Bit_Position) = 1 Then
Count = Count + 1
Vals(Count)= bt10
Count = Count + 1
Vals(Count)= bt20
Else '= 0
Count = Count + 1
Vals(Count)= bt20
Count = Count + 1
Vals(Count)= bt10
End if
Next Bit_Position
Count = Count + 1
Vals(Count)= bt10 + bt20 'Stop bit, 30us Low
'Send the pulses
Call OutputCaptureEx(Out_pin, vals(), Count, 0)
End Sub
Sub Send_G35_Pulse(Out_pin as Byte, Value as Long)
'Send a single G-35 bulb pulse stream using "PulseOut"
'Disable Interrupts for better timing
Dim iflag as Byte
iflag = DisableInt()
Call PulseOut(Out_pin, 0.00001, 1) '10us Start bit
'Do the 26 data bits
'Bits are 30us and always start low, and end high, but low of 10us = 1, and 20us = 0)
For Bit_Position = 25 to 0 Step -1 'MSbit first
If GetBit(v, Bit_Position) = 1 Then
Call PulseOut(Out_pin, 0.00001, 0) '10us
Call PulseOut(Out_pin, 0.00002, 0) '20us
Else '= 0
Call PulseOut(Out_pin, 0.00002, 0) '20us
Call PulseOut(Out_pin, 0.00001, 0) '10us
End if
Next Bit_Position
Call PulseOut(Out_pin, 0.00003, 0) '30us Stop bit
'Restore Interrupts
Call EnableInt(iflag)
End Sub
|
|
|
| Back to top |
|
 |
twesthoff
Joined: 17 Mar 2006
Posts: 192
Location: Fredericksburg, VA
|
|
Posted: 21 January 2012, 3:27 AM Post subject: |
|
|
| Of course I am open to any comments/suggestions as this is my first draft with no testing at all. There may be more efficient ways to do this. |
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2514
Location: Portland, OR
|
|
Posted: 21 January 2012, 14:53 PM Post subject: |
|
|
| twesthoff wrote: | | I get a warning: "Warning(11): For loop may never terminate" for both of the For-Next loops which seems odd to me. | I suspect that it is because the variable Bit_Position is unsigned. A For loop with a negative step terminates when the loop variable is less than the limit value. Since the limit value is zero in this case and an unsigned variable is never less than zero, the termination condition will never be reached. |
|
| Back to top |
|
 |
twesthoff
Joined: 17 Mar 2006
Posts: 192
Location: Fredericksburg, VA
|
|
Posted: 21 January 2012, 15:12 PM Post subject: |
|
|
| dkinzer wrote: | | twesthoff wrote: | | I get a warning: "Warning(11): For loop may never terminate" for both of the For-Next loops which seems odd to me. | I suspect that it is because the variable Bit_Position is unsigned. A For loop with a negative step terminates when the loop variable is less than the limit value. Since the limit value is zero in this case and an unsigned variable is never less than zero, the termination condition will never be reached. |
I see what you are saying, but I have never seen that behavior in any other dialect of Basic I have used. I have done similar things many times over the years.
If I change Bit_Position to an Integer will that fix it? (I'm thinking it will)
Thanks for the education.
Tom W |
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2514
Location: Portland, OR
|
|
Posted: 21 January 2012, 15:25 PM Post subject: |
|
|
| twesthoff wrote: | | Of course I am open to any comments/suggestions [...] | The strategy employed in Send_G35_Pulse() would need some "tuning" to get the timing closer to being correct. As written, the timing is affected not only by the calls to PulseOut() but also by the execution time of the surrounding code. Further, as noted in the documentation, PulseOut() has a significant, non-zero setup time, meaning that the total execution time of a PulseOut() call is some amount more than the specified time.
In general, when coding a "bit-bang" implementation like this it is best to use as few subroutine/function calls as possible. Those that you do use you'll need to characterize in order to fully understand their timing implications.
A strategy that may work better for this application is to set up a free running timer and then pace the execution of the code by watching for the timer to hit a series of values. As long as you can get all of the needed work done in a given step before reaching the timer value for the next step the timing will be very close to that required. (Incidentally, this is the technique that we use internally to implement the 1-wire functionality.) I'll see if I can post something to better illustrate this idea later today or tomorrow. |
|
| Back to top |
|
 |
twesthoff
Joined: 17 Mar 2006
Posts: 192
Location: Fredericksburg, VA
|
|
Posted: 21 January 2012, 16:25 PM Post subject: |
|
|
Thanks Don,
I had expected that I would need to account for the items you mentioned and was going to look at the waveform on the scope and fiddle with it until it was as close as possible to ideal.
The timer idea sounds good too. I have done that before in assembly code on several processors, but not in ZBasic, so I would like to see what you come up with.
Eventually, if this works, I want to store sequences of these pulses with timestamps and send them at the correct intervals.
Tom W. |
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2514
Location: Portland, OR
|
|
Posted: 21 January 2012, 16:48 PM Post subject: |
|
|
| twesthoff wrote: | | I have never seen that behavior in any other dialect of Basic I have used. | The same issue would arise in VisualBasic were it not for run-time overflow checks. For example, if you run the program below in VB you'll get a run-time overflow error when the control variable is zero and then decremented. If not for that run-time error, the loop would run forever. ZBasic does not implement that type of run-time checking so the warning is issued. Of course, not all such cases can be detected at compile-time. | Code: | Sub Main()
Dim b As Byte
For b = 10 To 0 Step -1
Debug.Print CStr(b)
Next b
End Sub |
|
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2514
Location: Portland, OR
|
|
Posted: 21 January 2012, 21:19 PM Post subject: |
|
|
| twesthoff wrote: | | I have done that before in assembly code on several processors, but not in ZBasic, so I would like to see what you come up with. | I whipped up the code below. It is not fully tested but the logic analyzer display looked right for a few test cases.
I didn't put in a lot of comments so feel free to ask questions about parts that aren't clear. Basically, the compare match capability of a timer is used to control the transitions of a state machine. The state machine has 28 states in total - state 1 is for the start bit, state 28 is for the stop bit and in between, the even states are for the low segment of a data bit and the odd states are for the high segment of a data bit. In each state, the code waits for a compare match, sets the output and selects the correct delta to apply to the compare match register to reach the next time marker. Note that the code that awaits the compare match is duplicated in each of the four types of states so that the latency between recognizing the arrival at the time marker and setting the output state is minimized. Note, further, that the special setting/clearing registers of the xmega port structure are used to make the code as fast as possible.
| Code: | ' This code is written specifically for an xmega-based ZX device.
Option Include Port_t ' include the xmega port structure
'#define DEBUG
Const DebugPin as Byte = B.0 ' used as a logic analyzer trigger signal
Const G35Pin as Byte = B.1
Sub Main()
#if defined(DEBUG)
Call PutPin(DebugPin, 1)
#endif
Call SendG35(G35Pin, &H55)
End Sub
Sub SendG35(ByVal pin as Byte, ByVal data as UnsignedLong)
' ensure that the timer is not already in use
If (Register.TimerF1Busy) Then
Exit Sub
End If
' mark the timer as being in use
Register.TimerF1Busy = True
' constant for the number of data bits to send
Const BitCount as Byte = 26
' constant for the total number of states: start bit, 2 states per bit, stop bit
Const LastState as Byte = (BitCount * 2) + 2
' constants for 10uS, 20uS and 30uS durations
Const Delta_10us as UnsignedInteger = CUInt(10e-6 * CSng(Register.CPUFrequency) + 0.5)
Const Delta_20us as UnsignedInteger = CUInt(20e-6 * CSng(Register.CPUFrequency) + 0.5)
Const Delta_30us as UnsignedInteger = CUInt(30e-6 * CSng(Register.CPUFrequency) + 0.5)
' constant for the timer "compare match" flag
Const CCAF as Byte = &H10
' define the port address, structure and mask corresponding to the given pin
Dim portAddr as UnsignedInteger
Dim port as Port_t Based portAddr
Dim pinMask as Byte
portAddr = Register.DDR(pin).DataAddress
pinMask = PortMask(pin)
' define the bit mask for sending the 26 bit stream, MSB first
Dim dataMask as UnsignedLong
datamask = Shl(1, bitCount - 1)
' set the pin to be output low
port.OUTCLR = pinMask
port.DIRSET = pinMask
' disable interrupts for the duration
Dim sreg as Byte
sreg = DisableInt()
' prepare the timer to run at 1x the processor frequency
Register.TCF1_INTCTRLA = 0
Register.TCF1_INTCTRLB = 0
Register.TCF1_PER = &Hffff
Register.TCF1_CTRLE = 0
Register.TCF1_CTRLD = 0
Register.TCF1_CTRLC = 0
Register.TCF1_CTRLB = 0 ' use "normal" mode
Register.TCF1_CCA = 500 ' set the initial compare match value (large enough to handle initialization)
Register.TCF1_INTFLAGS = CCAF ' reset the compare match flag
Register.TCF1_CTRLA = 1 ' use the 1X prescaler
' output the bit stream
Dim state as Byte
Dim delta as UnsignedInteger
Dim deltaLo as UnsignedInteger
Dim deltaHi as UnsignedInteger
Dim needDelta as Boolean
needDelta = True
deltaLo = 0
deltaHi = 0
#if defined(DEBUG)
Register.PORTB_OUTTGL = 1
#endif
For state = 1 to LastState
' if needed, compute the low/high deltas for the next bit
If (needDelta) Then
If ((data And dataMask) = 0) Then
deltaLo = Delta_20uS
deltaHi = Delta_10uS
Else
deltaLo = Delta_10uS
deltaHi = Delta_20uS
End If
dataMask = Shr(dataMask, 1)
needDelta = False
End If
' take an action depending on the state
If (state = 1) Then ' start bit
' wait for the marker
Do While ((Register.TCF1_INTFLAGS And CCAF) = 0)
Loop
' set the bit high
port.OutSet = pinMask
delta = Delta_10us
ElseIf (state = LastState) Then ' stop bit
' wait for the marker
Do While ((Register.TCF1_INTFLAGS And CCAF) = 0)
Loop
' set the bit low
port.OutClr = pinMask
delta = Delta_30us
ElseIf ((state And 1) = 0) Then ' data bit, low segment
' wait for the marker
Do While ((Register.TCF1_INTFLAGS And CCAF) = 0)
Loop
' set the bit low
port.OutClr = pinMask
delta = deltaLo
Else ' data bit, high segment
' wait for the marker
Do While ((Register.TCF1_INTFLAGS And CCAF) = 0)
Loop
' set the bit high
port.OutSet = pinMask
delta = deltaHi
' indicate the need to recompute the next set of deltas
needDelta = True
End If
' set the compare match for the next marker, reset the match flag
Register.TCF1_CCA = Register.TCF1_CCA + delta
Register.TCF1_INTFLAGS = CCAF
Next state
' wait for the final marker and turn off the timer
Do While ((Register.TCF1_INTFLAGS And CCAF) = 0)
Loop
#if defined(DEBUG)
Register.PORTB_OUTTGL = 1
#endif
Register.TCF1_CTRLA = 0
Register.TimerF1Busy = False
Call EnableInt(sreg)
End Sub |
|
|
| Back to top |
|
 |
twesthoff
Joined: 17 Mar 2006
Posts: 192
Location: Fredericksburg, VA
|
|
Posted: 21 January 2012, 21:44 PM Post subject: ShiftOut/ShiftOutEx |
|
|
Thanks Don, I'll study this code and try it on Monday and compare it to the two subs I wrote just to learn more. This is the first time I have tried the XMega chips...
Tom |
|
| Back to top |
|
 |
|