Programare

Mda, pare să fie cam prost pentru o chestie de 150€, un ESP32 de $5 îl bate la fund pe toate planurile (și e și mai ușor de programat).
 
Am gasit problema. Stupiditatea stupiditatilor posibile! :capu:

Keypad-ul are butoanele conectate intre pinul liniei si pinul coloanei. Am conectat coloanele la pinii cu external interrupt si liniile la niste GPIO ca sursa de curent.
Am o functie care determina care dintre butoane este apasat: intrerupe curentul pe linii, apoi activeaza liniile una cate una si se uita care pin de coloana devine activ. Dar ca sa functioneze, intreruperile externe trebuie dezactivate, pinii lor trebuie comutati pe GPIO-input, scanati, apoi pusi la loc pe functia de intreruperi externe, si in final activate la loc intreruperile externe.
Ca sa nu am erori la apasarea butonului (sa fie numarat de mai multe ori la o singura apasare), am setat un interval minim de 100ms intre eliberarea butonului si o noua apasare, altfel noua apasare "nu se pune".
Intreruperea timer-ului are doua roluri: sa "cante" la beep si sa monitorizeze butoanele apasate. Problema e urmatoarea: ca sa determine daca butonul inca este apasat, trebuie rulata functia aia care determina ce buton e apasat. Acea functie presupune dezactivarea si la final reactivarea intreruperilor. Iar cand intreruperile sunt reactivate... ghici! Se declanseaza din nou. Scurtand intreruperea timer-ului, intreruperea externa (relativ lunga si complexa, ca presupune iarasi scanarea butoanelor) se suprapune acum fix peste urmatoarea intrerupere a timer-ului. => intrerupere ratata => beep la jumatate din frecventa normala.
 
Pare un pic ciudat. Adică apare trigger pe întreruperea externă (pe butoane) atunci când dai enable la întrerupere? N-ar trebui să facă trigger doar când se schimbă input-ul?
 
E din cauza de bug. Dupa functia care gaseste butonul, activez intai intreruperea si apoi curentul pe liniile keypad-ului. Daca butonul e inca apasat (si este, ca nu-s ciocanitoare) => rising edge => intrerupere. Zbang!
Am rezolvat. In timp ce rezolvam am constatat inca un bug: o intrerupere scurta a apasarii unui buton, gen contact imperfect in mijlocul apasarii, face ca butonul sa nu mai fie monitorizat si timer-ul pentru buton ridicat sa masoare de la acea scurta intrerupere pana la urmatoarea apasare. Din cauza asta trebuie acum sa rescriu ambele intreruperi.

Bah, da' ce greu e sa n-ai bug-uri! :fluier:
 
Poți să blochezi procesoare cu așa ceva (să nu mai poată procesa altceva pentru că stă tot timpul în tratare de întreruperi), de-aia unul din primele lucruri care trebuie învățate când începi să folosești întreruperi e să ai grijă să nu triggerezi întreruperi din rutina de tratare întreruperi :smile:. Întotdeauna enable-ul de întrerupere se face fix înainte să ieși din tratare.
 
Deci numaratorul de celule merge superb. A mers chiar si piticul ala al meu cu oprirea completa a procesorului cand n-are nimic de facut.
Asadar declar proiectul incheiat.
Daca e cineva interesat, l-am postat pe gitlab, cu tot cu poze si documentatie.
 
Nu te-aș angaja ca programator, dar asta probabil știi deja :biggrin:. Dar faptul că ai făcut ceva care și merge e mai mult decât mulți alții. Și are și documentație, ceea ce e mult mai mult decât alții.

Codul e foarte dens, e greu de înțeles flow-ul pentru că te împiedici de implementări opace pentru fiecare chestie mică. Înțeleg că nu îți place să folosești biblioteci (am avut un șef cu un sindrom foarte grav de not invented here, investea sute de ore de muncă doar ca să nu folosească o bibliotecă deja existentă; era foarte apreciat pentru că era un workaholic și mulți nu știau că face chestii care există deja; rezultatul a fost că după câțiva ani s-a plictisit complet și s-a apucat de cu totul altceva, iar tot ce făcuse până atunci a ajuns gunoi extrem de repede, că nu avea nimeni chef să întrețină o chestie fără viitor). Dar dacă nu folosești biblioteci, măcar implemetează mai multe funcții care să facă codul mai ușor de folosit - ex: empty string de N ori cu for - nu, faci o funcție. La lcd la fel, switch-ul cu prima/a doua linie e mult cod dublat, n-are rost.
 
Nu vad sensul sa scriu o functie care sa inlocuiasca o singura linie de cod. Sau 2.
Ce ma enerveaza cand citesc codul altora este ca la fiecare 2 linii trebuie sa ma intrerup din citit si sa caut in alta parte ce naiba face functia peste care am dat. Daca trebuie sa mai si deschid alt document... gata! Am inchis tot si la revedere.

PS: Nu prea inteleg la ce te referi cand zici "implementari opace".
PS2: Eu am invatat programare fara profesor si singurele surse de invatare pe care le-am putut intelege au fost alea care nu importa nimic. Pentru mine "opac" inseamna black-box-ul unui #include care nu stiu ce face.
Tu nu stii cat discomfort mi-a produs bootloaderul care imi contamineaza creatia... :facepalm:
 
Last edited:
Prin implementare opacă înțeleg că nu pot să urmăresc ce vrea să facă programul pentru că mă împiedic la fiecare linie de chestii mici pe care trebuie să le înțeleg iar și iar, și unde un copy/paste pus prost poate să strice tot. Un cod ok scris ar trebui să aibă câteva comentarii ca să înțelegi ce se întâmplă; dacă are nevoie de câte un comment pe fiecare linie înseamnă că e opac :smile:.

for(x=0;x<=32;x++) str[x]=32; //Empty str
nu ar suna mai bine
empty_str(str); ?

Dacă îți fac cadou un display cu 20x4 pentru drăcia ta, va trebui să cauți peste tot unde ai pus 32 și să schimbi în 80; dar nu merge search and replace, că ai pus explicit 32 și în loc să zici ' ' (spațiu), așa că mult succes. Și cam la fel și cu restul codului. E ok, funcționează, dar peste 3 ani vei vrea să-l modifici, o să dureze mult mai mult decât ar trebui.
 
Mie empty_str(str); nu-mi spune ce anume scrie functia in str[]. Ar putea sa o umple cu 0x0 sau cu '0', sau ar putea sa scrie un singur 0x0 la inceput si sa nu se mai oboseasca cu toate cele 32 de caractere. Deci daca intalnesc asa ceva, trebuie sa ma duc la functie sa vad ce face.
Ar fi mai explicit fill_str_with_spaces(); dar cine foloseste nume din astea?
Oricum, multumesc pentru sugestii. Intr-un fel ai dreptate. Printre comentariile alea ar fi necesara si o vedere mai de ansamblu a modului general de functionare a programului. Mi s-a mai intamplat la alte programe sa imi ia ceva timp sa inteleg ce am facut acolo, cu toate ca era comentat.

-----------------------

Alta, bash:
Code:
myfunc(){
 echo "0"
 return 1
 }

if myfunc ; then echo "Unu." ; else echo "Zero." ; fi
=> Zero.
Cum scriu if-ul sa ia in considerare return 1, nu echo 0 ?
 
Bash:
retval=0

myfunc(){
echo "0"
retval=1
}

myfunc

if [ $retval == 1 ]; then echo "Unu." ; else echo "Zero." ; fi
 
Last edited:
Cu return nu merge? Functia face niste ping-uri care dureaza. As prefera sa dau return mai devreme daca e cazul, nu sa astept pana sunt gata toate pingurile.
Si as vrea ca functia sa fie in conditie, nu in exterior, ca o pun intr-un "until <functie> ; do <blabla> ; done".
 
If-ul nu ia în considerare echo 0, nu contează echo-ul :smile:. Doar că ai tu invers if-ul. Un program care se termină normal întoarce 0 (și if ; asta verifică, true e dacă întoarce 0).

Code:
[miahi@dbservice ~]$ ./test5.sh
0
OK
[miahi@dbservice ~]$ more test5.sh
myfunc(){
 echo "0"
 return 0
 }

if myfunc ; then echo "OK" ; else echo "Not OK" ; fi
 
Alta:

Compilez hiawatha pentru router. Foloseste CMAKE. Dupa mult chin cu cross-compilatorul Asus (a se citi "a lu' peste"), a mers compilat.
Din motive de root read-only, pe router trebuie instalat in /opt/bin, /opt/etc, /opt/lib, etc.
Toolchain-ul este in /build/rtn18u/release/src-rt-6.x.4708/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3/
Sysroot este in /build/rtn18u/release/src-rt-6.x.4708/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3/arm-brcm-linux-uclibcgnueabi/sysroot

Nu stiu daca am inteles prea bine cum e cu staging dir si system root, dar intentia mea este:
- sa puna output-ul compilarii in /build/hiawatha/staging/
- sa instaleze programul cu toate fisierele lui (bin, lib, etc, man) in directoarele corespunzatoare din /build/hiawatha/pkgroot/
- impachetat din pkgroot cu toate directoarele si despachetat pe router in /opt, sa mearga fara alte interventii

Asadar am folosit:
Code:
rm -rf hiawatha-10.10
tar -xvf hiawatha-10.10.tar.gz

PATH=/build/rtn18u/release/src-rt-6.x.4708/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3/bin:$PATH
STAGING_DIR=/build/hiawatha/staging
LD_LIBRARY_PATH=/build/rtn18u/release/src-rt-6.x.4708/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3/lib

cd hiawatha-10.10
cmake \
-DENABLE_CACHE=OFF -DENABLE_DEBUG=OFF -DENABLE_IPV6=OFF -DENABLE_MONITOR=OFF -DENABLE_RPROXY=OFF -DENABLE_TLS=ON -DENABLE_TOMAHAWK=OFF -DENABLE_TOOLKIT=OFF -DENABLE_XSLT=OFF \
-DCMAKE_INSTALL_PREFIX=/opt \
-DCMAKE_TOOLCHAIN_FILE=../cmake.toolchain
make
make install
... unde cmake.toolchain este:
Code:
SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_SYSTEM_VERSION 2.6)
SET(CMAKE_SYSTEM_PROCESSOR arm)
SET(CMAKE_SYSROOT /build/rtn18u/release/src-rt-6.x.4708/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3/arm-brcm-linux-uclibcgnueabi/sysroot)
SET(CMAKE_STAGING_PREFIX /build/hiawatha/staging)
SET(CMAKE_INSTALL_PREFIX /build/hiawatha/pkgroot)
SET(CMAKE_C_COMPILER /build/rtn18u/release/src-rt-6.x.4708/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3/bin/arm-brcm-linux-uclibcgnueabi-gcc)
SET(CMAKE_CXX_COMPILER /build/rtn18u/release/src-rt-6.x.4708/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3/bin/arm-brcm-linux-uclibcgnueabi-g++)
SET(CMAKE_FIND_ROOT_PATH /build/rtn18u/release/src-rt-6.x.4708/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3)
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

Dar:
1) make pune toate executabilele si libs in ./staging, deci OK pana aici...
2) make install nu muta nimic in ./pkgroot
3) make install vrea sa puna toate celelalte fisiere (etc, man, exemple, scripturi) in /opt/ (adica in root pe host!). Daca ii dau voie, se finalizeaza cu succes.
4) Mutat de pe unde le pune, pe router in /opt, da eroare ca nu-si gaseste .so-urile. Carevasazica le-a link-uit in /lib, nu /opt/lib.

Cum se rezolva abrambureala asta?

PS: Cu acelasi toolchain am obtinut mtd-utils functional, dar am impresia ca ala e static.
 
Last edited:
Păi presupun că ar trebui să setezi și toate celelalte directoare (CMAKE_INSTALL_BINDIR, CMAKE_INSTALL_SBINDIR ... ), nu doar prefixul. Iar bibliotecile dinamice sunt căutate în LD_LIBRARY_PATH, să te asiguri că ai corect variabila setată când pornește, dacă muți lucruri.
 
Pai nu inteleg. LD_LIBRARY_PATH e variabila routerului. Programele din firmware cauta in /lib, programele de pe stick (entware) cauta in /opt/lib, fara ca vreunul dintre ele sa-si seteze ceva special.
De fapt, ma uit in programele de pe stick si vad string-uri gen /opt/lib/ceva.so.0, deci sunt hard-coded in program. Cum fac sa aiba si al meu la fel?
 
Last edited:
Code:
# ldd hiawatha
        libcrypt.so.0 => /lib/libcrypt.so.0 (0x40156000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x400c1000)
        libz.so.1 => /usr/lib/libz.so.1 (0x401ba000)
        libmbedtls.so.12 => /opt/lib/hiawatha/libmbedtls.so.12 (0x40173000)
        libmbedx509.so.0 => /opt/lib/hiawatha/libmbedx509.so.0 (0x401d3000)
        libmbedcrypto.so.3 => /opt/lib/hiawatha/libmbedcrypto.so.3 (0x401f5000)
        libc.so.0 => /lib/libc.so.0 (0x40248000)
        libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x4010d000)
        libmbedx509.so.0 => not found
        libmbedcrypto.so.3 => not found
        libmbedcrypto.so.3 => not found
        ld-uClibc.so.0 => /lib/ld-uClibc.so.0 (0x400f0000)
Intai gaseste libmbedx509.so.0, apoi nu mai gaseste? WTF?
 
Back
Top