PDA

Voir la version complète : Detection de la force de pression du stylet


Saik
01/02/2008, 23h29
Voilà, je débute sur la scène DS :p (avec PAlib pour l'instant, c'est quand même bien pratique !)
Je voulais donc voir comment détecter la pression du stylet, et après quelques recherches infructueuses je décidais de regarder la dsTek...
Puis j'ai découvert ceci : http://forum.gbadev.org/viewtopic.php?t=14396&highlight=pressure
et celà : http://focus.ti.com/lit/ds/symlink/tsc2100.pdf


J'ai aussi découvert l'existence de Stylus.Pressure dans Palib (j'avoue que j'ai mis du temps à le voir celui là :whst:). Mais ce dernier ne me donnait vraiment pas satisfaction côté précision. J'ai fait une petite fonction inspirée de l'aide de Jesse qui marche plutôt bien, la voilà donc :
#define STYLUS_PRESSURE_MIN 60
#define STYLUS_PRESSURE_MAX 90

u8 stylusPressure(void)
{
static u8 pression = STYLUS_PRESSURE_MIN;
u8 pression_new = (IPC->touchX * IPC->touchZ2) / (IPC->touchZ1 << 6) - (IPC->touchX >> 6);
if(Stylus.Held != 1)
{//Si le stylet n'est pas utilisé, on renvoi 0
pression = STYLUS_PRESSURE_MIN;
return 0;
}
//Limitations aux bornes
if(pression_new < STYLUS_PRESSURE_MIN)
pression_new = pression;
if(pression_new > STYLUS_PRESSURE_MAX)
pression_new = STYLUS_PRESSURE_MAX;

//Affaiblissement du mouvement par frame pour éviter les sauts brutaux
if(pression != 0)
pression += (s8)(pression_new - pression) / 2;
else
pression = pression_new;
return (pression - STYLUS_PRESSURE_MIN);
}

La fonction renvoi donc un entier de 0 à 29 :
-0 si pas de contact du stylet
-sinon, plus le chiffre est faible plus la pression est forte


J'ai volontairement laissé Stylus.Pressure tranquille, mais rien de vous empèche de remplacer la variable "pression" par Stylus.Pressure afin de l'utiliser comme le reste de Stylus...

Je vous encourage aussi vivement à calibrer la pression, la valeur min et max pouvant différer selon les consoles et lez zones de l'écran.

Voilà, j'espère que ça pourra servir ^^

Si vous avez des idées d'optimisation ou autre, n'hésitez pas :)

dolarcles
01/02/2008, 23h34
Oh, l'écran peut donc également détecter le niveau de pression, pas seulement le toucher. Je viens d'apprendre qqch, merci !

Quentin
01/02/2008, 23h41
Pareil.
Donc sur Opera faut bien appuyer fort pour cliqué sur les liens ^^.

Neumann
01/02/2008, 23h56
Vous avez jamais testé Colors ?
Il utilise cette fonctionnalité

Pixou
02/02/2008, 00h30
Oh, l'écran peut donc également détecter le niveau de pression, pas seulement le toucher. Je viens d'apprendre qqch, merci !

tu t'es jamais servi de Colors ?!

Arialia
02/02/2008, 00h35
Bienvenue Saik

et merci pour ta fonction :bravo:

Pour un premier message félicitations B)

Ah colors :wub:

dolarcles
02/02/2008, 12h37
Hé non, jamais utilisé Colors, je suis trop mauvais en dessin. M

Mais je suis surpris car malgré les nombreux jeux auxquels j'ai joué, je n'ai pas mémoire d'une quelconque utilisation de cette possiblité.

Saik
02/02/2008, 12h53
C'est probablement du au manque de précision de la détection, les résultats variants selon les consoles...


Je pense faire un outil de calibrage que je posterai ici ;)

johnko54
02/02/2008, 14h52
Je pense faire un outil de calibrage que je posterai ici ;)

et pourquoi ne pas t'allier avec Arialia pour faire un "super-outil de calibrage" :w00t:

