Skocz do zawartości

Czytak do ebooków (czyli epub to speech)


Pomocna odpowiedź

(edytowany)

A więc jestem już po przesłuchaniu jednego rozdziału z książki, efekty są całkiem zachęcające. Na razie:

Muszę dodać jakiś filterek RC między wyjściem DAC a słuchawkami (szumy kwantyzacji są niestety słyszalne). Na szczęście użyteczne pasmo jest ograniczone do 8 kHz, z prób wyszło mi 10Ω i 4.7µF. Niestety akurat mam same jakieś wielgaśne elektrolity, a nie będę jednego kondensatora zamawiać na Allegro... poczekam aż się zbierze coś więcej (a już się zbiera).

Kwestia akcentowania jednosylabowych zaimków na końcu zdania pozostaje wciąż otwarta, na razie działa warunek, że zaimek jest akcentowany gdy poprzedzająca sylaba nie  była akcentowana i poprzedzający wyraz nie jest czasownikiem, lub gdy poprzedzający czasownik jest akcentowany co najniej na trzeciej sylabie od końca. Zobaczymy w praniu, co z tego wyjdzie.

Muszę dodać możliwość zmiany przez (interfejs www) tematów (np. "do 10 Dywizji" ma być "do dziesiątej dywizji" a nie "do dziesięć dywizji", temat "wojsko" to załatwia tylko trzeba mieć możliwość zaznaczenia, że takiego trzeba użyć). Przy okazji zrobić to samo z językami (wymowa zwrotów obcojęzycznych).

Kilka drobnych błędów (w tym jeden śmieszny - program mylił tytuły przy uploadzie więcej niż jednej książki w sesji - po prostu zapomniałem o czyszczeniu tablicy "mainTitle" przed rozpoczęciem przetwarzania; a śmieszny dlatego że dosłownie zgłupiałem kiedy czytak stwierdził, że "Zaginiony symbol" napisała Marinina) udało mi się poprawić od ręki.

Natomiast bardzo zadowolony jestem z klawiatury, i dlatego dzisiaj właśnie o niej.

Układ okazał się intuicyjny. Z jednej strony łatwo wymacać odpowiedni klawisz, z drugiej zastosowanie typowych tact switchy wymaga zdecydowanego wciśnięcia, co dość skutecznie eliminuje możliwość przypadkowego wduszenia nie tego co trzeba. Czyli tu nie ma co zmieniać - może najwyżej przycisk "play" w jakiś sposób wyraźniej oznaczę (np. będzie o milimetr wyższy), ale znając mój zapał do rozbierania całego urządzenia (niestety, inaczej nie da się wymienić klawisza) będzie to gdzieś w okolicy świętego Nevera.

No, ale do rzeczy.

Założenia (tym razem techniczne) są następujące:

  • Klawiatura matrycowa 3x3, bez możliwości wciśnięcia więcej niż jednego klawisza jednocześnie[1];
  • Odczyt klawiatury w przerwaniu zegarowym, eventy klawiatury ustawiane w kolejce
  • Każdy klawisz może pracować w następujących trybach)
  1. Standard - po naciśnięciu generowany jest event CLICKED, po puszczeniu nie jest generowany żaden event.
  2. Long - po naciśnięciu nie jest generowany żaden event, po puszczeniu wcześniej niż po 500 msec generowany jest event CLICKED. Jeśli klawisz jest przytrzymany dłużej, po upływie 500 msec generowany jest event LONG i wtedy po puszczeniu niue jest generowany żaden event
  3. Repeat - po naciśnięciu generowany jest event CLICKED, generowany jest również okresowo w trakcie przytrzymania klawisza. Po puszczeniu generowany jest event RELEASED
  4. Combo - po naciśnięciu nic nie jest generowane. Po puszczeniu wcześniej niż po upływie 500 msec generowany jest event CLICKED. Po upływie 500 msec i później okresowo generowany jest event REPEAT, a po puszczeniu event RELEASED

