External event managment and IR remote control

If you have a simple question and just want an answer.
Dohny
Member++
Posts: 26
Joined: Tue Dec 03, 2013 6:57 pm

External event managment and IR remote control

Post by Dohny »

Hello,

I'm editing USB audio SW for XUF208 procesor, I have disabled SPDIF as I don't need it and I'm using spared resources for my own user task. I've connected OLED display, rotary encoder, buttons and IR remote receiver.

Most things are fine, but I have problems with events. As I'm used to using interrupts on other platforms, I used events the same way, obviously not correct way, as it's taking whole core just to do IR receiver - if I put anything into the IR task loop that takes more than about 1ms, it just messes up the timing. It seems that its not working as an event, but its just pooling the input port for a change. Can you guys please have a look at my code what im doing wrong?

Code: Select all

#include <timer.h>
#include <platform.h>
#include <safestring.h>
#include <print.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <print.h>
#include "xc_ptr.h"
#include "gpio_access.h"
#include "SSD1331.h"
#include "gfx.h"
#include "audio.h"

UG_GUI gui;

typedef unsigned char uint8_t;
typedef unsigned int uint32_t;

in port sensor = XS1_PORT_1B;
in port p_enc = XS1_PORT_1L;
in port p_dir = XS1_PORT_4E;

void wait_us_task(int microseconds)
{
    timer t;
    unsigned time;

    t :> time;
    t when timerafter(time + (microseconds * 100)) :> void;
}

void audiohw_task(void)
{
    char str[15];

    SSD1331_init();
    UG_Init(&gui, 96, 64);
    UG_SelectGUI(&gui);
    UG_FillScreen(0x00);
    UG_FontSelect(FONT_6X10);

    uint8_t debug = 0;

    uint32_t low_pulse = 0;
    uint8_t ir_codes[3] = {0,0,0};
    uint32_t fresh_codes = 0;
    uint32_t last_edge = 0;
    uint32_t buffer = 0;
    uint32_t buffer_count = 0;
    uint32_t can_repeat = 0;
    uint32_t preamble = 0;

    uint32_t idle_max = 120*1000*100;
    timer tick;
    uint32_t next_tick;
    tick :> next_tick;

    set_port_inv(sensor);

    while(1)
    {

        //wait_us_task(1000); //this messes up timing

        select {

        case tick when timerafter(next_tick) :> void:
            // timeout, clean up
            next_tick += idle_max;
            can_repeat = buffer_count = preamble = 0;
            break;

        case sensor when pinsneq(low_pulse) :> low_pulse:
            // sensor signal changed state
            uint32_t now;
            tick :> now;
            next_tick = now + idle_max;
            uint32_t width = (now - last_edge)/100;
            last_edge = now;

            if (!low_pulse)
            { // look for preamble mark
                if ((7200<width) && (10800>width))
                {
                    //sprintf(str, "width: %d", width);
                    //UG_PutString(10, 30, C_RED, (char*) str);
                    preamble = 1;
                    buffer_count = 0;
                }
            }
            else switch (preamble)
            {
            case 1:  // look for preamble space, or repeat code
                if ((3600<width) && (5400>width))
                {
                    can_repeat = 0;
                    preamble = 2;
                    break;
                }
                else if ((1800<width) && (2700>width) && can_repeat)
                {
                    fresh_codes = 1;
                    //remote.codeReady();
                }
                preamble = 0;
                break;
            case 2:  // look for 32 data bits
                buffer >>= 1;
                if ((1350<width) && (2025>width))
                {
                    buffer |= 0x80000000;
                }
                else if ((450>=width) || (675<=width))
                {
                    preamble = 0;
                    break;
                }
                if (32 <= ++buffer_count)
                {
                    ir_codes[1] = buffer; // low address
                    buffer>>=8;
                    ir_codes[2] = buffer; // high address
                    buffer>>=8;
                    ir_codes[0] = buffer; // ir code
                    buffer>>=8;
                    uint8_t check = ~buffer;
                    // accept any address at the driver level
                    can_repeat = fresh_codes = (check==ir_codes[0]);
                    if (fresh_codes)
                    {
                        sprintf(str, "h%x h%x h%x\n", ir_codes[1], ir_codes[2], ir_codes[0]);
                        UG_PutString(10, 30, C_RED, (char*) str);
                    }
                    preamble = 0;
                }
                break;
            }
            break;
        }
    }
}
Thank you for any helpfull information.

