multiple PWM capture

Discussion specific to the ZX-1281 and ZX-1280 microcontrollers as well as the ZX-1281 and ZX-1280 Dev Boards.
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

sturgessb wrote:Could that be set up to be an I2C slave?
Yes. It's just like the other native mode ZX devices except that it has less RAM and Program Memory as well as fewer I/O pins.
- Don Kinzer
dlh
Posts: 395
Joined: 15 December 2006, 12:12 PM
Location: ~Cincinnati

Post by dlh »

dkinzer wrote:Another option is the 28-pin ZX-328n, soon to be announced. It is currently available in limited quantities but we expect to have ample stock within a week or so.
Very interesting.

Some of the newer 8-bit PICs (in the 18F family) have register bits that allow switching polarity for the hardware USARTs. Do any of the AVR chips allow this?
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

sturgessb wrote:TCCR1B |= (1 << CS10); // Set up timer
The CS10 identifier is defined in some device-specific include file to be the bit number of that control bit. The << operator in C is shift left so (1 << CS10) just creates a value with a particular bit set. The |= operator in C means to take the value on the left, logically OR the value on the right and write it back.

The equivalent in ZBasic would be:

Code: Select all

Const CS10 as Byte = &H01

Register.TCCR1B = Register.TCCR1B Or CS10
To get the timer to clock at 1X in free-running mode, the following code would be sufficient:

Code: Select all

If Semaphore&#40;Register.Timer1Busy&#41; Then
  Register.TCCR1A = 0
  Register.TCCR1B = &H01
  Register.TCCR1C = 0
End If
This example uses the Semaphore() function to make sure that the timer is not already busy and, if available, to set the flag indicating that it is busy.
- Don Kinzer
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

dlh wrote:Do any of the AVR chips allow this?
None that I've seen. That would be a handy feature.
- Don Kinzer
sturgessb
Posts: 287
Joined: 25 April 2008, 6:34 AM
Location: Norwich, UK

Post by sturgessb »

Thanks Don, got it working now!

prescale of 8 seems to be a good match.

Maybe an 8 bit timer would be good enough, i presume that is TCNT2. are all the setting for that timer in TCCR2?

EDIT: Cool timer 2 can give max resolution of 1-200 steps for RC receiver channels, thats not far off the 255 standard range. I take it there are no other tricks to dealing with timer overflow, can it be detected and then added to the following count? if that makes sence?


Cheers

Ben
sturgessb
Posts: 287
Joined: 25 April 2008, 6:34 AM
Location: Norwich, UK

Post by sturgessb »

at the moment I have the following code in a task...

Code: Select all

Call WaitForInterrupt&#40;zxPinRisingEdge, 0&#41;
Register.TCNT2 = 0 'reset to zero
Call WaitForInterrupt&#40;zxPinFallingEdge, 0&#41;
tempint = Cint&#40;Register.TCNT2&#41;
would there be any performance benefit to changing this to an ISR?
dlh
Posts: 395
Joined: 15 December 2006, 12:12 PM
Location: ~Cincinnati

Post by dlh »

dkinzer wrote:
dlh wrote:Do any of the AVR chips allow this?
None that I've seen. That would be a handy feature.
They also have an independent, internal clock for the USART which eliminates the need for a crystal. For some applications a 20-pin PIC is about all that you need.
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

sturgessb wrote:would there be any performance benefit to changing this to an ISR?
Perhaps. In your code there will be a task switch between the time when the external interrupt occurs and when the timer is read. If you used an ISR, it is likely that there would be less latency until the timer is read.

For multiple channels, you won't be able to set the timer to zero. Essentially, you just want the difference between the timer readings at the beginning and the end of the pulse, accounting for timer rollover. You'll probably also want an ISR for timer overflow in which you would increment another 16-bit variable. This would effectively give you a 32-bit timer value.

Code: Select all

Structure pwmData
  Dim isValid as Boolean
  Dim width as UnsignedLong
End Structure

Dim timerHigh as UnsignedInteger
Dim pwm0Data as pwmData
Dim timerData&#40;1 to 2&#41; as UnsignedInteger
Dim width as UnsignedLong Alias timerData

ISR TIMER1_OVF&#40;&#41;
  ' increment the high word of the timer value
  timerHigh = timerHigh + 1
End ISR

ISR INT0&#40;&#41;
  Const INT_MASK as Byte = &H03
  Const INT_RISING as Byte = &H03
  Const INT_FALLING as Byte = &H02

  ' store the timer value
  timerData&#40;1&#41; = Register.TCNT1
  timerData&#40;2&#41; = timerHigh

  ' see if the trigger was the rising or falling edge
  If &#40;&#40;Register.EICRA And INT_MASK&#41; = INT_RISING&#41; Then
    ' rising edge, reset for falling edge trigger
    Register.EICRA = &#40;Register.EICRA And NOT INT_MASK&#41; Or INT_FALLING

    ' initialize the data structure for the beginning of a pulse
    pwm0Data.width = width
    pwm0Data.isValid = False
  Else
    ' falling edge, reset for rising edge trigger
    Register.EICRA = &#40;Register.EICRA And NOT INT_MASK&#41; Or INT_RISING

    ' compute the pulse width
    pwm0Data.width = width - pwm0Data.width
    pwm0Data.isValid = True
  End If
