Topic: tech dcc pc src prev next

tech dcc pc src > controller.c

#include "controller.h"

#include <util/delay.h>

#include "dcc.h"
#include "lcd.h"
#include "menu.h"
#include "pins.h"
#include "wdt.h"

unsigned char pot_to_speed(adc_type pot, bool steps_128)
{
    return (unsigned char)(
        steps_128 ?
        (unsigned char)((pot / 8 > 126) ? 126 : (pot / 8)) :
        (unsigned char)((pot / 32 > 28) ? 28 : (pot / 32))
        );
}

void transmit_speed(
        const cfg_loco *loco,
        unsigned char speed,
        bool direction
        )
{
    switch(loco->speed_steps)
    {
        case SPEED_STEPS_128:
            DCC_Transmit(Packet_CreateSpeed128(loco->address, direction, speed));
            break;
        case SPEED_STEPS_28:
            DCC_Transmit(Packet_CreateSpeed28(loco->address, direction, speed));
            break;
    }
}

void Controller_CheckOverCurrent(adc_type current)
{
    if(current > HERMES_CURRENT_LIMIT)
    {
        DCC_Stop();
        _delay_ms(100);
        LCD_ReInit();
        Menu_Notice("Short");
        Wdt_Reset();
    }
}

unsigned char Controller_FunctionMenu(
        const cfg_loco *loco,
        functions_type *function_state
        )
{
    if(loco->f_enable == 0)
        return CFG_NULL_INDEX;

    typedef struct
    {
        const cfg_loco *loco;
        functions_type *function_state;
        unsigned char index;
        unsigned char selected;
    } state;

    state context = {
        .loco = loco,
        .function_state = function_state,
        .index = 0,
        .selected = CFG_NULL_INDEX
    };

    // Get the currently selected function number.
    unsigned char selected_function(const state *context_)
    {
        unsigned char index = (unsigned char)(context_->index + 1);
        functions_type f = context_->loco->f_enable;
        for(unsigned char i = 0; i < 32; ++i)
        {
            if(f & 1)
                --index;

            if(index == 0)
                return (unsigned char)i;

            f >>= 1;
        }
        return 0;
    }

    // Get the state of the currently selected function.
    bool selected_function_state(const state *context_)
    {
        unsigned char f_no = selected_function(context_);
        return (*context_->function_state >> f_no) & 1;
    }

    void button(void *v, const switch_type sw)
    {
        state *context_ = (state*)v;

        if(HERMES_SWITCH_NO(sw) == HERMES_SWITCH_BACK)
            Menu_Finish();

        if(HERMES_SWITCH_NO(sw) == HERMES_RE_SEL)
        {
            context_->selected = selected_function(context_);
            Menu_Finish();
        }

        // The rotary encoder moves through the list.

        if(HERMES_SWITCH_NO(sw) == HERMES_RE_ACW)
        {
            context_->index = (unsigned char)(
                (Cfg_LocoFunctions(context_->loco) + context_->index - 1) %
                Cfg_LocoFunctions(context_->loco)
                );
            Menu_Redraw();
        }

        if(HERMES_SWITCH_NO(sw) == HERMES_RE_CW)
        {
            context_->index = (unsigned char)(
                (context_->index + 1) % Cfg_LocoFunctions(context_->loco)
                );
            Menu_Redraw();
        }

        if(HERMES_SWITCH_NO(sw) == HERMES_RE_SEL)
        {
            unsigned char f_no = selected_function(context_);
            context_->selected = f_no;

            /*if((context_->loco->f_momentary >> f_no) & 1)*/
            /*{*/
                /*// Transmit on/off.*/
                /*Controller_TransmitFunctionGroup(*/
                        /*context_->operations->loco.address,*/
                        /**context_->function_state | (1UL << f_no),*/
                        /*Controller_FunctionGroup(f_no),*/
                        /*HERMES_MOMENTARY_FUNCTION_ACTIVATION_COUNT*/
                        /*);*/
                /*Controller_TransmitFunctionGroup(*/
                        /*context_->operations->loco.address,*/
                        /**context_->function_state,*/
                        /*Controller_FunctionGroup(f_no),*/
                        /*HERMES_MOMENTARY_FUNCTION_DEACTIVATION_COUNT*/
                        /*);*/
            /*}*/
            /*else*/
            /*{*/
                /*if((*context_->function_state >> f_no) & 1)*/
                /*{*/
                    /*// Transmit 'off'.*/
                    /**context_->function_state = *context_->function_state & ~(1UL << f_no);*/
                /*}*/
                /*else*/
                /*{*/
                    /*// Transmit 'on'.*/
                    /**context_->function_state = *context_->function_state | (1UL << f_no);*/
                /*}*/
            /*}*/
        }
    }

    void draw(void *v)
    {
        state *context_ = (state*)v;
        LCD_DefineHermesChars(HERMES_LCD_CUSTOM_MODE_MENU);
        unsigned char f_no = selected_function(context_);
        LCD_Clear();
        LCD_PutS("Fn. ");
        // Function numbers can only have one or two digits.
        if(f_no < 10)
            LCD_PutC(' ');
        LCD_PutI(f_no);
        LCD_PutS(
            ((context_->loco->f_momentary >> f_no) & 1) ?
                " (once)" :
                (selected_function_state(context_) ? " (ON)" : " (OFF)")
            );
        LCD_Move(0, 1);
        LCD_PutC(HERMES_LCD_CHAR_UP);
        LCD_PutS("     ");
        LCD_PutC(HERMES_LCD_CHAR_ACW);
        LCD_PutS("  ");
        LCD_PutC(HERMES_LCD_CHAR_CW);
    }

    Menu_EventLoop(button, draw, 0, &context);

    return context.selected;
}

