Archivo de la etiqueta: Avanzado

Tratamiento Avanzado de Strings

Como ya se ha visto anteriormente el tratamiento de strings es un parte muy importante en Arduino puesto que se usa muy frecuentemente y principalmente usamos en las comunicaciones, ya sea puerto serie, bluetooth, XBee, http, etc…

El uso de strings hace un uso intensivo de memoria lo que hace que podamos tener comportamientos extraños en los sketchs o incluso tengamos desbordamiento de memoria.

A la hora de usar strings en Arduino, podemos hacer uso de la clase String https://www.arduino.cc/reference/en/language/variables/data-types/stringobject/ que nos ofrece unos métodos y es muy sencilla de usar, a cambio de ser poco eficiente a nivel de SRAM o usar los stringshttps://arduino.cc/reference/en/language/variables/data-types/string/ como arrays de chars https://arduino.cc/reference/en/language/variables/data-types/char/ que es más complejo de manejar pero más potente y tenemos más control del uso de memoria y pueden usarse muchas de las funciones estandard de C++.

String (Objeto)

Arduino nos ofrece una clase llamada String que facilita el uso de de las cadenas de caracteres con unos métodos muy sencillos de usar y poder usar los operadores que conocemos para los Strings.

Se trata de una clase que permite usar y manipular cadenas de texto de una forma más sencilla que los strings. Puedes concatenar, añadir, buscar, etc… usando los métodos/funciones que ofrece esta clase.

Los Strings tienen un uso intensivo de memoria, pero son muy útiles y se van a utilizar mucho en el apartado de comunicación, por ese motivo es importante aprender a manejar los Strings.

Tener en cuenta que al no ser un tipo de dato propiamente dicho sino una clase, tienes unas funciones asociadas (métodos), operadores y unas propiedades. Es una abstracción del dato y para aprender a usarlo hay que leerse la documentación correspondiente.

Toda la información de la clase String está en: https://arduino.cc/reference/en/language/variables/data-types/stringobject/

Ver documentación de Arduino sobre la clase String:

Tutoriales de uso de String:https://arduino.cc/en/Tutorial/BuiltInExamples#strings

Prácticas Manejo de Strings

Ejecutar el ejemplo https://arduino.cc/en/Tutorial/StringLengthTrim donde se hace uso de las funciones length() y trim().

Ejecutar el ejemplo https://arduino.cc/en/Tutorial/StringStartsWithEndsWith donde se hace uso de las funciones StartsWith() y EndsWith().

Ejecutar el ejemplo https://arduino.cc/en/Tutorial/StringSubstring donde se hace uso de la función substring().

Ejecutar el ejemplo https://arduino.cc/en/Tutorial/StringToInt donde se hace uso de la función toInt()

Otra de las funciones más útiles de String es IndexOf() con ejemplos en https://www.arduino.cc/en/Tutorial/StringIndexOf

Ejercicio: Conectarse a www.aprendiendoarduino.com y analizar el texto descargado. El texto descargado es una página web que contiene datos de una música a tocar por el buzzer

Hacerlo usando la clase String.

Obtener y sacar por el puerto serie:

  • Tamaño de la web (número de caracteres)
  • Localizar las cadenas: “Inicio Notas”, “Fin Notas”, “Inicio Duración” y “Fin Duración”
  • Obtener las notas (frecuencia) y la duración de las notas. Luego reproducirlo.

Solución: https://github.com/jecrespo/aprendiendoarduino-Curso_Programacion_Arduino/tree/master/Ejercicio16-Strings_Avanzado

string (Array de chars)

Un string es un array de chars. Cuando se trabaja con grandes cantidades de texto, es conveniente usar un array de strings. Puesto que los strings son en si mismo arrays. En el reference de Arduino https://arduino.cc/reference/en/language/variables/data-types/string/

La notación de un string como array de chars es char*

El core de Arduino me ofrece varias funciones de análisis de caracteres: https://arduino.cc/reference/en/language/variables/data-types/string/

Una cadena en C++ es un conjunto de caracteres, o valores de tipo char, terminados con el carácter nulo, es decir el valor numérico 0 (\0). Internamente, en el ordenador, se almacenan en posiciones consecutivas de memoria. Este tipo de estructuras recibe un tratamiento muy especial, ya que es de gran utilidad y su uso es continuo. La manera de definir una cadena es la siguiente: char <identificador> [<longitud máxima>];

Cuando se declara una cadena hay que tener en cuenta que tendremos que reservar una posición para almacenar el carácter nulo terminador, de modo que si queremos almacenar la cadena “HOLA”, tendremos que declarar la cadena como: char Saludo[5]; Las cuatro primeras posiciones se usan para almacenar los caracteres “HOLA” y la posición extra, para el carácter nulo.

También nos será posible hacer referencia a cada uno de los caracteres individuales que componen la cadena, simplemente indicando la posición. Por ejemplo el tercer carácter de nuestra cadena de ejemplo será la ‘L’, podemos hacer referencia a él como Saludo[2].

Se puede manipular las cadenas de caracteres de la misma manera en que manipula cualquier otro tipo de array, sin embargo, es preferible hacer uso de una librería estándar especialmente escrita para manipulación de cadenas de caracteres, se trata de la librería <string.h> y que viene incluida con todo compilador de C, C++.

Reference de C++ para la clase string http://cplusplus.com/reference/string/string/ y http://cplusplus.com/reference/cstring/ con funciones como strcpy para strings null-terminated.

Los compiladores de C, C++ dan soporte a la biblioteca de funciones <string.h>, a la que accede por medio de la directiva #include <string.h>. No veremos en detalle todas las funciones contenidas en dicha biblioteca, y nos limitaremos a mostrar algunos ejemplos de ciertas funciones importantes.

  • strlen(): Obtener longitud de cadenas. Sintaxis: size_t strlen(const char *s);
  • strcpy(): Copiar cadenas. Sintaxis: char *stpcpy(char *dest, const char *src);
  • strcat(): Concatenar cadenas. Sintaxis: char *strcat(char *dest, const char *src);
  • strlwr(): Convertir a minúsculas. Sintaxis: char *strlwr(char *dest);
  • strupr(): Convertir a mayúsculas. Sintaxis: char *strupr(char *dest);
  • strchr(): Buscar carácter (hacia adelante). Sintaxis: char *strchr(char *s, int c);
  • strrchr(): Buscar carácter (hacia atras). Sintaxis: char *strrchr(char *s, int c);
  • strstr(): Buscar subcadena. Sintaxis: char *strstr(const char *s1, char *s2);
  • memset(): Establece el primer num bytes del bloque de memoria apuntado por ptr al valor especificado en value (interpretado como un unsigned char).. Sintaxis: void * memset ( void * ptr, int value, size_t num );

Reference:

En C++ también tenemos soporte a la clase cstring, que no debe confundirse con la <string.h>. Una de las ventajas que ofrece la clase cstring es que, a diferencia de las cadenas estándar, ésta posee la capacidad de crecer o disminuir su tamaño en tiempo de ejecución. Además, entre otras características destacables, la clase string soporta operaciones de asignación tales como: =, +, +=, etc.; y de comparación tales como: ==, <=, etc.

Documentacíon de la librería <string.h>: http://www.cplusplus.com/reference/cstring/

Clase string de C++: http://www.cplusplus.com/reference/string/string/

Ejercicio Strings_vs_strings: Partiendo de la base del ejercicio StringsComparisonOperators intentar hacer las operaciones de comparación de igualdad y distinto de los StringOne y StringTwo con string en lugar de String. Ver como es más complicado y para iniciarse en la programación es mejor usar String (objeto) que string (char array).

Solución: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio18-strings/_5-String_vs_string

Más información:

Ejercicios Avanzados  Strings

Ejercicio: Mandar un SMS mediante un módulo SIM800L, donde pido por consola el número y el mensaje en formato” <numero_telefono>-<mensaje>!”. Analizar esta cadena y separar en teléfono y mensaje y mandar mediante la función bool sendSms(char* number,char* text);

El primer análisis hacerlo con la clase String y luego pasar las variables teléfono y mensaje a char* que es lo que pide la librería.

Librería disponible en el gestor de librerías: https://github.com/VittorioEsposito/Sim800L-Arduino-Library-revised

HW: https://es.aliexpress.com/item/SIM800L-V2-0-5V-Wireless-GSM-GPRS-MODULE-Quad-Band-W-Antenna-Cable-Cap/32465895576.html

Solución: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio68-toCharArray

Ejercicio: Manejo de JSON mediante la librería disponible en el gestor de librerías ArduinoJson https://arduinojson.org y repositorio https://github.com/bblanchon/ArduinoJson

JSON: https://es.wikipedia.org/wiki/JSON

Abrir el ejemplo JsonParserExample de la librería y probarlo.

PROGMEM

El uso de strings como cadena de caracteres en Arduino, en lugar de usar la clase String, nos permite también almacenar los strings en la memoria flash en lugar de la SRAM gracias a PROGMEM https://arduino.cc/reference/en/language/variables/utilities/progmem/

A menudo es conveniente cuando se trabaja con grandes cantidades de texto, como un proyecto con una pantalla LCD o en comunicaciones, usar PROGMEM con arrays de strings.  Estos tienden a ser grandes estructuras, así que ponerlas en memoria de programa (flash) es a menudo deseable. El código siguiente ilustra la idea.

 
#include <avr/pgmspace.h>
const char string_0[] PROGMEM = "String 0";   // "String 0" etc are strings to store - change to suit.
const char string_1[] PROGMEM = "String 1";
const char string_2[] PROGMEM = "String 2";
const char string_3[] PROGMEM = "String 3";
const char string_4[] PROGMEM = "String 4";
const char string_5[] PROGMEM = "String 5";

// Then set up a table to refer to your strings.

const char* const string_table[] PROGMEM = {string_0, string_1, string_2, string_3, string_4, string_5};

char buffer[30];    // make sure this is large enough for the largest string it must hold

void setup()
{
  Serial.begin(9600);
  while(!Serial);
  Serial.println("OK");
}

void loop()
{
   for (int i = 0; i < 6; i++)
  {
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // Necessary casts and dereferencing, just copy.
    Serial.println(buffer);
    delay( 500 );
  }
}

Utilidades de PROGMEM: https://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html

