PDA

View Full Version : Teensy 3 Encoder Lib



Lotus Pack
11-17-2012, 04:47 PM
Hi Chaps,
Does anyone know if the encoder.h will work on Teensy 3 using Ardunio 1.0.2?
I have this working fine on Teensy 2 and collects pulses from my Linier Encoders has not missed a single pulse and want upgrade to the faster T3.
I have downloaded Arduino version 1.0.2 but this does not have the encoder library included.
Regards,
LP :)

PaulStoffregen
11-17-2012, 09:35 PM
Try replacing the util/direct_pin_read.h with this:



#ifndef direct_pin_read_h_
#define direct_pin_read_h_

#if defined(__AVR__) || defined(__MK20DX128__)

#define IO_REG_TYPE uint8_t
#define PIN_TO_BASEREG(pin) (portInputRegister(digitalPinToPort(pin)))
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
#define DIRECT_PIN_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0)

#elif defined(__SAM3X8E__)

#define IO_REG_TYPE uint32_t
#define PIN_TO_BASEREG(pin) (portInputRegister(digitalPinToPort(pin)))
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
#define DIRECT_PIN_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0)

#elif defined(__PIC32MX__)

#define IO_REG_TYPE uint32_t
#define PIN_TO_BASEREG(pin) (portModeRegister(digitalPinToPort(pin)))
#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
#define DIRECT_PIN_READ(base, mask) (((*(base+4)) & (mask)) ? 1 : 0)

#endif

#endif


This doesn't provide any of the intense optimization that I did for AVR, but it should at least work. I'll look at optimizing Encoder for ARM sometime next year.

djsz
11-24-2012, 05:24 PM
Fully understanding the lack of optimization, I'd like to ask a clarifying question regarding performance of the code you posted, Paul: When I run the basic example from the Encoder library (on a T3 on a breadboard with hardware set up for a much larger sketch), it works really well. However, when I add a simple Serial.println(myEncoder.read()); to the loop in the much larger sketch I have, it responds but with no correlation to the left/right differentiation of the encoder...I think it's missing single signals in the quadrature wave and getting confused.

My sketch is 40k right now, so it's pretty large, and I'm using 2 interrupts for button presses, the SD library, and an I2C display. My assumption is that the raw reads are just too slow and some percentage are getting lost, which leads to inaccuracy in the reported encoder position. Might there be anything else I can do in my sketch to make position reading more accurate before the ARM optimizations are done next year?

tni
01-22-2013, 04:01 PM
Given that the K20 family has a hardware quadrature decoder (available with 2 alternate locations/pin pairs, both seem to be usable), it would be nice if that was supported...

PaulStoffregen
01-22-2013, 05:49 PM
Given that the K20 family has a hardware quadrature decoder (available with 2 alternate locations/pin pairs, both seem to be usable), it would be nice if that was supported...

That would be nice. Care to submit a patch?

LarryP
02-04-2013, 05:34 PM
I took a first look at hardware-based encoder decoding on the T3. For what they're worth, here are my notes from scoping out hardware decoding.


Don't look for encoder in the monstrous data "sheet;" instead search for quadrature. The chip can also decode step/direction style encoders, but searching for quadrature will get you to the right places.

This chip has one channel of hardware quadrature decoder. Hardware encoder decoding *must use* both channels on the second timer module, FTM1 -- can't switch it to other timer channels.

Paul, do you know of any T3 code in your distribution that must use FTM1? If so, please let me know what.
My hope is that other timer-based functionality does not use FTM1 (except for PWM/capture/compare on pins 3&4.) If nothing else needs to use FTM1, that would lower the odds that encoder decoding will break something else.

From the T3 schematic and sect 10.3.1 (datasheet pinout table, p. 194, fifth and sixth row up from bottom),
quadrature decoding is available on chip pins 28/29, which correspond to T3 pins 3/4
or alternatively, chip pins 35/36, which correspond to pins A3/A4 on the T3. Since decoding needs FTM1 anyhow, I'd start using T3 pins 3 and 4. Since decoding is an alternate function for those pin pairs, their default functions would need to be switched to encoder mode.

