devo prendere il valore di un lettore angolare AS5047P (SPI) collegato a una manopola graduata da 0 a 7 gli spazi fra i numeri sono tutti regolari un (cerchio diviso in 7 spicchi)
girata a mano il valore va stampato sul display ovviamente devo stampare
il range da 000 a 700 in base a come si trova/sposta la manopola,
L'AS5047P da valori da zero a oltre 4000
che si fa ? quale è il sistema migliore che impiega meno risorse/tempo/cicli macchina
fare piu di 4000 "if" mi sembra allucinante
c'e qualche sistema formula? in caso che la funzione MAP fosse imprecisa pe questo scopo?
Come fa ad essere imprecisa ? ? ?
long map(long x, long in_min, long in_max, long out_min, long out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
Guglielmo
ma così te la devi scrivere, che non serve
map() in Arduino è intera...
che comunque su 4000 impulsi l'errore non è apprezzabile
P.S. un cerchio diviso da 0 a 7 è diviso in 8 spicchi
In che senso? La manopola a fine corsa non ti darà mica valori "approssimativi", ci sarà pure un valore massimo... E poi non si capisce, tu vuoi avere valori da 0 a 700 o da 0 a 7? Ci fai vedere un pezzo di codice?
beh diciamo che lo zero e il 7 coincidono o sono vicinissimi,
il chip non e a impulsi come l'encoder in base alla posizone da un valore da zero a 4096
il chip da il valore da zero a 4086
il quadrante deklla manopola va da zero a 7
7 tacche maggiori (con tacche intermedie)
sul display deve apparire un numero da 000 a 700
es a 4096 deve visualizzare 700
a 2048 deve visualizzare 350
E quindi fare int valore = map(lettura, 0, 4096, 0, 700);
non ti basta?
ho messo il post per avere qualche indicazione per un codice piu veloce possibile nonostante adopero un arduino nano rp2040 a 133 mhz,
sicuramente il codice a prova di errore sarebbe
if ( lettura_chip == 2048) lettura_chip = 350;
ripetuto per tutti i valori del chip (4096 vollte)
anche se "corretto" sembra che qualcosa non va
Siano 'n' e 'q' due unsigned long, con 'n' che va da 0 a 4095 (4096 valori), otteniamo 'q' da 0 a 700:
n <<= 1;
q = n;
n <<= 4;
q += n;
n <<= 1;
q += n;
n <<= 1;
q += n;
n <<= 1;
q += n;
n <<= 2;
q += n;
n <<= 2;
q += n;
q >>= 15;
o più semplicemente:
q = (n * 5602) >> 15;
Ma no
Se non vuoi usare la map, che è la maniera più semplice, precisa e veloce che tu possa usare, te la cavi comunque con massimo 7 righe
Basta pensare a rovescio
Adesso è tardi
Se non ti viene in mente nulla ne parliamo domani pomeriggio
si potrebbe tentare es a dividere in 7 zone il numero
se cade tra 0 e 1
tra 1 e 2
tra 2 e 3 etc
es se il numero è tra 6 e 7 ci si concentra li e si tralasciano i numeri tra 0 e 6
non ti nego che il codice non l'ho "capito" ma cerchero di documentarmi :
La formula che ti serve è semplicemente dividere N per 5.85
Ma per evitare calcoli floating point allora ho moltiplicato per 5602 e dividiso per 32768 (32768/5602=5.849)
La divisione per una potenza di due si ottiene più velocemente shiftando i bit a destra, in questo caso di 15 posizioni.
Edit: oppure lo facciamo ancora più preciso: moltiplichiamo per 11203 e dividiamo per 65536. Sono 16 shift a destra (due byte da spostare in blocco di due posizioni), cioè il valore 0..700 lo troveremo già in chiaro nei due byte alti dell'unsigned long dopo la moltiplicazione.
Invece di shiftare otteniamo quindi il valore tramite una union, non so se si può andare più veloci di così.
Esempio funzionante su wokwi, basta avviare la simulazione e girare la manopola
Il codice lo riporto anche qui per completezza:
//--------------------------------------------------------------------
union {
uint32_t n;
struct {
uint16_t l;
uint16_t h;
} hl;
} dataConverter;
int precN;
//--------------------------------------------------------------------
void setup()
{
Serial.begin(9600);
}
//--------------------------------------------------------------------
void loop()
{
int n = analogRead(A0) * 4.002932551; // n da 0 a 4095
if (n != precN)
{
precN = n;
Serial.print("IN: ");
Serial.print(n);
Serial.print(" ");
dataConverter.n = 11203UL * n;
Serial.print("OUT: ");
Serial.println(dataConverter.hl.h); // da 0 a 700
}
delay(20);
}
//--------------------------------------------------------------------
Devo ripensarci a questa cosa
Qualcuno mi spiega perché la map() non va bene, che si risolve con una riga di codice, invece di tutte le altre soluzioni che, per carità andranno pure bene, ma a me sembra cercare di reinventare l'acqua calda costruendo un bollitore alimentato con una lente ed una serie di specchi che portano il sole all'interno di una grotta fino alla paglia sotto alla pentola, invece di usare un fiammiero...
Concorquoto
Comunque il fiammifero c'è,
non ti eri accorto che la lente brucia la corda che tiene la molla che sfrega la carta vetrata sul fiammifero tenuto fermo da una morsa dentro alla paglia. I particolari sono importanti
La specifica richiesta è la velocità. La map in questo caso effettua molte operazioni aritmetiche inutili al posto di una sola divisione per 5.85
Volendo velocizzare la divisione usando solo aritmetica intera, basta moltiplicare per 11203 e prendere i due byte alti (non mi sembra una complicazione ).
Per avere velocità di esecuzione credo che la cosa migliore sia una tabella di lookup (che potresti generare con un semplice script Python ad esempio), ma francamente con un RP2040 a 133Mhz credo sia un po' inutile (bisognerebbe capire lo ragione per cui è necessaria velocità e fare due conti)
Si ma era in confronto rispetto ali oltre 4000 "if()" ipotizzati... Concordo che la map() ha codice leggermente ridondante per questo caso (visto che gli estremi partono entrambi da zero), ma da un utente che nel post iniziale si chiede se la map() sia "imprecista" (che ancora non ho capito cosa intenda) non credo ci si possa aspettare che il suo codice sia così "misson critical" da richiedere di risparmiare qualche millisecondo (o anche meno).
Se può permettersi 8 kbyte di memoria occupata dalla LUT allora si
In ogni caso comprimere una scala da 0 a 4095 dentro un range da 0 a 700 porta ad avere cinque o sei valori in ingresso rappresentati con un solo punto in uscita.