Usar la tabla de cadenas en la memoria de programa (flash) requiere el uso de funciones especiales para recuperar los datos. La función strcpy_P copia un string desde el espacio del programa (flash) en un string en la memoria RAM (“buffer”). Debemos asegurarnos de que la cadena de recepción en la memoria RAM es lo suficientemente grande como para alojar cualquier string que está recuperando desde el espacio del programa

Las funciones para manejar PROGMEM están en http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html que podemos ver son iguales que las de <string.h>, pero seguidas de “_P”

Tener en cuenta que las variables deben ser definidas de forma global y como constantes, o definido con la palabra clave static, con el fin de trabajar con PROGMEM.

Recordar que podemos usar la macro F() junto con el métodos print y println de forma que todo lo que hay dentro de los métodos se guarda en la memoria flash.

Ejemplo: Serial.println(F(“This string will be stored in flash memory”));

Para saber como funciona PROGMEM ver  la pagina 347 y 354 de http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_datasheet.pdf y donde lo metes lo de MEMPROG

Anuncios

Puertos Digitales Arduino Avanzado

La mayoría de los pines de los microcontroladores son multipropósito, es decir, en función de su configuración se comportan de una forma u otra, tal y como se muestra en la figura.

El ATmega328p como cualquier otro microcontrolador tiene registros, algunos de estos registros están conectados con los puertos de entrada/salida, cada puerto tiene un nombre específico y sus registros asociados, de hecho, el 328p tiene los puertos B, C y D, y cada puerto 8 pines de la MCU conectados. En función del encapsulado puede haber una restricción en el número de pines. Por ejemplo en el paquete de 28 pines PDIP.

Cada pin puede tener múltiples funciones, como la generación de PWM, o las capacidades de ADC, los pines 6 y 7 del PORTB son los pines de entrada para el oscilador de cristal, y el pin 6 del PORTC corresponde al botón de reinicio.  En esta imagen se puede ver todas las funciones alternativas que cada pin puede tener.

NOTA: Esta nomenclatura del los GPIO luego el IDE de Arduino se ha encargado de nombrarlos de una forma más sencilla y consecutiva, más fácil de entender.

Por ejemplo en el Wemos D1 basado en el ESP8266, internamente los GPIO tiene una nomenclatura que puede llevar a error, pero luego el soporte para estas placas en el IDE de Arduino hace que coincida con la nomenclatura de Arduino y con lo etiquetado en la placa.

Entradas y Salidas Digitales Arduino a Fondo

Una señal digital es un tipo de señal generada por algún tipo de fenómeno electromagnético en que cada signo que codifica el contenido de la misma puede ser analizado en término de algunas magnitudes que representan valores discretos, en lugar de valores dentro de un cierto rango. Por ejemplo, el interruptor de la luz sólo puede tomar dos valores o estados: abierto o cerrado, o la misma lámpara: encendida o apagada.

Más información:

Los sistemas digitales, como por ejemplo un microcontrolador, usan la lógica de dos estados representados por dos niveles de tensión eléctrica, uno alto, H y otro bajo, L (de High y Low, respectivamente, en inglés). Por abstracción, dichos estados se sustituyen por ceros y unos, lo que facilita la aplicación de la lógica y la aritmética binaria. Si el nivel alto se representa por 1 y el bajo por 0, se habla de lógica positiva y en caso contrario de lógica negativa.

Cabe mencionar que, además de los niveles, en una señal digital están las transiciones de alto a bajo y de bajo a alto, denominadas flanco de bajada y de subida, respectivamente. En una señal digital, se denomina flanco a la transición del nivel bajo al alto (flanco de subida) o del nivel alto al bajo (flanco de bajada).

Para interactuar con los pines digitales de Arduino ya conocemos las funciones que nos ofrece Arduino en https://www.arduino.cc/en/Tutorial/DigitalPins como digitalRead() y digitalWrite().

Pero a bajo nivel estas funciones están manejando registros de los puestos B, C y D. Los estados y comportamiento de cada puerto y sus pines se maneja mediante tres registros. Hay un registro dedicado para cada puerto que define si cada pin es una entrada o una salida, que es el registro de DDRX, donde x es la letra del puerto que queremos configurar, en el caso de la Arduino hay DDRB, DDRC y DDRD. Como toda variable lógica, cada bit en los registros DDRX puede ser 1 ó 0, poner un bit específico de DDRX a 1 configura el pin como salida y ponerla a 0 configura el pin como una entrada.

Los pines usados en la placa Arduino poseen tres puertos en el caso de ATmega328p (Arduino Uno):

  • B (pines digitales del 8 al 13)
  • C (entradas analógicas)
  • D (pines digitales del 0 al 7)

El Arduino Mega presenta varios puertos B,C,D,E,F, etc.

Cada puerto es controlado por tres registros, los cuales también están definidos como variables en el lenguaje del Arduino.

  • El registro DDR, determina si el pin es una entrada o una salida (1 salida, 0 entrada).
  • El registro PORT controla si el pin está en nivel alto (1) o en nivel bajo (0).
  • El registro PIN permite leer el estado de un pin. (solo lectura)

Dentro del Microcontrolador ATmega328P esta es la relación de los puestos con los pines:

Cada bit de estos registros corresponden con un solo pin; por ejemplo el bit menos significativo de los registros DDRB, PORTB, y PINB hace referencia al pin PB0 (pin digital 8)

Dentro de la avr-libc tenemos un fichero de definición de los registros para cada microcontrolador. Estos fichero se pueden encontrar en la ruta: C:\Program Files (x86)\Arduino\hardware\tools\avr\avr\include\avr

Para ATmega328p el fichero iom328p.h es donde se encuentran las definiciones para el microcontrolador. Ver que las direcciones de los registros van desplazados 0x20 posiciones de memoria por los fuses que son 4 bytes de memoria permanente llamados fuses que determinan cómo se va a comportar el microcontrolador, si tiene bootloader, a que velocidad y voltaje va a funcionar, etc… .

El fichero <avr/sfr_defs.h> está incluido en todos los ficheros <avr/ioxxxx.h> que usan unas macros definidas en sfr_defs.h que hacen que los special function registers parezcan variables de C a las que podemos llamar para obtener su valor:

 
#define PORTA   _SFR_IO8(0x02)
#define EEAR    _SFR_IO16(0x21)
#define UDR0    _SFR_MEM8(0xC6)
#define TCNT3   _SFR_MEM16(0x94)
#define CANIDT  _SFR_MEM32(0xF0)

Por ejemplo, las limitaciones eléctricas en el microcontrolador son por puerto y por pin:

Práctica: Registros PORT Arduino

Un ejemplo que configura pines 0,1,2,3 como entradas digitales y los pines 4,5,6,7 como salidas digitales, se hace mediante el comando: DDRD = 0b11110000;

Si queremos poner todos los pines como salidas: DDRD = 0b11111111;

Al utilizar Registros DDR tenemos la ventaja de que con solo una instrucción podemos declarar el pin como entrada o salida, sin embargo con pinMode() necesitaríamos 8 instrucciones.

La relación entre los registros B, C y D y los pines es:

Leer los registros al poner varios pines como INPUT, INPUT_PULLUP y OUTPUT.

Probar con este código:

 
void setup() {
  //PORTD maps to Arduino digital pins 0 to 7
  pinMode(2, INPUT_PULLUP);
  pinMode(3, OUTPUT);
  digitalWrite(3, HIGH);
  pinMode(4, INPUT);
  pinMode(5, OUTPUT);
  digitalWrite(5, LOW);
  pinMode(6, INPUT_PULLUP);
  pinMode(7, OUTPUT);
  digitalWrite(7, HIGH);
  Serial.begin(9600);
}

void loop() {
  Serial.println("DDRD - The Port D Data Direction Register. DDRX, 1=OUTPUT, 0=INPUT");
  Serial.println(DDRD, DEC);
  Serial.println(DDRD, HEX);
  Serial.println(DDRD, BIN);  
  Serial.println("PIND - The Port D Input Pins Register. Lectura INPUT");
  Serial.println(PIND, DEC);
  Serial.println(PIND, HEX);
  Serial.println(PIND, BIN);
  Serial.println("PORTD - The Port D Data Register. Escritura OUTPUT");
  Serial.println(PORTD, DEC);
  Serial.println(PORTD, HEX);
  Serial.println(PORTD, BIN);
  delay(10000);
}

Poner los pines de input (2, 4 y 6) a 5V y GND y ver como cambia el contenido del registro PIN.

Solución Ejercicio07: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_Avanzado_2017/tree/master/Ejercicio07-Registos_PORT_simple

Manejo Registros de Pines Arduino

Par poder manejar registros disponemos de unas funciones especiales para registros.

Operadores Bit a Bit:

  • & (bitwise and)
  • | (bitwise or)
  • ^ (bitwise xor)
  • ~ (bitwise not)
  • << (bitshift left) (IMPORTANTE)
  • >> (bitshift right)

Tutorial de matemáticas con bits: http://playground.arduino.cc/Code/BitMath

Referencia de <avr/sfr_defs.h> Special Function Registers:

Manipulación de puertos: https://www.arduino.cc/en/Reference/PortManipulation

Tutorial de Bit Math: http://playground.arduino.cc/Code/BitMath

Escribir en un pin

Ya podemos decir al Atmega cómo serán utilizados sus pines, pero queremos saber cómo leer y escribir datos en dichos pines, de modo que para escribir datos en un determinado puerto, se utiliza el registro PORTx, éste es fácil de recordar, donde x es el nombre del puerto, y después de la configuración de un pin como salida es sólo una cuestión de poner 0 o 1 en el registro PORTx para controlar que el pin de este en estado alta o baja.

Un ejemplo sería:

 
DDRD = 0b11111111; // Todos los pines de PORTD son salidas.
PORTD = 0b11111111; // Todos los pines de PORTD están en estado alto.
DDRD = 0b11111111; // Todos los pines de PORTD son salidas.
PORTD = 0b00000000; // Todos los pines de PORTD están estado bajo.

Se debe tener cuidado cuando se utiliza PORTD y el puerto serie porque los pines 0 y 1 del PORTD son los utilizados por la USART y si se pone estos dos como entradas o salidas, la USART será incapaz de leer o escribir datos en los pines. Este es un ejemplo del cuidado que se debe tener al usar esta programación en lugar de la capa de programación que nos ofrece Arduino.

Leer un Pin