The timer/counter blocks in the K20 chip are amazingly capable, but also amazingly complex. Take a look at figure 35-1, the block diagram for the timer modules. Thus getting quad decode working -- without breaking other things -- doesn't look that all easy. The chapter on timers is 100+ pages!! Sect 35.4.25 (p. 807) was a good starting point for me for encoder decoding. Although it's complex, I *love* some of the features of the K20's quad decode. (I wouldn't have killed for those in grad school, but I'd probably have considered maiming.) <Just kidding!>

I don't have any encoders handy, so I'm not going to start on encoder/decoding software until I do. But next time I order from either Sparkfun or Adafruit, I'll put a couple low-rez ones in my order.

I'm not promising to deliver working encoder code, but one of my projects would go better with an encoder as a user input (vs. a limited-angle pot.) So when I get to that, I anticipate taking a shot at T3 encoder code.

Please post here if I've gotten something wrong in my notes or if you have comments or other relevant info. -- Larry

Jp3141
02-04-2013, 05:58 PM
This chip has one channel of hardware quadrature decoder. Hardware encoder decoding *must use* both channels on the second timer module, FTM1 -- can't switch it to other timer channels.

Paul, do you know of any T3 code in your distribution that must use FTM1? If so, please let me know what.
My hope is that other timer-based functionality does not use FTM1 (except for PWM/capture/compare on pins 3&4.) If nothing else needs to use FTM1, that would lower the odds that encoder decoding will break something else.


I think that analogWrite on pins 3 & 4 uses FTM1 -- see pins_teensy.c

PaulStoffregen
02-04-2013, 06:03 PM
I'm planning to work on Encoder again in about 1-2 weeks. My main focus will be on making sure the interrupts are properly used.

I'll take a shot at special code to use FTM1 if pins 3 and 4 are selected. But if I can't get it in a day or two, it'll probably go onto my low-priority list to do later. There are lots of other really important things to do..... and Encoder using interrupts is very capable.

Jp3141
02-04-2013, 06:54 PM
I'm planning to work on Encoder again in about 1-2 weeks. My main focus will be on making sure the interrupts are properly used.

I'll take a shot at special code to use FTM1 if pins 3 and 4 are selected. But if I can't get it in a day or two, it'll probably go onto my low-priority list to do later. There are lots of other really important things to do..... and Encoder using interrupts is very capable.

Paul -- what I think is valuable would be to have some documentation about which MCU features the Arduino features use -- e.g. AnalogWrite() on channels 3&4 'take over' the FTM1; tone() uses PIT3 etc.

LarryP
02-04-2013, 07:20 PM
I think that analogWrite on pins 3 & 4 uses FTM1 -- see pins_teensy.c

@Jp3141,

Thanks for your response; glad to see others interested.

As I understand it, the existence of analogWrite (a flavor of PWM) for pins 3 or 4 shouldn't be a problem for encoder reading via those pins. Yes, both AnalogWrite() on pins 3,4 and hardware supported encoder reads must use the same counter module, FTM1. But since any I/O pin can only be in one mode at a time, I think the issue for those pins is that they can't be used for anything else while configured for encoder decoding. I think that's the same for any pin with alternate functions.

However, using T3 pins A2 and A3 for encoder inputs means that you can't do analogWrite (or any other timer-based function) on T3 pins 3 and 4, because FTM1 (needed for both) is busy counting encoder steps. Whenever possible, I'd avoid using A2 and A3 for anything digital, including analogWrite (really PWM, a digital output with lots of sharp edges.) IMHO, better to keep those sharp edges as far from analog signals as possible.

Configuring only one pin of either possible pair for encoder reading would limit what you could do with it's mate. But configuring only one pin of a pair for encoder reading doesn't make much sense. IMHO, for a one channel encoder (AKA digital tach) it'd be better to use input capture mode on a non-encoder pin.

jedix
02-06-2013, 11:11 AM
I'm also very interested in the Encoder library, and if it could be optimized with hardware quadrature decoder it would be awesome ! I'm not good enough to help you developping this kind of stuff, but I fully support you guys ! :)

Wozzy
02-06-2013, 12:51 PM
I agree, that it's important to have a functional encoder library working on the Teensy 3.0.
However, this discussion reminded me of this high performance SPI Quadrature decoder chip that I used very successfully in the past.
One advantage, is that it completely relieves the Micro-controller from monitoring the encoders. When ever you poll it, the current value is there.

