02_Notas_Varias - jospicant/Attiny GitHub Wiki

Variables

  • La Arquitectura del AVR tiene 32 registros de trabajo que dotan de eficiencia al código C ya que estos registros trabajan directamente sobre una ALU donde se puede operar con dos de ellos y devolver el resultado al registro en un único ciclo de reloj.

  • Se debe intentar evitar el uso de variables globales ya que estas se posicionan en la memoria SRAM (memoria escasa) y para acceder a ellas, primero se cargan a un registro de trabajo, se realizan manipulaciones y se vuelven a guardar en la SRAM ocasionando así más código y por lo tanto más lentitud.

  • Las variables locales normalmente se asignan a un registro de trabajo las manipulaciones se hacen sobre el propio registro y una vez se sale de la función a la que pertenece dicha variable queda libre.

  • Cuando en una función se quiere que una variable mantenga el valor al terminar la función, se tienen que definir como variables estáticas, dichas variables se cargan en un registro de trabajo al inicio de la función (primera llamada) y al final se guardan en la SRAM, son más eficientes que las variables globales si dentro de la función se accede variables veces a la variable ya que hasta el final de la función no se almacena en la SRAM, la variable global cada vez que se accede a ella está en la SRAM y se debe cargar en un registro de trabajo general.

  • La memoria de I/O del AVR es fácilmente accesible en C. Todos los registros en la memoria I/O son declarados normalmente en un fichero de cabecera "ioxxxx.h" donde xxxx es el part number del AVR (Ejemplo_ #include <iotnx5.h> <iotn85.h> ....).

  • Se pueden definir macros para la manipulación y testeo de bits en los registros de I/O, este es un ejemplo pero seguramente Arduino ya los tiene definidos:

#define SETBIT(ADDRESS,BIT)    (ADDRESS | =  (1<<BIT))

#define CLEARBIT(ADDRESS,BIT)  (ADDRESS & = ~(1<<BIT))

#define CHECKBIT(ADDRESS,BIT) (ADDRESS & (1 << BIT))

Ejemplos:

SETBIT(GIMSK,INT0);
CLEARBIT(GIMSK,PCIE);

if (CHECKBIT(ADCSRA,ADEN)){

  CLEARBIT(ADCSRA,ADEDN);
}
  • Arduino nos ofrece las siguientes funciones para manipulación/testeo de bits:
bitSet(value, bit);
bitClear(value, bit);
bit_is_clear(sfr, bit);
bit_is_set(sfr, bit);
  • Si hay varias variables globales es más eficiente recolectarlas todas en un estructura struct ya que esto facilita al compilador realizar direccionamiento indirecto, el compilador podrá cargar y almacenar usando desplazamientos.

Clock

Reloj

  • Mediante el FUSE CKSEL[3:0] podremos seleccionar cual es la fuente del reloj principal, proveniente del reloj interno (8 MHz o 16 MHz),o oscilador externo (cristal), y con el registro de configuración CLKPR (CLKPS[3:0]) configuramos el preescalado del reloj seleccionado.

  • Por defecto el Attiny viene con la configuración de fuses del CKSEL[3:0] = 0010 SUT[1:0] = 10 y CKDIV8 = 0 (programado ), por lo que el reloj seleccionado es de 8MHz y el preescalado incial es de CLKPS=0011, obteniendo un reloj de 1 MHz.

CKSEL

  • El Fuse CKDIV8 determina el valor del preescalado del reloj inicial, si **CK8DIV = 1 ** (No programado), el preescalado del reloj **CLKPS = 0000" (factor de dvisión 1), si CK8DIV = 0 ( programado), el preescalado inicial CLKPS = 0011 (factor de división por 8). Esta caracterísca de preescalado se usa cuando el reloj fuente tiene una frecuencia mayor de la permitida por el micro, la aplicación debe hacer el preescalado correcto si se diese el caso. Por defecto viene configurado sin preescalado ( CK8DIV = 0 CLPKS =0000 ).

  • Inicialmente en función de CKDIV8 partiremos de un prescalado inicial y si no queremos ese preescalado, deberemos cambiarlo por software, para cambiar el preescalado debemos desactivar las interrupciones para asegurarnos que el valor de CLKPS[3:0] se graba correctamente.

CLKPS-Preescalado

  • Podemos ver los valores de los FUSES usando por ejemplo el programador TL866 ( SW MiniPro v6.85 ):

Fuses por defecto

  • Nota: check marcado es un "0" ( "0" = programado )

  • Nota: la función delay(ms) coincide con el cálculo de milisegundos cuando el reloj está a 1MHz configurado.

  • TRUCO: Si configuramos el FUSE CKOUT podremos ver la señal del reloj con la que trabaja el Attiny pinchando con un osciloscopio en el pin 3 (PB4).

Interrupciones

  • Después de finalizar una rutina de interrupción, las interrupciones se vuelven a habilitar automáticamente, esto se debe tener en cuenta por si quieres controlar cuando habilitar o no las interrupciones (esto es importante).

  • En la rutina de interrupción no se debe poner casi código, una opción en poner solo "cli()" al principio para que no se produzcan más interrupciones mientras se ejecuta dicha interrupción y alguna variable tipo booleana a nivel de flag ( tipo volatile ) para poder controlar el paso por el vector de interrupción al volver de la interrupción y en función de ello actuar.

  • Las interrupciones deben deshabilitarse cuando se quiere cambiar el preescalado del reloj para asegurarse que la esritura del cambio no se ha interrumpido.

Ahorro de consumo

  • si no vamos a usar el ADC, este se debe deshabilitar antes de entrar en cualquier modo de bajo consumo (sleep)

  • El registro PRR nos permite reducir el consumo parando el reloj de distintos periféricos **(ADC,USI,temp0,temp1), es conveniente deshabilitar todos los módulos que no se usen.

void enable_prr()
{
power_spi_disable();
power_timer0_disable();
power_timer1_disable();
power_twi_disable();
}
  • Es recomendable configurar todos los pines que no se usen como salidas con resistencias pull-up con el fin de tener un nivel lógico definido y evitar conmutaciones innecesarias.

  • Si no necesitamos un reloj de 8MHz, es conveniente usar frecuencias de reloj lo más lentas ( menor consumo ) que se necesite para nuestra aplicación ( Se configura el registro CLKPR para configurar el preescalado del reloj).

Ejemplo:

clock_prescale_set(clock_div_4); // This divides the clock by 4

⚠️ **GitHub.com Fallback** ⚠️