W trybie Combo i Repeat event zawiera numer powtórzenia, począwszy od 1.
W każdej chwili można zmienić tryb pracy dowolnego klawisza.

Ponieważ to typowa klawiatura matrycowa, zacząłem od funkcji skanowania matrycy. Funkcja zwraca wartość int z ustawionym jednym bitem odpowiadającym klawiszowi, lub 0 w przypadku gdy żaden klawisz nie jest naciśnięty lub naciśnięto więcej niż jeden klawisz. Ponieważ funkcja wywoływana jest z przerwania co 10 msec, kompilator umieszcza jej kod w pamięci RAM aby nie odczytywać bez potrzeby pamięci FLASH/PSRAM (które i tak są często-gęsto odczytywane).

static uint16_t IRAM_ATTR scanKeys(void)
{
    int r, c;
    for (r=0; r<3; r++) {
        pinMode(rowPins[r],INPUT_PULLUP);
    }
    uint16_t mapa=0;
    int n=0;
    for (c=0; c<3; c++) {
        pinMode(colPins[c],OUTPUT);
        digitalWrite(colPins[c], LOW);
        for (byte r=0; r<3; r++) {
            if (!digitalRead(rowPins[r])) {
                mapa |= 1 << (3*c+r);
                n++;
            }
        }
        digitalWrite(colPins[c],HIGH);
        pinMode(colPins[c],INPUT);
    }
    // jeśli jest więcej niż jeden bit  pewnie wciśnięto dwa klawisze
    if (n>1) return 0;
    return mapa;
}

Oczywiście należało zdefiniować wcześniej kody klawiszy i kody eventów, tak że w pliku nagłówkowym mam:

// kody klawiszy
#define BKEY_PLAY 0x08
#define BKEY_LOUX 0x02
#define BKEY_SILX 0x01
#define BKEY_FAST 0x80
#define BKEY_SLOW 0x40
#define BKEY_CLOCK 0x04
#define BKEY_PREV 0x10
#define BKEY_NEXT 0x20
#define BKEY_WAI 0x100

// kody eventów
enum {
    KEYMSG_NONE = 0,
    KEYMSG_CLICK,
    KEYMSG_REPEAT,
    KEYMSG_RELEASED,
    KEYMSG_LONG,
    KEYMSG_MAX
};

// struktura eventu
extern volatile struct keyMessage {
    int key;
    int msg;
    int repc;
} keyMessage;

Jak widać, keyMessage jest zmienną globalną; pozwala to na odczyt ostatniego eventu z różnych miejsc programu.
Dodatkowo, trzy zmienne statyczne zwierają informację (maska bitowa), czy dany klawisz pracuje w tym trybie. Ponieważ procedura odczytu klawiatory jest oedczytywania w przerwaniu zegarowym, postanowiłem skorzystać (nie pierwszy raz zresztą) z biblioteki ESP32TimerInterrupt. Tak więc początek pliku z kodem do klawiatury wygląda tak:
 

#include <Arduino.h>
#include <ESP32_TimerInterrupt.h>
#include "czytak.h"

static ESP32Timer ITimer1(1); // timer do przerwania zegarowego

static int rowPins[] = {5,18,19}; // piny klawiatury
static int colPins[] = {21,22,23};

static volatile uint16_t xmask_long, // maska dla trybu Long
                        xmask_combo, // maska dla trybu Combo
                        xmask_repeat; // maska dla trybu Repeat

// makra do sprawdzenia trybu pracy klawisza

#define _isKeyCombo(m) (xmask_combo & (m))
#define _isKeyLong(m) (xmask_long & (m))
#define _isKeyRepeat(m) (xmask_repeat & (m))

volatile struct keyMessage keyMessage; // to chyba najlepsze miejsce dla tej zmiennej


