wiki:ReverseEngineering/Encoders

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

--

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;

...
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;
}