wiki:ReverseEngineering/Encoders

Version 4 (modified by art, 14 years ago) (diff)

Name changed from openbm/encoders to Encoders

Encoders

On the  E39-forum.de somebody found out the manufacturer of compatible rotating knobs for the 4:3 monitor. I think for the 16:9 it could be the same. The original knobs where a special development for Alpina. The compatible one is that one: Grayhill 61C22-01-04-02,  data sheet.

Two types

In the original bord monitor there are two types of rotary knobs. The left one, is responsible for volume setting, which is also called RADIO knob. The right one, responsible for menu navigation, which is also called BMBT knob. The interesting part about both of them, they are of different hardware types.

The right one, is mainly hall-effect based type, which gives a grey code sequence on its both ends: 00-01-11-10-00. The sequence depends on the direction of the rotation. The output is changed step-by-step. The left one, is optically based and gives a sequence: 00-11-00 on each rotation step. However, inbetween this output the bits 01 or 11 are also triggered, to indicate the direction. In the internet there is tons of codes which show how to interface such encoders. Here is a simplified example of how I am interfacing these both encoders:

        // definitions
        #define ENC_BMBT_OUT1 (1 << 1)
        #define ENC_BMBT_OUT2 (1 << 2)
        #define ENC_BMBT 0
        #define ENC_RADIO_OUT1 (1 << 6)
        #define ENC_RADIO_OUT2 (1 << 5)
        #define ENC_RADIO 1

        // global variables to remember current states
        uint8_t g_enc_last[2];
        uint8_t g_enc_delta[2];

        // special tracking of radio encoders, since they have intermediate states
        int8_t g_enc_last_radioRotaryStateCW;
        int8_t g_enc_last_radioRotaryStateCCW;

...

//-----------------------------------------------
// Check state of BMBT and RADIO rotary encoders
// Call this method whenever there is a change in the encoder state (i.e. poll for change)
//-----------------------------------------------
void check_encoders()
{
        // poll current state of both encoders
        uint8_t state = get_encoder_state();

        // get value for BMBT encoder
        int8_t new = 0;
        if( (state & ENC_BMBT_OUT1) ) new  = 0b11;
        if( (state & ENC_BMBT_OUT2) ) new ^= 0b01;
        int8_t diff = g_enc_last[ENC_BMBT] - new;
        if( diff & 1 )
        {
            g_enc_last[ENC_BMBT] = new;
            g_enc_delta[ENC_BMBT] += (diff & 2) - 1;            // bit 1 = direction (+/-)
        }
        if (g_enc_delta[ENC_BMBT] < 0)
        {
            set_active(BMBT_CCW);
            set_not_active(BMBT_CW);
        }else if (g_enc_delta[ENC_BMBT] > 0)
        {
            set_active(BMBT_CW);
            set_not_active(BMBT_CCW);
        }
        g_enc_delta[ENC_BMBT] = 0;


        // check state of RADIO encoder
        new = 0;
        if( (state & ENC_RADIO_OUT1) ) new  = 0b11;
        if( (state & ENC_RADIO_OUT2) ) new ^= 0b01;
        diff = g_enc_last[ENC_RADIO] - new;
        if( diff & 1 )
        {
            g_enc_last[ENC_RADIO] = new;
            g_enc_delta[ENC_RADIO] += (diff & 2) - 1;           // bit 1 = direction (+/-)
        }
        if (g_enc_delta[ENC_RADIO] < 0 && !g_enc_last_radioRotaryStateCCW)
        {
            set_active(RADIO_CCW);
            set_not_active(RADIO_CW);
        }else if (g_enc_delta[ENC_RADIO] > 0 && !g_enc_last_radioRotaryStateCW)
        {
            set_active(RADIO_CW);
            set_not_active(RADIO_CCW);
        }
        g_enc_delta[ENC_RADIO] = 0;
}

Please note that this is just an example I extracted from my software. The method check_encoders() has to be called everytime when there is a change in encoder state. This can be achieved by polling the encoders. Other way could be to interface both encoders with PCF8574 and to wait on the interrupt indicating that output of encoders changed. The latter case is implemented in the hardware of OpenBM.