Para leer en el pin y poder leer los datos de los sensores o cuando se pulsa un botón de un pin digital configurado como entrada, vamos a utilizar un tercer registro llamado PINX, donde de nuevo x es el nombre del puerto donde se encuentra el pin, así que primero con DDRX decimos al microcontrolador que queremos algunos pines como entradas digitales, y luego usando PINX leemos sus valores

Ejemplo:

 
DDRD = 0b00000000; // Todos los pines del PORTD son entradas
char my_var = 0; // variable para guardar la información leída en PORTD
my_var = PIND; // Lee PORTD y pone la información en la variable

Es casi tan fácil como puede ser usar el digitalWrite o digitalRead de Arduino, pero con acceso directo al puerto se puede ahorrar espacio en el la memoria flash y también puede ganar mucha velocidad, porque las funciones Arduino puede tomar más de 40 ciclos de reloj para leer o escribir un solo bit en un puerto, además para leer un solo bit el código es bastante complejo con un montón de líneas que ocupan por lo menos unos 40 bytes, que podría ser un pequeño ahorro en flash y es un gran paso para acelerar cualquier programa.

No es normal que se necesite leer o escribir en un puerto completo en cada momento, por ejemplo, si queremos encender un LED, o leer un botón sólo tendrá que utilizar un pin, y escribir todos los bits uno a uno cada vez que queremos cambiar un valor en un puerto, es una tarea aburrida, pero la librería C de AVR tiene algunas pocas palabras definidas como Px(0..7), donde x es de nuevo el puerto que desea utilizar y 0..7 es el valor del pin individual de dicho puerto, por lo que para iluminar un LED debemos hacer algo como esto:

 
DDRD = (1<<PD2); // Configura el pin 2 de PORTD como salida.
PORTD = (1<<PD2); // El pin 2 de PORTD tiene ahora un valor lógico 1.

La operación << es un operador binario que desplaza hacia la izquierda el número de posiciones especificadas.

Para leer el estado de un botón:

 
DDRD = 0b11111101; //Configura pin 1 de PORTD como entrada y el resto salida
char my_var = 0; //Variable para guardar la información leída en PORTD
my_var = (PIND & (1<<PD1)); /* Lee el pin 1 de PORTD y lo coloca en la variable.*/

Explicación de la operación binaria bit a bit: https://www.arduino.cc/en/Reference/Bitshift

También se puede utilizar la macro Px0..7 varias veces en una misma instrucción, por ejemplo, en este código, se ejecutará algo de código sólo si al menos uno de los botones está pulsado:

 
DDRD = 0b11111100; // Los pines 0 y 1 de PORTD son entradas, y el resto salidas.
if (PIND & ((1<<PD0) | (1<<PD1))) {
/* Algún código dentro del if() que se ejecutará solo si al menos uno de los dos botones se encuentra activado. */
}

Mediante los registros también podemos controlar las resistencias internas de pullup. Cuando hay un botón que puede tener dos estados, uno es desconectado, y cuando se presiona hará una conexión entre los pines del microcontrolador y permite por ejemplo, conectarse a masa, pero cuando se desconecta, no hay nada que fuerce un valor estable en el pin de entrada, y el pin puede leer 1 ó 0 ya que el pin es muy sensible al ruido electromagnético, como una pequeña antena. Se puede resolver este problema de dos maneras similares, una es para conectar una resistencia de 10 Kohms o más entre el Vcc (+5 v) y el pin de entrada, o usar los pull-ups del microcontrolador que tienen integrados, también hace más simples los circuitos.

Para habilitar las resistencias pullup tenemos que hacer algo que puede resultar un poco extraño, no existe un registro dedicado para activar o desactivar el pull-ups, estos son activados o desactivados escribiendo 1 o 0 respectivamente en el registro PORTx cuando el registro DDRX se configuran como entradas. Ejemplo:

 
DDRD = 0b00000000; // Todos los pines de PORTD son entradas.
PORTD = 0b00001111; // Habilito las Pull-ups de los pines 0,1,2 y 3
char my_var = 0; // Creo una variable para guardar la información leída en PORTD.
my_var = PIND; // Leo PORTD y colocó la información en la variable.

Si se ejecuta este código, sin tener nada conectado a PORTD, los cuatro bits más altos de la variable my_var puede ser 0 ó 1, cualquier combinación posible de ellos porque son flotantes (actúan como pequeñas antenas), pero los cuatro bits más bajos leerá todos un 1 debido a que el pull-ups imponen una señal de 5V débil que se lee como un valor lógico 1.

En un sentido básico esto es todo lo que se necesita saber para dominar la manipulación directa de los puertos. La manipulación de bits enseña cosas más ingeniosas como las máscaras de bits, las operaciones AND, OR, NOT y XOR y cómo configurar y limpiar los bits en un registro y algunos buenos trucos con las operaciones de desplazamiento derecho e izquierdo, esta técnica puede acelerar un sketch y son muy útiles cuando se utilizan los puertos digitales.

Ventajas y Desventajas que nos ofrece al utilizar los registros:

  • Desventajas:
    • El código es mucho más difícil de depurar y mantener, y es mucho más difícil de entender. Solo lleva algunos microsegundos al procesador ejecutar código, pero podría llevar horas descubrir por qué no funciona y arreglarlo.
    • Es mucho más fácil causar mal funcionamiento no intencionado usando el acceso directo a un puerto. Con DDRD = B11111110, el pin 0 se debe dejar como una entrada. El pin 0 la línea de recepción (RX) en el puerto serial. Podría ser muy fácil causar el puerto serial deje de funcionar por cambiar el pin 0 a una salida.
  • Ventajas:
    • Se puede cambiar los pines de estado muy rápido, en fracciones de microsegundos. Las funciones digitalRead() y digitalWrite() se componen cada una de ellas de cerca de una docena de líneas de código, lo cual se convierte en unas cuantas instrucciones máquina.
    • Cada instrucción máquina necesita un ciclo de reloj a 16MHz, lo cual puede sumar mucho tiempo en aplicaciones muy dependientes del tiempo. El Registro PORT (Puerto) puede hacer el mismo trabajo en muchos menos ciclos de trabajo.
    • Algunas veces necesitamos configurar muchos pines exactamente al mismo tiempo. Por lo que usar las funciones digitalWrite (10,HIGH), seguida de la función digitalWrite (11,HIGH), causará que el pin 10 se ponga en nivel alto varios microsegundos después que el pin 11, lo cual puede confundir circuitos digitales conectados al Arduino, cuyo funcionamiento dependa del tiempo preciso del cambio de esos bits.
    • Si te estás quedando sin memoria, se pueden usar estos trucos para hacer que tu código sea más pequeño. Usando este método se necesitan muchos menos bytes de código compilado que si se hace un bucle para que se vaya cambiando cada pin uno por uno.

En las librerías podemos ver que usan la manipulación de registros en lugar de las instrucciones que nos ofrece el core de Arduino lo que las hace más rápidas.

En el siguiente enlace de github puede verse el fichero wiring_digital.c que es parte del core de Arduino donde se puede ver el código de las instrucciones pinMode(), digitalWrite() y digitalRead(): https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/wiring_digital.c

Y en el fichero pins_arduino.h está la definición de pines: https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/variants/standard/pins_arduino.h

Más información en:

Práctica: Operaciones con Entradas y Salidas Digitales

Leer Pin

  • Configurar mediante el registro DDRB el pin 9 como entrada y el resto de pines del PORT B como salida.
  • Configurar el pin 9 como INPUT_PULLUP mediante el registro PORTB
  • Usando el registro PORTB poner a LOW todos los pines de OUTPUT salvo el pin 10
  • Sacar por el monitor serie el estado de los pines del PORTB mediante el registro PINB

Solución Ejercicio08: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_Avanzado_2017/tree/master/Ejercicio08-Leer_pin

Velocidad Read

Para demostrar que la operación digitalRead() es lenta hacer un ejercicio que lea 1000, 10000 y 100000 veces el puerto 9 configurado como entrada, mediante digitalRead() y mediante PIND y saque el tiempo que ha costado cada operación.

Solución Ejercicio 09: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_Avanzado_2017/tree/master/Ejercicio09-Velocidad_Read

Microcontroladores Arduino a Fondo

Un microcontrolador es un integrado capaz de ser programado desde un ordenador y seguir la secuencia programada.

Como vimos anteriormente, Arduino es una plataforma para programar de forma sencilla algunos microcontroladores de la familia AVR de Atmel http://es.wikipedia.org/wiki/AVR y también microcontroladores Atmel ARM Cortex-M0+ o Intel http://www.intel.com/content/dam/support/us/en/documents/boardsandkits/curie/intel-curie-module-datasheet.pdf, y un hardware con ese MCU donde acceder físicamente a sus puertos.

Pero también Arduino y su entorno de programación se está convirtiendo en un estándar de facto para la programación de cualquier tipo de placas de desarrollo y prototipado, es decir, de otro tipo de microcontroladores no incluidos en los productos de Arduino. Gracias a la comunidad es posible programar otros microcontroladores de ST microelectronics o los ESP8266 y ESP32 de espressif http://espressif.com/ que podemos comprar con placas como la nodeMCU http://nodemcu.com/index_en.html o la bluepill http://wiki.stm32duino.com/index.php?title=Blue_Pill.

Puesto que Arduino es una plataforma open source disponemos de toda la documentación de los microcontroladores usados.

Por ejemplo, el microcontrolador de Arduino UNO es el ATmega328p y toda la documentación la tenemos en http://www.microchip.com/wwwproducts/en/ATmega328p. El data sheet completo es un documento de 442 páginas que podemos ver en http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_Datasheet.pdf. Cuando necesitemos más información o cómo funciona este microcontrolador debemos ir a este documento.

Como muestra de la documentación que tenemos disponible:

  • Página 34 tenemos el detalle de cómo se distribuye la memoria en el ATmega328p
  • Página 97 tiene en detalle los puertos digitales I/O y página 100 donde da el código para definir un pines a high y low y también como input pullup.
  • Página 436 tenemos los 3 encapsulados posibles para este microcontrolador
  • Página 316 nos muestra que este microcontrolador tiene un sensor de temperatura integrado y que es posible habilitarlo para leer su temperatura.
  • Página 378 se pueden ver los consumos de Arduino y la dependencia entre la frecuencia máxima de reloj y el Vcc.
  • Página 428 hay un resumen de todos los registros del microcontrolador y su dirección de memoria.

Cuando el microcontrolador ejecuta una instrucción que definimos en el sketch, internamente hace muchas operaciones y cada una de esas operaciones se ejecuta en un ciclo de reloj. Para el ATmega 328p que tiene una frecuencia de 16 MHz, es decir, cada ciclo tarda 0,0000000625 segundos = 0,0625 microsegundos = 62,5 nanosegundos

Así se ejecutaría una instrucción, en cada ciclo de reloj se ejecuta cada subinstrucción.

La importancia de conocer el ciclo de ejecución de instrucciones en un microcontrolador estriba en que en ocasiones es necesario calcular de forma precisa el tiempo de ejecución de los bucles para actuar en tiempo real.

Cálculos de la velocidad de las operaciones en Arduino: http://forum.arduino.cc/index.php?topic=200585.0

El método para calcular estas tablas está en http://forum.arduino.cc/index.php?topic=200585.0

Diferencia entre los microcontroladores de 8 bits, 16, y 32 bits, es tamaño de palabra que manejan e influye en los registros y direccionamiento de memoria: http://es.wikipedia.org/wiki/Palabra_(inform%C3%A1tica)

Este mismo análisis hecho con el ATmega328P, podemos hacerlo con otros microcontroladores:

Dentro de los microcontroladores, la tendencia es a usar MCUs de 32 bits con arquitectura ARM. La arquitectura ARM es el conjunto de instrucciones de 32 y 64 bits más ampliamente utilizado en unidades producidas. Concebida originalmente por Acorn Computers para su uso en ordenadores personales, los primeros productos basados ​​en ARM eran los Acorn Archimedes, lanzados en 1987.

La relativa simplicidad de los procesadores ARM los hace ideales para aplicaciones de baja potencia. Como resultado, se han convertido en dominante en el mercado de la electrónica móvil e integrada, encarnados en microprocesadores y microcontroladores pequeños, de bajo consumo y relativamente bajo costo. En 2005, alrededor del 98% de los más de mil millones de teléfonos móviles vendidos utilizaban al menos un procesador ARM. Desde 2009, los procesadores ARM son aproximadamente el 90% de todos los procesadores RISC de 32 bits integrados.

La arquitectura ARM es licenciable. Esto significa que el negocio principal de ARM Holdings es la venta de núcleos IP, estas licencias se utilizan para crear microcontroladores y CPUs basados ​​en este núcleo. ARM Cortex M es un grupo de procesadores RISC de 32 bits licenciados por ARM Holdings. La web oficial es http://www.arm.com/products/processors/cortex-m. Además existen otras familias de ARM: https://en.wikipedia.org/wiki/List_of_ARM_microarchitectures

Más información:

Para saber más de microcontroladores, ver estos recursos:

AVR vs PIC:

Esquema lógico de Arduino

El funcionamiento interno de un microcontrolador se puede explicar con un diagrama de bloques o esquema lógico, donde se ven en cada bloque cada unidad interna del microcontrolador y cómo se comunica con el restos de unidades.

Arquitectura de microcontroladores: http://sistdig.wikidot.com/wiki:arquitectura

Diagrama de bloques simplificado de un microcontrolador. Se compone de tres bloques fundamentales: la CPU ( central Processing Unit), memoria (RAM y ROM) y las entrada y salidas. Los bloques se conectan entre sí mediante grupos de líneas eléctricas denominadas buses o pistas. Los buses pueden ser de direcciones (si transportan direcciones de memoria o entrada y salida), de datos (si transportan datos o instrucciones) o de control (si transportan señales de control diversas). La CPU es el cerebro central del microprocesador y actúa bajo control del programa almacenado en la memoria. La CPU se ocupa básicamente de traer las instrucciones del programa desde la memoria, interpretarlas y hacer que se ejecuten. La CPU también incluye los circuitos para realizar operaciones aritméticas y lógicas elementales con los datos binarios, en la denominada Unidad Aritmética y Lógica (ALU: Aritmetic and Logic Unit).

Diagramas de bloques de un microcontrolador PIC:

Diagrama de bloques de un microcontrolador AVR de Atmel, incluido el ATmega328p:

El sistema de reloj determina la velocidad de trabajo del microcontrolador. Con 16 MHZ se ejecuta una instrucción en 62,5 nanosegundos (1/16 Mhz), correspondiente a 1 ciclo de máquina. El microcontrolador tiene diferentes opciones de circuito de reloj tal como lo muestra la siguiente imagen:

En un registro interno del microcontrolador se encuentran 5 opciones diferentes de reloj que son seleccionadas por medio de un Multiplexor. De este multiplexor sale la señal de reloj, la cual pasa a través de un prescaler, este prescaler se puede utilizar para reducir la frecuencia, reducir el consumo de energía y mejorar la estabilidad de la señal de reloj.El factor del prescaler va de 1 a 256, en potencias de 2. En Arduino, por defecto está desactivado, por consiguiente trabaja a la frecuencia del oscilador externo.

La señal de reloj es distribuida por la unidad de control a los diferentes bloques existentes: la CPU, las memorias, los módulos de entrada/salida, los contadores/timers, el SPI y la USART, al igual que el conversor Análogo Digital ADC.

El microcontrolador ATmega328  tiene tres timers (timer 0, timer 1, timer 2) que también se pueden usar como contadores. Los timers 0 y 2 son de 8 bits y el timer 1 de 16. Estos timers tienen un módulo de preescalado para su propia señal de reloj que puede provenir de su sistema de reloj interno o por pines externos (modo contador).

Son módulos que funcionan en paralelo a la CPU y de forma independiente a ella. El funcionamiento básico consiste en aumentar el valor del registro del contador al ritmo que marca su señal de reloj.

Usando el reloj interno o un cristal externo puede ser utilizado para medir tiempos puesto que utiliza una señal periódica, precisa y de frecuencia conocida; mientras que si la señal viene de un pin externo puede contar eventos que se produzcan en el exterior y que se reflejen en cambios de nivel de tensión de los pines.

Estos contadores también forman parte del generador de señales PWM y permiten configurar tanto la frecuencia como el ciclo de trabajo.

Registros de memoria

Todos los microcontroladores tienen un conjunto de instrucciones que suele ser un conjunto pequeño al tratarse de arquitectura RISC. La CPU cuenta con ese número de instrucciones que sabe ejecutar.

El conjunto de instrucciones para los microcontroladores Atmel de 8 bits es: http://www.atmel.com/Images/Atmel-0856-AVR-Instruction-Set-Manual.pdf

En el caso del ATmega328p, tiene una arquitectura RISC con 131 instrucciones, la mayoría de ellas ejecutadas en un solo ciclo de reloj.

Más información sobre la ALU del Atmega328p en la página 25 de http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_Datasheet.pdf  

Los registros son unas zonas concretas de la memoria RAM accesibles directamente desde la CPU o desde otros elementos del microcontrolador que permite hacer operaciones directamente y de forma más rápida.

Trabajar con registros de memoria puede ser difícil si sólo se escribe un programa en lenguaje ensamblador. Al utilizar el lenguaje de programación de alto nivel como es C basta con escribir el nombre del registro y su dirección de memoria, a partir de esa información, el compilador selecciona el registro necesario. Las instrucciones apropiadas para la selección del registro serán incorporadas en el código durante el proceso de la compilación.

Más información: https://en.wikipedia.org/wiki/Processor_register

La memoria RAM en el ATmega328p se divide en varias partes, todos los grupos de registros se ponen a cero al apagar la fuente de alimentación. La SRAM del 328p se distribuye de la siguiente forma:

Las primeras 32 localizaciones de la memoria son el fichero de registros (Register File). Las siguientes 64 localizaciones de memoria es la standard I/O memory y después las 160 siguientes localizaciones son la Extended I/O memory. Por último las siguientes 2K localizaciones son la memoria interna SRAM.

Las 5 diferentes modos de direccionamiento para los datos de memoria son:

  • Direct – The direct addressing reaches the entire data space.
  • Indirect with Displacement – The Indirect with Displacement mode reaches 63 address locations from the base address given by the Y- or Z-register.
  • Indirect – In the Register File, registers R26 to R31 feature the indirect addressing pointer registers.
  • Indirect with Pre-decrement – The address registers X, Y, and Z are decremented.
  • Indirect with Post-increment – The address registers X, Y, and Z are incremented.

Los 32 registros de propósito general, los 64 I/O Registers, los 160 Extended I/O Registers y los 2K bytes de SRAM interna en el dispositivo son todos accesibles mediante estos modos de direccionamiento.

Los registros de propósito general se utilizan para almacenar los datos temporales y los resultados creados durante el funcionamiento de la ALU. Los 32 General Purpose Working Registers están directamente conectados a la ALU, permitiendo ser accedidos dos registros de forma independiente en una sola instrucción ejecutada en un ciclo de reloj.

Seis de los 32 registros de propósito general pueden ser usados como 3 punteros de registros de 16-bit de acceso indirecto para direccionamiento del espacio de datos, habilitando cálculos eficientes de direcciones. Uno de estos punteros de dirección puede ser usado como un puntero de dirección para búsqueda en las memoria Flash del microcontrolador. Estos registros con funciones añadidas con los registros de 16-bit X-, Y- y Z-. Más información en la página 28 de http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_datasheet.pdf

Para más información ver página 35 de http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_datasheet.pdf

Los I/O registers localizados en las direcciones 0x20 a 0xFF, a diferencia de los registros de propósito general, su propósito es predeterminado durante el proceso de fabricación y no se pueden cambiar. Como los bits están conectados a los circuitos particulares en el chip (convertidor A/D, módulo de comunicación serial, etc), cualquier cambio de su contenido afecta directamente al funcionamiento del microcontrolador o de alguno de los circuitos. Esta es la forma en que a bajo nivel se interactúa por ejemplo con los pines del microcontrolador.

Un resumen de todos los registros I/O del ATmega328p se puede ver en la página 428 de http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_datasheet.pdf

Registros para:

Más información sobre registros y su uso en:

Práctica: Registros Arduino

Veamos algunos valores de los registros de Arduino con el sketch ShowInfo de http://playground.arduino.cc/Main/ShowInfo

Este sketch dispone de un menú que nos permite hacer varias operaciones, pulsar opción i (Show Information) y t (Timer Register Dump) para ver datos de los registros.

Este sketch es bastante complejo pero puede servir como plantilla cuando queramos acceder desde un sketch a un registro concreto.

Algunos detalles del sketch:

