ShiftOut/ShiftOutEx

Discussion about the ZBasic language including the System Library. If you're not sure where to post your message, do it here. However, do not make test posts here; that's the purpose of the Sandbox.
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

I've empirically determined that the initial compare match value can safely be cut back to 100. The code only requires about 40 cycles to get to the testing loop for the first time mark in state 1. That change cuts back the total time that interrupts are disabled to about 825uS - still considerable and possibly problematic depending on what else the application needs to have handled.

Code: Select all

    Register.TCF1_CCA = 100       ' set the initial compare match value (large enough to handle initialization)
- Don Kinzer
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

I occurred to me that the code that computes the length of the low and high segments could be moved into the "even" and "odd" state cases thus eliminating the need for the needDelta flag and the deltaLo and deltaHi variables.

Code: Select all

        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

            ' compute the duration of this segment
            If ((data And dataMask) = 0) Then
                delta = Delta_20uS
            Else
                delta = Delta_10uS
            End If

        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

            ' compute the duration of this segment
            If ((data And dataMask) = 0) Then
                delta = Delta_10uS
            Else
                delta = Delta_20uS
            End If

            ' shift the mask for the next data bit
            dataMask = Shr(dataMask, 1)
- Don Kinzer
twesthoff
Posts: 247
Joined: 17 March 2006, 6:45 AM
Location: Fredericksburg, VA

Post by twesthoff »

Thanks Don -
I was thinking of that same thing when I was studying the code this morning, but I hadn't understood it enough yet to try it. You are doing some interesting things that I am not familiar with. I haven't read all of the Zbasic documentation for a while. I will have some questions on a few items.

One quick question:
What is "port_t"?
Option Include Port_t

I hope to try it this afternoon. I realized that the 24x is 3.3V and I have to connect it to a 5V circuit, so I have some hardware to work out. I want to be able to use all of the pins on port A and Port C so I was looking at some TTL input buffer chips. Of course for testing, I can use just one pin and a voltage divider or something similar.
Tom
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

twesthoff wrote:What is "port_t"?
That is one of the pre-defined structures available in ZBasic. The Port_t structure represents the collection of registers comprising an I/O port of an AVR device, containing the data direction register, the input register, the output register and (for xmega devices) several other registers. Atmel's application note AVR1313 - Using the XMEGA IO Pins may be useful to get a better understanding of the various registers (e.g. OUTCLR) used.

The code could be simplified if you were willing to make it work for one particular pin determined at compile-time. However, you had specified that you wanted it to work for a run-time specified pin. To achieve this goal, the ZBasic Library routine PutPin() could have been used but my objective was to minimize the number of calls to other routines in order to better control the execution time. So, rather than using PutPin(), I used Register.DDR.DataAddress to get the address of the port structure and then used that address as the base address of the port variable. The value returned by PortMask(), containing a 1 in the bit position corresponding to the I/O pin, is then used to manipulate the particular I/O pin as needed.