portMUX_TYPE mux2 = portMUX_INITIALIZER_UNLOCKED; // mutex dla zapisu/odczytu

Jak wspomniałem, konieczne jest przyporządkowanie klawiszy do odpowiedniego trybu pracy zależnie od stanu urządzenia. Ponieważ z punktu widzenia klawiatury możliwe są cztery stany (czytanie, pauza, lista książek, serwer) mamy tu dalej odpowiednie maski dla każdego trybu:

static const uint16_t amask_long[] = {
    BKEY_WAI | BKEY_CLOCK,
    BKEY_WAI | BKEY_CLOCK | BKEY_PLAY,
    BKEY_WAI | BKEY_CLOCK | BKEY_PLAY,
    BKEY_WAI | BKEY_CLOCK};

static const uint16_t amask_combo[] = {
    BKEY_PREV | BKEY_NEXT,
    BKEY_PREV | BKEY_NEXT,
    0,
    0};

static const uint16_t amask_repeat[] = {
    BKEY_SILX | BKEY_LOUX,
    BKEY_SILX | BKEY_LOUX,
    BKEY_SILX | BKEY_LOUX,
    0};

Jako że eventy są ustawiane w kolejkę, stworzyłem bufor kołowy oraz funkcję, która wprowadza di owego bufora kolejne eventy:

volatile struct keyMessage keyboardRing[8];
volatile uint8_t keyringPos=0, keyringLen=0;

static void pushKeyMsg(int key, int message, int repeat)
{
    if (keyringLen == 8) keyringPos=(keyringPos+1) & 7;
    else keyringLen ++;
    int n = (keyringPos + keyringLen-1) & 7;
    keyboardRing[n].key = key;
    keyboardRing[n].msg = message;
    keyboardRing[n].repc = repeat;    
}

Teraz potrzebna była funkcja, zmieniająca tryby pracy klawiszy. Funkcja oprócz owej zmiany robi jeszcze jedną ważną rzecz: blokuje wszelkie eventy pochodzące od klawisza aż do jego puszczenia. Przydaje się to, gdy np. wciśnięty klawisz zmienia tryb ze Standard na inny - wtedy mimo że np. klawisz jest w trybie Repeat, nie generuje niepotrzebnych eventów.

static volatile bool ignoreRelease;
static volatile bool ignoreKeep;

// zmiana trybu pracy klawiatury, wywoływana z programu

void setKeyMaskType(int param)
{
    portENTER_CRITICAL(&mux2);
    xmask_long = amask_long[param];
    xmask_combo = amask_combo[param];
    xmask_repeat  = amask_repeat[param];
    ignoreRelease = true;
    ignoreKeep = true;
    keyringLen=0;
    portEXIT_CRITICAL(&mux2);
}    

Pozostało teraz zebrać to wszystko w całość i stworzyć funkcję, która zamieni informacje o wciśniętych klawiszach na serię eventów. Kilka dodatkowych zmiennych służy do przechowywania informacji między kolejnymi wywołaniami funkcji:
 

static uint16_t lastKey; // poprzedni odczyt
static bool keyDebounce; // czy w stanie debouncingu?
static uint32_t keyMs; // czas ostatniej operacji
static bool lastRepKey; // klawisz w trybie powtarzania
static int reptCtl; // numer powtórzenia


