Continuous pin read using FlexIO

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Continuous pin read using FlexIO

293 Views
lukasz_iwaszkie
Contributor II

Hey community

 

   I can't get FlexIO to work. I'm on imxrt1011 (eval board) and wanted to periodically read the FLEXIO pin 1. I use the code pasted below:

 

void init ()
{
        static constexpr uint32_t PIN_NO = 1;
        static constexpr uint32_t SHIFTER_NO = 0;
        static constexpr uint32_t TIMER_NO = 0;

        CLOCK_EnableClock (kCLOCK_Flexio1);

        flexio_config_t fxioUserConfig{};
        FLEXIO_GetDefaultConfig (&fxioUserConfig);
        FLEXIO_Init (FLEXIO1, &fxioUserConfig);

        flexio_shifter_config_t shifterConfig{};
        shifterConfig.timerSelect = TIMER_NO; // Which timer controls the shifter.
        shifterConfig.timerPolarity = kFLEXIO_ShifterTimerPolarityOnPositive;

        shifterConfig.pinConfig = kFLEXIO_PinConfigOutputDisabled; // I guess this mean "input" in kinda indirect way.
        shifterConfig.pinSelect = PIN_NO;                          // Pin to read.
        shifterConfig.pinPolarity = kFLEXIO_PinActiveHigh;         //

        shifterConfig.shifterMode = kFLEXIO_ShifterModeReceive; // One of these 6 modes.
        shifterConfig.parallelWidth = 0;
        shifterConfig.inputSource = kFLEXIO_ShifterInputFromPin;
        shifterConfig.shifterStop = kFLEXIO_ShifterStopBitDisable;
        shifterConfig.shifterStart = kFLEXIO_ShifterStartBitDisabledLoadDataOnEnable;

        FLEXIO_SetShifterConfig (FLEXIO1, SHIFTER_NO, &shifterConfig);

        flexio_timer_config_t fxioTimerConfig{};
        fxioTimerConfig.triggerSelect = FLEXIO_TIMER_TRIGGER_SEL_SHIFTnSTAT (1U);
        fxioTimerConfig.triggerPolarity = kFLEXIO_TimerTriggerPolarityActiveHigh;
        fxioTimerConfig.triggerSource = kFLEXIO_TimerTriggerSourceInternal;

        fxioTimerConfig.pinConfig = kFLEXIO_PinConfigOutputDisabled;
        fxioTimerConfig.pinSelect = 0; // ?
        fxioTimerConfig.pinPolarity = kFLEXIO_PinActiveHigh;

        fxioTimerConfig.timerMode = kFLEXIO_TimerModeSingle16Bit;
        fxioTimerConfig.timerOutput = kFLEXIO_TimerOutputOneNotAffectedByReset;
        fxioTimerConfig.timerDecrement = kFLEXIO_TimerDecSrcOnFlexIOClockShiftTimerOutput;
        fxioTimerConfig.timerReset = kFLEXIO_TimerResetNever;
        fxioTimerConfig.timerDisable = kFLEXIO_TimerDisableNever;
        fxioTimerConfig.timerEnable = kFLEXIO_TimerEnabledAlways; // This probably makes trigger config irrelevant. Does it?
        fxioTimerConfig.timerStop = kFLEXIO_TimerStopBitDisabled;
        fxioTimerConfig.timerStart = kFLEXIO_TimerStartBitDisabled;
        fxioTimerConfig.timerCompare = 0xff;

        FLEXIO_SetTimerConfig (FLEXIO1, TIMER_NO, &fxioTimerConfig);
        FLEXIO_ClearShifterErrorFlags (FLEXIO1, 0xff);
        FLEXIO_ClearTimerStatusFlags (FLEXIO1, 0xff);
        FLEXIO_Enable (FLEXIO1, true);

        auto waitForShifterStatus = [] {
                while (!(FLEXIO_GetShifterStatusFlags (FLEXIO1) & (1 << SHIFTER_NO))) {
                };
        };

        waitForShifterStatus ();

        auto prev = FLEXIO1->SHIFTBUF[SHIFTER_NO];
        // uint32_t pinPrev = FLEXIO_ReadPinInput (FLEXIO1);

        while (true) {

                /*
                 * In 16-bit counter mode, the timer status flag is set when the
                 * 16-bit counter equals zero and decrements. Timer number is 0.
                 */
                while ((FLEXIO_GetTimerStatusFlags (FLEXIO1) & (1 << SHIFTER_NO)) == 0) {
                }

                /*
                 * For SMOD=Receive, the status flag is set when SHIFTBUF has been loaded with data from Shifter
                 * (SHIFTBUF is full), and the status flag is cleared when SHIFTBUF register is read.
                 *
                 * fxioTimerConfig.timerCompare has impact on how frequently this condition is asserted. So
                 * the timer seems to work.
                 */
                waitForShifterStatus ();

                if (auto err = FLEXIO_GetShifterErrorFlags (FLEXIO1); (err & (1 << SHIFTER_NO)) != 0) {
                        asm ("bkpt");
                }

                auto curr = FLEXIO1->SHIFTBUF[SHIFTER_NO];
                // GPIO_PortToggle (GPIO1, 1u << 19);

                if (prev != curr) {
                        prev = curr;
                        GPIO_PortToggle (GPIO1, 1u << 19);
                }

                // uint32_t pinCurr = FLEXIO_ReadPinInput (FLEXIO1);

                // if (pinPrev != pinCurr) {
                //         pinPrev = pinCurr;
                //         GPIO_PortToggle (GPIO1, 1u << 19);
                // }
        }
}

 

 

In mcuxpresso config tool I configured FlexIO clock to 3.75MHz my PIN config is like this:

lukasz_iwaszkie_0-1729253421689.png

I use the config tool, but the program file structure is custom (config tool files are compiled in however).

I feed square wave into FLEXIO pin 1 (pin D0 on the eval board) and also I've got an oscilloscope hooked up to GPIO19 (D10 on the evk). I expect to see a square signal (with frequency dependent on the input signal frequency and timer compare config) but it's not there.

I think that the timer works, because when I uncomment the FLEXIO_ReadPinInput part (at the bottom) I can see on GPIO19 that pinPrev != pinCurr is asserted periodically. Under debugger I verified that this is indeed bit no 1 that is begin toggled, so looks like the pin works.

However no matter what I do FLEXIO1->SHIFTBUF reads 0x80000000 when read for the first time, and then it reads only 0. What do I do wrong?

0 Kudos
Reply
5 Replies

275 Views
MultipleMonomials
Contributor IV

Hmm... One issue I see here is the use of kFLEXIO_TimerDecSrcOnFlexIOClockShiftTimerOutput.  This causes the Shift Clock for the timer to be equal to its output clock (the output clock is a clock generated by a timer which toggles each time the timer compares).  Since the timer compare is set to 0xFF, this means that the shift clock will be the flexio clock divided by 512 (there's an extra /2 thrown in because the output clock *toggles* each time the timer compares).  All good so far, but the issue is that each time a timer generates a comparison, the shift register stores and then resets its shift buffer.  And, shift registers cannot shift and store on the same clock, so the input data (current value of the pin) will be lost and not stored when that happens.

So basically, what we need to do is make sure that the shift clock has at least 1 rising edge *before* the shifter's store flag (aka the timer's compare line) goes high.

An easy way to do that would be to use kFLEXIO_TimerModeDual8BitBaudBit mode instead of 16-bit mode.  This splits the timer into two counters, where the lower one generates the shift clock and the upper one generates the compare line.  So something a bit like:

        fxioTimerConfig.timerMode = kFLEXIO_TimerModeDual8BitBaudBit;
        fxioTimerConfig.timerCompare = 0x01ff;

This will generate a shift clock of flexio clock / 512.  Then, every 2nd shift clock, the upper counter will expire and generate a compare pulse which will trigger the shifter to save into shiftbuf and reset its contents.

Hope this makes sense, I have done a fair amount of experimenting and reverse engineering work with FlexIO timers and this is based on what I discovered.

Would also recommend this thread for a bit more background information on how shifters work and how to "tap" flexio signals and route them to pins: https://community.nxp.com/t5/i-MX-RT-Crossover-MCUs/FlexIO-not-working-in-Match-Continuous-Mode/m-p/...

227 Views
lukasz_iwaszkie
Contributor II

And as I feared it turned out that my continuous reception is skipping on some bits. I fed 10MHz to FlexIO pin 26. FlexIO clock == 120MHz. I set fxioTimerConfig.timerCompare = 0x4000; and fxioTimerConfig.timerMode = kFLEXIO_TimerModeDual8BitBaudBit; So the clock speed is the maximum (0x00; which seems to correspond to 60 MHz as seen from the patterns below) and 0x40 stands for "the number of shift clock edges in the transfer". I configured eDMA (2 transfers linked in a ping-pong style) and my destination buffer looks like:

0xC71C71C7 = 11000111000111000111000111000111
0x38E38E38 = 00111000111000111000111000111000
0xE38E38E3 = 11100011100011100011100011100011
0x1C71C71C = 00011100011100011100011100011100

 So there are single (?) bits missing and I'm afraid this is due to this limitation that you mentioned that FlexIO can't shift and store on the same cycle. So my understanding is that it shifts 31 bits, and instead of shifting the 32th it does the "storing" which copies shifter contents to the SHIFTBUFn register and this way it loses this last bit.

It makes me wonder if such a continuous, uninterrupted transfer is even possible? Alas RM section on FlexIO welcomes you with this warning about FlexIO limitation and encourages you to "One workaround is to use other timer count the number bit periods and generate the disable signals." - but it says about disabling the timer. Wouldn't that stop the receiving completely?

0 Kudos
Reply

191 Views
MultipleMonomials
Contributor IV

Hmmm.  I'm not sure, but if I remember correctly, FlexIO shifters can be set to shift on either the rising or falling edge of the clock.  Maybe you need to set the shifter to use the falling edge by inverting the clock input?

While shifters cannot shift and store at the exact same time, they can shift and store one half-cycle apart.  So if you set it up so that storing and shifting happen on opposite half cycles, you can capture a signal continuously.  I've gone through the official NXP SPI example and this is the technique they used!

38 Views
lukasz_iwaszkie
Contributor II

What worked at the end was to switch to fxioTimerConfig.timerCompare = 0x3f00; (from 0x4000), and also I set shifterConfig.timerPolarity = kFLEXIO_ShifterTimerPolarityOnNegitive; That (and many more hours on eDMA configuration) finally gave me uninterrupted stream @60 Msps from a single shifter.

Tags (1)
0 Kudos
Reply

247 Views
lukasz_iwaszkie
Contributor II

Let me cite the classic: "thing of beauty, joy forever". That works beautifully sir. I was under the false impression that shifters are always driven directly by the main FlexIO clock, which, when you think about it, makes little sense, because all 8 shifters would run at the same speed, which contradicts the word "flexible". So actually a flexio timer provides both the clock signal for shifting, AND separately the timer_store_data signal (page 1611 of 1010RM) and they are different things...

I'll experiment with eDMA, and will try to read a pin the fastest I can (continuously for a longer period of time). To my understanding it should be possible to do at least 60MHz (given that the FlexIO clock is 120MHz). I think 60 because of this limitation  that FlexIo can't shift and store in the same cycle.

Tags (1)
0 Kudos
Reply