Solución Ejercicio 05: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_Avanzado_2017/tree/master/Ejercicio05-showInfo

Práctica: Temperatura Interna Microcontrolador

La mayoría de los nuevos chips AVR (utilizados en el Arduino) tiene un sensor de temperatura interno. No suele utilizarse, ya que no es exacta. Sin embargo, hay varias situaciones en las que se puede utilizar este sensor.

La temperatura interna es la temperatura dentro del chip, al igual que la temperatura de la CPU de un ordenador. Si el Arduino no está durmiendo, esta temperatura aumentará. Si los pines de salida se utilizan para dar corriente (por ejemplo encender leds) la temperatura interna aumenta más. Esta temperatura no puede usarse para medir la temperatura ambiente.

En situaciones con altas temperaturas una lectura de temperatura calibrada podría evitar daños. La mayoría de los chips AVR más recientes tienen un rango de temperatura de hasta 85 grados Celsius. Esta funcionalidad podría utilizarse para apagarse a 80 grados Celsius.

Más información: http://playground.arduino.cc/Main/InternalTemperatureSensor

Ejecutar el sketch leer y entender lo que hace y probarlo. Comparar con otros Arduinos y calibrar.

Ver en la página 306 y 316 de la documentación del microcontrolador: http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_datasheet.pdf

La función GetTemp() es la que calcula la temperatura. Para ello configura la referencia interna del conversor analógico digital y lo habilita. Luego lee el dato del ADC mediante el registro ADCW y hace el cálculo para obtener la temperatura.

Como el offset puede variar de una MCU a otra, para calibrar, lo mejor es leer la temperatura de una placa fría y comparar con la temperatura exterior.

Solución Ejercicio 06: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_Avanzado_2017/tree/master/Ejercicio06-InternalTemperature

UART y USB en Arduino

Ya hemos visto  anteriormente que la UART o USART es el puerto serie hardware que todos los microcontroladores tienen al menos uno y la comunicación serie es la base de casi cualquiera de las comunicaciones de los microcontroladores.

El “Bus Universal en Serie”, en inglés: Universal Serial Bus más conocido por la sigla USB, es un bus estándar industrial que define los cables, conectores y protocolos usados en un bus para conectar, comunicar y proveer de alimentación eléctrica entre computadores, periféricos y dispositivos electrónicos.

Su desarrollo partió de un grupo de empresas del sector que buscaban unificar la forma de conectar periféricos a sus equipos, por aquella época poco compatibles entre si, entre las que estaban Intel, Microsoft, IBM, Compaq, DEC, NEC y Nortel. La primera especificación completa 1.0 se publicó en 1996, pero en 1998 con la especificación 1.1 comenzó a usarse de forma masiva.

La forma que tenemos de comunicar Arduino con el ordenador y para programarlo es mediante el USB. Casi todos los Arduinos y compatibles tienen un interfaz USB y sino usaremos un cable FTDI que nos hará la función de convertirnos el interfaz serie de la UART de Arduino a un interfaz USB. No confundir con lo visto anteriormente de uso del ICSP para programar, puesto que en ese caso programamos directamente la MCU sin necesidad de un bootloader.

El interfaz USB en el ordenador nos permite tener un puerto serie virtual que usamos para conectar con Arduino. Además de comunicación, USB es un interfaz de alimentación.

Físicamente un  USB tiene 4 pines:

Pin 1=> Alimentación con un voltaje de 5V DC
Pin 2 y 3 => Sirven para la transmisión de datos del BUS
Pin 4 = Masa o tierra

Aunque la mayoría de pc’s ya ofrecen protección interna se incorpora un fusible rearmable de intensidad máxima 500mA con la intención de proteger tanto la placa Arduino como el bus USB de sobrecargas y cortocircuitos. Si circula una intensidad mayor a 500mA por el bus USB (Intensidad máxima de funcionamiento), el fusible salta rompiendo la conexión de la alimentación. Podemos probar esto cortocircuitando GND y 5V en Arduino.

En el caso del USB el 0 y 1 se distingue por la diferencia de voltaje entre D+ y D-

Más información de USB:

Y para saber todo sobre USB ver “USB in a nutshell”: http://www.beyondlogic.org/usbnutshell/usb1.shtml

Para que un Arduino tenga un interfaz USB, necesita de un chip que ofrezca un interfaz USB, en algunos casos el propio microcontrolador ya dispone de ese interfaz y en otros caso utiliza un segundo microcontrolador con interfaz USB.

Tutorial USB:

Cuando conectamos un USB al ordenador, necesitamos un driver o trozo de software que nos  implemente la comunicación con el USB y nos monte un puerto serie virtual. Estos drivers deben estar certificados por http://www.usb.org/home y pagar las correspondientes licencias de uso. En el caso de Arduino los drivers ya están incluidos desde windows 8.1 y tienen su licencia de uso, por ese motivo cuando conectamos Arduino, automáticamente nos reconoce el dispositivo.

Todos los dispositivos USB tienen un USB vendor ID (VID) que identifica al fabricante y que es otorgado por http://www.usb.org/developers/vendor/ y un product ID (PID) que identifica el producto de ese vendedor. De esta forma es posible identificar un dispositivo USB por el ordenador. Más información: http://www.oshwa.org/2013/11/19/new-faq-on-usb-vendor-id-and-product-id/

Listado de VID:

Como sabemos hay dos Arduinos (arduino.cc y arduino.org) y por lo tanto hay dos VID para Arduino:

  • 2a03  dog hunter AG (arduino.org)
  • 2341  Arduino SA (arduino.cc)

Ejemplo de problemas con el uso de los drivers USB según el chip que lleve Arduino: http://blog.make-a-tronik.com/instalando-drivers-usb-serial-ch340g-para-arduino/

Cada dispositivo necesita tener unico product id y vendor id. Los VID son vendidos a los fabricantes y los PID son elegidos por cada compañía. Por ejemplo FTDI es el propietadior del VID #0403 y ellos dan a sus chips los IDs entre #0000 y #FFFF (65536 PIDs). Arduino tiene su propio VID y para el Arduino UNO el PID es el #0001

Si quisiéramos hacer cuenta propia placa compatible con Arduino hay varias opciones:

  • Usar un chip FTDI en lugar del 16u4 que ya tiene un VID
  • Si quieres hacer más de una placa y comercializar, tendrás que comprar un VID a usb.org en http://www.usb.org/developers/vendor/ y pagar la licencia
  • Si es para hacer una placa como experimento, puedes elegir un VID/PID que no interfiera con ninguno de los dispositivos del ordenador.
  • También es posible comprar licencias para un VID/PID de empresas que desarrollen dispositivos USB.

Sin embargo no es posible usar el VID de Arduino cuando quieres distribuir tu propia placa compatible con Arduino. Leer: http://electronics.stackexchange.com/questions/16665/arduino-uno-usb-vid-pid

En linux es posible ver mejor el proceso de reconocimiento de un dispositivo USB, leer: http://playground.arduino.cc/Linux/All

Tester de VID y PID basado en Arduino: https://learn.adafruit.com/simple-arduino-based-usb-vid-and-pid-tester/lets-do-this-thing

Para actualizar el firmware del Atmega16u2 que nos hace de interfaz entre el USB y el microcontrolador principal del Arduino UNO, seguir este procedimiento: http://arduino.cc/en/Hacking/DFUProgramming8U2.

Para comprobar la versión de firmware ir al gestor de dispositivos en Windows y buscar el Arduino, luego botón derecho y propiedades y buscar en driver details el VID, PID y el revision number.

La última versión del firmware está en https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/firmwares/atmegaxxu2 y usar un programador DFU como flip http://www.atmel.com/tools/flip.aspx para cargar el nuevo firmware.

Cuando usamos un Arduino basado en el microcontrolador ATmega32u4 como el leonardo, nos encontramos con que se hace una re-enumeración del puerto serie en el reset.

Puesto que estas placas no tienen un chip dedicado para manejar la comunicación serie y tienen un interfaz USB integrado, eso significa que el puerto serie es virtual, es una implementación software tanto en el ordenador como en la placa. Así como el ordenador crea una instancia del puerto serie cuando lo enchufas, el Arduino Leonardo crea una instancia serie cuando se ejecuta el bootloader. La placa es una instancia de un USB driver Connected Device Class (CDC), esto significa que cada vez que reseteas la placa, la conexión USB serie es rota y restablecida de nuevo. La placa desaparecerá de la lista de puertos serie y la lista se re-enumerará. Cualquier programa que tenga una conexión serie abierta con el Arduino Leonardo perderá su conexión. En el caso del Arduino cuando se resetea la placa se está reseteando el procesador principal ATmega328p sin cerrar la conexión USB que es mantenida por el microcontrolador secundario ATmega16u2. Esta diferencia de comportamiento tiene implicaciones en la carga y comunicación con el microcontrolador.

Los Arduinos basados en el ATmega32u4 aparecen también como un dispositivo genérico USB de teclado y ratón, y puede ser programado para controlar estos dispositivos usando las clases MouseKeyboard https://www.arduino.cc/en/Reference/MouseKeyboard, de forma que Arduino puede conportarse como un teclado u un ratón.

Más información: https://en.wikipedia.org/wiki/USB_communications_device_class

Cuando usamos un Arduino basado en el ATmega32u4 debemos usar la función if(serial) https://www.arduino.cc/en/Serial/IfSerial que indica si el puerto serie está preparado y en el caso del Leonardo, indica si la USB CDC serial connection está abierta. De esta  forma nos aseguramos que arranca el programa de Arduino cuando ya hay conexión entre el Arduino y el ordenador, sino podríamos perder los primeros envíos del Arduino al ordenador.

En el Arduino Leonardo, la clase Serial se refiere a la comunicación USB CDC, para la comunicación por TTL en los pines 0 y 1, se debe usar la clase Serial1.

En los Arduino basados en Atmega32u4 como el Arduino Leonardo usar:

void setup() { 
 //Initialize serial and wait for port to open:
  Serial.begin(9600); 
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB
  }
} 

V_USB es una implementación software de un dispositivo USB para microcontroladores USB, haciendo posible construir hardware USB con casi cualquier MCU AVR, sin necesidad de un chip adicional. Se trata de un puerto USB software. Más información:

USART Arduino

La USART es el puerto serie de los microcontroladores que en el caso de Arduino están conectadas a los pines 0 y 1.

