Zastanawiam się nad stworzeniem oprogramowania zasilanego bateryjnie za pomocą kontrolerów EFM Gekko (http://energymicro.com/) i chciałbym, aby kontroler zasnął, ilekroć nie ma w tym nic użytecznego. W tym celu wykorzystywana jest instrukcja WFI (Wait For Interrupt); spowoduje przejście procesora w tryb uśpienia do momentu wystąpienia przerwania.
Gdyby sen był zaangażowany poprzez przechowywanie czegoś w innym miejscu, można użyć operacji wyłączających ładowanie / wyłączających przechowywanie, aby wykonać coś takiego:
// dont_sleep zostanie załadowane 2 za każdym razem, gdy coś się stanie // powinien zmusić główną pętlę do cyklowania przynajmniej raz. Jeśli przerwanie // występuje, że powoduje zresetowanie go do 2 podczas następującej instrukcji, // zachowanie będzie wyglądało tak, jakby przerwanie nastąpiło po nim. store_exclusive (load_exclusive (dont_sleep) >> 1); while (! dont_sleep) { // Jeśli wystąpi przerwanie między kolejną instrukcją a wyjątkiem store_exlusive, nie śpij load_exclusive (SLEEP_TRIGGER); if (! dont_sleep) store_exclusive (SLEEP_TRIGGER); }
Jeśli wystąpiłoby przerwanie między operacjami load_exclusive i store_exclusive, efektem byłoby pominięcie opcji store_exclusive, co spowodowałoby, że system ponownie uruchomiłby pętlę (aby sprawdzić, czy przerwanie ustawiło dont_sleep). Niestety Gekko używa instrukcji WFI zamiast adresu zapisu, aby uruchomić tryb uśpienia; pisanie kodu jak
if (! dont_sleep) WFI ();
naraziłoby się na ryzyko, że może wystąpić przerwa między „if” a „wfi” i ustawi dont_sleep, ale wfi i tak wykona polecenie. Jaki jest najlepszy wzór, aby temu zapobiec? Ustaw PRIMASK na 1, aby zapobiec przerwaniu przez procesor procesora tuż przed wykonaniem WFI, i wyczyść go natychmiast po? Czy jest jakaś lepsza sztuczka?
EDYTOWAĆ
Zastanawiam się nad bitem zdarzenia. Według ogólnego opisu, chciałby, aby był przeznaczony do obsługi wieloprocesorowej, ale zastanawiał się, czy może działać coś takiego:
if (dont_sleep) SEV (); / * Powoduje usunięcie flagi zdarzenia WFE, ale nie uśpienie * / WFE ();
Każde przerwanie, które ustawia Don't_sleep, powinno również wykonać instrukcję SEV, więc jeśli przerwanie nastąpi po teście „if”, WFE wyczyści flagę zdarzenia, ale nie przejdzie w tryb uśpienia. Czy to brzmi jak dobry paradygmat?
Odpowiedzi:
Nie w pełni to zrozumiałem
dont_sleep
, ale jedną z rzeczy, które możesz wypróbować, jest wykonanie „głównej pracy” w module obsługi PendSV, ustawionej na najniższy priorytet. Następnie wystarczy zaplanować PendSV od innych programów obsługi za każdym razem, gdy potrzebujesz czegoś zrobić. Zobacz tutaj, jak to zrobić (dotyczy M1, ale M3 nie różni się zbytnio).Inną rzeczą, której możesz użyć (być może razem z poprzednim podejściem), jest funkcja Sleep-on-exit. Jeśli włączysz tę funkcję, procesor przejdzie w tryb uśpienia po wyjściu z ostatniej procedury obsługi ISR, bez konieczności wywoływania WFI. Zobacz kilka przykładów tutaj .
źródło
Umieść go w krytycznej części. ISRs nie będą działać, więc nie ryzykujesz, że dont_sleep zmieni się przed WFI, ale nadal będą budzić procesor i ISR wykonają się, gdy tylko sekcja krytyczna się skończy.
Twoje środowisko programistyczne prawdopodobnie ma krytyczne funkcje sekcji, ale mniej więcej tak:
EnterCriticalSection to:
ExitCriticalSection to:
źródło
Twój pomysł jest w porządku, to właśnie implementuje Linux. Zobacz tutaj .
Przydatny cytat z wyżej wspomnianego wątku dyskusyjnego, aby wyjaśnić, dlaczego WFI działa nawet przy wyłączonych przerwaniach:
źródło
Przy założeniu, że:
Następnie rozwiązaniem jest użycie PRIMASK do blokowania przerwań między sprawdzaniem poprawności flagi a WFI:
źródło
Co z trybem Sleep on Exit? To automatycznie przechodzi w tryb uśpienia za każdym razem, gdy kończy się procedura obsługi IRQ, więc po skonfigurowaniu tak naprawdę nie działa żaden „tryb normalny”. Nastąpi przerwanie IRQ, budzi się, uruchamia moduł obsługi i wraca do snu. Nie wymaga WFI.
źródło