Unitate 3 / 9

Sisteme încorporate și generare de cod pentru microcontroler

Câștiguri:

  • Abilitatea de a genera cod încorporat dat de registrul și contextul hardware pentru Arduino/STM32/ESP32 cu AI
  • Abilitatea de a recunoaște și corecta modele încorporate, cum ar fi întreruperi, temporizatoare și bucle neblocante în ieșirea AI
  • Abilitatea de a revizui codul generat în termeni de memorie, în timp real și securitate înainte de a-l încărca pe hardware

În mecatronică, microcontrolerul este locul în care ideile se întâlnesc cu lumea fizică. Carduri precum Arduino, STM32, ESP32; citește senzori, acţionează dispozitive de acţionare, comunică și face toate acestea în condiţii de memorie limitată, putere de procesare limitată și cerințe stricte de sincronizare. Inteligența artificială este foarte utilă în acest domeniu: poate extrage setările de registru pe baza fișei de date a unui senzor, poate codifica un protocol de comunicare, poate seta o întrerupere a temporizatorului. Dar sistemele încorporate sunt unul dintre domeniile în care AI produce cele mai „falsuri convingătoare”; deoarece adresele de registru, măștile de biți și comportamentul de sincronizare sunt specifice plăcii și o eroare de un bit perturbă întregul comportament. În această unitate, vom acoperi cum să generați codul încorporat cu AI și cum să îl revizuim înainte de a-l încărca în hardware.

Diferența codului încorporat față de software-ul pur

Un program desktop are o mulțime de memorie, sistem de operare și ușurință de depanare. Codul încorporat nu are majoritatea dintre acestea:

Dimensiune

desktop

sistem încorporat

memorie

Nivel GB

Nivel KB (de exemplu, 2 KB RAM)

sincronizare

În general flexibil

Strâns, în timp real

depanare

Ușor (depanator, jurnal)

Hard (JTAG, serial, LED)

Rezultat eroare

Programul se blochează

Actuatorul/hardware-ul poate fi deteriorat

accesul la resurse

rezumate ale sistemului de operare

Acces direct la Registru

Aceste diferențe determină criteriile dvs. de evaluare a ieșirii AI: utilizarea memoriei, în timp real (neblocare) și acuratețea registrului hardware sunt întotdeauna în partea de sus a listei de verificare.

Blocker vs Non-Block Code

Cea mai frecventă greșeală făcută de începători (și frecvent făcută de AI) este utilizarea delay(). delay(1000) blochează procesorul timp de 1 secundă; În această perioadă nu pot fi cititi alți senzori, nici butoane nu pot fi controlate. Acest lucru este inacceptabil în mecatronică. În schimb, se folosește un model neblocant bazat pe millis().

// BAD: blocker -- procesorul nu poate face nicio altă lucrare timp de 1 secundă void loop() { digitalWrite(LED, HIGH); întârziere (1000); // totul se oprește digitalWrite(LED, LOW); întârziere (1000); // Un buton de urgență nu poate fi citit în acest moment!}// BINE: non-blocking -- bucla nu este blocată, alte sarcini rulează dacă (nowMs - previousMs >= interval) { previousMs = nowMs; ledStatus = !ledStatus; digitalWrite(LED, ledStatus); } buttonCheck(); // poate rula în fiecare ciclu sensorRead(); //poate rula în orice buclă}

Modelul de non-blocare este baza mecatronicei încorporate: bucla de control curge continuu, nicio sarcină nu blochează alta. A spune AI „nu utilizați întârziere, scrieți fără blocare bazată pe milis” atunci când scrieți codul îmbunătățește direct calitatea ieșirii.

Sfat: Căutați întârziere (în codul încorporat din AI. Dacă vedeți o întârziere în bucla de control principală, cel mai adesea acel cod nu este potrivit pentru un sistem în timp real și ar trebui rescris.

Întreruperi și temporizatoare

Capturăm evenimente critice în timp (impuls codificator, buton, eșantionare periodică) cu întreruperi în loc să așteptăm în bucla principală. Rutina de întrerupere (ISR) trebuie scrisă scurt și cu atenție: fără întârzieri, Serial.print sau calcule lungi în ea; Variabilele partajate sunt marcate ca volatile.

volatile long encoderCounter = 0; // ISR și bucla sunt partajate -> stare volatilă void enkoderISR() { // ISR scurt: doar numărați, nu faceți nicio altă lucrare dacă (digitalRead(ENC_B)) enkoderCounter++; else encoderCounter--;}void setup() { pinMode(ENC_A, INPUT_PULLUP); pinMode(ENC_B, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(ENC_A), enkoderISR, RISING);}void loop() { contor lung; fără întreruperi(); //scurte întreruperi pentru contor de citire atomică = encoderCounter; întrerupe(); // tranzacționați în siguranță cu contorul...}

Trei puncte critice din acest exemplu sunt cele în care AI-ul ratează adesea: (1) encoderCounter partajat trebuie să fie „volatil” sau optimizarea compilatorului va pierde actualizări; (2) ISR ar trebui să fie scurt; (3) La citirea unei variabile multi-octeți în bucla principală, întreruperile pentru citirea atomică trebuie închise pentru o perioadă scurtă de timp, altfel ISR poate interveni în timpul citirii și poate fi citită jumătate/valoarea coruptă (condiție de cursă). Asigurați-vă că verificați dacă aceste trei sunt prezente în codul AI.

Precizia înregistrării și fișa de date

AI poate denatura în mod credibil adresa de registru a unui senzor sau bitul de configurare al unui MCU. De exemplu, registrul de gestionare a energiei al unui IMU MPU6050 este 0x6B; Dacă AI-ul dă acest lucru ca 0x6A, codul este compilat, pare să funcționeze, dar senzorul nu se trezește. Astfel de erori sunt detectate numai în comparație cu fișa de date.

// Trezire MPU6050: conform fișei de date PWR_MGMT_1 = 0x6B, valoare 0x00#define MPU_ADDR 0x68#define PWR_MGMT_1 0x6B // <-- VERIFICAȚI din datasheetWire.beginTransmission(MPU_ADDR);Wire.Wire.write(PWR_IREMTwrite(PWR_0x60); // se trezește din modul de repausWire.endTransmission(true);

Atenție: Verificați fiecare adresă de registru, masca de biți și adresa I2C/SPI dată de AI din fișa de date. Aceste valori sunt specifice cardului și cipului; Valoarea pe care AI „își amintește” poate fi dintr-o altă revizuire a cipului. Registrul incorect duce în tăcere la un comportament incorect.

Solicitare slabă / Solicitare puternică

SLAB: „Citiți senzorul de temperatură pe ESP32.” (Care senzor? Ce protocol? Ce pin? Cod generic, probabil greșit.) STRONG: „Citește un senzor de temperatură DS18B20 pe ESP32 (cadru Arduino) de la GPIO4 cu OneWire. 85). Vă rugăm să specificați fiecare bibliotecă și conexiune pin pe care o utilizați în primul comentariu.