UART: http://es.wikipedia.org/wiki/Universal_Asynchronous_Receiver-Transmitter. Las funciones principales de la UART son de manejar las interrupciones de los dispositivos conectados al puerto serie y de convertir los datos en formato paralelo, transmitidos al bus de sistema, a datos en formato serie, para que puedan ser transmitidos a través de los puertos y viceversa.

Arduino se conecta a nuestro ordenador a través del puerto USB, pero el puerto USB se debe conectar al microcontrolador a través del puerto serie, por ello debemos entender cómo están relacionados el puerto USB y el puerto serie.

En un Arduino usamos el puerto USB para tres funciones: cargar nuestro programa ya compilado en la memoria flash, conectarnos al puerto Serie (UART) predefinido en cada Arduino para comunicarnos durante la ejecución del programa y adicionalmente alimentar a Arduino. Todo ello se puede hacer sin la necesidad del puerto USB, pero dada la facilidad de uso y que todos los ordenadores disponen de un puerto USB, nos facilita mucho hacer estas operaciones.

El puerto serie conectado al USB lo usamos como puerto de consola o puerto de debug.

La UART normalmente no genera o recibe directamente las señales externas entre los diferentes módulos del equipo. Usualmente se usan dispositivos de interfaz separados para convertir las señales de nivel lógico del UART hacia y desde los niveles de señalización externos, como puede ser RS232 o RS485. Una UART está normalmente en un circuito integrado usado para comunicaciones serie sobre un ordenador u otro dispositivo periférico.

Los microcontroladores como el ATmega328p tiene USART (Universal Syncronous Asyncronous Receiver Transmitter). Una USART puede trabajar igual que una UART, pero tiene la capacidad adicional de actuar como síncrona, esto significa que los datos van sincronizados con una señal de reloj. El reloj es recuperado desde los propios datos o enviado como como una señal externa. En ese caso no se usan los bits de start y stop, lo que permite una velocidad en baudios mayor al operar en modo síncrono porque la temporización de los bits tienen una cierta garantía y más bits pueden ser usados para datos en lugar de cabeceras.

Por el contrario la UART dispone de una señal de reloj interno y los datos en el bus pueden tener una temporización más irregular. Las UART requieren bits de start y stop y los datos asíncronos sólo se sincronizan con los bits de start y stop. La palabra asíncrono indica que la UART recupera la temporización de los caracteres desde el flujo de datos, usando el bit de start y el de stop para indicar el marco de cada carácter.

En la transmisión síncrona, la señal de reloj es recuperada de forma separada del stream de datos, esto mejora la eficiencia de la transmisión al haber más bits de datos en la transmisión. Una conexión asíncrona no manda nada cuando el dispositivo que transmite no tiene nada que mandar, pero un interfaz síncrono debe mandar caracteres para mantener el sincronismo entre emisor y receptor, normalmente el carácter ASCII “SYN” se encarga de rellenar este hueco y esto puede ser hecho automáticamente por el dispositivo transmisor.

Diferencia entre UART y USART:

Para saber todo sobre la USART de Arduino y la comunicación serie ver:

La configuración de la UART de los microcontroladores AVR necesita acceder a unos registros:

  • USART Band Rate Register – UBRRH &UBRRL
  • USART Control and Status Register A – UCSRA
  • USART Control and Status Register B – UCSRB
  • USART Control and Status Register C – UCSRC
  • USART Data Buffer Register – UDA

UART es un módulo de hardware que traduce los datos de paralelo a serial para ser transmitidos, las UARTs son usadas comúnmente en conjunto con estándares de comunicación como EIA, RS-232, RS-422 o RS-485. la designación “universal” indica que el formato de los datos y las velocidades de transmisión pueden ser configuradas.

UART es normalmente un circuito integrado individual usado para comunicaciones de un sistema de cómputo, son normalmente incluidas en microcontroladores. Actualmente estos circuitos pueden comunicarse de manera sincrónica y asincrónica y son conocidos como USART. Las funciones principales de chip UART son: manejar las interrupciones de los dispositivos conectados al puerto serie y convertir los datos en formato paralelo, transmitidos al bus de sistema, a datos en formato serie, para que puedan ser transmitidos a través de los puertos y viceversa.

La USART de Arduino es un puerto de comunicaciones muy versátil (UNIVERSAL SYNCHRONUS and ASYNCHRONUS serial RECEIVER and TRANSMITTER). Se puede configurar como síncrono o como asíncrono. Los pines de conexión de la placa UNO que se utilizan con este periférico interno son los siguientes:

  • PIN 0 (RXD)   – Puerto D del microcontrolador  PD0
  • PIN 1 (TXD)   – Puerto D del microcontrolador  PD1
  • PIN 13 (SCK) – Puerto B del microcontrolador  PB15 (Sólo para el modo síncrono)

Para poder realizar la comunicación, los dispositivos que se vayan a comunicar deben conocer varios aspectos de ésta. El primero es la velocidad a la que se va a realizar, es decir a qué baudios se va a realizar la transmisión.

La comunicación comienza con una señal de Start, seguida de los bits a enviar, y se pueden seleccionar entre 5 y 9 bits a mandar, después tenemos que seleccionar si va a haber un bit de paridad para comprobar errores y por último si tenemos uno o dos bits de Stop. Estos parámetros han de estar configurados de igual manera en los dos dispositivos que se van a comunicar.

En la placa UNO se utiliza la USART para que el Atmega328  se comunique con el otro microcontrolador ATmega16U2 que hace la interface al puerto USB. Los leds (TX LED y RX LED) parpadean para indicar actividad en el puerto serie a través de la USART.

El baudio es una unidad de medida utilizada en telecomunicaciones, que representa el número de símbolos por segundo en un medio de transmisión digital. Cada símbolo puede codificar 1 o más bits dependiendo del esquema de modulación, un bit siempre representa dos estados, por lo tanto baudios por segundo no siempre es equivalente a bits por segundo, los símbolos son las unidades de información estas se representan en bits, de manera que la tasa de bits será igual a la tasa de baudios sólo cuando sea 1 bit por símbolo.

Principales características del módulo USART, para mayor información ver datasheet del microcontrolador.

  • Full Duplex Operation (Independent Serial Receive and Transmit Registers)
  • Asynchronous or Synchronous Operation
  • Master or Slave Clocked Synchronous Operation
  • High Resolution Baud Rate Generator
  • Supports Serial Frames with 5, 6, 7, 8, or 9 Data bits and 1 or 2 Stop Bits.

Antes de continuar es importante resaltar que AVR UART y USART son totalmente compatibles en términos de registros, generación de tasa de baudios, operaciones de buffer y funcionalidad de buffer en el transmisor/receptor. A continuación un resumen rápido de la configuración del módulo UART.

  1. Establecer la velocidad de transmisión en emisor y receptor debe ser la misma para poder realizar la comunicación.
  2. Establecer el número de bits de datos que deben ser enviados.
  3. Mantener el buffer listo, si es una transmisión cargarlo con el dato a transmitir, si es una recepción almacenar el dato recibido para poder recibir mas datos sin perder información
  4. Por último habilitar el transmisor/receptor de acuerdo con el uso que se le desee dar.

En el caso de la transmisión sincrónica (USART) es necesario enviar el reloj del sistema el microcontrolador que envía esta señal se llama Maestro y el otro se denomina esclavo; para transmisión Asíncrona no se hace esta denominación para los transmisores/receptores.

La tasa de baudios se establece en el registro de 16 bits UBRR:

Más información: http://sistdig.wikidot.com/wiki:usart  

Más información sobre la USART del MCU ATmega328p, ver página 170 de http://www.atmel.com/images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf

Estándares de comunicación serie que definen las características eléctricas de los drivers y los receptores:

Explicación de la diferencia entre comunicación RS232 y TTL: RS-232 vs. TTL Serial Communication y http://www.atmel.com/Images/doc4322.pdf

USB to Serial en Arduino

Cuando conectamos Arduino a un ordenador mediante un cable USB, el ordenador instala un puerto serie virtual (COM) desde el cual podemos acceder al puerto serie de Arduino. Por este motivo es necesario instalar en el ordenador los drivers de interfaz USB de los microcontroladores de Atmel, esto se hace automáticamente al instalar el IDE de Arduino.

Arduino UNO como otros Arduinos disponen de microcontroladores que no tienen un interfaz USB incorporado, por lo tanto no se pueden conectar directamente a un Ordenador. Para ello usan un segundo microcontrolador que hace de intermediario, generalmente un ATmega16u2 que dispone de un interfaz USB. Este microcontrolador dispone de un firmware especial que facilita la carga del programa en la flash del Arduino y comunicar posteriormente por el USB.

Este firmware y su código se puede ver en https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/firmwares/atmegaxxu2/arduino-usbserial

Los microcontroladores de 8 bits mega AVR con interfaz USB están configurados de fábrica con un bootloader USB, este permite hacer un In-System Programming desde el interfaz USB sin eliminar la parte del sistema o sin una aplicación preprogramada y sin un un interfaz externo de programación.

USB DFU (Device Firmware Update) Bootloader: http://www.atmel.com/Images/doc7618.pdf

Más información del microcontrolador en: http://www.atmel.com/Images/Atmel-7766-8-bit-AVR-ATmega16U4-32U4_Datasheet.pdf

Niveles lógicos

Los niveles lógicos de los microcontroladores, son los niveles físicos de voltaje por los que se interpreta un microcontrolador un 1 y un 0. Niveles lógicos: https://en.wikipedia.org/wiki/Logic_level

En el caso de los ATmega AVR usan Tecnología TTL: http://es.wikipedia.org/wiki/Tecnolog%C3%ADa_TTL. Su tensión de alimentación característica se halla comprendida entre los 4,75V y los 5,25V (como se ve, un rango muy estrecho). Normalmente TTL trabaja con 5V. Los niveles lógicos vienen definidos por el rango de tensión comprendida entre 0,0V y 0,8V para el estado L (bajo) y los 2,2V y Vcc para el estado H (alto). La velocidad de transmisión entre los estados lógicos es su mejor base, si bien esta característica le hace aumentar su consumo siendo su mayor enemigo. Motivo por el cual han aparecido diferentes versiones de TTL como FAST, LS, S, etc y últimamente los CMOS: HC, HCT y HCTLS. En algunos casos puede alcanzar poco más de los 400 MHz.

TTL logic levels: https://learn.sparkfun.com/tutorials/logic-levels/ttl-logic-levels

