Professional Documents
Culture Documents
OBJETIVOS
MATERIAL REQUERIDO.
Un pulsador.
LAS INTERRUPCIONES
Nunca he sabido muy bien que tiene esto de las interrupciones, que hacer temblar a programadores curtidos como si
fueran unos novatos.
Recuerdo que en una época en que nos dedicábamos a los microprocesadores (cuando los dinosaurios dominaban la
tierra) y comenzábamos a jugar con las interrupciones, había un porcentaje de técnicos, ingenieros electrónicos o
informáticos, que aun comprendiendo la idea de las interrupciones, parecía que su cerebro no podía abarcarlas y
sencillamente las ignoraban elegantemente.
Mentarle las interrupciones a muchos Arduineros ya fogueados, en muchos casos supone que recordarán
inmediatamente la necesidad de salir urgentemente a hacer algo. Nunca he sabido porque pasa esto, pero vamos a
intentar ponerlo remedio inmediatamente.
A un nivel básico, una interrupción es una señal que interrumpe la actividad normal de nuestro microprocesador y salta a
atenderla. Hay tres eventos que pueden disparar una interrupción:
Un evento hardware, previamente definido.
Un evento programado, o Timer
Una llamada por software.
Cuando un evento dispara una interrupción, la ejecución normal del micro se suspende (ordenadamente para poder
volver) y salta a ejecutar una función especial que llamamos Interrupt Service Handler o ISH (Servicio de gestión de
interrupción).
Cuando el ISH finaliza, el procesador vuelve tranquilamente al punto donde lo dejó y sigue con lo que estaba como si no
hubiese pasado nada.
El concepto de interrupción nace de la necesidad imperiosa de reaccionar de forma
inmediata en respuesta a un acontecimiento electrónico fulgurante, que no admite demora.
Bien sea por la urgencia del suceso o porque algo se podría perder de forma irrecuperable
sino reaccionamos con suficiente presteza.
Pero ¿Qué hay tan urgente que no pueda esperar? ¿Es que nuestros Duinos no son lo suficientemente rápidos para ver
cada poco si hay una señal de alarma? ¿Por qué complicarnos la vida con una cosa tan extravagante?
La respuesta como siempre es… depende. Nuestro Arduino puede estar liado y solo leerá la señal de un pin de tanto en
tanto. Y si la señal que aparece se desvanece antes de que hayamos ido a ver, ni siquiera lo sabremos, porque aunque
los Duinos son rápidos una señal electrónica lo es varios millones de veces más. Este es otro motivo por el que usar
En la jerga técnica, a pasar de vez en cuando a ver como está el asunto, se le llama Polling.
Por otro lado las interrupciones nos ofrecen una ventaja enorme a la hora de organizar nuestro programa. Se define la
función que se ejecutará al recibir una interrupción dada y se ejecuta limpiamente cuando ocurre, no hay que comprobar
si se da o no una situación.
Simplemente te olvidas y se ejecutará única y exclusivamente cuando se alce la interrupción. No me digáis que no es
elegante (SI, es una obsesión).
En realidad, nosotros funcionamos por interrupciones habitualmente, en respuesta a sucesos no previstos que nos sacan
de la rutina habitual.
Imagínate que estás viendo tu serie favorita en la tele y estas esperando a tu colega, amigo o novia.
Hay dos maneras de abrirle la puerta. Una es pasar a ver si ha llegada cada, digamos dos minutos, para ver si esta con
cara de pánfilo/pánfila en la puerta esperando a que le abramos.
La otra es establecer una interrupción, para eso están los timbres. Cuando tu compi llega, pulsa el timbre. Tu paras tu
capitulo tranquilamente, dejas el refresco en la mesa y vas a abrirle.
Cuando vuelves con él, reanudas tu peli y recoges el refresco. ¿Qué tienen de raro las interrupciones? ¿Qué me decís
del teléfono o de los Whatsapp? Es la misma idea. Y lo mismo pasa con tu Arduino.
¿Por qué voy a renunciar a las interrupciones y dedicarme a pasar por la puerta cada poco? Es absurdo. Las
interrupciones no tienen nada de extraño ni de incognoscible. Dedícale un poco de tiempo y te encontrarás una
herramienta magnifica que te resolverá limpiamente más de un problema.
TIPOS DE INTERRUPCIONES
Un evento hardware,
Un evento programado, o Timer
Una llamada por software.
Nos encontramos con que Arduino no soporta las interrupciones por software y punto.
¿Y entonces porque hemos hablado de ellas? Pues, porque otros entornos de programación las aceptan y no será raro
que en el futuro Arduino también.
Los eventos programados o Timers, son muy interesantes y tendrán una sesión monográfica propia en un futuro próximo.
Pero por ahora vamos a meternos con las interrupciones disparadas por hardware.
Estas interrupciones hardware, se diseñaron por la necesidad de reaccionar a suficiente velocidad en tiempos
inimaginablemente cortos a los que la electrónica trabaja habitualmente y a los que ni siquiera el software era capaz de
reaccionar.
La idea que debéis que tener en mente es que vamos a definir una función que se ejecutará de forma asíncrona, sin
planificación, cuando se ocurra un cierto suceso electrónico.
Lo
Un pin de Arduino que recibirá la señal de disparo
Una condición de disparo
Una función que se ejecutará, cuando se dispare la interrupción (Llamada call back
function).
primero es un pin de Arduino donde conectaremos el “timbre” de llamada. Dependiendo del modelo Arduino que
tengamos, tendremos varias posibilidades:
MODELO
INT 0 INT 1 INT 2 INT 3 INT 4 INT 5
ARDUINO
MEGA 2 3 21 20 19 18
DUE Todos los pines del DUE pueden usarse para interrupciones.
Leonardo 3 2 0 1 7
Esto quiere decir que el Arduino UNO puede definir dos interrupciones hardware llamadas 0 y 1, conectadas a los pines
2 y 3 (Para que no sea fácil).
El Mega como es habitual en él, acepta nada menos que 6 interrupciones diferentes. Y el DUE, muy sobrado, exhibe su
poderío.
Si
LOW, La interrupción se dispara cuando el pin es LOW.
CHANGE, Se dispara cuando pase de HIGH a LOW o viceversa.
RISING, Dispara en el flanco de subida (Cuando pasa de LOW a HIGH).
FALLING, Dispara en el flanco de bajada (Cuando pasa de HIGH a LOW).
Y una solo para el DUE: HIGH se dispara cuando el pin esta HIGH.
nuestra call back function se llama Funcion1 (), para activar la interrupción usaremos:
Donde Interrupt es el número de la interrupción, ISR será Funcion1 y mode es una de las condiciones que hemos visto
arriba. Así en un Arduino UNO podría ser:
Suponiendo que hemos enchufado la señal de interrupción al pin 2 de Arduino. Vamos a ver algún ejemplo de
interrupciones.
ESQUEMA DE CONEXIONES
Es una especie de costumbre en Arduino, usar un pulsador para ilustrar el concepto de Interrupción, así que nos
plegáramos a ello. Vamos a utilizar un típico circuito para leer un pulsador con un pullup.
void setup()
{ pinMode(2, INPUT);
Serial.begin(9600);
}
void loop()
{ bool p = digitalRead(2);
Serial.println(p);
}
El resultado seria normalmente 1, por el pull up y la lectura bajaría a 0, al pulsar el botón. Nada nuevo en esto.
Pero vamos a reescribir el programa para establecer una interrupción en el pin 2 (Interrupción 0) .El programa quedara
más o menos así:
int contador = 0;
int n = contador ;
void setup()
{ Serial.begin(9600);
attachInterrupt( 0, ServicioBoton, FALLING);
}
void loop()
{
if (n != contador)
{ Serial.println(contador);
n = contador ;
}
}
void ServicioBoton()
{ contador++ ;
}
En primer lugar fijaros que hemos eliminado la definición de entrada del pin 2, porque no vamos a usarlo como input
estrictamente. Con definir la interrupción es suficiente.
En segundo lugar usamos attachInterrupt() pasándole como parámetros la interrupción 0, que es el pin2 de Arduino
UNO. SI fuese la Interrupción 1, la conectaríamos al pin 3 (Anda que…)
Le pasamos el nombre de la función CallBack ServicioBoton, que es de lo más sencilla. Un variable global contador,
guarda el número de pulsaciones. Lo único que hace la función de servicio es aumentar contador en uno cada vez que
se pulsa y volver.
Y por último el trigger es FALLING porque el estado es normalmente HIGH y baja a LOW al pulsar el botón, utilizaremos
el disparo con el flanco de bajada, o sea FALLING o LOW.
El
Un disparador en inglés es trigger y es el nombre que encontrareis en la jerga técnica.
loop comprueba si el número de pulsaciones ha cambiado y si es así lo imprime, pero puede dedicarse a hacer cualquier
cosa, porque no vamos a perder ninguna pulsación.
Os puede parecer una manera extravagante de hacer las cosas pero no me digáis que no es elegante. De hecho todos
los lenguajes modernos de alto nivel para Windows, Mac o Linux utilizan la programación por eventos que es algo
Cuando pulsamos el botón, el número que aparece no aumenta de uno en uno si no a golpes. ¿Por qué?
Pues como ya vimos en su día, se debe a los rebotes del pulsador. Decíamos en la sesión “Condicionales y botones”, que
para eliminar el rebote de los botones, tenemos que hacer el debouncing y allí lo hacíamos con un delay de 250 ms.
Pero vamos a tener un problema. No podemos usar un delay dentro de una interrupción. No funciona. ¿Cómo dice?
Haz lo que quieras pero no te demores. Acaba cuanto antes y lárgate.
Hay cosas que no funcionan, como las funciones delay (), millis () y cualquier cosa
que dependa de interrupciones o timers.
Debes entender que una interrupción es como un estado de excepción, que se puede
usar sin reparos, pero entendiendo que hay que hacer el trabajo y salir cuanto antes.
Además cuando definimos una variable global como contador, de la que depende una función ISR, se recomienda
definirla como volatile y no como una global normal.
Si
Estrictamente hablando, volatile no es un tipo de variable, sino una directiva para el
compilador.
Eso significa que la variable en cuestión, debe ser almacenado de un cierto modo que
evite algunos problemas raros que pueden surgir cuando una variable puede ser
cambiada por el ISR o por el programa (algo que no ocurre en nuestro ejemplo).
Bajo ciertas circunstancias puede surgir un conflicto y volatile lo evita y por eso se
recomineda hacerlo así siempre.
necesitas un retraso para algo, o sea , un delay, siempre es mejor, comprobar el tiempo transcurrido y decidir si se toma
la acción o no. Veamos otro ejemplo
void setup()
{ pinMode(2, INPUT);
Serial.begin(9600);
attachInterrupt( 0, ServicioBoton, LOW);
}
void loop()
{ if (n != contador)
{ Serial.println(contador);
n = contador ;
}
}
void ServicioBoton()
{
if ( millis() > T0 + 250)
{ contador++ ;
T0 = millis();
}
}
En primer lugar definimos contador como volátil, por prescripción médica, y definimos otra variable global T0 para
almacenar el tiempo a partir del cual contaremos.
En la ISR la diferencia es que comprobamos si el valor actúan de millis es mayor en 250 ms a la última vez que paso por
la interrupción. Si no es así, lo consideramos un rebote y lo ignoramos olímpicamente. Por el contrario si ha pasado un
tiempo prudencial incrementamos contador.
La ventaja d este sistema es que no congelamos el procesador con un delay, si no que le dejamos seguir con su trabajo,
atendiendo otras interrupciones, por ejemplo.
Así es. Mientras una interrupción esta activa millis está congelada y su valor no cambiará, pero sigue pudiéndose leer.
Mientras estas dentro de una interrupción, todas las demás interrupciones, son
ignoradas, por eso, nada que dependa de otras interrupciones funciona.
Por eso es importante salir pronto, para garantizar que no nos perdemos nada de
interés.
Mientras una interrupción está activa, millis() y micros()se congelan. Eso quiere decir
que si tienes unos cuantos miles de interrupciones por segundo (Como si estas
midiendo la frecuencia de una onda de audio) tu medida de tiempo con millis o micros
se puede ver distorsionada.
Por ultimo os conviene saber que existen algunas otras instrucciones relativas a las interrupciones:
noInterrupts(), Desactiva la ejecución de interrupciones hasta nueva orden.
Interrupts(), Reinicia las interrupciones definidas con attachInterrupt().
detachInterrupt( num Interrupt), Anula la interrupción indicada.
RESUMEN DE LA SESIÓN
Vimos los posibles disparos de una interrupción por flanco de subida, bajado o LOW.
Anterior Siguiente
(109) Comments
int leer_tpv=0;
int escribir_tpv=0;
int led_salida_cafe=13;
int led_entrada_cafe=2;
int estado_cafe=0;
void setup() {
pinMode(led_salida_cafe,OUTPUT);
pinMode(led_entrada_cafe,INPUT);
Serial.begin(9600);
void loop() {
int leer_tpv=Serial.read() – ‘0’;
switch (leer_tpv) {
case 1://el tpv envia desbloquear molino cafe
digitalWrite(led_salida_cafe,HIGH);
break;
case 2: //el tpv envia bloquear molino cafe
digitalWrite(led_salida_cafe,LOW);
break;
default:
break;
}
estado_cafe=digitalRead(led_entrada_cafe);
Serial.println(estado_cafe);
delay(500);
Hola Victor, si tratas de leer un pin al que no tengas nada conectado, el valor fluctúa
constantemente. De ahí las lecturas de 0 y 1 sin aparente sentido. UN saludo.
Reply
Reply
Reply
Reply
admin 13 Feb 2017
Hola Giovanny, en primer lugar no debes hacer delay dentro de las interrupciones porque
simplemente no funcionan como esperas. No use delays dentro de las rutinas de servicio de
las interrupciones vale.
En cuanto al tema e que cuenta de mas, te recomiendo hacer algunas pruebas con disparo por
Falling y rising porque a veces puedes tener una idea equivocada de lo que pasa (Nos pasa a
todos) pero despues de hacer pruebas de este estilo puede que veas algo de luz ¿Vale?
Pruebalo y me dices
2. serial.print? idem comentario del ejemplo del slave receiver, lo hace dentro de interrupción
3. delay()
4. otras???
En el programa que estoy intentando hacer de vez en cuando el programa se cuelga y lo tiene
que reanimar un watchdog. Parece que ocurre en la comunicaciones entre maestro-esclavo,
concretamente PERO SOLO algunas veces, cuando el maestro manda ordenes marcha paro al
esclavo, el problema es que no ocurre siempre, solo de vez en cuando en el mismo punto.
Desde mi inexperiencia tras revisar los foros estoy intentando “limpiar” todo lo posible lo que
hace el esclavo, pero encuentro contradicciones entre las recomendaciones y los ejemplos del
IDE.
MUCHAS GRACIAS
No estoy seguro de que programa es el que me mencionas y pero la idea que tengo es que dentro de
una interrupcion no funcionan ni el delay ni las funciones serial ya que ambas dependen a su vez de
otras interrupciones por lo que aun cuando puediesen funcionar (Que creo que no) no seria interesante
de usar
Reply
#include
#include “U8glib.h”
#include
#include
U8GLIB_ST7920_128X64_1X u8g(23, 17, 16); //SPI Com: SCK = en = 18, MOSI = rw = 16, CS = di = 17
long lastencoderValue = 0;
int lastMSB = 0;
int lastLSB = 0;
void loop() {
if(digitalRead(encoderBttn)){
//button is not being pushed
}else{
Serial.println(“bttn ON”);
}
Serial.println(pos);
delay(500);
nA = digitalRead(encoderPinA);
nB = digitalRead(encoderPinB);
while((nA==1)&&(nB==1)){
//Serial.print (“pinA: “);
//Serial.println (nA);
//Serial.print (“pinB: “);
//Serial.println (nB);
Serial.println(pos);
nA = digitalRead(encoderPinA);
nB = digitalRead(encoderPinB);
};
}
void valorPos(){
Serial.println(“dentro del metodo”);
nA = digitalRead(encoderPinA);
nB = digitalRead(encoderPinB);
if(nA==0){
pos–;
}
if(nB==0){
pos++;
}
}
Reply
tonny 14 Oct 2016
Hola, o sea que en el arduino mega2560 no todos los pines pueden hacer uso de la librería
PinChangeInt.h?? El mega tiene 53 pines de los cuales los que van conectados al encoder de
la pantalla son el 31,33 y 35 (pines digitales); ya probe otro código (el de abajo) pero igual no
me funciona son ninguno de los pines que estan conectados al encoder; no sé si tenga algo que
ver con lo que encontré sobre los pines del mega : Interrupt External ->2 3 and 18-21 ;
Interrupt Pin Change -> 10-15 and A8-A15 and SS, SCK, MOSI, MISO.
Éste código es de los ejemplos de la librería y lo usé pero no pasaba nada cuando movía el
encoder o pulsaba el encoder (usando: #define ARDUINOPIN 35 )
#include
// Notice that values that get modified inside an interrupt, that I wish to access
// outside the interrupt, are marked “volatile”. It tells the compiler not to optimize
// the variable.
volatile uint16_t interruptCount=0; // The count will go back to 0 after hitting 65535.
// In the loop, we just check to see where the interrupt count is at. The value gets updated by
the
// interrupt routine.
void loop() {
delay(1000); // Every second,
Serial.print(“Pin was interrupted: “);
Serial.print(interruptCount, DEC); // print the interrupt count.
Serial.println(” times so far.”);
}
Reply
Segun el manuela en el mega puedes usar los siguientes pine spara enlazar
interrupciones
Por lo que veo tu has definido ARduinopin para la interrupcion , como el pin 33
pero ese pin no reconoce interrupcione spor lo que no va a funcionar. Tienes que
elegir uno de la lista
Reply Es la primera vez que escribo. Primero mis impresiones y luego quiero comentar algo sobre mis resultados
con las interrupciones y preguntaros si estoy en lo cierto.
Me ha impresionado bastante el enfoque tan didáctico y ameno de este curso. Tengo (tenía) experiencia de
programación y me quedé en Pascal, sin apenas programar en C. Calcula mi edad… He hecho aplicaciones
y utilidades de sistemas para clientes y he programado PICs como hobby en código máquina, así que sé de
sobra lo que es programar. Pero tenía un tema pendiente que era entender el porqué de la famosa
programación orientada a objetos. Con tus breves pero clarificadoras lecciones lo he entendido a la
perfección. !Muchas Gracias!
Estoy siguiendo con todo detalle este curso y quisiera comentar una curiosidad que he observado con el
tema de las interrupciones y los rebotes del pulsador.
He realizado un montaje con vuestro KIT del curso, con 8 LEDs en secuencia de ir y venir, tipo coche
fantástico. Mas un pulsador que cada vez que lo pulsas cambia alternativamente el delay para que el
“movimiento” de luces sea más deprisa o más despacio.
He seguido el código para evitar los rebotes de esta lección, pero tenia curiosidad por saber el número de
rebotes y añadí un contador adicional de rebotes que siempre se incrementa en la rutina de interrupción, sin
pasar por el “if”. Pues bien, cada vez que pulso lo más rápido que puedo el pulsador, el contador bueno se
incrementa 1, como debe ser, pero el otro contador de rebotes, que cuenta las veces que se dispara la
interrupción al pulsar el botón, se incrementa cada vez no unas cuantas veces sino del orden de 5.000 o
10.000 veces (cinco mil a diez mil veces). Al tercer o cuarto pulsado del botón, el contador de rebotes
entero ya se ha desbordado y aparece negativo.
No me podía imaginar que la rutina de interrupción entrase tantas miles de veces, con un simple rebote de
pulsador. Lo que imaginé era que como la condición de interrupción programada era “LOW”, la
interrupción se disparaba continuamente mientras el pin estuviese LOW, es decir el botón pulsado. Al
cambiar la condición a “FALLING”, entonces el comportamiento es lo que yo esperaba. El numero de
rebotes es uno o dos, como máximo, o incluso ninguno, pulsando con firmeza y/o delicadeza.
Entonces parece ser que con la condición “LOW”, mientras se mantenga el pin LOW, se dispara
continuamente la interrupción. Habría que programar algún código en la rutina de interrupción para
deshabilitar esta interrupción, mientras se ejecuta la ISR. Y luego al final de la rutina de interrupción
volver a habilitar la interrupción para estar preparado para la siguiente.
Creo recordar que los PICs que programé hace años eso era automático, pero parece que con el PIC de este
arduino no es así y mientras se mantenga la condición LOW, se dispara una y otra vez la interrupción a la
máxima velocidad que permite el chip. ¿Estoy en lo cierto? ¿Podéis comentarlo si sabéis sobre este tema?
En fin, Muchísimas gracias por este curso que me está haciendo disfrutar de nuevo después de tantos
años…
Un Fuerte Abrazo,
Alberto
void setup() {
start = millis(); // se cargas a la variable start con el valor de millis(), o sea un tiempo t1 en mseg
pinMode (led, OUTPUT);
Serial.begin(9600);
void loop() {
while (millis() – start threshold)
digitalWrite(led, HIGH);
else
digitalWrite(led, LOW);
// print the analog value:
Serial.println(analogValue);
void setup() {
start = millis(); // se cargas a la variable start con el valor de millis(), o sea un tiempo t1 en mseg
pinMode (led, OUTPUT);
Serial.begin(9600);
void loop() {
while (millis() – start threshold)
digitalWrite(led, HIGH);
else
digitalWrite(led, LOW);
// print the analog value:
Serial.println(analogValue);
Reply
Si lo que quieres es anular sólo una interrupción utilizarías la función detachInterrupt( num Interrupt),
pasándole como parámetro el número de la interrupción, no el pin al que está asociada.
Reply La verdad muchas gracias por tus tutoriales, tu web es un referente cuando buscas empezar con arduino y
también cuando quieres dominarlo, en serio gracias.
Lo pregunto porque mi idea es que arduino se conecte con otro dispositivo (computadora, raspberry, otro
arduino) y que ambos dispositivos puedan emitir mensajes así como recibirlos de forma ordenada, lo único
que se me ocurre para esto es poner los métodos de leerSerial() y enviarMensaje() seguidos uno del otro
pero ¿qué sucede si caundo envío el primer mensaje para que el otro dispositivo me responda y este
responde arduino está de nuevo en enviarMensaje()? Si arduino solo funcionara como servidor no habría
problemas pero la idea es que también pueda enviar mensajes.
Saludos.
Para una comunicacion serie entre tu arduino y porejemplo un PC o Rasberry, usaria directamente la
conexion USB y tendras un monton de capas de drives de bajo nivel asegurandose de que van
recogiendo en un buffer lo que va entrando
Lo unico que tienes que hacer es no demorarte demasiado con delays y demas y realizar el loop con
cierta soltura ¿Valeee?
Reply Tengo un problema con un proyecto que me encuentro desarrollando pero aqui es mi problema:
Una vez que activo mi señal de entrada,por medio de un potenciometro condiciono un tiempo de trabajo
entre 100 ms y 1500 ms. El detalle que necesito que aunque la entrada siga en estado alto siga corriendo el
tiempo de trabajo y al terminar el estado cambie a LOW. :smiley-confuse:
Alguien que pueda aconsejarme que instruccion usar…a continuacion dejo mi codigo donde
SCI es la entrada!
CO es la salida!
offtime es el mapeo de la entrada analógica
[code]
void setup() {
// Incializa el pin del boton como entrada
pinMode(pinpot, INPUT);
pinMode(SCI,INPUT);
// Incializa el pin del led piloto y salida
pinMode(LCO, OUTPUT);
pinMode(CO, OUTPUT);
// Inicializa la comunicacion serial
Serial.begin(9600);
void loop(){
//Lee boton
p1=digitalRead(SCI);
// Lee el valor del pot
tw = analogRead(pinpot);
// Transforma este valor en el valor que usaremos para el led
// Para ello usaremos la funcion map()
OffTime = map(tw, 0, 1023, OnTimeMin, OnTimeMax);
// Utilizamos este valor para iluminar el led
// check to see if it's time to change the state of the LED
unsigned long currentMillis = millis();
Ed 31 May 2016
Vale Juanma!, muchas gracias!, ya lo revisaré, la pregunta era básicamente como controlar los
dos bits del rotary(A y B) que te dan los 4 estados posibles con los que se calcula si se
Reply incrementa o se decrementa. Pero al estar esos dos pins asociados a la interrupción y no poder
usarlos como valores ordinarios de entrada, tenía duda de como hacerlo, pero creo que será
como dices tu.
Ed 30 May 2016
Hola Juanma, muy chulo el post, estoy empezando en arduino y me ha gustado mucho, me has
enganchado para leer el resto de entradas!
Reply Tengo una duda conceptual que querría aclarar, estaba mirando información de las
interrupciones para un rotary encoder. la pregunta es, si pongo interrupciones a los dos pins que
leen el estado A y B, como hago para leer sus valores cuando llamo al callback si los pines stán
asociados a las interrupciones, de forma que no he podido definir variables para recuperarlos?
Recuerda que un experto, suele ser alguien que se ha leído el manual antes que tu.
GIVE A REPLY
Name (required)
Email (required)
URL
Post comment
WordPress Anti-Spam by WP-SpamShield
CATEGORIAS
DE LOS
PRODUCTOS
Elige una
Elige una categoría
categoría
Copyright Prometec