By the way, you'll need to change the code in order to compile it for a ZX-32a4 or ZX-24x because those devices don't have timer F1. Instead, you may want to use Timer C0 or D0, for example. (Timer C1 is used for the RTC, D1 for software UART channels and E0 for I/O timing.

The code could have been written to allow the timer to be specified at run-time rather than being hard coded. The xmega devices have pre-defined structures TC0_t and TC1_t representing the two types of timers (the difference is basically the number of compare registers available). Note, however, that the current version of the ZBasic Language Reference Manual has those structures mis-labeled as Timer8_t and Timer16_t, respectively even though it correctly identifies them as "type 0" and "type 1".
- Don Kinzer
twesthoff
Posts: 247
Joined: 17 March 2006, 6:45 AM
Location: Fredericksburg, VA

Post by twesthoff »

Don,
I have a question as to the structure definitions used in the code you wrote vs what is in the ZBasicRef.pdf

Near the bottom of page 153 there is the title: ATmega-based ZX Devices for the second set of structures. Is this supposed to be ATXmega?
I see they are labeled incorrect as you mentioned (Timer8_t) but the individual elements do not have names like in your code is TCF1_INTCTRLA = 0, and on p.154 it is INTCTRLA = 0

Your program has Register.TCF1_INTCTRLA = 0 if I change all of the TCF1 to TCD1 will it work for Timer D1 on the ZX-24x.

I'm sure I am missing something simple. I feel I understand how you used port, as in: port.OUTCLR = pinMask

As far as the logic of the code goes, I follow that OK.

I plan on using a 74HCT244 as my 3.3V to 5V TTL converter. Let me know if there is a better choice for an 8-bit port.

Tom
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

twesthoff wrote:Near the bottom of page 153 there is the title: ATmega-based ZX Devices for the second set of structures. Is this supposed to be ATXmega?
Yes.
twesthoff wrote:[T]he individual elements do not have names like in your code is TCF1_INTCTRLA = 0, and on p.154 it is INTCTRLA = 0
Notice, however, the similarity. Each of the timers has an INTCTRLA register. If you refer to them via the Register.<reg-name> construct, the various INTCTRLA registers must somehow be distinguished - that is the purpose of the prefix like TCF1_. The prefix has the form TC<letter><number>_ where <letter> ranges from A to F (depending on the particular xmega device) and <number> ranges from 0 to 1. The xmega32A4 has timers named TCC0, TCC1, TCD0, TCD1 and TCE0 while the xmega128A1 has all of those plus TCE1, TCF0 and TCF1. The "type 0" timers have four compare registers while the "type 1" timers have only two compare registers. Beyond that difference, the two timer types are virtually identical.

Since the TC0_t/TC1_t structures are meant to refer to an arbitrary timer, the timer-specific prefix is omitted but the structure members are otherwise named like the Register.<reg-name> components.
twesthoff wrote:Your program has Register.TCF1_INTCTRLA = 0 if I change all of the TCF1 to TCD1 will it work for Timer D1 on the ZX-24x.
Yes, but note that TCD1 is the only timer that can be used for the software UART channels. If you know that you won't be using any software UART channels it is acceptable to use TCD1. See the Timers section of the ZBasic System Library Manual for more information on timer usage.
- Don Kinzer
twesthoff
Posts: 247
Joined: 17 March 2006, 6:45 AM
Location: Fredericksburg, VA

Post by twesthoff »

Thanks for the great explanation.

I understood what the prefix was doing, but I was confused that the element names were not the same. In the manual it says:
One or more of the pre-defined structures may be added to your application with the Option Include directive using the structure names below.
So I took that to mean that the exact structure would be included in the program just as if I had typed it in. I would have expected an error because the element names in the structure would not be the same as those used in the program.

If there was a sentence or two saying something like this, I would have understood it better:

The following structures are representative of the actual structures that will automatically be added to your application. If the cpu you are using has three timers, three structures will be added to your application, one for each timer. In your program you must add a prefix to each element name to identify the timer you wish to reference. For example, to refer to the CtrlA register of Timer D1 you would use: Register.TCD1_CtrlA

I know you are planning to edit those manual pages anyway because of the errors you pointed out previously, and maybe you had planned on making a similar change to what I suggest above. You are a much better writer than I, but something like this would have made it more clear.

Anyway, thanks for the help. I am learning a lot and hope to have time tomorrow to actually test the hardware and the code on the ZX-24x.

Using the timer like you show will be much more accurate and require less "tuning" than the other methods I had coded. I will try all three methods just to see how they work.

I am only planning to use the hardware UART, not the software UART, so D1 should be OK.
twesthoff
Posts: 247
Joined: 17 March 2006, 6:45 AM
Location: Fredericksburg, VA

Post by twesthoff »

Don,

Quick question:
Is the first Do-While-Loop necessary in (state = 1)?

The last Do-While-Loop at the bottom would ensure that the sequence had finished previously and the "If (Register.TimerD1Busy) Then" at the beginning of the sub should ensure the timer is ready to go.

Tom
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

twesthoff wrote:I understood what the prefix was doing, but I was confused that the element names were not the same.
I think that some confusion remains. In the code that I originally posted, the timer registers are accessed directly using the Register.<reg-name> construct. In contrast, the port registers are accessed indirectly with the via a pre-defined Port_t structure based at an address determined at run-time.
- Don Kinzer
twesthoff
Posts: 247
Joined: 17 March 2006, 6:45 AM
Location: Fredericksburg, VA

Post by twesthoff »

In contrast, the port registers are accessed indirectly with the via a pre-defined Port_t structure based at an address determined at run-time.
Yes, I saw tha,t and understand how that was done. I had to think about it a bit though.
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

twesthoff wrote:Is the first Do-While-Loop necessary in (state = 1)?
Yes, because it provides a uniform starting point upon which all subsequent time marks are based. The "start bit" is output from immediately after that loop until the first even-numbered state, a duration of approximately 10uS as you had specified. In summary, then, the output due to a particular state begins when the output is set in that state (right after the compare match is detected) and lasts until the compare match detection in the subsequent state.

The first setting of the CCA register is somewhat arbitrary. It must be long enough that it is never reached before the wait loop in the state=1 case. Any additional wait beyond that merely extends the overall time that interrupts are disabled and adds no further benefit. In the first set of code I posted, I set CCA to 500. Later, I determined that the minimum was in the 40-50 range and posted a modification setting it to 100.
- Don Kinzer
twesthoff
Posts: 247
Joined: 17 March 2006, 6:45 AM
Location: Fredericksburg, VA

Post by twesthoff »

What would happen if that first Do-While loop was replaced with:
Register.TCD1_CCA = 0
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

twesthoff wrote:What would happen if that first Do-While loop was replaced with: Register.TCD1_CCA = 0
By that time, the timer's CNT register will be at some non-zero value (the timer began running with the write to CTRLA). Consequently, setting CCA to zero would cause a very long wait at the wait loop in state 1 the first time, until the timer wraps around. Remember, a compare match flag is set when the timer's CNT register matches a compare register, e.g. CCA.

You could eliminate the loop in state=1 by waiting to start the timer there:

Code: Select all

        If &#40;state = 1&#41; Then              ' start bit
            ' set the threshold for the next state
            Register.TCD1_CCA = Delta_10us
            delta = 0

            ' set the bit high and start the timer
            port.OutSet = pinMask
            Register.TCD1_CTRLA = 1       ' use the 1X prescaler
Another alternative would be to set the CNT register to zero in state=1.
- Don Kinzer
twesthoff
Posts: 247
Joined: 17 March 2006, 6:45 AM
Location: Fredericksburg, VA

Post by twesthoff »

Another alternative would be to set the CNT register to zero in state=1.
That is what I was trying to do, but I had the wrong register.

I'd like you to know I got it working successfully! I had to reverse the timing sequence for the 0 and 1 bits. I had them reversed.

I tried the OutputCaptureEx method too, but didn't get any results even close to what I had expected, so I am going to work on that more just to learn about OutputCaptureEx.

This code seems very similar as what would be done for the software UARTS if they were expanded to be 68bits at a speed of 10us per bit. Three 10us bits would make up each one of these bits. I would only have to change the middle one (of 3) to be a high or low.

I will post the final version here when I'm done cleaning up the code, so others may use it.
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

twesthoff wrote:I got it working successfully!
Excellent.
twesthoff wrote:This code seems very similar as what would be done for the software UARTS if they were expanded to be 68bits at a speed of 10us per bit.
True enough. However, a 10uS bit time would be equivalent to 100K baud on the serial channel. That speed is too high to be supported in the way that the software UARTs are implemented (timer interrupt at 4X the maximum bit rate).
- Don Kinzer
Post Reply