arduino - millis ()

10

Copiado de la referencia Arduino - millis ()

Consejo : Tenga en cuenta que el parámetro para millis es un largo sin signo, se pueden generar errores si un programador intenta hacer cálculos matemáticos con otros tipos de datos, como ints.

¿Qué tipo de matemática? ¿Qué otro tipo de procesamiento se excluye al trabajar con millis?

¿Podría alguien aclarar esta afirmación y / o dar algún ejemplo?

usuario3060854
fuente

Respuestas:

8

Cada vez que escribe una ecuación en C / C ++, los tipos de datos que se utilizan tienen un efecto muy real en la salida de la ecuación.

Cada tipo como int, floaty unsigned longtener diferentes comportamientos, y tomar una cierta cantidad de espacio en la memoria para almacenar.

int (en arduino) se almacena en 16 bits, con la mitad de sus valores dados a números negativos, la mitad 1 dada a valores positivos y un valor dado a 0. Eso le da un rango de -2 ^ 15 (-32,768) a + 2 ^ 15-1 (32,767).

unsigned long(en arduino) es de 32 bits, pero ninguno se designa como negativo. su rango es entonces de 0 a 2 ^ 32-1 (4294967295).

¿Qué tipo de matemática? ¿Qué otro tipo de procesamiento se excluye al trabajar con millis?

El quid de la cuestión es que cuando el tiempo de devolución de Millis ha pasado de 32767 y trataste de almacenarlo en un int, el arduino no pudo hacerlo, porque intno puede contener un número tan grande. El tipo de matemática que está fuera de los límites es la matemática que sucede a tipos de datos más pequeños, no a operaciones específicas. Quizás estos ejemplos ayuden:

  int i = 32767;
  Serial.println(i);
  //No problems here; it fits just fine

32767

  i = 32767 + 1;
  Serial.println(i);
  //Oh no, the value didn't fit

-32768

  unsigned long fake_millis = 42000;
  i = fake_millis;
  Serial.println(i);
  //This is an example of millis going past an int

-23536

  i = -10;
  unsigned int j = i;
  Serial.println(j);
  //no way to put a negative number in an unsigned value

65526

  uint32_t k = fake_millis;
  Serial.println(k);
  //unsigned long is a uint32_t on arduino; this works great!

42000

La forma en que esto se implementa es realmente genial; Si está interesado en saber de dónde provienen estos números y por qué se extienden de la manera en que lo hacen, debe buscar las mismas explicaciones de las representaciones de números complementarios de dos.

BrettAM
fuente
Solo una simple pregunta: declarar "unsigned long fake_millis"; es igual a "uint_32 fake_millis;" ?
user3060854
Sí, esos son iguales en el arduino. La diferencia es que unsigned longpuede cambiar con diferentes plataformas (por ejemplo, x86), uint32_tsiempre habrá 32 bits sin signo en todas partes.
BrettAM
1
Cabe señalar que su segundo ejemplo ( 32767 + 1) produce un comportamiento indefinido, que casi siempre es algo malo . Sus otros ejemplos son comportamientos documentados en los que puede confiar.
Edgar Bonet
6

millis()devuelve un unsigned long, que es un entero sin signo de 32 bits en el Arduino. Cuando intentas hacer algo como unsigned int time = millis() - 1000, intentas almacenarlo en un entero sin signo de 16 bits unsigned int. Un entero de 16 bits nunca puede contener un valor de 32 bits.

De acuerdo con la especificación C , párrafo 6.3.1.3, los 16 bits superiores se descartan.

Si es posible, mantenga la millis()salida en unsigned longy solo use tipos de datos con menos bits cuando esté absolutamente seguro de que no perderá bits.

Aquí hay más información sobre moldes explícitos en C: https://stackoverflow.com/a/13652624/1544337

Comunidad
fuente
Gracias por la edición Eliminé la referencia a uint32_t porque es algo diferente. uint32_t garantiza que tiene un entero sin signo de 32 bits, sin signo largo no (aunque en Arduino lo es). También mencioné que es un tipo de 32 bits.
Entonces, ¿no dirías que es más correcto decir que devuelve un uint32_tque resulta ser un unsigned longon arduino?
BrettAM
@BrettAM de acuerdo con los documentos, esta función devuelve un unsigned long.
Se eliminaron los comentarios antiguos, ya que toda la discusión tiene poco sentido con la versión actual de la respuesta. Sólo mantener este para el registro: si la conversión es a una firmado entero ( int time = millis() - 1000), el resultado es similar: los 16 bits superiores se descartan. Esta vez, el estándar C dice que el resultado está definido por la implementación, y el comportamiento se especifica en la documentación de gcc sobre el comportamiento de los enteros definido por la implementación (cuarto punto).
Edgar Bonet
2

Cuando quieras hacer cosas con millis () solo recuerda inicializar tu variable con el tipo "uint32_t"

Entonces, haga algo como "uint32_t last_millis" donde almacenará la salida de la función "millis ()".

De lo contrario, como dijeron los demás, se desbordaría al pasar de 31,000, lo que sucederá bastante rápido.

nemik
fuente