top of page

Look Up Tables in embedded

Actualizată în: 19 feb. 2018

Dese sunt situatiile cand ai nevoie sa folosesti anumite valori de ADC insa uC-ul nu este suficient de rapid incat sa efectueze calculele ce stau la baza convertirii valorii pe biti, intr-o valoare reprezentata in unitati cunoscute in Sistemul International[SI] ori in unitati derivate.


Pentru astfel de momente, sunt mai multe alternative:

  • Se renunta la precizie incat procesorul sa efectueze calculele mai usor

  • Logica aditionala care sa duca la efectuarea calculelor, doar uneori, incat sa descarce procesorul de calcule aditionale

  • In cazul in care memoria este suficienta, se poate apela la folosirea conceptului de Look Up Table (LUT)

Ne vom concentra in continuare pe ultima varianta mentionata.


Care sunt avantajele folosirii unui LUT?

Incarcarea foarte mica a procesorului in convertirea valorii achizitionate este principalul avantaj. Pe langa aceasta, valorile sunt deja precalculate. Un alt avantaj il reprezinta configurabilitatea. Este suficienta schimbarea unor valori in ROM, incat intervalele de referinta pentru conversie sa fie schimbate.


Care sunt dezavantajele folosirii unui LUT?

Singurul dezavantaj al folosirii acestei metode, este cantitatea aditionala de memorie, necesara stocarii tabelului.


Cum configurez un LUT?

Configurarea unui LUT presupune cunoasterea unei relatii de corespondenta intre unitatile de ADC si unitatile reprezentate în SI (consideram o unitate a temperaturii definita în grade Celsius).

Relatia este asemenea urmatorului argument:

“Daca ADCul a inregistrat o valoare de 25, inseamna ca termistorul meu indica -10 grade Celsius.”

Reiese astfel, ca avem nevoie de un tablou unidimensional care sa stocheze doar temperaturile, deoarece valoarea convertita din tensiune in biti, se afla stocata in registrul canalului aferent de ADC.


Trecand la concret, consideram un ADC cu precizie pe 8 biti:

#define MAX_RAW_ADC_VALS 256u


Intr-un fisier de configurare vom avea:


/* BatteryMonitor_Cfg.h */

#ifndef BATT_MON_CFG_H /*protectie la incluziune multipla*/

#define BATT_MON_CFG_H


[...]

/* vectorul unidimensional ce stocheaza temperatura */

extern sint8_t BatteryMonitor_TemperaturesLut[MAX_RAW_ADC_VALS];

[...]


#endif BATT_MON_CFG_H /* aici se inchide directiva de preprocesare */


Urmeaza definitia vectorului:


/* BatteryMonitor_Cfg.c */

#include “BatteryMonitor_Cfg.h”


/* definim vectorul unidimensional ce stocheaza temperaturile */

sint8_t BatteryMonitor_TemperaturesLut[MAX_RAW_ADC_VALS] =

{

-10,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3,-3,

-2,-2,-1,-1,0,0,1,2,2,3,3,4,4,5,5,6,

6,7,7,8,8,9,9,10,10,11,11,12,13,13,14,14,

15,15,16,16,17,17,18,18,19,19,20,20,21,21,22,22,

23,24,24,25,25,26,26,27,27,28,28,29,29,30,30,31,

31,32,32,33,33,34,35,35,36,36,37,37,38,38,39,39,

40,40,41,41,42,42,43,43,44,44,45,46,46,47,47,48,

48,49,49,50,50,51,51,52,52,53,53,54,54,55,55,56,

56,57,58,58,59,59,60,60,61,61,62,62,63,63,64,64,

65,65,66,66,67,67,68,69,69,70,70,71,71,72,72,73,

73,74,74,75,75,76,76,77,77,78,78,79,80,80,81,81,

82,82,83,83,84,84,85,85,86,86,87,87,88,88,89,89,

90,91,91,92,92,93,93,94,94,95,95,96,96,97,97,98,

98,99,99,100,100,101,102,102,103,103,104,104,105,105,106,106,

107,107,108,108,109,109,110,110,111,111,112,112,113,114,114,115,

115,116,116,117,117,118,118,119,119,120,120,121,121,122,122,123

};