static void IRAM_ATTR keyboardIntI(void)
{
    int mask = scanKeys();
    uint32_t msec = millis();
    if (keyDebounce) {
        if (millis() - keyMs < 50) return; // najprostszy debouncing - odczekanie 50 msec
        keyDebounce = false;
    }
    if (!mask) { // żaden klawisz nie jest wciśnięty
        if (!lastKey) return; // jeśli wcześniej też nie był, nie mamy nic do roboty
        ignoreKeep = false;
        keyDebounce = true;
        keyMs = millis();
        if (ignoreRelease) {
            ignoreRelease = false; // nic nie robimy jeśli trzeba zignorować release
        }
        else if (_isKeyCombo(lastKey)) {
            pushKeyMsg(lastKey, lastRepKey ? KEYMSG_RELEASED: KEYMSG_CLICK, reptCtl);
        }
        else if (_isKeyLong(lastKey)) {
            pushKeyMsg(lastKey, KEYMSG_CLICK, 0);
        }
        else if (_isKeyRepeat(lastKey)) {
            pushKeyMsg(lastKey, KEYMSG_RELEASED, 0);
        }
        lastKey = 0;
        lastRepKey=0;
        return;
    }
    if (mask == lastKey) { // przytrzymany klawisz
        if (_isKeyLong(mask)) {
            if (!ignoreKeep && msec - keyMs >= 500UL) { // klawisz przytrzymany powyżej 500 msec
                ignoreRelease = true;
                ignoreKeep = true;
                pushKeyMsg(lastKey, KEYMSG_LONG, 0);
            }
        }
        else if (_isKeyRepeat(mask)) {            
            if (!ignoreKeep && msec - keyMs >= 500UL) {
                keyMs = msec-320; // ustalone doświadczalnie
                pushKeyMsg(lastKey, KEYMSG_CLICK, ++reptCtl);
            }
        }
        else if (_isKeyCombo(mask)) {
            if (!ignoreKeep && msec - keyMs >= 500UL) {
                keyMs = msec-320;
                lastRepKey = true;
                pushKeyMsg(lastKey, KEYMSG_REPEAT, ++reptCtl);
            }
        }
        return;
    }

    // wciśnięcie klawisza
    keyDebounce = true;
    keyMs = msec;
    lastRepKey = false;
    ignoreKeep = false;
    ignoreRelease = false;
    if (lastKey) { // dla combo zawsze musi być release
        if (_isKeyCombo(lastKey)) {
            pushKeyMsg(lastKey, KEYMSG_RELEASED, reptCtl);
        }
        lastKey = 0; // w sumie niepotrzebne, ale kto wie jak się funkcja rozbuduje
    }
    reptCtl = 0;
    lastKey = mask;
    if (_isKeyCombo(mask) || _isKeyLong(mask)) {
        return;
    }
    if (!_isKeyRepeat(mask)) ignoreRelease = true;
    pushKeyMsg(lastKey, KEYMSG_CLICK, 0);
}

// można to było zrobić w poprzedniej funkcji, ale lubię
// jak każdemu ENTER odpowiada dokładnie jeden EXIT

static void IRAM_ATTR keyboardInt(void)
{
    portENTER_CRITICAL_ISR(&mux2);
    keyboardIntI();
    portEXIT_CRITICAL_ISR(&mux2);
}

// no i inicjalizacja wywoływana z setup()

void initKeyboardSys(void)
{
    ITimer1.attachInterrupt(10000, keyboardInt);
}

Pozostała tylko funkcja wywoływana w głównej pętli odbierająca eventy z kolejki, jak widać, funkcja zwraca true jesli odebrano event:

bool getKeyMessage(void)
{
    bool rc;
    portENTER_CRITICAL(&mux2);
    if (!keyringLen) {
        keyMessage.key = 0;
        keyMessage.msg = 0;
        rc = false;
    }
    else {
        keyMessage = keyboardRing[keyringPos];
        keyringPos = (keyringPos+1) & 7;
        keyringLen--;
        rc=true;
    }
    portEXIT_CRITICAL(&mux2);
    return rc;
}

A przykładowy kod w pętli głównej:

void loop(void)
{
    if (getKeyMessage()) { // od tego momentu ważna jest zawartość zmiennej keyMessage
        obsluga_klawiatury();
        return;
    }
    /*
    reszta kodu
    */
}

Mam nadzieję, że kod jest na tyle prosty, że nie wymaga dalszego komentarza.
W rzeczywistości jest tego tam trochę więcej, ale opuściłem fragmenty związane stricte z czytakiem.

