Фикс: гонка _state с _e0/_e1 в uEncoderISR::tick (общий байт битовых полей)#2
Open
dkxmercury wants to merge 1 commit into
Open
Фикс: гонка _state с _e0/_e1 в uEncoderISR::tick (общий байт битовых полей)#2dkxmercury wants to merge 1 commit into
dkxmercury wants to merge 1 commit into
Conversation
_state лежит в одном байте с _e0/_e1, которые пишет tickISR из прерывания. Присвоение битового поля компилируется в read-modify-write всего байта (на AVR: ld / ori / st), поэтому прерывание между чтением и записью теряло своё обновление ног. Дальше pollRaw считал переход от старых значений и терял либо разворачивал щелчок.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ещё одна находка, теперь в новом
uEncoderISR. Тут гонка между главным циклом и прерыванием, которая изредка съедает или разворачивает щелчок.В чём дело
В
tick()присвоение_stateстоит послеinterrupts():Выглядит безобидно, ведь
_stateбольше никто не трогает. Но_state- битовое поле, и оно лежит в одном байте с_e0/_e1:4+2+1+1 = ровно 8 бит, то есть один байт. А
_e0/_e1пишетpollRawизtickISR, то есть из прерывания.Запись битового поля - это read-modify-write целого байта. Вот что реально генерит avr-g++ для ATmega328 на присвоение только
_state:Если между
ldиstприлетает прерывание по CHANGE,tickISRобновляет_e0/_e1, а потомstкладёт байт обратно изr24- со старыми ногами. Запомненное состояние ног отстаёт от реальности, и следующий переход вpollRawсчитается от протухших значений: щелчок либо теряется, либо уезжает в обратную сторону. Ловится редко и выглядит как необъяснимый глюк.Фикс
Занести присвоение внутрь секции, она и так уже есть:
Ничего не стоит по памяти и почти ничего по времени - секция удлиняется на три инструкции.
Как проверял
Раскладку не угадывал, а измерил: обнулял объект, писал одно поле, смотрел байты.
_state,_e0,_e1- все в байте 0. На ATmega328 раскладка та же,static_assert(sizeof(uEncoderVirt) == 3)проходит. Ассемблер выше - оттуда же, avr-g++ 5.4.0,-Os -mmcu=atmega328p. После правки собирается чисто и на AVR, и на x86, предупреждений нет.Ещё два места рядом, чинить не стал
Не тащу в этот PR, чтобы не мешать всё в кучу, но раз уж копал:
reset()пишет_stateиз главного цикла - тот же байт, та же гонка. В опросном режиме неважно (прерывания нет), а в ISR-режиме ловится теоретически._revи_posделят байт 1 (видно в замере выше).setEncReverse()обычно зовут один раз вsetup()доattachInterrupt, так что на практике не стреляет.Если это в принципе интересно - обе пары можно развести вообще без потери памяти, просто переставив поля так, чтобы в одном байте лежало только то, что пишет главный цикл, а в другом только то, что пишет прерывание. По битам сходится в те же 3 байта. Но это уже вопрос вкуса, и лезть в раскладку без спроса не буду.
Видел твой ответ в uButton#3 про то, что PR не принимаешь и пишешь сам в своём стиле - твоё право, спорить не собираюсь. Если возьмёшь как есть, буду рад. Если удобнее переписать по-своему - тоже отлично, главное чтобы починилось.