cfg_address_type Controller_AccessoryMenu(cfg_address_type *on_accessories)
{
    typedef struct
    {
        // List of all accessories.
        cfg_address_type accessory[CFG_ACCESSORY_LIST_LENGTH + 1];
        // Index into 'accessory'.
        int accessory_index;
        // Selected accessory.
        cfg_address_type selected;
        // List of accessories in the 'on' state.
        cfg_address_type *on_accessories;
    } state;

    state context = {
        .accessory_index = 0,
        .selected = CFG_NULL_ADDR,
        .on_accessories = on_accessories
    };

    Cfg_Accessories(context.accessory);

    // Don't bother to list accessories if the accessory list is empty.
    if(context.accessory[0] == CFG_NULL_ADDR)
        return CFG_NULL_ADDR;

    void draw(void *v)
    {
        state *context_ = (state*)v;
        LCD_DefineHermesChars(HERMES_LCD_CUSTOM_MODE_MENU);
        LCD_Clear();
        cfg_address_type accessory_address = context_->accessory[context_->accessory_index];
        const bool accessory_state = (
                Cfg_IndexInList(accessory_address, context_->on_accessories) !=
                CFG_NULL_INDEX
                );
        LCD_PutS("Ac. ");
        LCD_PutI((int)accessory_address);

        LCD_Move(8, 0);
        LCD_PutS(
                accessory_state ?
                "(ON)" : "(OFF)"
                );

        LCD_Move(0, 1);
        LCD_PutC(HERMES_LCD_CHAR_UP);
        LCD_PutS("     ");
        LCD_PutC(HERMES_LCD_CHAR_ACW);
        LCD_PutS("  ");
        LCD_PutC(HERMES_LCD_CHAR_CW);
        LCD_PutS("     ");
        LCD_PutC(HERMES_LCD_CHAR_OK);
    }

    void button(void *v, switch_type sw)
    {
        state *context_ = (state*)v;

        if(HERMES_SWITCH_NO(sw) == HERMES_SWITCH_BACK)
            Menu_Finish();

        if(HERMES_SWITCH_NO(sw) == HERMES_RE_SEL)
        {
            context_->selected =
                context_->accessory[context_->accessory_index];
            Menu_Finish();
        }

        if(HERMES_SWITCH_NO(sw) == HERMES_RE_ACW)
        {
            context_->accessory_index =
                (Cfg_AddressCount(context_->accessory) + context_->accessory_index - 1) %
                Cfg_AddressCount(context_->accessory);
            Menu_Redraw();
        }

        if(HERMES_SWITCH_NO(sw) == HERMES_RE_CW)
        {
            context_->accessory_index =
                (context_->accessory_index + 1) %
                Cfg_AddressCount(context_->accessory);
            Menu_Redraw();
        }
    }

    Menu_EventLoop(button, draw, 0, &context);

    return context.selected;
}

unsigned char Controller_FunctionGroup(unsigned char function_no)
{
    if(function_no < 5)
        return 1;
    else if(function_no < 9)
        return 2;
    else if(function_no < 13)
        return 3;
    else if(function_no < 21)
        return 4;
    else if(function_no < 29)
        return 5;

    return 6;
}

void Controller_TransmitFunctionGroup(
        cfg_address_type address,
        functions_type function_state,
        unsigned char group,
        unsigned char repeats
        )
{
    switch(group)
    {
        case 1:
            DCC_TransmitRepeat(
                    Packet_CreateFunction0_4(
                        address,
                        (function_state & 0b11111)
                        ),
                    repeats
                    );
            break;
        case 2:
            DCC_TransmitRepeat(
                    Packet_CreateFunction5_8(
                        address,
                        ((function_state >> 5) & 0b1111)
                        ),
                    repeats
                    );
            break;
        case 3:
            DCC_TransmitRepeat(
                    Packet_CreateFunction9_12(
                        address,
                        ((function_state >> 9) & 0b1111)
                        ),
                    repeats
                    );
            break;
        case 4:
            DCC_TransmitRepeat(
                    Packet_CreateFunction13_20(
                        address,
                        ((function_state >> 13) & 0b11111111)
                        ),
                    repeats
                    );
            break;
        case 5:
            DCC_TransmitRepeat(
                    Packet_CreateFunction21_28(
                        address,
                        ((function_state >> 21) & 0b11111111)
                        ),
                    repeats
                    );
            break;
    }
}