Być może kod stanie się inspiracją dla kogoś, kto potrzebuje podobnej funkcjonalności - o ile wiem żadna znana biblioteka takiej nie zapewnia.

I jeszcze jedno: opisałem tu pierwszą (oczywiście działającą) wersję kodu. Doczekała się ona kilku ważnych przeróbek, ale o tym następnym razem.

Stay tuned!

---

[1] Z tym "jednocześnie" to nie tak do końca - w rzeczywistości można bez problemu wykryć niektóre kombinacje - o tym jednak potem.

Edytowano przez ethanak
Link to post
Share on other sites
52 minuty temu, ethanak napisał:

Muszę dodać jakiś filterek RC między wyjściem DAC a słuchawkami (szumy kwantyzacji są niestety słyszalne). Na szczęście użyteczne pasmo jest ograniczone do 8 kHz, z prób wyszło mi 10Ω i 4.7µF.

Myślisz, że szum kwantyzacji wynika z 13bitowych próbek, czy z samego DACa? Jakiego DACa używasz? Może też szum z zasilania - tu bramki Schmitta mogą pomóc. Kiedyś jak robiłem analizator widma na ATMega'dze (jeszcze na studiach, które ponoć nic nie uczą) to używałem aktywnych filtrów na przełączanych kondensatorach. Wtedy to była nowość i Maxim mi wysłał sample na uczelnie. Jak się tak bawisz audio możesz spróbować, może wyślą Ci tez sampla:  https://www.maximintegrated.com/en/products/parametric/search.html?fam=filt. Taki filtr piątego rzędu pomógł mi się pozbyć harmonicznych z zegara ATMega'i, które ciągle pojawiały się na widmie.

Link to post
Share on other sites
41 minut temu, pmochocki napisał:

Myślisz, że szum kwantyzacji wynika z 13bitowych próbek

W tym przypadku akurat mam uczciwe 16-bitowe próbki (mój WROVER ma 16 MB Flash, czyli nie muszę oszczędzać).

42 minuty temu, pmochocki napisał:

Jakiego DACa używasz

Konkretnie taki moduł.

Podejrzewam, że tu sumują się:

  • Niska częstotliwość próbkowania (16 kHz);
  • Średnia (to taki eufemizm) jakość owych próbek (innych nie ma);
  • Bezpośrednie podłączenie słuchawek do wzmacniacza klasy D przeznaczonego do współpracy z głośnikiem i który nie ma żadnych filtrów na wyjściu;
  • Sama zasada działania algorytmu Cośtam-OLA, który robi bardzo dziwne patentowane rzeczy z próbkami (na szczęście patenty wygasły).

W związku z tym słuchawki łapią cały syf powyżej 8 kHz. Szum przeszkadza co prawda tylko wtedy, kiedy w otoczeniu jest dokładnie cicho (noc w pokoju), ale jednak przeszkadza. Nie mam zamiaru jednak bawić się w jakieś wymyślne filtry, jeśli prosty RC skutkuje, a przy okazji trochę tłumi wyższe częstotliwości z użytecznego pasma (nie muszę się bawić w cyfrowe filtry). Zresztą - mam jutro wpaść do kumpla elektronika i zajumać mu delikatnie kondensator 🙂

Przy okazji: zakłócenia od zasilania raczej nie wchodzą w grę - jeśli Mbrola odtwarza akurat pauzę, to w słuchawkach jest cisza.

 

Link to post
Share on other sites
1 godzinę temu, ethanak napisał:

Konkretnie taki moduł.

Też niedawno kupiłem, ale nie znalazłem jeszcze czasu na zabawę.

1 godzinę temu, ethanak napisał:

