IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Raspberry Pi Discussion :

Front montant et descendant d'un bouton poussoir !


Sujet :

Raspberry Pi

  1. #1
    Expert éminent sénior Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    février 2011
    Messages
    5 509
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : février 2011
    Messages : 5 509
    Points : 16 441
    Points
    16 441
    Par défaut Front montant et descendant d'un bouton poussoir !
    Salut à tous.

    Je sollicite vos compétences pour un problème auquel je m'attaque.
    Voici le datasheet du broadcom 2835, celui de la raspberry pi 3B+.

    Je désire manipuler les GPIO de la Raspberry Pi.
    Autrement dit, je ne désire pas passer par les bibliothèques déjà existante mais le faire moi-même.

    J'arrive à allumer et éteindre une led, ainsi que détecter la pression d'une bouton poussoir.
    Mon problème n'est pas d'ordre électronique ais programmation système des GPIO de la Raspberry pi.

    Pour les accès, je passe par "/dev/map". J'ai programmé cela dans ma fonction "GPIO_Start()".
    Voici les fonctions que j'ai créés et que j'utilise déjà :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    /************************************************************/
    /*     Bibliothèque de Gestion des GPIO avec "/Dev/Mem"     */
    /*                                                          */
    /************************************************************/
    
    #include <errno.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <unistd.h>
    
    #include <sys/mman.h>
    #include <bcm_host.h>
    
    #include "mygpio.h"
    
    
    /*************************************/
    /*                                   */
    /*     Déclaration des Variables     */
    /*                                   */
    /*************************************/
    
    typedef	 unsigned int		uint32_t;
    
    volatile uint32_t		*_gpio;
    
    /*************************************/
    /*                                   */
    /*     Déclaration des Fonctions     */
    /*                                   */
    /*************************************/
    
    void GPIO_Start(void)
    {
    	unsigned int	_base, _size;
    		 int	_fd;
    	void		*_map;
    
    	_base = bcm_host_get_peripheral_address() + 0x200000;
    	_size = bcm_host_get_peripheral_size();
    
    	if ((_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0)
    	{
    		printf("Open '/dev/mem' Error\n");
    		exit(-1);
    	}
    
    	_map = mmap(	NULL,
    			_size,
    			PROT_READ|PROT_WRITE,
    			MAP_SHARED,
    			_fd,
    			_base);
    
    	close(_fd);
    
    	if (_map == MAP_FAILED)
    	{
    		printf("Mmap Error : %8X\n", (int)_map);
    		exit(-1);
    	}
    
    	_gpio = (volatile uint32_t*)_map;
    }
    
    
    void GPIO_In(unsigned short _pin)
    {
    	*(_gpio +  0 + (_pin/10)) &= ~(7<<((_pin%10)*3));
    }
    
    void GPIO_Out(unsigned short _pin)
    {
    	*(_gpio +  0 + (_pin/10)) &= ~(7<<((_pin%10)*3));
    	*(_gpio +  0 + (_pin/10)) |=  (1<<((_pin%10)*3));
    }
    
    void GPIO_On(unsigned short _pin)
    {
    	*(_gpio +  7 + (_pin/32)) = 1<<(_pin%32);
    }
    
    void GPIO_Off(unsigned short _pin)
    {
    	*(_gpio + 10 + (_pin/32)) = 1<<(_pin%32);
    }
    
    unsigned char GPIO_Get(unsigned short _pin)
    {
    	return ((*(_gpio + 13 + (_pin/32)) & (1<<(_pin%32)))?_HIGH_:_LOW_);
    }
    
    void GPIO_Put(unsigned short _pin, unsigned short _val)
    {
    	if (_val > 0)	GPIO_On(_pin);
    	else		GPIO_Off(_pin);
    }
    
    /*---------------------------------------------------------------*/
    /*                                                               */
    /*---------------------------------------------------------------*/
    
    unsigned char GPIO_Event(unsigned short _pin)
    {
    	return	((*(_gpio + 16 + (_pin/32)) &  (1<<(_pin%32)))?_HIGH_:_LOW_);
    }
    
    void GPIO_Rising(unsigned short _pin)
    {
    	*(_gpio + 19 + (_pin/32)) |= 1<<(_pin%32);
    }
    
    void GPIO_Falling(unsigned short _pin)
    {
    	*(_gpio + 22 + (_pin/32)) |= 1<<(_pin%32);
    }
    
    void GPIO_High(unsigned short _pin)
    {
    	*(_gpio + 25 + (_pin/32)) |= 1<<(_pin%32);
    }
    
    void GPIO_Low(unsigned short _pin)
    {
    	*(_gpio + 28 + (_pin/32)) |= 1<<(_pin%32);
    }
    
    void GPIO_Finish(void)
    {
    	unsigned short _pin;
    							/*---------------------------------------*/
    	for (_pin=0; _pin<28; _pin++)	GPIO_In(_pin);	/* GPIO Function Select                  */
    	*(_gpio +  7) &= 0;	*(_gpio +  8) &= 0;	/* GPIO Pin Output  Set                  */
    	*(_gpio + 10) &= 0;	*(_gpio + 11) &= 0;	/* GPIO Pin Output  Clear                */
    	*(_gpio + 16) &= 0;	*(_gpio + 17) &= 0;	/* GPIO Pin Event          Detect Status */
    	*(_gpio + 19) &= 0;	*(_gpio + 20) &= 0;	/* GPIO Pin Rising  Edge   Detect Enable */
    	*(_gpio + 22) &= 0;	*(_gpio + 23) &= 0;	/* GPIO Pin Falling Edge   Detect Enable */
    	*(_gpio + 25) &= 0;	*(_gpio + 26) &= 0;	/* GPIO Pin High           Detect Enable */
    	*(_gpio + 28) &= 0;	*(_gpio + 29) &= 0;	/* GPIO Pin Low            Detect Enable */
    							/*---------------------------------------*/
    	_gpio = MAP_FAILED;
    }
    
    void GPIO_Delay(unsigned int _elapse)
    {
    	usleep(_elapse);
    }
    J'arrive à faire fonctionner les fonctions suivantes :
    --> GPIO_Start()
    --> GPIO_In()
    --> GPIO_Out()
    --> GPIO_Off()
    --> GPIO_On()
    --> GPIO_Get()
    --> GPIO_Put()
    --> GPIO_Finish()

    Par contre, j'ai des difficultés pour ce qui concerne les autres fonctions :
    --> GPIO_Event()
    --> GPIO_Rising()
    --> GPIO_Falling()
    --> GPIO_High()
    --> GPIO_Low()

    Mon but est le suivant.
    Quand je manipule un bouton poussoir, j'ai deux mouvements qui sont détectés, à savoir la pression et le relâchement.
    Cela se traduit par les termes "Rising" et "Falling".
    Voir à ce sujet, la datasheet de la broadcom de la raspberry que j'ai donné ci-dessus.

    D'après ce que j'ai compris, il faut positionner la GPIO dans la section "Rising" ou "Falling".
    Je suppose l'un ou l'autre mais pas les deux (à confirmer).

    Si le bouton poussoir est enfoncé et "Rising" est sélectionné alors un "Event" sera déclenché.
    Si je relâche le bouton poussoir, il ne se passera rien, car je n'ai pas sélectionné "Falling".

    Inversement, si je sélectionne "falling", "Event" se déclenche quand je relâche le bouton poussoir.

    Tout ce que j'ai pu faire jusqu'à présent est de planter non pas le programme, par le système d'exploitation.

    Je ne sais pas si mes six fonctions sont correctes.
    Et j'aimerai savoir comment je dois faire pour manipuler ces fronts montants et descendants sans planter mon OS.

    Merci.
    Cordialement.
    Artemus24.
    @+
    Si vous êtes de mon aide, vous pouvez cliquer sur .
    Mon site : http://www.jcz.fr

  2. #2
    Expert éminent sénior Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    février 2011
    Messages
    5 509
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : février 2011
    Messages : 5 509
    Points : 16 441
    Points
    16 441
    Par défaut
    Salut à tous.

    Je viens de tester en utilisant la bibliothèque "BCM2835" et ça plante aussi.
    Voici l'exemple issu du site de Mike MacCauley, le créateur de cette bibliothèque.
    J'ai mis à jour la version de la bibliothèque installé chez moi. Je suis bien dans la version 1.71.
    Je n'y comprends plus rien.

    Cordialement.
    Artemus24.
    @+
    Si vous êtes de mon aide, vous pouvez cliquer sur .
    Mon site : http://www.jcz.fr

  3. #3
    Expert éminent sénior Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    février 2011
    Messages
    5 509
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : février 2011
    Messages : 5 509
    Points : 16 441
    Points
    16 441
    Par défaut
    Salut à tous.

    Voici le programme qui gère le front montant d'un bouton poussoir :
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    /********************************************/
    /*                                          */
    /*     Gestion des GPIO avec "/Dev/Mem"     */
    /*                                          */
    /*------------------------------------------*/
    /*          Led : GPIO 21 : Pin 40          */
    /*          Key : GPIO 17 : Pin  8          */
    /********************************************/
     
    #include <stdio.h>
    #include <stdlib.h>
     
    #include "mygpio.h"
     
     
    /**************************/
    /*                        */
    /*     Main Procedure     */
    /*                        */
    /**************************/
     
    int main(void)
    {
    	unsigned short	_led=21, _key=17, _val=0, _i;
     
    	printf("\e[1;1H\e[2J");			/* Clear Screen */
    	printf("\t+---------------------------+\n");
    	printf("\t|     Event Rising Edge     |\n");
    	printf("\t+---------------------------+\n\n");
     
    	GPIO_Start();
     
    	GPIO_In(_key);			GPIO_Out(_led);
     
    	GPIO_Edge(_key, 1);		/* 1 : Rising Edge Enable */
     
    	for (_i=0; _i<10; _i++)
    	{
    		if (GPIO_Wait(_key))	GPIO_Put(_led, (_val=1-_val));
    		GPIO_Delay(100000);
     
    		printf("\t------- %d -------\n", _i);
    	}
     
    	GPIO_Finish();
    	printf("That's all Folks !\n");
    	exit(EXIT_SUCCESS);
    }
    Et voici les fonctions de ma bibliothèques :
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    /************************************************************/
    /*                                                          */
    /*     Bibliothèque de Gestion des GPIO avec "/Dev/Mem"     */
    /*                                                          */
    /************************************************************/
     
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
     
    #include <sys/mman.h>
    #include <bcm_host.h>
     
    #include "mygpio.h"
     
     
    /*************************************/
    /*                                   */
    /*     Déclaration des Variables     */
    /*                                   */
    /*************************************/
     
    	 unsigned int		_size;
    volatile unsigned int		*_gpio;
    	 void			*_map;
     
    /************************************/
    /*                                  */
    /*     Déclaration des Tableaux     */
    /*                                  */
    /************************************/
     
    unsigned short _tab[54][7];
     
     
    /*************************************/
    /*                                   */
    /*     Déclaration des Fonctions     */
    /*                                   */
    /*************************************/
     
    void GPIO_Delay(unsigned int _elapse)
    {
    	usleep(_elapse);
    }
     
    void GPIO_Start(void)
    {
    	unsigned int	_base, _fd;
     
    	_base = bcm_host_get_peripheral_address() + 0x200000;
    //	_size = bcm_host_get_peripheral_size();
     
    	_size = 256;
     
    	if ((_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0)
    	{
    		printf("Open '/dev/mem' Error\n");
    		exit(-1);
    	}
     
    	_map = mmap(	NULL,
    			_size,
    			PROT_READ|PROT_WRITE,
    			MAP_SHARED,
    			_fd,
    			_base);
     
    	close(_fd);
     
    	if (_map == MAP_FAILED)
    	{
    		printf("Mmap Error : %8X\n", (int)_map);
    		exit(-1);
    	}
     
    	_gpio = (volatile unsigned int *)_map;
     
    /*--------------------------------*/
    /*     Initialisation Tableau     */
    /*--------------------------------*/
     
    	memset(&_tab, 0, sizeof(_tab));
    }
     
    /*---------------------------------------------------------------*/
    /*                                                               */
    /*---------------------------------------------------------------*/
     
    void GPIO_In(unsigned short _pin)
    {
    	*(_gpio +  0 + (_pin/10)) &= ~(7<<((_pin%10)*3));
    	GPIO_Delay(10000);
     
    	_tab[_pin][0] = 1;
    }
     
    void GPIO_Out(unsigned short _pin)
    {
    	*(_gpio +  0 + (_pin/10)) &= ~(7<<((_pin%10)*3));
    	GPIO_Delay(10000);
    	*(_gpio +  0 + (_pin/10)) |=  (1<<((_pin%10)*3));
    	GPIO_Delay(10000);
     
    	_tab[_pin][0] = 2;
    }
     
    void GPIO_On(unsigned short _pin)
    {
    	*(_gpio +  7 + (_pin/32)) &= (1<<(_pin%32));
    	GPIO_Delay(10000);
    }
     
    void GPIO_Off(unsigned short _pin)
    {
    	*(_gpio + 10 + (_pin/32)) &= (1<<(_pin%32));
    	GPIO_Delay(10000);
    }
     
    unsigned char GPIO_Get(unsigned short _pin)
    {
    	return ((*(_gpio + 13 + (_pin/32)) & (1<<(_pin%32)))?1:0);
    }
     
    void GPIO_Put(unsigned short _pin, unsigned short _val)
    {
    	if (_val > 0)	GPIO_On(_pin);
    	else		GPIO_Off(_pin);
    }
     
    /*---------------------------------------------------------------*/
    /*                                                               */
    /*---------------------------------------------------------------*/
     
    unsigned char GPIO_Wait(unsigned short _pin)
    {
    	do {
    		GPIO_Delay(100);
    	} while (!((*(_gpio + 16 + (_pin/32)) & (1<<(_pin%32)))?1:0));
     
    	GPIO_Delay(100);
    	*(_gpio + 16 + (_pin/32)) |= 1<<(_pin%32);
    	GPIO_Delay(100);
     
    	return 1;
    }
     
    void GPIO_Edge(unsigned short _pin, unsigned short _mode)
    {
    	switch (_mode)
    	{
    		case 1:		/* Rising  Edge Detect Enable */
    				*(_gpio + 19 + (_pin/32)) |= (1<<(_pin%32));
    				break;
     
    		case 2:		/* Falling Edge Detect Enable */
    				*(_gpio + 22 + (_pin/32)) |= (1<<(_pin%32));
    				break;
     
    		case 3:		/* High Detect Enable */
    				*(_gpio + 25 + (_pin/32)) |= (1<<(_pin%32));
    				break;
     
    		case 4:		/* Low  Detect Enable */
    				*(_gpio + 28 + (_pin/32)) |= (1<<(_pin%32));
    				break;
     
    		case 5:		/* Asynchronous Rising  Edge Detect Enable */
    				*(_gpio + 31 + (_pin/32)) |= (1<<(_pin%32));
    				break;
     
    		case 6:		/* Asynchronous Falling Edge Detect Enable */
    				*(_gpio + 34 + (_pin/32)) |= (1<<(_pin%32));
    				break;
     
    		default:	/* Nothing */
    				printf("Edge : Mauvaise sélection !\n");
    				exit(-1);
    				break;
    	}
     
    	GPIO_Delay(10000);
    	_tab[_pin][_mode] = 1;
    }
     
    void GPIO_Pull(unsigned short _pin, unsigned int _mode)
    {
    	*(_gpio + 37) &= _mode;
    	GPIO_Delay(10);
     
    	*(_gpio + 38 + (_pin/32)) |= (1<<(_pin%32));
    	GPIO_Delay(10);
     
    	*(_gpio + 37)             &= 0;
    	*(_gpio + 38 + (_pin/32)) &= 0;
    }
     
    void GPIO_Finish(void)
    {
    	unsigned short	_pin;
     
    	for (_pin=0; _pin<54; _pin++)
    	{
    		if (_tab[_pin][0])			/* GPIO Pin in & out */
    		{
    			if(_tab[_pin][0] == 2)	GPIO_Off(_pin);
    			GPIO_In(_pin);
    		}
     
    		if (_tab[_pin][1])			/* Rising  Edge Detect Disable */
    		{
    			*(_gpio + 19 + (_pin/32)) &= ~(1<<(_pin%32));
    			GPIO_Delay(10000);
    		}
     
    		if (_tab[_pin][2])			/* Falling Edge Detect Disable */
    		{
    			*(_gpio + 22 + (_pin/32)) &= ~(1<<(_pin%32));
    			GPIO_Delay(10000);
    		}
     
    		if (_tab[_pin][3])			/* High Detect Disable */
    		{
    			*(_gpio + 25 + (_pin/32)) &= ~(1<<(_pin%32));
    			GPIO_Delay(10000);
    		}
     
    		if (_tab[_pin][4])			/* Low  Detect Disable */
    		{
    			*(_gpio + 28 + (_pin/32)) &= ~(1<<(_pin%32));
    			GPIO_Delay(10000);
    		}
     
    		if (_tab[_pin][5])			/* Asynchronous Rising  Edge Detect Enable */
    		{
    			*(_gpio + 31 + (_pin/32)) &= ~(1<<(_pin%32));
    			GPIO_Delay(10000);
    		}
     
    		if (_tab[_pin][6])			/* Asynchronous Falling Edge Detect Enable */
    		{
    			*(_gpio + 34 + (_pin/32)) &= ~(1<<(_pin%32));
    			GPIO_Delay(10000);
    		}
    	}
     
    	if (munmap(_map, _size) != 0)
    	{
    		printf("Munmap Error : %8X\n", (int)_map);
    		exit(-1);
    	}
     
    	_map  =                          MAP_FAILED;
    	_gpio = (volatile unsigned int *)MAP_FAILED;
    }
    Je lance une première fois le programme.
    Il s'exécute et va jusqu'au bout.
    Je relance le programme, s'exécute jusqu'au bout.
    Comment je le sais ?
    Il affiche "That's all Folks !", mais après l'OS plante.
    Je perds la connexion et je suis obligé de redémarrer la raspberry pi 3B+.

    Je lance le programme suivant :
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    /*****************************************************/
    /*                                                   */
    /*          Gestion des GPIO avec "/dev/mem"         */
    /*                                                   */
    /*     Faire Clignoter alternativement deux Leds     */
    /*                                                   */
    /*---------------------------------------------------*/
    /*      GPIO 21 : Pin 40       GPIO 20 : Pin 38      */
    /*****************************************************/
     
    #include <stdio.h>
    #include <stdlib.h>
     
    #include "mygpio.h"
     
     
    /**************************/
    /*                        */
    /*     Main Procedure     */
    /*                        */
    /**************************/
     
    int main(void)
    {
    	unsigned short	_led1=21, _led2=20;
    	unsigned short	_val1=0,  _val2=1;
     
    	printf("\e[1;1H\e[2J");                 /* Clear Screen */
    	printf("+--------------------+\n"),
    	printf("|     Blink Leds     |\n");
    	printf("+--------------------+\n\n");
     
    	GPIO_Start();
     
    	GPIO_Out(_led1);
    	GPIO_Out(_led2);
     
    	for (int i = 0; i < 50; i++)
    	{
    		GPIO_Put(_led1, (_val1=1-_val1));
    		GPIO_Put(_led2, (_val2=1-_val2));
     
    		GPIO_Delay(100000);
    	}
     
    	GPIO_Finish();
    	exit(EXIT_SUCCESS);
    }
    Ce programme qui fait clignoter deux leds, ne plante pas l'OS.
    J'ai beau chercher la cause, je ne vois pas ce que j'ai mal fait !

    Si une âme charitable pourrait me dire la cause de ce plantage, je lui en serai reconnaissant.

    Une ressource mal libérée ? Un oubli de ma part dans le traitement des GPIO ?

    Cordialement.
    Artemus24.
    @+
    Si vous êtes de mon aide, vous pouvez cliquer sur .
    Mon site : http://www.jcz.fr

  4. #4
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    avril 2002
    Messages
    3 097
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : avril 2002
    Messages : 3 097
    Points : 11 203
    Points
    11 203
    Par défaut
    Bonjour,
    Ce pourrait être un watchdog qui déclenche un reset, croyant voir un plantage (programme figé) mais je ne suis pas sur.

    Essayes de retirer le GPIO_Delay(100000); dans ton programme principal pour voir.
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

  5. #5
    Expert éminent sénior Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    février 2011
    Messages
    5 509
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : février 2011
    Messages : 5 509
    Points : 16 441
    Points
    16 441
    Par défaut
    Salut Vincent Petit.

    Je désespérais d'avoir une réponse. Merci pour ton intervention.

    Citation Envoyé par Vincent Petit
    Ce pourrait être un watchdog qui déclenche un reset, croyant voir un plantage (programme figé) mais je ne suis pas sur.
    Je ne sais pas ce que c'est un Watchdog.

    C'est la même bibliothèque de fonctions GPIO que j'utilise, avec d'autres programmes, comme Blink, Switch et Interrupt.
    Blink et Switch ne posent aucun problème.

    Par contre, Interrupt fonctionne jusqu'au bout (test avec des printf) mais je n'ai plus aucun accès.
    (écran figé et au bout de quelques secondes, je perds la connexion wifi puisque je suis en headless. Impossible de se reconnecter.)

    Plus rarement, il plante sur le "wait()".

    Citation Envoyé par Vincent Petit
    Essayes de retirer le GPIO_Delay(100000); dans ton programme principal pour voir.
    Je l'utilise partout. Ce n'est pas lui qui est en cause.
    Si je le retire, mon programme ne fonctionne plus.

    Je pense que l'origine de mon problème se trouve soit dans "edge()", "wait() ou "finish()".

    Mon idée est que j'ai mal compris comment utiliser le "EVENT".
    Il faut l'activer (edge()), puis le remettre à zéro (wait()) juqu'à la prochaine pression du BP.

    Cordialement.
    Artemus24.
    @+
    Si vous êtes de mon aide, vous pouvez cliquer sur .
    Mon site : http://www.jcz.fr

  6. #6
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    avril 2002
    Messages
    3 097
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : avril 2002
    Messages : 3 097
    Points : 11 203
    Points
    11 203
    Par défaut
    Salut,
    Ce que je veux dire c'est qu'il pourrait y avoir un problème avec le task scheduler de l'OS qui pense que ton programme est planté alors que tu ne fais qu'attendre.

    C'est ce que tu as écrit ci dessous qui me fait pensé à ça.
    Citation Envoyé par Artemus24 Voir le message
    Par contre, Interrupt fonctionne jusqu'au bout (test avec des printf) mais je n'ai plus aucun accès.
    (écran figé et au bout de quelques secondes, je perds la connexion wifi puisque je suis en headless. Impossible de se reconnecter.)
    [...]
    Plus rarement, il plante sur le "wait()".

    Citation Envoyé par Artemus24 Voir le message
    Je l'utilise partout. Ce n'est pas lui qui est en cause.
    Si je le retire, mon programme ne fonctionne plus.
    D'accord mais il n'y a pas de plantage à la fin ?

    Citation Envoyé par Artemus24 Voir le message
    Je pense que l'origine de mon problème se trouve soit dans "edge()", "wait() ou "finish()".

    Mon idée est que j'ai mal compris comment utiliser le "EVENT".
    Il faut l'activer (edge()), puis le remettre à zéro (wait()) juqu'à la prochaine pression du BP.
    Je crois aussi qu'il y a un problème peut être avec le wait(), je ne peux pas t'aider d'avantage car là ça me dépasse mais on dirait vraiment un problème avec l'OS qui pense ton programme planté.




    *watchdog : tous les microcontrôleurs sont équipés d'un watchdog, c'est un petit compteur hardware (comme les timers mais en moins évolués) que tu règles pour qu'il déclenche une interruption, voir carrément un reset du micro, au bout d'un certain temps. Le logiciel doit cycliquement rafraichir le watchdog pour remettre à 0 le compteur avant le déclenchement de l'interruption ou du reset. Il faut avoir un maîtrise temporelle du programme pour faire ça mais si jamais ce dernier plante alors le compteur dans le watchdog arrive à la valeur préréglée et c'est le reset ou le déclenchement d'une interruption (le compteur programme saute dans une zone mémoire correspondant au programme d'interruption même s'il était planté au préalable). Les task schedulers, pour ceux que j'ai vu, sont bâtis autour des Timers mais le Wathcdog peut jouer un rôle en arrière plan
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

  7. #7
    Expert éminent sénior Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    février 2011
    Messages
    5 509
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : février 2011
    Messages : 5 509
    Points : 16 441
    Points
    16 441
    Par défaut
    Salut Vincent Petit.

    Citation Envoyé par Vincent Petit
    D'accord mais il n'y a pas de plantage à la fin ?
    Ce que j'ai constaté, le programme "Interrupt" (c'est son nom) fonctionne normalement jusqu'au bout du traitement.
    J'ai mis un "printf()" juste avant la sortie final du traitement, et celui-ci s'affiche !

    A vrai dire, je ne sais pas si l'OS plante ou pas à ce moment, car il ne me redonne pas la main à la fin du programme.
    Je devrais avoir à nouveau le prompt qui s'affiche en ligne de commande, ce qui n'est pas le cas.
    Et comme dit dans mes messages précédents, je perds la connexion au niveau de putty et impossible de me reconnecter.

    Citation Envoyé par Vincent Petit
    Ce que je veux dire c'est qu'il pourrait y avoir un problème avec le task scheduler de l'OS qui pense que ton programme est planté alors que tu ne fais qu'attendre.
    C'est la conséquence de la sortie de mon programme "Interrupt".

    Citation Envoyé par Vincent Petit
    Je crois aussi qu'il y a un problème peut être avec le wait(), je ne peux pas t'aider d'avantage car là ça me dépasse mais on dirait vraiment un problème avec l'OS qui pense ton programme planté.
    J'ai fait mes tests avec la Raspberry Pi 3B+.
    Je me suis inspiré de la bibliothèque "BCM2835" de Mike McCauley et bien sûr de la documentation sur cette raspberry.
    A vrai dire, elle est succincte et ne dit pas comment je dois gérer cela dans un programme.
    Elle dit simplement comment ça fonctionne.

    J'ai pourtant mis dans le sous-programme "Finish()", la remise à zéro de ce que j'ai activé précédemment.
    Comme toi, cela me dépasse mais il faudra bien que je trouve une solution.

    Merci pour tes explications sur le watchdog.

    Cordialement.
    Artemus24.
    @+
    Si vous êtes de mon aide, vous pouvez cliquer sur .
    Mon site : http://www.jcz.fr

  8. #8
    Expert éminent sénior Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    février 2011
    Messages
    5 509
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : février 2011
    Messages : 5 509
    Points : 16 441
    Points
    16 441
    Par défaut
    Salut Vincent Petit.

    Le BP que j'utilise est en pull-up, sans gestion électronique de l'anti-rebond.
    Est-il possible que cela pose un problème dans la dernière itération de mon programme ?
    Genre un rebond apparait après le "wait()" ?

    Cordialement.
    Artemus24.
    @+
    Si vous êtes de mon aide, vous pouvez cliquer sur .
    Mon site : http://www.jcz.fr

  9. #9
    Membre du Club
    Homme Profil pro
    retraité
    Inscrit en
    avril 2019
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 73
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : retraité

    Informations forums :
    Inscription : avril 2019
    Messages : 37
    Points : 40
    Points
    40
    Par défaut
    Bonjour Artemus.
    Quelque chose m'étonne dans la partie bibliothèque lors du mapping de la mémoire.
    la récupération de la taille nécessaire est mise en commentaire et tu forces la taille par _size = 256;
    Cette valeur me parait bien faible et je pense qu'il y a peut être par la suite un écrasement mémoire.
    Peux tu faire un test avec une plus grande valeur ?
    Bon courage.

    Complément : sur le site https://www.pieter-jan.com/node/15 on trouve la valeur #define BLOCK_SIZE (4*1024)

  10. #10
    Expert éminent sénior Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    février 2011
    Messages
    5 509
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : février 2011
    Messages : 5 509
    Points : 16 441
    Points
    16 441
    Par défaut
    Salut Vincent Boulou.

    a) Je ne m'intéresse qu'aux GPIO (General Purpose I/O).
    L'adresse de base pour la RPi 3B+ est : 0x7E200000.
    Le déplacement va de 0x00 jusqu'à 0xB0.

    J'ai majoré par un déplacement de 0x0100, soit 256 en décimal.
    Cela englobe bien la plage des GPIO. Voire pour cela la documentation.

    J'ai fait le test, en mettant la valeur retournée par "bcm_host_get_peripheral_size();".
    La valeur du _SIZE est : "0x01000000".

    Le programme s'arrête de la même façon que précédemment.

    b) si j'avais un problème avec ce _size, je n'aurai pas accès aux GPIO et j'aurai un plantage dès le début du programme, ce qui n'est pas mon cas.

    c) Pour ce qui est du pull-up, je ne le gère pas dans le programme car il est géré électroniquement dans le montage du bouton poussoir.

    d) Il y a une chose que je n'ai pas géré, puisque je ne sais pas trop à quoi cela sert.
    Dans la bibliothèque "BCM2835", il est question de "Hysteresis".

    Voici les initialisations au début de la bibliothèque :
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #ifdef BCM2835_TEST
    // this is a simple test program that prints out what it will do rather than 
    // actually doing it
    int main(int argc, char **argv)
    {
    	// Be non-destructive
    	bcm2835_set_debug(1);
    
    	if (!bcm2835_init())
    	return 1;
    
    	// Configure some GPIO pins fo some testing
    	// Set RPI pin P1-11 to be an output
    	bcm2835_gpio_fsel(RPI_GPIO_P1_11, BCM2835_GPIO_FSEL_OUTP);
    	// Set RPI pin P1-15 to be an input
    	bcm2835_gpio_fsel(RPI_GPIO_P1_15, BCM2835_GPIO_FSEL_INPT);
    	//  with a pullup
    	bcm2835_gpio_set_pud(RPI_GPIO_P1_15, BCM2835_GPIO_PUD_UP);
    	// And a low detect enable
    	bcm2835_gpio_len(RPI_GPIO_P1_15);
    	// and input hysteresis disabled on GPIOs 0 to 27
    	bcm2835_gpio_set_pad(BCM2835_PAD_GROUP_GPIO_0_27, BCM2835_PAD_SLEW_RATE_UNLIMITED|BCM2835_PAD_DRIVE_8mA);
    Voici la fonction :
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    // Set GPIO pad behaviour for groups of GPIOs
    // powerup value for al pads is
    // BCM2835_PAD_SLEW_RATE_UNLIMITED | BCM2835_PAD_HYSTERESIS_ENABLED | BCM2835_PAD_DRIVE_8mA
    void bcm2835_gpio_set_pad(uint8_t group, uint32_t control)
    {
    	volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group*2;
    	bcm2835_peri_write(paddr, control | BCM2835_PAD_PASSWRD);
    }
    Les constantes sont :
    --> BCM2835_PAD_GROUP_GPIO_0_27 : 0
    --> BCM2835_PAD_SLEW_RATE_UNLIMITED : 0x10
    --> BCM2835_PAD_DRIVE_8mA : 0x03

    --> BCM2835_PADS_GPIO_0_27 : 0x002C

    L'adresse de base de cette section est 0x7E100000.
    Elle correspond à ceci :
    --> BCM2835_GPIO_PADS : 0x100000

    Cela va se traduire chez moi par un appel à "nmap()" comme je l'ai fait avec les GPIO.
    Je remplace le 0x200000 par 0x100000 et je stocke le pointeur dans _pads.
    Je traduis ce hysteresis par ceci :
    Merci de ta participation !


    Cordialement.
    Artemus24.
    @+
    Si vous êtes de mon aide, vous pouvez cliquer sur .
    Mon site : http://www.jcz.fr

  11. #11
    Expert éminent sénior Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    février 2011
    Messages
    5 509
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : février 2011
    Messages : 5 509
    Points : 16 441
    Points
    16 441
    Par défaut
    Salut à tous.

    Après quelques recherches sur le net, je suis tombé sur cette discussion fort intéressante :
    --> Groups.google.com.

    Le même problème que je rencontre avec la gestion des GPIO sur le fronts montants (Rising Edge).
    Mon programme n'est pas en cause, encore que, je devrais l'améliorer.

    La solution est à mettre dans le fichier "/boot/config.txt" :
    Il s'agit d'une interruption matérielle.

    Si quelqu'un peut expliquer la cause de mon problème, je lui en serait reconnaissant.

    Cordialement.
    Artemus24.
    @+
    Si vous êtes de mon aide, vous pouvez cliquer sur .
    Mon site : http://www.jcz.fr

  12. #12
    Modérateur

    Avatar de Vincent PETIT
    Homme Profil pro
    Consultant en Systèmes Embarqués
    Inscrit en
    avril 2002
    Messages
    3 097
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Consultant en Systèmes Embarqués
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : avril 2002
    Messages : 3 097
    Points : 11 203
    Points
    11 203
    Par défaut
    Salut,
    Citation Envoyé par Artemus24 Voir le message
    .Est-il possible que cela pose un problème dans la dernière itération de mon programme ?
    Genre un rebond apparait après le "wait()" ?
    Ah si, si, si, dans les microcontrôleurs on doit parfois gérer ce cas de figure au risque de rerentrer plusieurs fois dans le programme d'interruption.

    Dans les microcontrôleurs on a des "flags" materiels. Ce sont des registres comme les autres à ceci prés qu'ils s'activent directement par le matériel. Par exemple quand le registre Timer déborde (le compteur a fait 1 tour) on a un flag qui passe à 1 :

    1- Si on a attaché une interruption à se flag alors il est automatiquement effacé (repasse à 0) et le compteur programme saute à l'adresse du programme d'interruption pour l'exécuter.

    2- Si on n'a pas attaché d'interruption alors charge au logiciel d'aller vérifier le flag cycliquement pour exécuter l'action liée au débordement du Timer mais charge aussi au logiciel d'effacer le flag à la main (souvent on écrit un 0 dessus).

    Dans le cas 1 il peut y avoir un piège par exemple à cause d'un rebond : une broche passe à 1, le flag passe à 1, une interruption est attachée, le flag passe à 0, le programme d'interrupteur se lance.... et là pas de bol car un rebond s'est manifesté pendant qu'on était dans le programme d'interruption, donc le flag est repassé à 1 et hop on resaute dans l'interruption aussitôt sortie de celle-ci.

    (L'astuce consiste à ajouter une ligne à la fin du programme d'interruption qui met manuellement le flag à 0, bien entendu il faut que le programme d'interruption dure plus longtemps que les rebonds sinon ça ne règle pas le problème)
    La science ne nous apprend rien : c'est l'expérience qui nous apprend quelque chose.
    Richard Feynman

  13. #13
    Expert éminent sénior Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    février 2011
    Messages
    5 509
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : février 2011
    Messages : 5 509
    Points : 16 441
    Points
    16 441
    Par défaut
    Salut Vincent Petit.

    J'ai émis cette hypothèse car je ne comprenais pas la nature du problème que je rencontrais.
    Depuis que j'ai trouvé la solution, à savoir mettre "dtoverlay=gpio-no-irq" dans le fichier "/boot/config.txt", mon programme fonctionne correctement.
    Je ne dis pas "bien" car j'ai quelques problèmes de compréhensions sur le fonctionnement de ma Raspberry Pi.

    Bien que cette solution réponde à mon attente, elle me pose des problèmes puisque ma raspberry ne gère plus les interruptions matériels.
    C'est pourquoi, je cherche comment psotionner les GPIO avec IRQ et sans IRQ.
    J'ai fait une tentative, mais cela n'a rien donné.
    Dans la documentation, il s'agit du chapitre "7. Interrupts", les interruptions.
    Entre autre, j'ai positionné les registres suivants :
    --> 0x21C Disable IRQs 1
    --> 0x220 Disable IRQs 2
    --> 0x224 Disable Basic IRQs
    pour désactiver les IRQ, mais cela n'a pas fonctionné.

    Pour revenir à ton message, il y a dexu cas, les évènements et les interruptions.
    Je gère actuellement les évènements dans mon programme en faisant la distinction des diff&rents fronts (Edge) que je peux avoir.

    Comme la Raspberry possède plusieurs coeurs, au lieu des évènements, j'aimerai gérer cela en tant qu'interruption.
    J'ai pas encore essayé. Je préfère le débranchement à une fonction (handler) qui va gérer le BP quand il est sollicité.
    Je peux mettre un handler selon le type de front que je dois gérer.

    Cordialement.
    Artemus24.
    @+
    Si vous êtes de mon aide, vous pouvez cliquer sur .
    Mon site : http://www.jcz.fr

  14. #14
    Expert éminent sénior Avatar de Artemus24
    Homme Profil pro
    Agent secret au service du président Ulysses S. Grant !
    Inscrit en
    février 2011
    Messages
    5 509
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Agent secret au service du président Ulysses S. Grant !
    Secteur : Finance

    Informations forums :
    Inscription : février 2011
    Messages : 5 509
    Points : 16 441
    Points
    16 441
    Par défaut
    Salut à tous.

    J'ai pu me constituer deux boutons poussoirs (Pull-up & Pull-down) anti-rebonds et les tester.
    Pour cela, j'avais besoin de résistances à 100K ohms, de diodes, de condensateurs à 220 nano farad et d'une bascule de Schmitt.
    J'ai suivi les indications que l'on m'avait communiqué dans un autre sujet consacré à mes questions sur l'électronique.

    Le programme que j'ai mis au point, teste les fronts montants et descendants.
    S'il y a des rebonds, je peux obtenir deux fronts montants (ou plus) ou deux fronts descendants (ou plus) consécutifs.
    Mon test confirme que je ne ne suis pas dans ce cas de figure. La gestion des anti-rebonds fonctionne correctement.
    J'ai d'ailleurs supprimé le "GPIO_Delay()" de la boucle général qui ne sert plus.

    Quand à la bascule de Schmitt (ou trigger), elle me sert à deux choses :
    --> à déclencher le fronts montants ou descendants sur un seuil.
    --> à inverser le résultat du pull-up. (Je désire obtenir "0" pour éteint et "1" pour allumé).

    Conclusion.
    Le test sur les GPIO est terminé. Et par voie de conséquence, l'écriture de ma bibliothèque aussi.
    Il y encore quelques mises au point, mais dans l'ensemble, ca fonctionne !

    Cordialement.
    Artemus24.
    @+
    Si vous êtes de mon aide, vous pouvez cliquer sur .
    Mon site : http://www.jcz.fr

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Détection front montant /descendant sur booléen dans une table
    Par bndexsen dans le forum Développement
    Réponses: 3
    Dernier message: 06/03/2017, 13h46
  2. Bloc compteur des fronts montants ou descendants
    Par Noura2013 dans le forum Simulink
    Réponses: 4
    Dernier message: 18/07/2013, 18h55
  3. Capture de fronts montants avec un PIC16F876
    Par sylvain42 dans le forum C
    Réponses: 3
    Dernier message: 12/06/2006, 14h48
  4. [FW2.0][VB.net] évenement front montant
    Par grand_prophete dans le forum Windows Forms
    Réponses: 9
    Dernier message: 09/05/2006, 17h25
  5. Source LaTeX : faire un bouton poussoir dans le PDF
    Par pierrot_latex dans le forum Editeurs / Outils
    Réponses: 3
    Dernier message: 24/02/2006, 14h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo