În laboratorul de astăzi veți învăța cum să interactionați cu diferite periferice prin I2C și cum datele de la hardware sunt transmise utilizatorului prin diferite niveluri de redirectare. Vom explora atât noțiuni low-level cât și aspecte mai high-level (arhitectura software a driver-elor in NuttX) construite peste.
Un modul de kernel (sau un device driver) este o bucată specializată de cod care este compilată și adăugată la kernel-ul sistemului de operare. În acest mod, sistemul de operare are posibilitatea de a interacționa cu diferite periferice - în absența driverelor, un kernel nu ar putea accesa lumea exterioară CPU-ului. De exemplu, dacă ne dorim ca sistemul nostru să poată transfera date prin controller-ul de I2C, avem nevoie de un driver pentru I2C care să programeze aceste tranzacții de transfer de date. Analog, dacă ne dorim să interacționăm cu un senzor, avem nevoie de un device driver care știe să programeze acel senzor.
Din punct de vedere al user-ului, driverele pot să fie expuse în două moduri:
I2C (Inter-Integrated Circuit) este un protocol de comunicație serială dezvoltat de Philips (acum NXP) în anii ’80, folosit pe scară largă pentru a conecta microcontrolere cu senzori, memorii, afișaje și alte periferice. Este foarte popular în proiecte embedded datorită simplității și numărului redus de pini necesari.
I2C este un protocol care functioneaza sub paradigma master-slave prin care un “master” (de obicei microcontroller-ul, in cazul nostru ESP32S3-ul) citeste sau scrie date de pe un device denumit “slave” (cele mai comune device-uri de acest fel in lumea embedded fiind senzorii).
Astfel, magistrala (bus-ul) este definita din doua linii de comunicatie:
Transmisia datelor se face in mod bidirectional (master → slave in cazul operatiei de write si slave → master in caz de read), insa nu este full-duplex - doar un singur device poate sa detina controlul SDA-ului in orice moment de timp. SCL-ul este intotdeauna generat de master-ul comunicatiei si este propagat catre toate dispozitivele de tip slave.
Fiecare dispozitiv slave este identificat printr-o adresa pe 7 biti la care se adauga un bit pentru R/W. Astfel:
DRV2605L este un driver haptic proiectat cu scopul de a controla motoare de vibratie care in final sa poata oferi feedback utilizatorului prin vibratii. Avantajul acestui device este ca, desi poate fi programat sa genereze vibratii constante, vine cu o biblioteca interna de efecte haptice, fara a fi nevoie ca user-ul sa creeze trenul de vibrații manual.
In cadrul NuttX, arhitectura software prin care o aplicatie interactioneaza cu senzorul DRV2605L presupune mai multe niveluri de redirectare:
Codul este impartit in doua componente, upper half driver si lower half driver. Deoarece majoritatea codului unui driver poate fi refolosit, a fost introdus upper half driver pentru a oferi un API uniformizat si standardizat pentru toate device-urile. Componenta care este particulara fiecarui device si se ocupa de accesul efectiv la hardware este driver-ul lower half, neaccesibil user-ului in mod direct. Acesta este invocat de catre driver-ul upper half printr-un API intern.
LSM6DSL este un senzor 3D care combina in acelasi dispozitiv un accelerometru si un giroscop. Desi pe sistemul nostru este conectat prin I2C, acesta poate comunica si prin SPI.
Spre deosebire de DRV2605L, arhitectura software este mai simpla in cazul acestui device, sistemul de operare oferind doar un singur nivel de redirectare:
Apelurile de sistem sunt interceptate de catre driver-ul dispozitivului prin char device-ul expus in /dev/lsm6dsl
, iar mai apoi comenzile de read/write sunt trimise direct device-ului hardware, fara a mai exista o componenta intermediara uniforma pentru toate device-urile. Acest API este imprumutat din Linux si este considerat “obsolete” in NuttX, doar device-urile “legacy” utilizand aceasta abordare.
Pentru a putea rula exercitiile, este nevoie să compilați NuttX folosind hacktorwatch:iot
, la care va trebui sa activati manual cateva config-uri folosindu-va de sistemul de build (make menuconfig
). Astfel, veti activa atat compilarea codului de driver si initializarea efectiva a hardware-ului, cat veti si incarca pe placa aplicatii de demo pe care le puteti rula.
Ca sa folositi senzorul de haptics, aveti nevoie de urmatoarele config-uri:
CONFIG_FF_DRV2605L
. In cadrul acestui exercitiu de set-up va trebui sa determinati care sunt acele dependinte si sa le activati mai intai pe acelea.
Cele doua config-uri de mai jos va vor oferi access la accelerometru si giroscop:
1. Pentru a va acomoda cu API-ul pe care NuttX il ofera pentru a interactiona cu cele doua device-uri, in cadrul acestui exercitiu va trebui sa inspectati codul si sa rulati aplicatiile de demo disponibile in repository-ul nuttx-apps
.
nuttx-apps/examples/drv2605l/
initializeaza device-ul cu anumite efecte de vibrare (upload_rom_effect
, upload_constant_effect
). Apelarea acestora se face ulterior, “on demand”, prin play_effect
.nuttx-apps/examples/lsm6dsl_reader
afiseaza la consola o data la doua secunde datele obtinute de la accelerometru si giroscop. Observati modul de interactiune cu senzorul: acesta este expus printr-o intrare in /dev/
care va trebui folosita in cadrul apelului de sistem open
, iar mai apoi folosim apeluri de ioctl
pentru a interactiona cu hardware-ul.
nuttx/include/nuttx/
. Puteți căuta recursiv din linia de comandă folosind grep -r <string>
.
2. In cadrul acestui exercitiu, vrem ca ceasul sa ne ofere informatii (prin vibrare) atunci cand viram stanga, respectiv dreapta.
lsm6dsl_reader
vom citi datele oferite de accelerometru si vom determina directia de mers.drv2605l
are o biblioteca interna de efecte de vibrare pe care o puteti consulta aici, la pagina 63. Alegeti doua efecte din acest tabel si incarcati-le pe ceas.
play_effect
. Parametrul care ne intereseaza si trebuie modificat este play.value
(trebuie sa fie setat pe 0 - disabled).
usleep
.
3. (Bonus) La exercitiul anterior, intensitatea de vibrare era absoluta (on/off). Ne dorim sa avem un mecanism mai complex prin care sa folosim vibrare graduala in functie de nivelul acceleratiei - cu cat acceleratia este mai mare, cu atat vibratia va fi mai puternica.