Nie mam zamiaru jednak bawić się w jakieś wymyślne filtry, jeśli prosty RC skutkuje, a przy okazji trochę tłumi wyższe częstotliwości z użytecznego pasma (nie muszę się bawić w cyfrowe filtry). Zresztą - mam jutro wpaść do kumpla elektronika i zajumać mu delikatnie kondensator 🙂

Oczywiście jak nie ma potrzeby to nie ma co komplikować. Daj znać jak zadziała ten filtr pierwszego rzędu. Trzymam kciuki.

 

Link to post
Share on other sites
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

12 godzin temu, pmochocki napisał:

Też niedawno kupiłem, ale nie znalazłem jeszcze czasu na zabawę.

Na razie nie stosowałem do niczego więcej poza syntezą mowy (RPi Zero W, ESP32), ale tu się sprawdza znakomicie.

A tak w ogóle to dobrze, że postanowiłem zrobić te tematy i języki - okazało się, że przygotowując dane omyłkowo wywaliłem temat wojsko zamiast (nieużywanego) scifi - gdybym tego nie zauważył długo bym się pewnie zastanawiał, dlaczego tej nieszczęsnej 10 Dywizji nie chce odmieniać 🙂

Link to post
Share on other sites
Dnia 18.10.2021 o 21:37, pmochocki napisał:

Daj znać jak zadziała ten filtr pierwszego rzędu.

Dopiero dzisiaj mogłem to sprawdzić - na razie mam nie taki kondensator jak chciałem ale to i tak prowizorka, a ładnie tnie jakieś 90% artefaktów.

  • Lubię! 1
Link to post
Share on other sites

Obiecałem trochę więcej o klawiaturze...

Tak jak wspomniałem, taka typowa klawiatura matrycowa może umożliwić wykrycie wciśnięcia dwóch klawiszy jednocześnie. Ponieważ trochę mi brakowało dodatkowych ustawień, postanowiłem dodać piąty tryb - ustawienia. Wejście w ten tryb wyzwalane jest wciśnięciem klawiszy pauza i poprzedni lub następny, wyjście dłuższym przytrzymaniem klawisza pauzy.

Ponieważ, nie chciałem komplikować reszty programu sprawdzaniem ile klawiszy na raz zostało wciśnięte, zdecydowałem się potraktować wciśnięcie takiej kombinacji jak wciśnięcie dodatkowych (nieistniejących) klawiszy. Tak więc dodałem dwie definicje:

#define BKEY_MULTI1 0x400
#define BKEY_MULTI2 0x800

w tablicach amask_* doszedł nowy element, czyli wyglądają teraz tak:

static const uint16_t amask_long[] = {
    BKEY_WAI | BKEY_CLOCK,
    BKEY_WAI | BKEY_CLOCK | BKEY_PLAY,
    BKEY_WAI | BKEY_CLOCK | BKEY_PLAY,
    BKEY_WAI | BKEY_CLOCK,
    BKEY_WAI | BKEY_PLAY};

static const uint16_t amask_combo[] = {
    BKEY_PREV | BKEY_NEXT,
    BKEY_PREV | BKEY_NEXT,
    0,
    0,
    0};

static const uint16_t amask_repeat[] = {
    BKEY_SILX | BKEY_LOUX,
    BKEY_SILX | BKEY_LOUX,
    BKEY_SILX | BKEY_LOUX,
    0,
    0};

a w funkcji scanKeys dopisałem:

    if (mapa == 0x18) {
        mapa = BKEY_MULTI1;
    }
    else if (mapa == 0x28) {
        mapa = BKEY_MULTI2;
    }
    else
    // jeśli jest więcej niż jeden bit  pewnie wciśnięto dwa klawisze
    if (n>1) return 0;

Tak na oko powinno to zadziałać... ale w rzeczywistości nie chciało. Po prostu puszczenie jednego klawisza było wykrywane jako oddzielny event. Tak więc trzeba to było nieco skomplikować.