Interesante nota de TI sobre como elegir la solución más adecuada para la conversión de niveles lógicos.

Niveles lógicos de Arduino: https://learn.sparkfun.com/tutorials/logic-levels/arduino-logic-levels

A medida que la tecnología avanza se crean dispositivos que necesitan menor consumo eléctrico y usan un menor voltaje de base (Vcc = 3.3 V en lugar de 5V). La técnica de fabricación es también diferente para dispositivos de 3.3V que permiten una huella menor y costes más pequeños. Para asegurar la compatibilidad de los dispositivos a 3.3V, se puede ver que la mayoría de los niveles de voltaje son casi los mismos para dispositivos de 3.3V y los de 5V. Un dispositivo de 3.3V puede interactuar con uno de 5V sin componentes adicionales. Por ejemplo un 1 lógico (HIGH) de un dispositivo de 3.3V será al menos 2.4V, esto sigue siendo interpretado como un 1 lógico en un dispositivo de 5V porque está por encima  de VIH que es 2V.

Sin embargo hay que tener en cuenta que un dispositivo de 3.3V es tolerante a señales de 5V, esta especificación es el “maximum input voltage” del dispositivo. En ciertos dispositivos de 3.3V, cualquier voltaje supuerior a 3.6V provocará un daño permanente en el chip, para evitar esto se puede usar un simple divisor de tensión (https://learn.sparkfun.com/tutorials/voltage-dividers) como una resistencia de 1KΩ y una de 2KΩ para bajar los 5V o usar un logic level shifter como https://www.sparkfun.com/products/12009

Voltaje 5V vs 3.3V http://jeelabs.org/2010/12/16/voltage-3-3-vs-5/

Cable FTDI

Cable FTDI es la forma más fácil de conectar el microcontrolador a un ordenador por USB. Consiste en un chip de conversión USB a Serie. Como ocurre con Arduino cuando lo conectamos, necesitamos los drivers de windows, cuando conectamos un cable FTDI también necesita sus drivers. Los driver FTDI vienen integrados en el IDE de Arduino.

FTDI es el fabricante más popular de chips conversores de USB a serie.

Drivers: https://web.archive.org/web/20141005060035/http://www.ftdichip.com/Drivers/VCP.htm

Productos de FTDI:

Chip FTDI:

FT231X tiene un buffer de comunicación mayor comparado con el FT232R. El FT231X tiene los niveles lógicos de 3.3 V y con entradas tolerantes a 5V. FTDI provee de drivers para los dispositivos USB incluyendo driver certificados para Windows, incluido Windows 10.

Interesante artículo sobre la comunicación serie con MCUs AVR: http://www.evilmadscientist.com/2009/basics-serial-communication-with-avr-microcontrollers/

Cable par comunicar con dispositivos de 3.3V: https://www.sparkfun.com/products/9873

Al comprar un cable FTDI hay que estar atento al pineado que no es el que usan los Arduino Ethernet y otros elementos, es posible que para conectarlo a un Arduino haya que intercambiar algunos pines. Este es el pineado correcto y los colores de los cables para un FTDI para Arduino:

ftdi

Existen en el mercado cable con otros colores y el pineado desordenado. Estos son los colores del cable y a que corresponde:

  • Red wire: 5V
  • Black wire: GND
  • White wire: RXD
  • Green wire: TXD
  • Yellow wire: RTS
  • Blue wire: CTS

Y el pineado bueno para arduino queda así:

ftdi luis

Los pines CTS y RTS es el flow control (control de flujo). El pin RTS es una salida y debe conectarse al pin de entrada CTS del otro dispositivo de la conexión a la UART.

  • Si el pin RTS está a 0, indica que puede aceptar más datos en el pin RXD
  • Si el pin RTS está a 1, indica que no puede aceptar más datos.

Los cambios de estado del pin RTS ocurren cuando el buffer del chip alcanza los últimos 32 bytes de espacio para dejar tiempo al dispositivo externo para que pare el envío de datos.

El pin CTS es una entrada y debe ser conectado a la salida RTS del dispositivo al otro extremo.

  • Si el pin CTS está a 0, indica que el dispositivo externo puede aceptar más datos y transmitirá por el pin TXD.
  • Si el pin CTS está a 1, indica que el dispositivo externo no puede aceptar más datos y parará la transmisión de datos en los siguientes 0 – 3 caracteres, dependiendo de lo que haya en el buffer de salida. Se debe tener en cuenta que al ser un dispositivo USB opera en base a paquetes y no a bytes.

CTS y RTS en RS232: https://en.wikipedia.org/wiki/RS-232#RTS.2C_CTS.2C_and_RTR

También en algunos dispositivos serie nos podemos encontrar con el pin DTR que es el control de flujo https://en.wikipedia.org/wiki/Data_Terminal_Ready. El DTR permite a Arduino hacer el auto reset cuando un nuevo sketch es cargado a la memoria flash. Esto permite cargar un sketch sin tener que pulsar el botón de reset.

Una de las líneas del hardware flow control (DTR) del ATmega8U2/16U2, están conectadas a la línea de reset del MCU ATmega328P a través de un condensador de 100 nanofaradios. Cuando esta línea se pone a 0 V, es capaz de resetear el MCU de Arduino. El IDE de Arduino usa esta capacidad para permitir cargar código a Arduino simplemente presionando el botón upload y hace que el el timeout del bootloader pueda ser menor y estar mejor coordinado con el comienzo de la carga del sketch.

Cuando se detecta un flujo de datos a través del interfaz USB conectando un ordenador al Arduino, automáticamente se resetea la MCU de Arduino y durante el siguiente medio segundo, el bootloader se ejecuta en Arduino. Como el bootloader está programado para ignorar datos malformados (cualquier cosa que no sea nuevo código de un sketch), interceptará los primeros bytes de los datos enviados a la placa tras la conexión. Por este motivo si un sketch está programado para recibir una comunicación por el puerto serie tras iniciarse, hay que asegurarse que el software que manda esa información espera al menos un segundo después de que se abra la conexión hasta que se manden los primeros datos.

Ojo con los chips FTDI falsos: http://hackaday.com/2016/02/01/ftdi-drivers-break-fake-chips-again/

Diferencia entre programadores AVR o adaptadores FTDI:

  • Los programadores AVR son más poderosos puesto que permite programar cualquier AVR, incluso los que vienen son bootloader de fábrica, pero también significa que es posible brickear el chip. Usando un programador se debe tener en cuenta que se sobreescribirá el bootloader.
  • Los adaptadores FTDI pueden enviar cualquier dato por puerto serie, incluyendo programar AVRs con un bootloader en ellos.

Más información de uso de un FTDI: https://cdn-learn.adafruit.com/downloads/pdf/ftdi-friend.pdf

Para las prácticas del curso disponemos de esta placa FTDI para programar tanto dispositivos a 5v como a 3.3 (los que habitualmente hay dispobles suelen ser a 5V), con selector de voltaje: http://www.tinyosshop.com/index.php?route=product/product&product_id=600, además este tiene DTR para conectar al reset y poder programar un arduino.

El FTDI usado usa el chip FT232 con características:

  • Interfaz estándar compatible con el tipo de controlador oficial de Arduino
  • Chipset FT232 original, funcionamiento estable, indicador de transmisión y recepción.
  • opción de fuente de alimentación 5V/3.3V
  • Chip de FT232R es la solución de un solo chip, con oscilador de cristal

FT232R tiene algunas funciones útiles:

Los microcontroladores AVR de Arduino para programarlos directamente por puerto serie, van a 5V, aunque si se usa un programador de 3.3v funciona porque el rx de arduino toma 0 cuando el voltaje es inferior a 0.7V y 1 cuando es superior a 2,4V.

Tal y como indica en las características del Arduino UNO en https://www.arduino.cc/en/main/arduinoBoardUno: “The Uno has a number of facilities for communicating with a computer, another Uno board, or other microcontrollers. The ATmega328 provides UART TTL (5V) serial communication, which is available on digital pins 0 (RX) and 1 (TX).”

Pero no solo se puede programar Arduino mediante el USB o por el puerto ICSP, sino que también es posible hacerlo mediante un cable FTDI conectado al puerto serie de Arduino. Para programar con el adaptador FTDI, necesitaré de un bootloader, con ICSP no.

Bootloader

Cuando cargamos un programa en Arduino desde el USB con el IDE, estamos haciendo uso del bootloader, se trata de un pequeño programa que ha sido guardado previamente en el microcontrolador de la placa y que nos permite cargar código sin necesidad de hardware adicional. El bootloader solo está activo unos segundos cuando se resetea el Arduino y después comienza el sketch que está cargado en la flash de Arduino y que hemos programado y subido a la placa.

El bootloader se ejecuta cuando el microcontrolador se enciende o se pulsa el botón reset, durante un corto espacio de tiempo espera que le llegue por el puerto serie un nuevo sketch desde el IDE de Arduino (este distingue un sketch de otra cosa porque tiene un formato definido). Si llega un sketch, este es guardado en la memoria flash y ejecutado, en caso contrario ejecuta el sketch anteriormente cargado.

La mayoría de los Arduinos tienen la función autoreset que permite al IDE de Arduino subir el código sin tener que pulsar el botón de reset.

El bootloader hace que parpadee el pin 13 (led integrado en la placa) cuando se ejecuta.

La mayoría de los microcontroladores de AVR pueden reservar una zona de la memoria flash (entre 256B a 4 KB) para el bootloader. El programa bootloader reprograma el microcontrolador para guardar en la memoria flash el código binario a través de cualquier interface disponible.

El bootloader de Arduino está programado en la memoria flash del ATMega328p y ocupa 0,5 KB de los 32KB disponibles. Este bootloader viene precargado en la memoria flash del microcontrolador y es lo que diferencia el ATMega328p de Arduino de otro que viene de fábrica.

El Arduino UNO viene con el microcontrolador ATMega328p precargado con un bootloader que permite cargar nuevo código sin un programador. El bootloader se comunica usando el protocolo STK500.

El protocolo STK500 http://www.atmel.com/Images/doc2525.pdf es propio de Atmel. Es un protocolo serie y los programadores emulan este protocolo sobre un puerto serie virtual en un USB. Originalmente STK500 fue un programador fabricado por Atmel y a raíz de ello liberaron el protocolo STK500.

En este enlace es posible obtener el código en C del protocolo STK500: http://www.atmel.com/dyn/resources/prod_documents/avr061.zip

Arduino decidió usar avrdude y STK500 serial bootloader para programar o cargar nuevos programas en Arduino sin necesidad de HW adicional. El bootloader de Arduino es esencialmente el bootloader STK500 de Atmel.

Las MCUs AVR de 8bits ATMega con interfaz USB integrado como son el ATmega16U2 y ATmega8U2, vienen de fábrica un USB bootloader en la sección de arranque de la flash. Este bootloader USB permite hacer In-System programming desde USB host controller sin la necesidad de un HW externo. En este documento se describe las funcionalidades del USB bootloader: http://www.atmel.com/Images/doc7618.pdf

El puerto serie durante el proceso de bootloader funciona a 19200 baudios.

El bootloader estándar de Arduino usa el protocolo STK500 versión 2 y por ese motivo es el valor que usamos en el IDE de Arduino.

Los comandos para “quemar” el bootloader en Arduino usan una herramienta open nsource llamada avrdude, de la que se hablará más tarde. Hay cuatro pasos a la hora de cargar el bootloader:

  • Desbloquear la sección de bootloader en el chip
  • Configurar los fuses en la MCU
  • Cargar el código del bootloader en el microcontrolador
  • Bloquear la sección del bootloader en la MCU

Todo esto es controlado por una serie de preferencia en el fichero de preferencias de Arduino: https://www.arduino.cc/en/Hacking/Preferences

Cuando el Boot Reset Fuse (BOOTRST) está configurado, el contador de programa en la memoria flash es inicializado a una dirección de memoria de un bloque en el extremo superior de la memoria (esto depende de como se hayan configurado los fuses, ver https://www.arduino.cc/en/uploads/Main/Arduino_Uno_Rev3-schematic.pdf para más información). El código que comienza no puede hacer nada, si realmente se quiere se podría poner su propio programa de allí si se utiliza un ICSP (los bootloaders por lo general no puede sobrescribirse a sí mismos).

Funciones de la AVR-lib para bootloader: http://www.nongnu.org/avr-libc/user-manual/group__avr__boot.html

El bootloader se trata de un programa especial y puede leer datos de una fuente externa como UART, I2C, CAN, etc… para reescribir el programa guardado en la memoria flash del microcontrolador. El bootloader busca un evento especial que puede ser cualquier cosa, pero para el desarrollo es más conveniente algo en el bus de datos que será puesto en la flash de la MCU. Si el bootloader ve ese evento especial, entonces entra en modo bootloading en el que hace un reflash de la memoria de programa del microcontrolador, pero si no aparece el evento, pasa el control al código del usuario.

El bootloader no consume RAM y los únicos efectos que tiene son modificaciones en los registros del hardware periferivo, pero un buen bootloader no debe dejar ningún estado perjudicial en el que encienda periféricos que malgasten energía cuando pones la MCU en modo sleep. Es una buena práctica inicializar los periféricos que se usan, así aunque el bootloader haga algo extraño, habremos inicializado como queremos que se comporte.

Avrdude es un programa para descargar y cargar a la memoria de las MCUs AVR de Atmel. Puede programar la Flash y la EEPROM y es soportado por el puerto serie.

Avrdude funciona mediante la línea de comandos y soporta los siguientes tipos de programadores:

  • Atmel’s STK500
  • Atmel’s AVRISP and AVRISP mkII devices
  • Atmel’s STK600
  • Atmel’s JTAG ICE (both mkI and mkII, the latter also in ISP mode)
  • appnote avr910
  • appnote avr109 (including the AVR Butterfly)
  • serial bit-bang adapters
  • PPI (parallel port interface)

Avrdude junto con otras herramientas se encuentran en: C:\Program Files (x86)\Arduino\hardware\tools\avr

Para cargar un sketch con avrdude en Arduino en lugar de usar el IDE, simplemente conectar el cable USB y presionar el botón de reset antes de ejecutar avrdude. Luego ejecutar con estas opciones:

  • Use -b 19200 to set the baud rate to 19200
  • The device signature reads dont seem to work so you’ll want to use -F
  • The programmer type is avrisp
  • The device type is -p m168
  • The port is whatever the FTDI chip shows up as

Un tutorial de avrdude se encuentra en http://ladyada.net/learn/avr/index.html

Más información:

Reparar el bootloader de un ATTiny: https://learn.adafruit.com/introducing-trinket/repairing-bootloader

Auto Reset

Para cargar un nuevo sketch en un microcontrolador, es necesario hacer un reset para pausar la ejecución de su programa y poder cargar el nuevo.

En el caso de los Arduinos, tienen la funcionalidad de auto-reset que está diseñado de forma que permite ser reseteado vía software conectado a un ordenador como el IDE de arduino. Una de las líneas de hardware flow control (DTR) del ATmega8U2/16U2 está conectada a la línea de reset de ATmega328 a través de un condensador de 100 nF.

El SW de Arduino usa esta capacidad para cargar el código simplemente pulsando el botón de carga en el IDE. De esta forma se sincroniza perfectamente el envío del nuevo sketch junto con el reset del microcontrolador.

Al resetear lo primero que arranca es el bootloader y puede comenzar la carga del sketch. El bootloader espera unos segundos para ver si un nuevo sketch se está cargando y en ese caso borra lo que hay en la flash y luego empieza a cargar el programa que está en la flash.

Arduino Uno dispone de un jumper soldado que se puede cortar para deshabilitar el auto-reset y luego estos se pueden soldar de nuevo para habilitarlo.

Más información en:

Bootloaders

Además del bootloader que disponemos desde el IDE de Arduino y el que viene precargado en los microcontroladores de Arduino, existen otros bootloaders con mejoras en algunos aspectos o para ofrecer nuevas características.

Codigo fuente del bootloader de Arduino: https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/bootloaders

Optiboot es un bootloader muy conocido que está más optimizado que el bootloader oficial de Arduino, permitiendo sketches mayores, carga más rápida.

Toda la información del optiboot:

HoodLoader2 te la opción de reprogramar el ATmega16u2 de un Arduino normal con sketcehs: https://github.com/NicoHood/HoodLoader2, se  trata de un proyecto muy intersante.

Como funciona el optiboot: https://code.google.com/archive/p/optiboot/wikis/HowOptibootWorks.wiki

Adaboot es otro bootloader optimizado cuyas mejoras ya las incluye optiboot. Más información: https://learn.adafruit.com/arduino-tips-tricks-and-techniques/bootloader

Optiloader es un sketch diseñado para automatizar las actualización de los bootloaders de Arduino usando otro Arduino como programador. Almacena múltiples copias de optiboot en la flash y cuando se inicia consulta al Arduino a cargar el bootloader y averigua la CPU que tiene e inicia la carga del bootloader ya programación adecuada de los fuses.

Interesante un TFTP bootloader: http://playground.arduino.cc/Code/TFTP_Bootloader_1

Más información: https://github.com/WestfW/OptiLoader

Cargar el Bootloader en Arduino

Antes de cargar el bootloader, debemos asegurarnos la placa seleccionada en el IDE para que al realizar el “quemado” del bootloader se configure el bootloader adecuado para cada placa y los comandos de carga del bootloader.

  • Arduino Uno y mini posee auto reset usando el optiboot bootloader
  • Arduino mega  posee auto reset y usa el stk500v2 bootloader

Para cargar o “quemar” el bootloader, necesitaremos un programador externo (in-system programmer). como USBtinyISP (https://learn.adafruit.com/usbtinyisp), un programador parallelo (http://arduino.cc/en/Hacking/ParallelProgrammer) u otro arduino con un programa adecuado cargado.

El programador se conecta a ICSP y debemos asegurarnos que lo conectamos correctamente y asegurarnos que hemos seleccionado la placa correcta, luego lanzar el comando herramientas > grabar secuencia de inicio desde el IDE de arduino. Este proceso tarda 15 o más segundos.

Más información:

En el fichero de preferencias están todos los datos para la carga del bootloader un función del modelo de bootloader: http://arduino.cc/en/Hacking/Preferences

Ver los ficheros boards.txt y programmers.txt en C:\Program Files (x86)\Arduino\hardware\arduino y ver es esa misma ruta los bootloader.

Más información en: https://code.google.com/p/arduino/wiki/Platforms

Para el Arduino mini hay diferencias a la hora de cargar el bootloader: http://arduino.cc/en/Hacking/MiniBootloader

Cargar sketches con un programador externo

Con un programador externo, además de cargar el bootloader a un microcontrolador, también podemos cargar los sketchs en la MCU sin necesidad del bootloader.

Esto nos permite usar el espacio completo de la memoria flash del microcontrolador, además de ahorrarnos el retraso que hay en el arranque cuando tenemos el bootloader.

Esto nos permite usar un arduino sin un bootloader y también es posible cargar el hex ya compilado en arduino con herramientas gráficas como XLoader: http://xloader.russemotto.com/ o con avrdude directamente como hemos visto anteriormente. La compilación puede ser con el IDE de Arduino o con cualquier otro compilador basado en avr-GCC

Para usar el programador externo debemos modificar ligeramente el fichero de preferencias del IDE de Arduino y debemos hacerlo con el IDE cerrado. Para encontrar el fichero de preferencias: http://arduino.cc/en/Hacking/Preferences

Debemos cambiar la línea  upload.using=bootloader por el identificador de uno de los programadores que tenemos en el fichero programmers.txt. Por ejemplo avrispmkii.

Después de hechos estos cambios, puedes cargar los sketches a la placa con el botón normal de upload, pero no es necesario pulsar el botón de reset, puesto que Arduino ya tiene la configuración de auto-reset.

Para volver a programar usando el bootloader, debemos volver a la configuración upload.using=bootloader en el fichero de preferencias y por supuesto el bootloader de nuevo en la placa.

Más información: http://arduino.cc/en/Hacking/Programmer

Ejemplos de programadores:

También es posible programar un ATtiny con una Raspberry Pi por SPI: http://www.instructables.com/id/Programming-the-ATtiny85-from-Raspberry-Pi/?ALLSTEPS

Ejercicios Bootloader

Ejercicio36-ArduinoISP: Usar un Arduino para hacer In-Sytem Programming a otro Arduino.

Usar un Arduino como pasarela para programar otro directamente sin bootloader.

ATENCIÓN –  esto eliminará el bootloader de Arduino

Tutoriales:

Solución: https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio36-ArduinoISP