There are several variants, but here is a link to the data sheet for the one I used: http://www.lsicsi.com/pdfs/Data_Sheets/LS7366R.pdf

The biggest problem, is that they only seem to be available direct from the manufacturer, with a minimum order of 10.

PaulStoffregen
02-06-2013, 12:51 PM
I read part of the manual again, in preparation to work on this in a couple weeks. Probably the trickiest part is going to be extending the 16 bit hardware counter to 32 bits while handling race conditions where the counter overflows or underflows during the read operation.

The one design decision where I could really use some input is the default the Encoder library should use for the input filters? The filter is described in section 35.4.4.1 starting on page 750.

I know some people are excited about the capability to accumulate very fast quadrature waveforms... perhaps up to 6 or 12 MHz? The maximum isn't perfectly clear to me, but I suspect it'll be some division of 48 MHz (the bus clock runs FTM1, not the cpu clock). However, I'm reluctant to make unfiltered the default for the Encoder library. It will be an option if you edit the code, but I know a lot of the encoders people commonly use have high frequency noise, so I really don't want to make unfiltered the default. This isn't like computer speed, where faster is always better. Faster without filtering means more susceptible to noise, contact chatter and signal problems.

The filter clocks at 12 MHz and can require up to 32 of its cycles for the signal to be considered high or low. I believe that means the slowest filter setting would limit the speed to 187.5 kHz (or 750k counts/sec). A fast default might be only 3 or 4 filter clocks, corresponding to 2 MHz or 1.5 MHz signals (8M or 6M counts/sec).

Anyway, a big question I have is what is the benefit you perceive or expect from the hardware quadrature encoder? Is it extremely low CPU overhead? Is it the possibility to measure much faster signals? If the speed is the reason you want to use it, can you give me some description of the source of the fast signals you expect to use?

LarryP
02-06-2013, 11:38 PM
Paul,

Regarding overflow/underflow/race-condition issues in encoder software:

From my reading of the data (I have trouble calling a 1200+ page document a) sheet section, the chip designers included some hardware to make handling overflow/underflow/race-conditions easier. Search for TOFDIR. Unlike many hardware encoder interfaces, this one not only has a flag for counter rollover, TOF, it also has one for the **direction** of the rollover, TOFDIR. Those flags would have made my grad-school experimental software much, much easier. Without any such flags, the only way I know to maintain an accurate encoder count is to (a) bound the maximum counting rate and then (b) make sure you sample the counter faster than it could possibly count halfway. With those constraints, extending the counter width is doable. With those two flags, one doesn't need to check the counter nearly so often (I think it'd now be the time to count one less than twice the counter's max count -- almost 4 times as long permitted between reads.)

Regarding the filter. I was quite pleased to see they have a filter; not all encoder interfaces have that. Much better with.

Can anybody reading this give an example of anything other than in the guts of a software-defined (really software-morphable) radio that produces digital signals in quadrature at frequencies above 2 MHz.?

I suspect I've used higher resolution encoders than most reading this -- 81,000 lines on the encoder disk, giving 324,000 quadrature counts per revolution. (Has anybody here used an encoder with more lines/disk? If so, please speak up!) That number, by itself, might make you think we'd need a counter that could handle tens or hundreds of MHz. Not so. If you take a look at the specsheet for that 81,000 line encoder, http://usa.canon.com/CUSA/assets/app/pdf/encoder/LaserRotaryEncoder.pdf
you'll see that there's now an even higher resolution version, the R1P20!?! However, if you take a look at the "Max Response" row in those specs, you'll see that the fastest that the raw quadrature signals can go is 2 MHz (and it isn't even for the highest resolution encoder.) Typical encoders used in motion control have no more than 4096 lines/disk (often significantly less.) even spinning at 10,000 RPM (which is cooking along!) that gives 4096*10,000 / 60 = approx 683 KHz. Most shaft encoders have a spec for maximum count rate -- which may well be significantly less than the max. speed constraints from the bearings, etc. involved.