End ISR
Note that extra work was required (the aliasing of an UnsignedLong over two UnsignedInteger values) to avoid calling an outside function from within the ISR. Avoiding calls to other functions reduces the number of registers that have to be saved/restored in the ISR.
- Don Kinzer
mikep
Posts: 796
Joined: 24 September 2005, 15:54 PM

Post by mikep »

You have pretty much written all of the required code now, Don. However for the ZX328n or ZX24n it is probably better to use the pin change interrupt for 4 I/O pins as there are not enough interrupts for 4 channels.

The ISR would be a little more complicated and additional state would be needed to know which pin was being timed and the start time for each one.
Mike Perks
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

mikep wrote:You have pretty much written all of the required code now...
Perhaps, but it is completely untested, however. One issue that is not handled is overflow of the timerHigh variable.
- Don Kinzer
mikep
Posts: 796
Joined: 24 September 2005, 15:54 PM

Post by mikep »

dkinzer wrote:Perhaps, but it is completely untested, however. One issue that is not handled is overflow of the timerHigh variable.
As I said in one of my earlier appends, you could reset the timer to zero in one of the periods when all 4 timers are idle or when the first new interrupt arrived.
Mike Perks
sturgessb
Posts: 287
Joined: 25 April 2008, 6:34 AM
Location: Norwich, UK

Post by sturgessb »

Thanks for the code Don, going through it now.

Do i need to place the ISR code in any particular place, or put something else in to get it to run. I say this as i just put the following in above Sub Main()...

Code: Select all

ISR TIMER2_OVF&#40;&#41;
  tempint = tempint + 1
End ISR 
I know that timer2 will be overflowing, but tempint is not increasing. any ideas?

Ben
spamiam
Posts: 739
Joined: 13 November 2005, 6:39 AM

Post by spamiam »

Using the 18mS dead time between the PWM pulses is an interesting approach. You may be able to tell when you have passed the 2mS period of PWM pulses and have a free 18 or so mS to do post-processing.

I am concerned about the ability to accurately and precisely measure the PWM pulse widths. You will need some careful control of extraneous interrupts. I really wonder if any of the ZX-XXXx models will suffice when the operating system is going to take ofer for a moment at unexpected times.

Not counting any operating system overhead, an interrupt probably has something like 20 clock cycles of overhead before you actually do the interupt processing, then at least 20 clock cycles of processing work. Call it a nice round 50 clock cycles. At 14MHz, then is 3uS per interrupt.

This seems to put an upper limit of precision of measurement of the pulse width to about 280 levels. Is this sufficient precision?

I think that if your were doing the same process on a ZBasic OS chip, that the upper limit of precision would be noticeably worse.

BTW, from what little I know about FPGA hardware (after playing with one for a couple of days!!), it could do this in its sleep. Not that I could tell one how to do it....

-Tony
sturgessb
Posts: 287
Joined: 25 April 2008, 6:34 AM
Location: Norwich, UK

Post by sturgessb »

another quick question. When using PulseIn, the docs state that it waits for the input pin to be in the idle state, and then waits for it to change to specified logic, and then obviously times the duration of that state.

When its waiting for the idle state and to reach desired logic, does it only halt the task its in, or the entire mcu?
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

sturgessb wrote:Do i need to place the ISR code in any particular place, or put something else in to get it to run. [...] I know that timer2 will be overflowing, but tempint is not increasing. any ideas?
The ISR can go anywhere. There is, of course, additional setup that is required. I didn't provide a complete program; just two prospective ISRs.

In order to get the Timer1 Overflow ISR to execute, you have to configure the timer for the correct clocking rate (as we've previously discussed) and enable the interrupt on overflow. The AVR has a global interrupt enable/disable but it also has a means to enable/disable each individual interrupt as well. On the mega1280, the Timer1 interrupt enable flags are in the register TIMSK1. The register TIFR1 contains flags that indicate if an interrupt would occur were it enabled. With this additional information in mind, the timer setup might look like this:

Code: Select all

If Semaphore&#40;Register.Timer1Busy&#41; Then
  Register.TCCR1B = 0    ' ensure that the timer isn't running
  Register.TIFR1 = &Hff  ' clear all pending timer interrupt flags
  Register.TIMSK1 = &H01 ' enable the overflow interrupt
  Register.TCCR1A = 0
  Register.TCCR1C = 0
  Register.TCCR1B = &H01 'start the timer running
End If
The line involving TIFR1 probably looks strange; claiming to clear some flags by writing &Hff to the register. If you read the datasheet carefully however, you'll note that this is proper procedure.
- Don Kinzer
Post Reply