Cheers.


User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

HI, this is exactly what I would expect - add a delay in your loop and you'll miss events.. Didn't look at the code in great detail but in general the form looks OK, although beware of printing (particularly time consuming formatted writes) in time critical sections. This will definitely break everything on the tile if xscope is not enabled.

Events (using select) are where the processor halts, and then vectors immediately to the event source when the event occurs. This is different from interrupts where the execution is preempted, the context saved, the ISR run, the context restored and you pickup from where you left off.

Events are much faster to respond, but to achieve multi-tasking within that core, you need to have the other tasks triggered by events in the same select. You will need to ensure that each of the handlers (cases) execute and reach the break quickly to make sure that you are ready to pickup the next event in a timely manner.

There is a mechanism in the tools to do this (combine multiple event driven tasks) call combinable tasks. There are caveats (make sure your cases are short, all combinable tasks must be while(1) select and default cases are not allowed) but it works well for low speed stuff like you are doing.

https://www.xmos.com/published/how-defi ... e-function
Dohny
Member++
Posts: 26
Joined: Tue Dec 03, 2013 6:57 pm

Post by Dohny »

So you are basically saing that I need to have everything event triggered? For rotary encoder and buttons that works OK (buttons are on 4 bit ports, so its kinda dodgy), but for driving my OLED, inicializing DAC and stuff like that not so much. I tried to have timer triggered event (5ms) and doing int to string conversion, sending data to display and level meters just take all the processing time. That is absolutely expected, as there is not a such thing as priority in events, at least I didnt find it.

Is there a way to get interrupts working? That should solve my problem with IR and rest of the things can just run in the task itself, as they are not time sensitive.

I know combinable tasks, but I don't think that is my solution here, as if my understanding is correct, it just combines all the events into one loop.
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

So you are basically saing that I need to have everything event triggered?
That is the XMOS (aka CSP) programming idiom, however you bring a good point in that interrupts can be a nice way of scheduling tasks in the same thread, especially if you only have one interrupt!

For rotary encoder and buttons that works OK (buttons are on 4 bit ports, so its kinda dodgy), but for driving my OLED, inicializing DAC and stuff like that not so much. I tried to have timer triggered event (5ms) and doing int to string conversion, sending data to display and level meters just take all the processing time. That is absolutely expected, as there is not a such thing as priority in events, at least I didnt find it.
You can order selects (so say which one fires if multiple are ready to go on each loop) but not preempt.
Is there a way to get interrupts working? That should solve my problem with IR and rest of the things can just run in the task itself, as they are not time sensitive.
Yes - we do actually use them in a couple of bits of IP I am aware of, but there's lots of assembly involved. I can point you there if interested.
I know combinable tasks, but I don't think that is my solution here, as if my understanding is correct, it just combines all the events into one loop.
Yes - and won't help if you have long functions that take too long and cause other deadlines to be missed.

However....what about this idea... What you really could do with is more cores. You can only have 6 normally because 500/6 = 83.33 which meets the required 80MHz for XUD. However, XS2 has a priority mode in the scheduler. If you put

Code: Select all

set_core_high_priority_on()
before XUD in main.xc you can guarrantee it gets 100MHz. You can then add 2 more tasks, each of which (and the rest of the normal priority cores) will get (500 - 100) / 7 = 57MHz.

That is enough for the reference design at lower channel count USB implementations.

You can now program using the normal programming idiom and not have to get tied down with interrupts etc..
Dohny
Member++
Posts: 26
Joined: Tue Dec 03, 2013 6:57 pm

Post by Dohny »

Hi, thanks you for reply.