Another data point re filtering: H-P, back before they split up, made an encoder interface chip (originally for their printer division.) That chip used a best-3-out-of-4 filter, fixed thus not programmable. So I suspect that a default of 4 should work well on the high-frequency range. There might be an issue on the other end of frequencies. Mechanical encoders with (inevitably) bouncy contacts will probably bounce too slowly to be caught by the hardware filter. Hopefully mechanical encoders are made such that one phase's bouncing ends before the other phase transitions (and probably starts bouncing.) I haven't used those, though I expect to soon. Optical or magnetic encoders should be fine with the encoder interface hardware on this chip.

What I see as the main benefit of the hardware quad decoder is that, once some poor bastard [1] publishes a timer-interrupt driven ISR to read the encoder periodically and extend the count to 32 or 64 bits (ISR shouldn't take more than a small fraction of the CPU), all we'll have to do (after a few calls to set up the decoder as desired) is call readEncoder()

[1] My intent in this post is to provide some data to assist that poor bastard.

PaulStoffregen
02-07-2013, 01:01 AM
Thanks. That's a lot of good perspective on actual encoder needs. :)


once some poor bastard [1] publishes a timer-interrupt driven ISR to read the encoder periodically and extend the count to 32 or 64 bits (ISR shouldn't take more than a small fraction of the CPU), all we'll have to do (after a few calls to set up the decoder as desired) is call readEncoder()

That's pretty much exactly what I intend to try with the Encoder library in a couple weeks. It already implements a 32 bit count based on pin change interrupts and/or polling. When/if I support the hardware encoder, it'll be extended to 32 bits.

LarryP
02-08-2013, 04:04 PM
The maximum isn't perfectly clear to me, but I suspect it'll be some division of 48 MHz (the bus clock runs FTM1, not the cpu clock).

Paul,

Datasheet sect. 35.1.2 states:

Fixed frequency clock is an additional clock input to allow the selection of an on
chip clock source other than the system clock.
The FTM block diagram shows this as well. So it appears we have some flexibility re clock sources for encoder decoding.

Regarding filtering for encoder signals:
I haven't yet found any description of exactly how the filter works. However, my understanding is that when set to N, it requires *something* about N successive samples of the raw encoder signal before changing state. When in quadrature mode, each full cycle of an encoder phase has both a low and a high half cycle. (I'm focusing on quadrature mode for now, rather than step/direction) To ensure that the signal makes it through the filter (so no counts are lost), I'm pretty sure we must ensure at least N samples in each half cycle, thus 2*N samples per full cycle. So, if the maximum frequency of the raw encoder phases is w, then we must clock the filters at 2*N*w or faster. If we can select 48 MHz to clock FTM1, then a filter value of N=4 means the encoder signals must be 48/(2*4) = 6 Mhz or lower. If we can only clock FTM1 at 12 MHz, then the encoder signals cannot exceed 1.5 MHz. I believe a default setting of either 3 or 4 for the encoder filtering is a reasonable starting point.

Kian
03-15-2013, 02:31 AM
Hi all,

Sorry for deviating from the discussion a bit. I tried to use the Encoder.h library with Teensy3, making modifications to util/direct_pin_read.h as suggested by Paul. The program compiles without errors and sort of works but not very reliably. I am using a standard mechanical rotary encoder and connecting the output of the encoder directly to pins 7 and 8 (no capacitors or damping circuit).

My code monitors the encoder in a loop and does a serial print on whether it is turned clockwise or anticlockwise. So, when I turn the encoder in 1 direction, it reports the wrong direction about 20% of the time. Anyone got the same problem?

PaulStoffregen
03-15-2013, 09:41 AM
Try moving from pin 8 to any pin less than 7.

Currently Encoder only supports up to 8 interrupts, on pins 0 to 7. I'm working on an update with will expand it to support interrupts on all pins.

jedix
05-12-2013, 11:02 AM
Is the hardware quadrature decoder implementation still topical ? Is it scheduled for teensyduino 1.15 ?

tni
07-30-2013, 08:07 AM
I'm using 12 mechanical rotary encoders - works fine with the current lib (including the bottom pins, which sure come in handy).

Even though the lib doesn't seem to do filtering/deglitching, I haven't seen any glitches so far (not doing hardware deglitching either, the encoders are wired directly to the Teensy).

(I originally asked about the hardware encoder because I wanted to use it with a motor and high resolution optical encoder, but I ended up with a motor controller that has a quadrature decoder built in.)

PaulStoffregen
07-30-2013, 04:31 PM
I'm using 12 mechanical rotary encoders - works fine with the current lib (including the bottom pins, which sure come in handy).

Any chance you could share a photo? I believe 12 is the most I've heard anyone actually used so far.

tni
07-31-2013, 06:56 AM
Any chance you could share a photo? I believe 12 is the most I've heard anyone actually used so far.
It's just an ugly prototype right now that needs to move to a real enclosure. Note the obese Teensy that's using some floppy connectors for the bottom pins and power / ground distribution.

The encoders are Alps EC11 with integrated push button (only two buttons connected). There are enough pins left to add a display...

741 742

samsul
10-12-2013, 11:19 PM
I'm using 12 mechanical rotary encoders - works fine with the current lib (including the bottom pins, which sure come in handy).

Even though the lib doesn't seem to do filtering/deglitching, I haven't seen any glitches so far (not doing hardware deglitching either, the encoders are wired directly to the Teensy). mlm leads (http://www.national-leads.com/)
thank.

Nantonos
10-13-2013, 07:35 PM
Skyler605, which model of encoders are you using?

bberger
11-16-2013, 03:08 PM
Hi Paul and other Arduino-Aliens,

is there any news on support for the Teensy 3.0 and the hardware encoder? I'm in the process of doing a FFB Steering Wheel (HID) project based on the Teensy 3.0 using a servo motor with 2.5k lines at max 1.5k rpm. I really need to read the encoder pulses a) very accurately and b) very light on the CPU as there are many other calculations going on which are critical.

Unfortunately I don't really have the skills to dig into the ARM datasheet (yet) but I kind of rely on some functional libraries to finish my (to be OpenSource) project. The market is really missing an OS solution for this particular type of game controller and I'd like to help out as good as I can to get something going here :)

Wozzy
11-18-2013, 06:32 PM
bberger,

Have a look at this high performance SPI Quadrature decoder chip that I used very successfully in the past.
One advantage, is that it completely relieves the Micro-controller from monitoring the encoders. When ever you poll it, the current value is there.

There are several variants, but here is a link to the data sheet for the one I used: http://www.lsicsi.com/pdfs/Data_Sheets/LS7366R.pdf

The biggest problem, is that they only seem to be available direct from the manufacturer, with a minimum order of 10.

JonHylands
12-20-2013, 12:45 PM
I just bought a Teensy 3.1, and I'm trying to use the interrupt-based Encoder library (with the mods supplied by Paul in post #2). I'm using this on a small robot I'm building, and hooking it up to 2 & 3. I'm using this encoder (http://www.pololu.com/product/2590) hooked up to their 100:1 gearmotor, and hooking it up to my logic analyzer and running the motor direct from a 7.4 volt battery, I'm seeing a very nice square sin wave on both A & B, at about 3.0 kHz each, offset 90 degrees.

1237

I assume this is well within the capabilities of using the interrupt based encoders.

I had to add the following to direct_pin_read.h (after making the change in post 2):


#if defined(__AVR__) || defined(__MK20DX128__) || defined(__MK20DX256__)

as I assume the chip on the 3.1 is a __MK20DX256__

I'm not getting any useful data out of the encoder. I'm using the following code:


/* Encoder Library - Basic Example
* http://www.pjrc.com/teensy/td_libs_Encoder.html
*
* This example code is in the public domain.
*/

#include <Encoder.h>

const int ledPin = 13;

// Change these two numbers to the pins connected to your encoder.
// Best Performance: both pins have interrupt capability
// Good Performance: only the first pin has interrupt capability
// Low Performance: neither pin has interrupt capability
Encoder myEnc(2, 3);
// avoid using pins with LEDs attached

void setup() {
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH); // set the LED on
delay(1000); // wait for a second
digitalWrite(ledPin, LOW); // set the LED off

Serial1.begin(57600);
Serial1.println("Basic Encoder Test 2:");
}

long oldPosition = -999;

void loop() {
long newPosition = myEnc.read();
if (newPosition != oldPosition) {
oldPosition = newPosition;
Serial1.println(newPosition);
}
digitalWrite(ledPin, HIGH); // set the LED on
delay(100); // wait for a second
digitalWrite(ledPin, LOW); // set the LED off
delay(900); // wait for a second
}

I added to LED blink code to make sure it was actually running (which it is). I'm using an FT232 hooked up to pins 0 & 1 for logging. I get the same non-results on the output console regardless of whether the LED code is there or not.

I spin the motor by hand, and the encoder has a 5-spoke wheel on it, so that generates a roughly 400-500 Hz signal. I got the following output:


Basic Encoder Test 2:
0
1

I get the same output when I turn on the motor and drive it that way.

Does the interrupt-driven encoder code work on the Teensy 3.1 at all?

- Jon

JonHylands
12-20-2013, 05:06 PM
I figured it out - its the INPUT_PULLUP that is the issue, perhaps only with this specific encoder. I changed lines 73 and 74 from Encoder.h as follows:


#ifdef INPUT_PULLUP
pinMode(pin1, INPUT);
pinMode(pin2, INPUT);
#else


and now it works perfectly.

- Jon

jleichty
03-13-2014, 06:32 AM
I'm interested in reading a fast (200 kHz, okay I guess that's not that fast) quadrature encoder (US Digital E4P) on a motor I have while doing a bunch of other stuff with the Teensy, including reading 4 much slower encoders with the software library. Cruising around the nets I found this Freescale application note that helped immensely -- I was basically able to copy their example while looking at the datasheets for microcontroller on the Teensy 3.0.
http://cache.freescale.com/files/32bit/doc/app_note/AN4381.pdf

