The basics of the operation for the rotary encoder is the delay actuation of a set of contacts that when tracked can indicate the direction of the movement: clockwise or counterclockwise.

Operation of a rotary encoder

Usually the contacts are pulled up and for a brief moment they are pulled down. Depending which one gets pulled to ground first the direction can be established. The best way to monitor this is by the means of interrupts - when the interrupt is triggered the state of the gpio pins can be read and the state calculated. A third option is normally build into the encoders and this is the ability to press on the knob in order to get a button functionality. This is also handles in the library.

The small module uses 2 queues as interface: one as input that gets events normally from  interrupts and one that provides the output events (position change or key pressed).

typedef struct rencoder_input_event_t {
    rencoder_input_type_t type;
    uint8_t gpio;
} rencoder_input_event_t;

typedef struct rencoder_output_event_t {
    rencoder_output_type_t type;
    union {
        struct {
            uint8_t direction;
            uint8_t position;
        };
        uint8_t key;
    };
} rencoder_output_event_t;

extern QueueHandle_t rencoder_input_queue;
extern QueueHandle_t rencoder_output_queue;

The main module task will listen for any input events and according to its state will trigger the output events.

state = (state << 2) + input_event.gpio;
if (state == 0b01001011) {
    if (position > min) {
        position--;
        output_event.type = rencoder_output_rotation;
        output_event.direction = RENCODER_DIR_CCW;
        output_event.position = position;
        xQueueSendToBack(rencoder_output_queue, &output_event, (TickType_t) 1);
    }
} else if (state == 0b10000111) {
    if (position < max) {
        position++;
        output_event.type = rencoder_output_rotation;
        output_event.direction = RENCODER_DIR_CW;
        output_event.position = position;
        xQueueSendToBack(rencoder_output_queue, &output_event, (TickType_t) 1);
    }
}

The source code for my demo can be found in my github repository: stm32f411ce-rotary-encoder. It uses the encoder to scroll through the contents of a serial i2c eeprom.