Saik
02/02/2008, 16h19
Je suis nouveau, donc je ne connais pas vraiment Arialia :p
Arialia a déjà fait quelque chose en rapport avec ça ? (désolé si la question est bête ou quoique ce soit, mais je ne connais réellement aucun d'entre vous ^^)

De toutes façons je pense pouvoir le faire assez rapidement, dès que j'ai un peu de temps :)

Saik
03/02/2008, 21h38
Chose promise...


Voilà donc un "calibrateur" qui marche plutôt bien (testé sur deux DS ayant des sensibilités très différentes, sans doute dues aux écrans protecteurs), mais probablement largement perfectionnable...

Voici le fichier "stylet.h"
#define STYLUS_PRESSURE_MIN 55
#define STYLUS_PRESSURE_MAX 95
#define STYLUS_PRESSURE_DIF 40

typedef struct
{
u32 pressionMin : 8;//127 max
u32 pressionMax : 8;
u32 checkMin : 1;
u32 checkMax : 1;
u32 coefMin : 7;//Plus le coefficient est élevé, plus la valeur est fiable
u32 coefMax : 7;
float32 taux;
} pressionEcran;
pressionEcran etat_ecran[32][24];

u8 stylusPressureRough(void)
{//Pression système initiale (approximative)
static u8 pression = 0;
u8 pressionNew;
pressionNew = (IPC->touchX * IPC->touchZ2) / (IPC->touchZ1 << 6) - (IPC->touchX >> 6);
/*Affaiblissement du mouvement par frame pour éviter les sauts brutaux
et les différences trop importantes entre les zones*/
if(pression != 0)
pression += (s8)(pressionNew - pression) / 3;
else
pression = pressionNew;
return pression;
}

u8 stylusPressure(void)
{//Version avec calibrage
static u8 pression = STYLUS_PRESSURE_MIN;
u8 pressionNew = (IPC->touchX * IPC->touchZ2) / (IPC->touchZ1 << 6) - (IPC->touchX >> 6);
u8 x = IPC->touchXpx / 8, y = IPC->touchYpx / 8;
if(Stylus.Held != 1)
{//Si le stylet n'est pas utilisé, on renvoi 0
pression = STYLUS_PRESSURE_MIN;
return 0;
}
//Limitations aux bornes
if(pressionNew < etat_ecran[x][y].pressionMin)
pressionNew = pression;
if(pressionNew > etat_ecran[x][y].pressionMax)
pressionNew = etat_ecran[x][y].pressionMax;

//Affaiblissement du mouvement par frame pour éviter les sauts brutaux
if(pression != 0)
pression += (s8)(pressionNew - pression) / 2;
else
pression = pressionNew;

//Mise en place du taux
return (u8)((float32)(pression - etat_ecran[x][y].pressionMin) * (float32)etat_ecran[x][y].taux);
}

u8 stylusPressureOld(void)
{//Ancienne version, sans calibrage
static u8 pression = STYLUS_PRESSURE_MIN;
u8 pressionNew = (IPC->touchX * IPC->touchZ2) / (IPC->touchZ1 << 6) - (IPC->touchX >> 6);
if(Stylus.Held != 1)
{//Si le stylet n'est pas utilisé, on renvoi 0
pression = STYLUS_PRESSURE_MIN;
return 0;
}
//Limitations aux bornes
if(pressionNew < STYLUS_PRESSURE_MIN)
pressionNew = pression;
if(pressionNew > STYLUS_PRESSURE_MAX)
pressionNew = STYLUS_PRESSURE_MAX;

//Affaiblissement du mouvement par frame pour éviter les sauts brutaux
if(pression != 0)
pression += (s8)(pressionNew - pression) / 2;
else
pression = pressionNew;
return (pression - STYLUS_PRESSURE_MIN);
}

void initialisePressure(void)
{//Initialise les valeurs de pression
u8 i, j;
for(i = 0; i < 32; i++)
{
for(j = 0; j < 24; j++)
{
etat_ecran[i][j].pressionMin = STYLUS_PRESSURE_MAX;//valeur absurde pour être modifiée
etat_ecran[i][j].pressionMax = STYLUS_PRESSURE_MIN;
etat_ecran[i][j].checkMin = 0;
etat_ecran[i][j].checkMax = 0;
etat_ecran[i][j].coefMin = 0;
etat_ecran[i][j].coefMax = 0;
etat_ecran[i][j].taux = 1.0;
}
}
}

void adjustPressure(void)
{//Passe à une différence de STYLUS_PRESSURE_DIF si possible, sinon fixe un taux
u8 i, j;
for(i = 0; i < 32; i++)
{
for(j = 0; j < 24; j++)
{
//Valeurs par défaut si nécessaire
if(etat_ecran[i][j].checkMin == 0)
etat_ecran[i][j].pressionMin = STYLUS_PRESSURE_MIN;
if(etat_ecran[i][j].checkMax == 0)
etat_ecran[i][j].pressionMax = STYLUS_PRESSURE_MAX;
//Ajustement des valeurs
if((etat_ecran[i][j].pressionMax - etat_ecran[i][j].pressionMin) > STYLUS_PRESSURE_DIF)
{//On réduit l'écart à STYLUS_PRESSURE_DIF si possible
etat_ecran[i][j].pressionMax = etat_ecran[i][j].pressionMin + STYLUS_PRESSURE_DIF;
}
else if((etat_ecran[i][j].pressionMax - etat_ecran[i][j].pressionMin) < STYLUS_PRESSURE_DIF)
{//Sinon on calcule un multiplicateur
etat_ecran[i][j].taux = (float32)STYLUS_PRESSURE_DIF / (float32)(etat_ecran[i][j].pressionMax - etat_ecran[i][j].pressionMin);
}
}
}
}

void adjustPressureXY(u8 x, u8 y)
{
//Ajustement des valeurs
if((etat_ecran[x][y].pressionMax - etat_ecran[x][y].pressionMin) > STYLUS_PRESSURE_DIF)
{//On réduit l'écart à STYLUS_PRESSURE_DIF si possible
etat_ecran[x][y].pressionMax = etat_ecran[x][y].pressionMin + STYLUS_PRESSURE_DIF;
etat_ecran[x][y].taux = 1.0;
}
else if((etat_ecran[x][y].pressionMax - etat_ecran[x][y].pressionMin) < STYLUS_PRESSURE_DIF)
{//Sinon on calcule un multiplicateur
etat_ecran[x][y].taux = (float32)STYLUS_PRESSURE_DIF / (float32)(etat_ecran[x][y].pressionMax - etat_ecran[x][y].pressionMin);
}
}

void updatePressure(void)
{
u8 x, y, pression;
x = IPC->touchXpx / 8;
y = IPC->touchYpx / 8;
if(Stylus.Held == 1)
{
x = IPC->touchXpx / 8;
y = IPC->touchYpx / 8;
pression = stylusPressureRough();
if(((etat_ecran[x][y].coefMax < 127) && (pression > etat_ecran[x][y].pressionMax)) || (etat_ecran[x][y].checkMax == 0))
{
etat_ecran[x][y].pressionMax = pression;
etat_ecran[x][y].checkMax = 1;
etat_ecran[x][y].coefMax++;
adjustPressureXY(x, y);
// etat_ecran[x][y].taux = (float32)STYLUS_PRESSURE_DIF / (float32)(etat_ecran[x][y].pressionMax - etat_ecran[x][y].pressionMin);
}
else if(((etat_ecran[x][y].coefMin < 127) && (pression > 30) && (pression < etat_ecran[x][y].pressionMin)) || (etat_ecran[x][y].checkMin == 0))
{
etat_ecran[x][y].pressionMin = pression;
etat_ecran[x][y].checkMin = 1;
etat_ecran[x][y].coefMin++;
adjustPressureXY(x, y);
// etat_ecran[x][y].taux = (float32)STYLUS_PRESSURE_DIF / (float32)(etat_ecran[x][y].pressionMax - etat_ecran[x][y].pressionMin);
}
}
}

void checkWeakPressure(void)
{//Prend les valeurs de la pression la plus faible
u8 i, j, x, y, pression;
while(Pad.Newpress.A != 1)
{
if(Stylus.Held == 1)
{
x = IPC->touchXpx / 8;
y = IPC->touchYpx / 8;
pression = stylusPressureRough();
if(pression > 70)
{
etat_ecran[x][y].pressionMax = (etat_ecran[x][y].pressionMax > pression)?(etat_ecran[x][y].pressionMax):(pression);
if(etat_ecran[x][y].coefMax < 127)
etat_ecran[x][y].coefMax++;
if(etat_ecran[x][y].checkMax == 0)
{
etat_ecran[x][y].checkMax = 1;
}
}
PA_OutputText(1, 0, 12, "Pression : %03d", pression);
}
for(i = 0; i < 24; i++)
{
for(j = 0; j < 32; j++)
{
PA_OutputText(0, j, i, "%d", etat_ecran[j][i].checkMax);
}
}
PA_WaitForVBL();
}
}

void checkStrongPressure(void)
{//Prend les valeurs de la pression la plus forte
u8 i, j, x, y, pression;
while(Pad.Newpress.B != 1)
{
if(Stylus.Held == 1)
{
x = IPC->touchXpx / 8;
y = IPC->touchYpx / 8;
pression = stylusPressureRough();
if(pression > 30)
{
etat_ecran[x][y].pressionMin = (etat_ecran[x][y].pressionMin <= pression)?(etat_ecran[x][y].pressionMin):(pression);
if(etat_ecran[x][y].coefMin < 127)
etat_ecran[x][y].coefMin++;
if(etat_ecran[x][y].checkMin == 0)
{
etat_ecran[x][y].checkMin = 1;
}
}
PA_OutputText(1, 0, 12, "Pression : %03d", pression);
}
for(i = 0; i < 24; i++)
{
for(j = 0; j < 32; j++)
{
PA_OutputText(0, j, i, "%d", etat_ecran[j][i].checkMin);
}
}
PA_WaitForVBL();
}
}

et "main.c"
#include <PA9.h> // Include pour PA_Lib
#include "stylet.h"
#include <stdlib.h>

int main(void)
{
u8 pression;
u8 x, y, checkPressure = 0;
PA_Init(); // Initialisation de la PA_Lib
PA_InitVBL(); // Initialisation d'un VBL standard
PA_InitText(0, 0);
PA_InitText(1, 0);
PA_SetTextCol(0, 0, 31, 0);
PA_SetTextCol(1, 24, 24, 24);
initialisePressure();//Important pour la calibration !

while(1)
{
if(checkPressure == 0)
{
PA_OutputSimpleText(1, 0, 5, "Test de la pression minimale");
PA_OutputSimpleText(1, 0, 7, "Passez le stylet doucement sur");
PA_OutputSimpleText(1, 0, 8, "tout l'écran jusqu'à n'avoir");
PA_OutputSimpleText(1, 0, 9, " plus que des 1");
PA_OutputSimpleText(1, 0, 11, "('A' pour quitter)");
checkWeakPressure();
PA_OutputSimpleText(1, 0, 5, "Test de la pression maximale");
PA_OutputSimpleText(1, 0, 7, "Passez le stylet fortement sur");
PA_OutputSimpleText(1, 0, 8, "tout l'écran jusqu'à n'avoir");
PA_OutputSimpleText(1, 0, 9, " plus que des 1");
PA_OutputSimpleText(1, 0, 11, "('B' pour quitter)");
checkStrongPressure();
adjustPressure();
PA_OutputSimpleText(1, 0, 5, " ");
PA_OutputSimpleText(1, 0, 7, " ");
PA_OutputSimpleText(1, 0, 8, " ");
PA_OutputSimpleText(1, 0, 9, " ");
PA_OutputSimpleText(1, 0, 11, " ");
checkPressure = 1;
}
PA_OutputSimpleText(1, 0, 5, " ");
PA_OutputSimpleText(1, 0, 7, " ");
PA_OutputSimpleText(1, 0, 8, " ");
PA_OutputSimpleText(1, 0, 9, " ");
PA_OutputSimpleText(1, 0, 11, " ");
PA_OutputText(1, 0, 10, "Pression Init : %03d", Stylus.Pressure);
pression = stylusPressure();

x = IPC->touchXpx / 8;
y = IPC->touchYpx / 8;
PA_OutputText(1, 0, 11, "Pression Opt : %03d", pression);
PA_OutputText(1, 0, 12, "Pression Old : %03d", stylusPressureOld());
PA_OutputText(1, 0, 13, "Min : %03d\t\tMax : %03d", etat_ecran[x][y].pressionMin, etat_ecran[x][y].pressionMax);
PA_OutputText(1, 0, 14, "Coef : Min : %03d\tMax : %03d", etat_ecran[x][y].coefMin, etat_ecran[x][y].coefMax);
PA_OutputText(1, 0, 15, "Taux : %f3", etat_ecran[x][y].taux);
updatePressure();
PA_WaitForVBL();
}

return (EXIT_SUCCESS);
}

Vous pouvez donc lancer la calibration à part avec checkWeakPressure(), checkStrongPressure() puis adjustPressure(), et/ou avoir une calibration constante (qui s'affine lors de l'execution de programme) avec updatePressure().

Y'a un bug d'affichage sur le taux, si quelqu'un sait pourquoi, merci :p

Sinon, la valeur retournée oscille maintenant entre 0 et 40, mais parfois un peu plus j'ai cru remarquer (sans doute à cause des arrondis des floats). Aussi, une forte pression peut maintenant renvoyer 0, donc n'utilisez ces fonctions que pour ce pour quoi elles ont été crées ;)