Here's what I came up with to test -- it seems to work when hooked up to my encoder through a level shifter, with the exception that it rolls over to 0 after 49151, whereas I would have expected it to roll over at 2^16 based on the 16-bit counter. Not sure why this is, but haven't looked too close yet. Really all I've looked at is that when I turn one way, the counter goes up, other way, goes down. Haven't yet checked for proper 1-to-1 counting.

#define SIM_SCGC6_FTM1_MASK 0x20000000
#define FTM_MODE_FTMEN_MASK 0x00000001
#define FTM_CONF_BDMMODE_3 0x000000C0
#define FTM_QDCTRL_QUADEN_MASK 0x00000001
#define FTM_SC_CLKS_3 0x00000018

void setup() {
// enable the clock for FTM1
SIM_SCGC6 |= SIM_SCGC6_FTM1_MASK;
// enable FTM1
FTM1_MODE |= FTM_MODE_FTMEN_MASK;
// set the counter to run in debug mode, not really necessary
FTM1_CONF |= FTM_CONF_BDMMODE_3;
// set the counter initial value
FTM1_CNTIN = 0;
// set the FTM for quadrature mode, default options
FTM1_QDCTRL |= FTM_QDCTRL_QUADEN_MASK;
// start the FTM by selecting external clock
FTM1_SC |= FTM_SC_CLKS_3;
// configure the input pins
PORTA_PCR12 = PORT_PCR_MUX(7); // Teensy pin 3
PORTA_PCR13 = PORT_PCR_MUX(7); // Teensy pin 4

// initialize serial
Serial.begin(115200);
Serial.println("Hardware Encoder Test");
}

void loop() {
Serial.println(FTM1_CNT);
delay(500);
}
P.S. This is my first ever Teensy sketch! Is there any way to access the register masks referenced in the application note as opposed to defining them myself?

Jp3141
03-13-2014, 07:45 AM
Many are in mk20dx128.h which is deep inside the Arduino directory. On a Mac, that's:
/Applications/Arduino.app/Contents/Resources/Java/hardware/teensy/cores/teensy3

For Teensy 3.1, it is mk20dx256.h

geekguy
03-16-2014, 11:09 PM
There are several variants, but here is a link to the data sheet for the one I used: http://www.lsicsi.com/pdfs/Data_Sheets/LS7366R.pdf
Good find! The LS7766DO (http://www.lsicsi.com/pdfs/Data_Sheets/LS7766.pdf) also looks very interesting - it handles dual quadrature encoders. I have requested a sample. My robot, W.A.L.T.E.R. has quadrature encoders on both motors, so I have an application for this chip.

Update: My sample request has been accepted, and I will be getting two LS7766DO parts. :)

8-Dale