Forum Index
HomeZBasic Home   Forum RulesForum Rules   Forum FAQForum FAQ   MemberlistMemberlist   UsergroupsUsergroups   RSS FeedRSS Feed
Site SearchSite Search   LinksLinks   DownloadDownload   Digests and SubscriptionsDigests and Subscriptions
ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in   RegisterRegister
ShiftOut/ShiftOutEx
Goto page 1, 2  Next
 
Post new topic   Reply to topic    Forum Index -> ZBasic Language
Author Message
twesthoff



Joined: 17 Mar 2006
Posts: 192
Location: Fredericksburg, VA

Posted: 19 January 2012, 15:02 PM    Post subject: ShiftOut/ShiftOutEx Reply with quote

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 Reply with quote

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: Reply with quote

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: Reply with quote

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: Reply with quote

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: Reply with quote

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: Reply with quote

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: Reply with quote

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: Reply with quote

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: Reply with quote

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: Reply with quote

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: Reply with quote

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: Reply with quote

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: Reply with quote

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 Reply with quote

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
Display posts from previous:   
Post new topic   Reply to topic    Forum Index -> ZBasic Language Time synchro. with the server - Timezone/DST with your computer
Goto page 1, 2  Next
Page 1 of 2

 


All content Copyright © 2005-2012 Elba Corp. All Rights Reserved.
Opinions expressed in posts are those of the author and not necessarily those of Elba Corp.
Powered by phpBB © 2001, 2005 phpBB Group