Postanowiłem, że po wykryciu wciśnięcia którejkolwiek ze znanych kombinacji klawiszy program ma czekać, aż wszystkie klawisze będą puszczone niezależnie od tego, co w tym czasie będzie się wyprawiać z klawiaturą. Tak więc dodałem nową zmienną:

static uint16_t lastMultiMask;

a w funkcji scanKeys zmieniłem dopisane linijki:

    if (lastMultiMask) {
        if (!mapa) {
            lastMultiMask = 0;
            return 0;
        }
        else mapa = lastMultiMask;
    }
    if (mapa == 0x18) {
        lastMultiMask = mapa;
        mapa = BKEY_MULTI1;
    }
    else if (mapa == 0x28) {
        lastMultiMask = mapa;
        mapa = BKEY_MULTI2;
    }
    else
    // jeśli jest więcej niż jeden bit  pewnie wciśnięto dwa klawisze
    if (n>1) return 0;

Po tym zabiegu wszystko zaczęło działać zgodnie z oczekiwaniami.

Aktualnie w trybie ustawień mam do dyspozycji:

  • zmianę wysokości głosu (dotychczas dostępną wyłącznie poprzez interfejs www)
  • poprawienie języka i tematu aktualnie czytanej książki (jw)
  • timer, przerywający czytanie i przechodzący w stan pauzy po upływie określonego czasu.

Aktualnie timer przerywa czytanie po zakończeniu zdania, docelowo chciałbym aby przerywał po zakończeniu akapitu lub rozdziału jeśli do końca zostało niewiele (do ustalenia: co oznacza "niewiele"), a dopiero jeśli koniec akapitu jest za daleko przerywał po zakończeniu zdania.

 

  • Lubię! 2
Link to post
Share on other sites

Ech... dalej problemy z akcentowaniem "się" na końcu frazy.

Na razie wymyślony naprędce algorytm: jednosylabowy zaimek osobowy i "się" na końcu frazy - akcentowany zaimek i nieakcentowane "się".

Przy okazji znalazłem kolejnego babola w translacji fonetycznej - "niezidentyfikowany" ma być wymawiane przez "z" a nie "ź"...

Słucham sobie dalej...

 

Link to post
Share on other sites

Usunąłem warunek końca frazy z tym nieszczęsnym "się" - na razie jest nieźle.

Teraz czekam na dostawę elementów, trzeba będzie tylko sprawdzić ten kondensator (zamówiłem 4.7 i 10 uF, zobaczymy co z tego wyjdzie), no i projekt docelowej płytki...

Najgorsze jest to, że DS3231 luzem kosztuje 50PLN w tme, a moduł z eepromem i koszykiem na baterię 15 PLN na alledrogo... chyba trzeba sobie przypomnieć do czego służy hotair 🙂

 

Link to post
Share on other sites
(edytowany)
4 godziny temu, ethanak napisał:

Najgorsze jest to, że DS3231 luzem kosztuje 50PLN w tme, a moduł z eepromem i koszykiem na baterię 15 PLN na alledrogo... chyba trzeba sobie przypomnieć do czego służy hotair 🙂

Aż sprawdziłem, bo nie mogłem uwierzyć, że tyle sobie liczą...

MCP7940N za 5zł?
https://pl.farnell.com/microchip/mcp7940n-i-ms/real-time-clock-calendar-64b-i2c/dp/2774923
Krótkie podsumowanie:

  • podtrzymanie bateryjne z poborem prądu około 1uA
  • alarm
  • I2C
  • dzień/miesiąc/rok hh:mm:ss
  • zasilanie 1.8-5.5V
  • mały - 8 nóżek
  • minus - potrzebny zewnętrzny kwarc

Hotair też jest opcją - chciałem tylko pokazać alternatywę...

EDIT: Dopiero sprawdziłem, że DS3231 ma dokładność 3ppm - więc Hotair to nie jest zła opcja 🙂

Edytowano przez pmochocki
Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.