prompt puternic; Oferă cip, cadru, senzor, protocol, pin, model de eșantionare, stare de eroare și constrângere de memorie. În acest fel, rezultatul este atât verificabil, cât și realist.

Verificați lista de verificare pentru codul încorporat

Înainte de a încărca ieșirea AI, treceți-o prin această listă:

  1. Blocare: Există o întârziere sau o blocare lungă în bucla principală?
  2. volatile: sunt variabilele partajate cu ISR volatile?
  3. Acces atomic: variabila partajată pe mai mulți octeți este sigur de citit?
  4. Înregistrare: sunt adresele și măștile de biți compatibile cu fișa de date?
  5. Memorie: șir de caractere, matrice mari, recursiunea creează depășirea nivelului KB?
  6. Gestionarea erorilor: sunt gestionate erorile de citire a senzorului, expirările de comunicare?
  7. Pornire sigură: Ieșirile actuatorului sunt plasate într-o stare sigură (pasivă) la pornire?

Mini carcasă

Inginerul de sisteme integrate Baran îi cere AI să scrie codul care citește IMU pentru o dronă. Codul se compilează și pare să funcționeze, dar valorile unghiului sunt lipsite de sens. Baran aplică lista de verificare: compară adresele registrului cu fișa de date și constată că AI-ul scoate incorect registrul de configurare a giroscopului (0x1A în loc de 0x1B), deci scala de sensibilitate este greșită. Odată corectate, valorile se stabilesc. Apoi delay(10) notificări în bucla principală; convertirea acesteia într-o structură bazată pe milis, deoarece blocarea buclei de control al zborului este inacceptabilă. În cele din urmă, vede că variabila contor partajat nu este volatilă și o adaugă. AI a dat rapid scheletul; Dar lista de recenzii a surprins trei erori separate: înregistrare, blocare și volatilitate, iar hardware-ul nu era deloc în pericol.

Greșeli comune

  • Omorârea răspunsului în timp real utilizând delay() în bucla principală de control.
  • Evitați să faceți volatilă variabila partajată cu ISR și să vă confruntați cu coruperea datelor silențioase.
  • Citirea unei variabile partajate pe mai mulți octeți în mod non-atom și generarea unei condiții de cursă.
  • Neverificarea măștilor de registru/biți date de AI cu fișa de date.
  • Crearea unui depășire a memoriei utilizând șiruri și matrice mari în RAM limitată.
  • Am uitat să securizeze inițial ieșirile actuatorului.

Pe scurt

  • Cod încorporat; Funcționează cu memorie limitată, sincronizare strânsă și acces direct la registru.
  • În bucla principală, în loc de întârziere, este utilizat un model neblocant bazat pe milis.
  • ISR este menținut scurt; variabilele partajate trebuie să fie volatile și accesate atomic.
  • Adresele de registru și măștile de biți sunt întotdeauna verificate în raport cu fișa de date; AI poate greși.
  • Memoria, tratarea erorilor și starea de pornire sigură sunt întotdeauna verificate.
  • prompt puternic; Include cip, cadru, senzor, protocol, pin și constrângeri.

Sarcina de aplicare

Alegeți un senzor (de exemplu, DS18B20, MPU6050 sau HC-SR04) și un microcontroler (Arduino/ESP32/STM32). Puneți AI să genereze un cod de citire neblocant cu șablonul puternic de prompt din această unitate. Apoi urmați unul câte unul cele șapte elemente ale listei de verificare: comparați cel puțin o valoare de registru/pin cu fișa de date, verificați întârzierile buclei, verificați starea volatilă a variabilelor partajate. Câte elemente au „trecut” la prima încercare și câte au avut nevoie de corectare? Notați fiecare problemă pe care o găsiți și rezolvarea acesteia.