Enfin, si quelqu'un voit un bug ou quoi que ce soit de bizarre, n'hésitez pas à le signaler :) J'ai peut-être fait ça un peu vite, il y a donc pas mal de chances pour qu'il y ait un ou deux bugs ^^'


P.S : les fonctions necessitent PAlib, mais pas dans leurs fonctionnements même, uniquement pour l'affichage... Donc si quelqu'un passant dans le coin ayant une bonne connaissance du bas niveau pour DS a une soudaine envie de transformer ça pour que ce soit portable, merci d'avance :D C'est que pour l'instant je ne connais que PAlib >_<

Edith me dit de vous dire que vous pouvez réduire le "jumping" du stylet en augmentant la valeur du dénominateur à la ligne "pression += (s8)(pressionNew - pression) / 2;" dans la fonction "stylusPressure". Ça pourra être bien plus confortable mais du coup, la fonction aura plus de mal à renvoyer les valeurs extrèmes.

Arialia
03/02/2008, 21h54
Ben en fait j'ai fais un programme de calibrage mais pour les coordonnées de l'écran tactile ... sert pour l'instant de diagnostic de l'écran tactile vu que je ne sauvegarde pas encore le calibrage dans les paramètres utilisateur de la DS ;)

Saik
03/02/2008, 22h21
OK ^^

D'ailleurs il serait préférable de calibrer les coordonnées de votre écran avant de calibrer la pression, sinon tout est faussé bien évidemment :p (enfin pas de beaucoup car de proche en proche la sensibilité à la pression des points de l'écran sont assez semblables... c'est pas forcément très français ce que je viens de dire mais l'idée est là ^^)

johnko54
04/02/2008, 22h02
et pourquoi ne pas t'allier avec Arialia pour faire un "super-outil de calibrage" :w00t:

+

D'ailleurs il serait préférable de calibrer les coordonnées de votre écran avant de calibrer la pression, sinon tout est faussé

=

CQFD (mais qu'il est fort ce Johnko !!!)