Can I please get more info about interrupts somewhere? It would be definetly helpfull. More threads not seem to help much, as my rotary ancoder also doesnt work properly with events. Its acting exactly as it shouldn't, fast response means it gets all the ringing and then its extremly sensitive to timing. Fast rotating it will do all kinds of inappropriate stuff. Interupts is the only way for me here it seems.
Assembly should not be a problem.

Thank you.
Dohny
Member++
Posts: 26
Joined: Tue Dec 03, 2013 6:57 pm

Post by Dohny »

Nothing? :(

Can you please explain how events exactly work? What happnes when event is fired? Why do I have a feeling its not really "strong" and deterministic way of getting stuff handled? Interrupt is fired, whole routine is handled and then it returns to whatever it was doing before. With external events it just acts like pooled input...

I'm starting to be kinda desperate, as Im trying to get rotary encoder working properly on one core with other things for three days now. IR remote, buttons and rotary encoder can be handled by 1MHz 8051. Its obvious that I must be doing something wrong, if 50MIPS 32 bit core with extended instruction set cannot handle it.

Thank you very much for any help, will be very appreciated.
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

Can you please explain how events exactly work? What happnes when event is fired? Why do I have a feeling its not really "strong" and deterministic way of getting stuff handled? Interrupt is fired, whole routine is handled and then it returns to whatever it was doing before. With external events it just acts like pooled input...
There is much documentation on this already such as:

https://www.xmos.com/download/private/X ... 28F%29.pdf

However, in essence, an event works by setting up your event sources (timer/io/channel), pausing the processor (PC stops) at a waiteu instruction and waiting for the event. When the event occurs, the event handler entry gets stuff directly into the PC and the processor starts. This is just a couple of cycles and many times faster than an interrupt, mainly because the context does not need to be saved and restored. However, it does mean that the core is paused when waiting for events - that's partly why you get lots of them.

Effectively what you get, using a select with event handlers, is an event driven state machine. This is ideal for the sort of I/O stuff you mention below. Think of it as a hardware accelerated while(1) {select} statement.

https://www.xmos.com/download/private/B ... 38A%29.pdf
I'm starting to be kinda desperate, as Im trying to get rotary encoder working properly on one core with other things for three days now. IR remote, buttons and rotary encoder can be handled by 1MHz 8051. Its obvious that I must be doing something wrong, if 50MIPS 32 bit core with extended instruction set cannot handle it.
This will be no sweat for a logical core... I would recommend getting each of these going as individual cores/tasks initially and then combine them together.


This is much more complex than you need, but a single task was able to read & decode 2 x motor control QEI at a sample rate of over 1MHz. Honestly, xmos can do things (especially IO) that are way out of reach of even the fastest MCUs:

https://www.xmos.com/support/boards?ver ... 825&page=7
https://github.com/xcore/sw_foc_motor_c ... oc_qei/doc

A much simpler pin event example (6.5 Waiting for a condition on an input pin) can be found on page 65 of this:

https://www.xmos.com/download/private/X ... 28F%29.pdf

This would be a great starting point for a quadrature decoder as you are looking for edges..

Also, may be worth reviewing this too:

http://www.xcore.com/forum/viewtopic.ph ... quadrature
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

Can I please get more info about interrupts somewhere? It would be definetly helpfull.
An interrupt is used in this library:

Code: Select all

https://www.xmos.com/support/libraries/lib_ethernet
If you look in the files mii_lite_driver.xc & mii_lite_interrupt.S you will see all of the stuff you need.
More threads not seem to help much, as my rotary ancoder also doesnt work properly with events. Its acting exactly as it shouldn't, fast response means it gets all the ringing and then its extremly sensitive to timing.
Do you mean you are seeing bounce? The xcore is definitely fast enough to see every transition of a bounce. You could of course artificially slow stuff down by not triggering off a transition, and sample on a timer instead. Or, you could trigger off an edge and then sample a few times periodically until you get same value..
Dohny
Member++
Posts: 26
Joined: Tue Dec 03, 2013 6:57 pm

Post by Dohny »

Thank you very much.

That should be all information I need. I'll see what I can do.