[...]


Iar in sursa vom avea conversia:


/* BatteryMonitor.c */

static sint8_t BatteryMonitor_ConvertToSi(uint8_t tmp_RawAdcCounts);


[...]

static sint8_t BatteryMonitor_ConvertToSi(uint8_t tmp_RawAdcCounts)

{

return BatteryMonitor_TemperaturesLut[tmp_RawAdcCounts];

}


Just the facts

Am creat un LUT pe care l-am populat cu fiecare valoare de temperatura aferenta valorii pe biti convertita de catre ADCul nostru.

I-am dat un scop extern incat sa avem LUTul izolat in fisierul de configurare, dar în acelasi timp, accesibil de catre oricine va include headerul de configurare.

Am declarat si definit functia care ne va converti valoarea pe biti din ADC, in valoarea temperaturii. Functia primeste ca parametru o valoare între 0 si 255, iar bazat pe aceasta, face asocierea cu vectorul de valori ce stocheaza temperatura reprezentata in grade Celsius.

Intr-o singura poza, rezumatul ar fi astfel:



Resurse utilizate de LUT:

Memorie LUT:

256 valori de temperatura x 1B = 256B de ROM


Memorie functie de conversie:

Difera în functie de arhitectura microcontrolerului si de compilator, insa considerand un Atmega328 (Arduino), iar codul compilat cu IAR, sectiunea de cod ocupa in memorie 11B ROM, dovada fiind poza cu rutina scrisa in limbaj de asamblare:


Timp de execuţie:

Conform datasheet-ului acestui controller, daca adunam instructiunile, obtinem:


LDI: 1 clock

MOVW: 1 clock

SUBI: 1 clock

SBCI: 1 clock

LD: 2 clocks

Total: 6 clocks

Analizand la nivel de asamblare codul, observam ca cea mai de durata operatiune pentru functia noastra de conversie o reprezinta accesul prin indirectare al vectorului de temperaturi (LD R16,Z). In cazul Arduino, Z este un registru special care poate retine adrese pe 16 biti, iar arhitectura sa pe 8 biti faciliteaza accesul la datele din memorie prin 2 ciclii de tact.


Utilizari alternative:

Desigur nu suntem limitati doar la acest caz, in care utilitatea LUTurilor se remarca exclusiv raportat la valori convertite din domeniul continuu in cel discret.

Un astfel de mecanism se poate folosi ori de cate ori, accesul rapid este necesar sau justificabil. Un exemplu concret in acest caz, este folosirea unui tabel pentru a da statusuri sau a genera erori, de asemenea, totul raportat la o corespondenta intre un input si rezultatul pe care il dam, bazat pe acest input.

Optiunile sunt nelimitate. Tipurile de date nu trebuie sa fie neaparat simple. Un exemplu putin mai interesant este necesitatea folosirii unui LUT care contine pointeri catre functii, iar corespondenta este data de un switch, care, bazat pe case-ul in care se afla, acceseaza aceasta functie.

[...]

switch (tmp_Phase_en):

{

case PREPARE_EN:

[...]

break;

[...]

}


(*DoPhase[tmp_Phase_en])();


Concluzie:

Ne aflam de multe ori in circumstante care ne obliga la a optimiza functionalitati din cadrul proiectului, iar in cazul existentei unui buffer suficient de memorie, conceptul de Look Up Table este de un real folos.


Voi cand gasiti necesara folosirea conceptului de LUT?


Comentariile si criticile sunt apreciate.


56 afișări0 comentarii

Postări recente

Afișează-le pe toate

Calea catre main() - intro

In liceu am invatat ca un program incepe de la main(), acest lucru fiind valabil in mai multe limbaje de uz intens. De ce main()? Ce-i...

Comments


bottom of page