jueves, 25 de agosto de 2011

Basubot VI

Buscando la forma de darle alguna forma de sensado de distancia a Basubot, me he encontrado con la forma de usar el A2610 de un ratón óptico que tenia en desuso, me he encontrado con esto . La libreria que se ofrece en el link anterior, permite obtener coordenadas x e y, asi como la velocidad. Enredando un poco he conseguido modificar la libreria para sacar algun valor de imagen, esto promete.




Como dije el motor es muy potente, asi que para controlar la traccion por software he añadido al montaje un sensor hall procedente de un ventilador de pc, de paso me sirve para medir velocidad, aceleracion y distancia recorrida. 


Y así va de momento, programando por no llorar ... que llegue ya el cargador !! xD
Lo que si llego fue el programador TinyUSB, monte un arduino casero como ya dije, y puse el arduino duelaminove sin micro como programador de sketch, alimentación y cable I/O con el pc.

lunes, 22 de agosto de 2011

Basubot V

Ya llego el motor 55A brushless pedido a china, y un par de baterías 3s 3000mah 35C, no me he podido resistir y se lo he montado a Basubot. Solo tengo por aquí un ESC 80A de avión, y ese le he montado mientras termino el hecho por mi.
Me he quedado muy pero que muy corto con el ratio de reducción de la transmisión, hay que tocar el mando de la emisora con mucho cuidado, solo los primeros mm dan una velocidad controlable, lo cual mas que un problema es una alegría, al ir todo esto controlado por arduino y un acelerometro, espero poder programarlo para que controle a velocidades que yo no puedo. Con el tiempo añadiré mas sensores, de momento mejor por partes.
Otro "pequeño percance" mas a la lista, como no tengo cargador lipo, la prueba la hice con una bateria li.ion 12v 1000 mah, fueron 5 minutos alucinantes, pero a sido demasiada demanda de corriente y como bien preví, se ha debido comunicar. En el patio en una cacerola la tengo calentita calentita ...

La idea final es hacer un modulo lo mas compacto posible, así que el arduino es casero. Atmega328p-pu  y acelerometro mma7260. De momento en bread board.


Es un vampiro de energía, pero también  un potentisimo motor. ( si, has visto bien, esta sujeto con epoxy xD )

Y asi va el pequeñin.

Siento no poder poner vídeo, cuando llegue el cargador lipo nuevo subiré.

domingo, 7 de agosto de 2011

Sustitución de celda en lipo 3s

Dos baterías lipo compradas en ebay a un vendedor cuyo mail ya no existe, las dos rotas a la primera carga. No se si el cargador Imax B3 comprado al mismo vendedor rompio las baterias, o las baterias rompieron el cargador, pero el recuento de perdidas es considerable, dos lipo 3s a la basura y un cargador roto. 
También esta el riesgo de incendio si no hubiera estado en casa vigilando como cargan, ya que me parecio asombroso lo rapido que se gastaron, llegando a 3.2v por celda en un par de minutos con una carga de maximo 4A, y las vigile. Y justo, la primera comienza a incharse, la saco al patio en un cubo metalico por si sale ardiendo ( ahi sigue ) y pongo a cargar la otra, el cargador hace chispas ... mido con voltimetro la segunda batería comprada al mismo vendedor y tambien tiene una celda rota. 
Después de intentar mandarle un mail al vendedor pidiendo el reembolso me he puesto a hacer una bateria 3s de las 4 celdas que quedan sanas y a salido esto:





Primero quitar la envoltura de plástico con cuidado de no perforar las celdas.

Con cuidado de no hacer demasiada fuerza mecánica, separamos la celda dañada ( la hinchada en este caso, la celda rota de la otra batería aunque da un voltaje de 1.7v no muestra síntoma visible de deterioro).


La celda mas dañada, separada del conjunto con el soldador y un poco de flux para fundir antes el estaño, de cara a no calentar de mas, y por que no decirlo, con cierto acongoje durante la operación. Si haces esto cuando la desmontes llévala a un centro de recogida de pilas lo antes posible y no la dejes sin supervision en tu casa, y si la dejas sola mejor en una caja ignífuga de metal, si la celda esta hinchada es una tanto % elevado de que salga ardiendo, y esta reacción en particular de las baterías de polímero de litio es muy bestia, si no he leído mal arden a 800ºc.

Una celda en buen estado procedente de la otra batería para sustituir la estropeada.

Comprobamos que la celda no este por debajo de 3v así como su polaridad.

Es muy importante no cambiar los polos, aburrete de mirar con el voltimetro, si te cuelas al unir las celdas chispazo enorme y riesgo de explosión de estas, rotura asegurada de la batería bla bla. Ten cuidado de no hacer corto con los pinchos del voltimetro también.

Soldamos la "nueva" celda al pack 3s procurando calentarlo lo menos posible, mejor con flux.

Medicion correcta del conjunto 3s (después de agotar la batería li-ion de 10 celdas 1200mA la batería  reparada da 12.23v, todo esto moviendo a basubot)

Un pelin de cinta kapton en la zona de soldado.

Y ahora tenemos una batería de la que no me fió por la basura de calidad que tiene de fabrica, por vieja o por lo que sea. Ahora la estoy usando como alimentación del arduino, receptor y servo de basubot. Si alguien tiene una buena batería con una celda rota por un exceso este texto le puede servir, no tiene mucha complicación, pero vemos antes de abrir como es una batería de 3 celdas de polímero de litio sin el embalaje y como están unidas sus celdas. 
No recomiendo usar lipo  de baja calidad como esta para nada, y si se hace no cargar sin supervision, estar muy atento a que no se infle ninguna celda, no provocar cortos, no descargar por debajo de 3.2v y usar cargadores de calidad de bajo C de carga con balanceador de voltaje en celdas, también mejor no descargar a demasiada velocidad. Yo he terminado usándola como alimentación del arduino, receptor y servo, dada la poca descarga demandada  para esto no creo me de mas problemas después de la reparación, siempre se rompe el miembro mas débil de un conjunto, y en mi caso ya se rompieron esas celdas, lo mismo mi lógica hasta funciona xD.

viernes, 5 de agosto de 2011

Basubot IV - parte II xD

Ya se acerca el día de estrellar el robot contra un bordillo por cualquier motivo lógico, pero solo una vez sucedido eso si ...
Haciendo honor a su nombre, la transmisión de Basubot es parte del coche de nitro metano (eje y corona dentada) pero sin el sistema de frenado ni los rodamientos ni demás del motor de combustión. Y de rodamientos he usado un par de alta velocidad montados sobre la parte con imán de un motor de disco duro, de momento solo con algo de epoxy, en la imagen se ve mejor.

jueves, 4 de agosto de 2011

Basubot IV

Ya va tomando forma. Lleno de mugre mires donde mires, pero ya no desmotiva al mirarlo ... dirección funcionando y control de velocidad funcionando. He tenido algún problema con la resistencia de sensado y de momento tengo los pines SENSE del L298 conectados a GND. Como se ve he terminado pegando la protoboad al disipador de basubot.


Uno de los motores 12v reciclados. Como se ve en el video este no esta conectado a transmisión, y aunque pequeña, me tocara ponerle una reductora. De momento para pruebas un trozo de cinta aislante y a mirar solo de reojo por si sale volando xD






Como prefiero reducir la cantidad de PCBs a usar de momento todo va en protoboard, este proyecto realmente es mi forma de aprender sobre motores CC, formas de control, etc. y así aunque algo lió seguir las pistas, es mucho mas rápido que una placa nueva cada metida de pata, y mas económico...



domingo, 31 de julio de 2011

Basubot III

De entre varias elecciones posibles la que lleva al camino mas simple suele ser la correcta, dijo no se quien alguna vez en algún sitio, creo ...xD

Esta "ida de pelota" viene por lo siguiente: Tengo la costumbre de coger un problema complicado que se me presente y quiera o no lo intento descomponer en varios mas simples fáciles de realizar. Esto esta guay si tu vida es la serie Mc Giver, a mi me trae bastantes "percances" para regocijo y escarnio mio. Pero en este caso, el tecnológico, a sido curioso. Por un lado me dedique a interpretar las señales ptm o algo así ( pendiente de revisión ), estas las genera un receptor Futaba de dos canales, reciclado del coche RC de nitro metano que uso de base para basubot. Según comande la "emisora" también Futaba, modelo T2ER. Con algo de código interpreto las señales recibidas y comando el servo de dirección, esto va guay me digo al ver los resultados, todo gira como debe.

Después me pongo a desarrollar un proto en base l298 y arduino que me maneje un motor 12V 2A, por encima de 20.000khz, frecuencia no audible, que no me genere un molesto pitido al bajar el duty cicle del 100%.  Y tambien guay, tan guay que incluso añado una resistencia de sensado de consumo al circuito y consigo sensar la corriente que pasa por el motor y con esto hago un sensor de torque y un limitador por software de la corriente que pasa po el l298 de cara a no quemarlo, guay me digo de nuevo, control de velocidad, dirección y torque aceptables.

Pero al juntar los dos trozos de codigo en uno solo, me doy cuenta que al toquetear los timer del atmega328p-pu la libreria servotimer2.h del arduino no va bien y el servo se me pira a derechas como los soldados de Franco, con un pitido muy molesto, justo lo que estaba evitando al cambiar la frecuencia del timer, en teoria (mi teoria ...) solo en los pines 11 y 3, con el codigo siguiente:

void setup() {
Serial.begin(9600);
pinMode(servoDosIn, INPUT);
byte mask = B11111000;
TCCR2B &= mask;
TCCR2B |= (0<<CS22) | (0<<CS21) | (1<<CS20);
}



Después de mucho pensar, joe ... es tema de frecuencia ... y si me salto la libreria timer2 y uso un analog ... pos no ... y si con un 555 genero una frecuencia secundaria de la que use el arduino?? ni lo probé por no tener aqui los componentes necesarios, pero me sigue pareciendo una buena idea. Modular los datos mandados al servo de forma aleatoria en base a lo recibido de cara a por obra y gracia de superman que esta en los cielos, controlar el servo de dirección, también fallido. Así que después de un rato largo, pero largo largo largo largo, he decidido no usar el arduino como interface I/O entre el receptor y el servo de dirección, así de simple ... asi que libero al arduino de esta carga de proceso, y yo me libero de generar pwm a dos frecuencias diferentes en un arduino. La otra idea era hacer un arduino dual core, pero esto a parte de una flipada, es otra historia.


Moraleja: "Fijate un objetivo distinto y tomate un vino tinto" que dijo Estopa

pierna IV ( el "retolno")

Ya recibí los l298, y fet varios pedidos a china. Asi que diseñe 3 pcb en eagle para el l298, pero metí la pata, para evitar vías puse pistas entre los contactos del l298, y no en una de las pcb, si no en las 3 ... ( yo cuando meto la pata, la meto bien xD ) y ahora tengo 3 pcb con fallos varios ... y hasta el lunes no hay mas placas de cobre. Pero algo he podido hacer, en la protoboard he montado un l298, cuatro diodos, una resistencia 10k 1/4w y una resistencia variable 10k. Con esto muevo el motor 12v cc 2A en los dos sentidos, con la resistencia variable vario la velocidad (pwm), y una resistencia 10k 1/4w para sensado de corriente.


Como se ve el código es muy sencillito. pin pwm 11 es pwm, pines digitales 7 y 8 controlan el sentido del motor, pin analógico 0 recoge el valor del potencio metro y se remapea a entre 0 y 254 y se le da a pin pwm 11, y pin analógico 1 recoge datos de la resistencia de sensado, esta me da un valor proporcional al consumo.

El siguiente paso es acoplarle el encoder óptico al motor, y con este obtener datos precisos de posición y velocidad,  con estos datos ya conocidos compararlos con la velocidad y posición deseadas, así podre añadir o quitar duty cicle por software para mantener una velocidad al margen de la carga del motor. La resistencia de sensado me permitirá poner un limite en el que dejar de subir duty cicle, de cara a no quemar el l298, ademas me sirve de sensor de torque.    




(no recomiendo usar este tipo de cable tan fino, si fuerzas demasiada corriente a pasar por ellos lo mas facil es que se fundan, pero para mover la reductora y frenar algo el eje con un destornillador valen, si tienes amperimetro todo esto es mucho mas facil ...)

sábado, 23 de julio de 2011

carril macro (II)

De los componentes comprados a china me han llegado todos menos los l298 y l293 ... asi que tren al centro a comprarlo al doble. La mayoria de componentes los he desoldado varias veces, por que aunque el cicuito es el mismo, ahora si he protegido y asegurado los contactos para evitar mas cortos, la pcb tiene una pinta horrible, pero es mi primera maquina y funciona ... xD )
Para evitarme tanto cable y liberar el arduino mega para otros fines pedi unos atmega328p-pu+cristal+condensador, por aqui tenia algun, pero eso otro dia que seguro la lio y quiero hacer mas fotos.




y estas son las fotos que van saliendo







viernes, 15 de julio de 2011

Que no hacer cuando haces PCBs

Con el lió de cables de la protoboard en el montaje del carril para fotografía, terminaron haciendo corto dos cables en un mal movimiento y L293 a la basura. así que me he puesto a intentar hacer una pcb funcional, todo un reto para mi xD

La primera mas o menos quedo bien, todas las pistas completas, etc. Pero tuve un problema con mi flush, por lo visto es conductor y me volatilizo la pista en la entrada +12V a la altura del condensador 100uf, ademas la imprimi del reves .. y esta ultima que he hecho cambiando todo el circuito me ha fatado calor o no he dejado enfriar el toner, ya que se ahn cortado un monton de pistas.



miércoles, 6 de julio de 2011

Pierna III - control de motor

No se si lo dije pero me cargue el ultimo ic que tenia por aquí capaz de aguantar el motor que estoy usando, así que hasta que lleguen me he puesto con otro motor que trae un encoder en una pequeña pcb.
Después de un ratazo con el código manejo el motor dc 12v canijo de las fotos a una resolución de 10 pasos por vuelta a esta velocidad de giro con un código optimizado en lo posible para que mientras este buscando la posición que le diga no tenga que hacer mas procesos que leer el encoder, el arduino no da para mucho en este sentido.

Realmente el encoder con mi código tiene una resolución de 50 pasos con medios pasos, osease 100 efectivos, pongamos que el motor es capaz de darnos unas 1000rpm, 1000rpm/60= a 16,6rpm por segundo que * 100 pasos de resolución del encoder nos da 1660 pulsos por segundo recibidos del encoder, lo que nos obligaría a leer pulsos cada menos de medio mili segundo. No se si el arduino podrá leer a mas velocidad pero yo de momento no se programarlo para ello, directamente el código pasa de cierta cantidad de pulsos del encoder, proporcional a la velocidad.














Esquema del L293




En el montaje de los servos el disco del encoder podría ir despues de la reductora del motor, así que la velocidad de giro a máxima corriente entregada al motor, no superaría las 300rpm, 5 por segundo a máxima velocidad, que seria 1 segundo entre (5 rps * 10 pasos por ejemplo) cada 20 ms una lectura, y a bajas velocidades.
Sabiendo el tiempo entre paso y paso, se puede ajustar la tensión de modo que a mas carga mas voltaje enviado al motor y a menos carga lo inverso, de modo que se mantengan las rpm deseadas lo mas posible, pero esto lo implementare en la próxima atacada que le de al código, ademas no tengo nada con lo que hacer el puente h del motor gordo.

Este seria el codigo asta ahora:


#define velocidadRodillaI 3 // timer 2 (pines 3 y 11) a 31khz
#define extensionRodillaI 52
#define flexionRodillaI 50


int hope, posicion, optico1B, optico2B, optico1, optico2, conta, dir;
int dig2, valor, numBytes;




void setup()
 {
  posicion=0, optico1B=0, optico2B=0, conta=0;
  byte mask = B11111000;
  TCCR2B &= mask; 
  TCCR2B |= (0<<CS22) | (0<<CS21) | (1<<CS20); 
        
  pinMode(24, INPUT);
  pinMode(26, INPUT);
  pinMode(velocidadRodillaI, OUTPUT);
  pinMode(extensionRodillaI, OUTPUT);
  pinMode(flexionRodillaI, OUTPUT);
  Serial.begin(9600);
}


void loop()
 {
   
   optico1B=optico1;            // guardamos la posicion anterior del encoder
   optico2B=optico2;
   optico1=digitalRead(24);    // leemos 0 o 1 del sensor optico en sus dos canales
   optico2=digitalRead(26);    // segun que canal sea primero uno y cual cero, sabremos la direccion de giro.
  
   if(optico1==0 && optico2==0) 
    {
      if(optico1B==1 && optico2B==0) posicion++;
      else if(optico1B==0 && optico2B==1) posicion--;
    } 
   else if(optico1==1 && optico2==1) 
    {
     if(optico1B==0 && optico2B==1) posicion++;
     else if(optico1B==1 && optico2B==0) posicion--;
    }



  if(hope < posicion) // avanza hasta
   {  
    analogWrite(velocidadRodillaI, 255); // duty cicle al 50%
    digitalWrite(extensionRodillaI, LOW);
    digitalWrite(flexionRodillaI, HIGH);
   }
  else if(hope > posicion) // retroceso hasta
   {  
    analogWrite(velocidadRodillaI, 255); // duty cicle al 50%
    digitalWrite(extensionRodillaI, HIGH);
    digitalWrite(flexionRodillaI, LOW);
   }
  else if(hope == posicion || hope==0) // ruitinas I/O cuando el micro queda "liberado" de cargas
   {
    analogWrite(velocidadRodillaI, 0); // duty cicle al 50%
    digitalWrite(extensionRodillaI, LOW);
    digitalWrite(flexionRodillaI, LOW);
    Serial.print("Actual: ");
    Serial.print(optico1, DEC); // por control de momento visualizamos los dos canales del encoder por serial
    Serial.print(" / ");
    Serial.print(optico2, DEC);
    Serial.print("  anterior: ");
    Serial.print(optico1B, DEC); // por control de momento visualizamos los dos canales del encoder por serial
    Serial.print(" / ");
    Serial.print(optico2B, DEC);
    Serial.print("  Pos: ");
    Serial.print(posicion, DEC);
    Serial.print("  Hope: ");
    Serial.println(hope);
    numBytes=Serial.available();
    if(numBytes>0 )
     {
      valor=Serial.read();
      if(valor!=0) valor=valor-48;
      dig2=Serial.read();
      dig2=dig2-48;
      Serial.flush();
      hope=(valor*10)+dig2;
     }
     
     //  posicionamos el motor en diferentes puntos una vuelta en un sentido y otra en el contrario
     //  asi que podemos borrar estos 4 if y posicionar por serial
     if(conta==0) dir=1;  
     if(conta==10) dir=0;
     
     if(dir==1)
      {
        hope=hope+2;
        conta++;
        delay(500);
      }
     else if(dir==0
     )
      {
        hope=hope-2;
        conta--;
        delay(500);
      }
      
    }


   
 } // FIN loop



Edito: dejo este link a pagina con brazo robotico y redes neuronales

martes, 5 de julio de 2011

Pierna II - encoder y pwm con arduino

A fuerza de leer mucho y quemar algún que otro componente, voy teniendo las cosas mas claras. Sigo pensando que usar un motor 12v potente (siento no poder concretar, ahorrando para un amperimetro decente ...) con una reductora adecuada, bien controlado por micro, y ciertos sensores que den su posición, va a ser mas económico y potente que intentar comprar servos de un torque y velocidad adecuados.
La otra opción es usar motores paso a paso, como tengo los 3 de la cnc haré pruebas de torque, tanto en estos como en el motor 12v dc. De momento no son mis favoritos.

Vamos a lo pensado de momento. Seria un motor 12v de taladro cordless con reductora a 300rpm, según fabricante. Como quotee en la ultima entrada, un humano bien entrenado puede conseguir una velocidad de movimiento muscular de 160rpm mas o menos, espero que en carga estas 300rpm no bajen muy por debajo del humbral humano.



"Solucionado" el tema del motor vamos a la parte electronica que lo contolara. Lo que ya tenia eran diodos rapidos (500ns) de 3A, los FR307, y ahora estoy esperando mosfet N y P de varios tipos que considero apropiados para hacer algun puente H, y algun ic puente H tipo L298, etc. De entre ellos decidire cual ira al final en el "servo" homemade que montara el bípedo.



Para que sea un servo como superman manda, aun tendriamos que poder posicionarlo con cierta precisión este en el punto que este. Para esto dudaba entre usar sensores hall o encoders, como ya "conozco" los sensores hall, primero implementare un encoder óptico en la rodilla que ya tengo prototipada. Ademas si usara sensores hall estos al medir el campo magnético de la bobina del motor, me pedirían algo mas potente que un arduino para leer su posición a la frecuencia adecuada. También son mas caros y un largo etcétera que hace que me incline por encoders ópticos.


ya tendríamos un buen torque y un sensor que nos diga hasta donde girar y con que fuerza / velocidad. Ahora seria controlar este montaje motor/mosfet/encoder, para esto he elegido un arduino ( como no xD ) por la facilidad con que la que hace cualquier montaje. Los chip Atmel usados por arduino tienen normalmente un reloj de 16 Mhz, aunque sus pines PWM vienen seteados a 488hz y yo necesito entre 20 y 40khz para los motores.

Podemos modificar esta frecuencia a una mas apropiada accediendo a ciertos registros del atmel en setup del sketch. Pego integra la informacion mas detallada que he encontrado de momento. El link aqui.

Secretos del PWM en Arduino

by Ken Shirriff
with further editing by Paul Badger
the original document
La modulación por ancho de pulso (PWM) puede ser utilizada en el Arduino de diferentes maneras, Este artículo explica tanto las técnicas simples de PWM como el como usar los registros de PWM directamente para un mayor control sobre el ciclo de trabajo (duty cycle) y la frecuencia. Este artículo se centra en los modelos Arduino Diecimila y Duemilanove, los cuales utilizan el ATmega168 y ATmega328.
Si no estas familiarizado con la Modulación de Ancho de Pulso, échale un vistazo al tutorial. Brevemente, una señal PWM es ona honda digital cuadrada, donde la frecuencia es constante, pero la fracción de tiempo en que la señal está encendida (el ciclo de trabajo) puede variar entre el 0 y el 100%.. 

Ejemplos de PWM

PWM tiene diferentes usos:
  • Atenuación de un LED.
  • Disponer de una salida analógica; si la salida digital está filtrada,
    esto proveerá de un voltaje entre el 0% y el 100%.
  • Generar señales de audio.
  • Proveer de un control de velocidad variable para motores.
  • Generar una señal modulada, por ejemplo para utilizar un LED infrarojo para control remoto.

Modulación de Ancho de Pulso simple con analogWrite

El lenguaje de programación de Arduino hace que el PWM sea fácil de usar; simplemente llama a analogWrite(pin, dutyCycle), donde dutyCycle es un valor entre 0 y 255, y pin es uno de los PWM pins (3, 5, 6, 9, 10, or 11). La función analogWrite ofrece un interface simple al hardware PWM, pero no ofrece ningún control sobre la frecuencia. ( Notese que a pesar del nombre de la función, la señal de salida es digital, a menudo haciendo referencia a una onda cuadrada).
Probablemente el 99% de los lectores pueden detenerse aquí y utilizar solamente analogWrite, pero hay otras opciones que ofrecen una mayor flexibilidad.

Bit-banging (Golpes de pulso) con PWM

Puedes "manualmente" implementar PWM en cualquier pin encendiendo y apagando repetidamente el pin en los tiempos deseados. Ejem.
void setup()
{
  pinMode(13, OUTPUT);
}

void loop()
{
  digitalWrite(13, HIGH);
  delayMicroseconds(100); // Aproximadamente 10% de ciclo de trabajo a 1KHz
  digitalWrite(13, LOW);
  delayMicroseconds(1000 - 100); // Aproximadamente 90% de ciclo de trabajo restante apagado
}
Esta técnica tiene la ventaja de poder ser utilizada en cualquier pin de salida digital, tienes el control completo del ciclo de trabajo y la frecuencia. La mayor desventaja es que cualquier interrupción afectará a la cuenta de tiempo, lo cual puede causar fluctuaciones considerables a menos que desabilites las interrupciones. La segunda desventaja es que no puedes tener la salida trabajando mientras el procesador hace alguna otra cosa. Finalmente, es dificil determinar las constantes para un ciclo de trabajo y frecuencia en particular a menos que cuentes cuidadosamente los ciclos, o modifiques los valores mientras haces la medición con un osciloscopio.
Un ejemplo más elaborado de para hacer PWM en todos los pins se puede encontrar aqui.

Usando directamente los registros de PWM del ATmega

El chip ATmega168P/328P tiene tres timers para PWM, controlando 6 salidas PWM. Manipulando directamente los registros del timer del chip, puedes obtener un mayor control que el que proporciona la función analogWrite.
La ficha del AVR ATmega328P proporciona una descripción detallada de los timers, pero la ficha puede ser dificil de comprender, debido a los muchos y diferentes modos de control y salida de los timers.
A continuación encontrarás unas palabras más ordenadas acerca de la relacción entre el lenguaje de Arduino y la ficha.

Los timers del Atmega 168/328.

El ATmega328P tiene tres timers conocidos como Timer 0, Timer 1, and Timer 2. Cada uno de ellos posee dos registros de salida comparados que controlan el ancho del pulso PWM para las dos salidas de los timers: cuando el timer alcanza el valor del registro comparado la correspondiente salida es conmutada. Las dos salidas de cada timer tienen normalmente la misma frecuencia, pero pueden tener diferentes ciclos de trabajo (dependiendo del respectivo registro de salida comparado).
Cada uno de los timers tiene un Predivisor (prescaler) que genera el tempodizador dividiendo el reloj del sistema por un factor como 1, 8, 64, 256 o 1024. El Arduino tiene un reloj de sistema a 16MHz y la frecuencia del temporizador será el reloj de sistema dividido por el factor del Predivisor. Notese que el Timer 2 posee un conjunto diferente de valores para el predivisor con respecto a los otros timers.
Los timers son complicados por sus diferentes modos. Los principales modos son "Fast PWM" y "Phase-correct PWM", los cuales se describen más adelante. Un timer puede trabajar de 0 a 255, o de 0 a un valor fijo. (El Timer 1 de 16-bits tiene modos adicionales que soportan valores de timer hasta los 16 bits). Cualquier salida puede además ser invertida.
Los timers tambiém pueden generar interrupciopnes en desbordamiento (overflow) y/o coincidiendo con alguno de los registros de salida comparados, pero esto está más allá de lo que cubre este artículo.
Registros de Timers
Muchos registros son utilizados para controlar cada timer. el Controlador de Registros del Timer/Contador TCCRnA yTCCRnB mantiene el control de los bits principales del timer. (Notese que TCCRnA y TCRnB no corresponden a las salidas A y B). Estos registros mantienen varios grupos de bits:
  • Waveform Generation Mode bits (WGM): Estos controlan el timer de modo general.
    (Estos bits se dividen entre TCCRnA y TCCRnB.)
  • Clock Select bits (CS): Controla el reloj predivisor
  • Compare Match Output A Mode bits (COMnA): Habilita/deshabilita/invierte la salida A
  • Compare Match Output B Mode bits (COMnB): Habilita/deshabilita/invierte la salida B
Los Registros de Salida Comparada OCRnA y OCRnB ajustan los niveles en los que las salidas A y B serán afectadas. Cuando el valor del timer coincida con el valor del registro, la correspondiente salida será modificada como se especifique por el modo.
Los bits son ligeramente diferentes para cada timer, así que consulta la ficha para más detalles. Timer 1 es un timer de 16-bits y tiene modos adicionales. El Timer 2 posee diferentres valores de predivisor.

PWM rápido (Fast PWM)

En el modo de PWM simple, el timer cuenta repetidamente desde 0 a 255. La salida se enciende cuando el timer está en 0, y se apaga cuando el timer coincide con el registro de salida comparado. A mayor valor en el registro de salida comparado, el ciclo de trabajo será mayor. Este modo es conocido como modo rápido de PWM.
El siguiente diagrama muestra las salidas para dos valores particulares de OCRnA y OCRnB. Notese que ambas salidas tienen la misma frecuencia, coincidiendo con la frecuencia de un ciclo completo del timer.

Modo PWM rápido (Fast PWM Mode)

El siguiente fragmento de código configura el PWM rápido en los pines 3 y 11 (Timer 2). Para resumir el registro de configuración, poniendo los bits del Modo de generación de Ondas (WGM - waveform generation mode) a 011 selecciona PWM rápido. Poniendo los bits COM2A y COM2B a 10 asigna PWM no-invertido (non-inverted PWM) para las salidas A y B. Poniendo el CS a 100 ajusta el predivisor a dividir el reloj por 64. (Como los bits son diferentes para los diferentes timers, consulta la ficha para ver los valores correctos). Los registros comparados de salida son arbritariamente ajustados a 180 y 50 para controlar el ciclo de trabajo del PWM de las salidas A y B. (Por supuesto puedes modificar directamente los registros en vez de utilizar pinMode, pero necesitas configurar los pins a OUTPUT).
pinMode(3, OUTPUT);
  pinMode(11, OUTPUT);
  TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
  TCCR2B = _BV(CS22);
  OCR2A = 180;
  OCR2B = 50;
En el Arduino Duemilanove, estos valores produce:
  • Frecuencia salida A: 16 MHz / 64 / 256 = 976.5625Hz
  • Duty cycle salida A: (180+1) / 256 = 70.7%
  • Frecuencia salida B: 16 MHz / 64 / 256 = 976.5625Hz
  • Duty cycle salida B: (50+1) / 256 = 19.9%
La frecuencia de salida son los de 16MHz de la frecuencia del reloj de sistema, dividido por el valor del predivisor (64), dividido por los 256 ciclos que necesita el timer para dar la vuelta completa. Notese que el PWM rápido (fast PWM) mantiene la salida levantada un ciclo más que el valor del registro comparado.

PWM de Corrección de fase (Phase-Correct PWM)

El segundo modo es llamaddo PWM de Corrección de fase (Phase-Correct PWM). En este modo, el timer cuenta de 0 a 255 y cuenta hacia abajo de nuevo hasta 0. La salida se apaga cuando el timer alcanza el valor del registro comparado cuando cuenta hacia arriba y se enciende cuando se alcanza mientras cuenta hacia abajo. El resultado es una salida más simétrica. La frecuenccia de salida será aproximadamente la mitad de el valor xxquexx para el modo PWM rápido, por que el timer corre en ambos sentidos arriba y abajo.

Ejemplo PWM de Corrección de fase

El siguiente fragmento de código configura el PWM de corrección de fase en los pines 3 y 11 (Timer 2). Los bits del modo de generación de ondas WGW estan configurados a 001 para el PWM de corrección de fase. Los otros bits son iguales que para el modo de PWM rápido.
pinMode(3, OUTPUT);
  pinMode(11, OUTPUT);
  TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20);
  TCCR2B = _BV(CS22);
  OCR2A = 180;
  OCR2B = 50;
En el Arduino Duemilanove, estos valores producen:
  • Frecuencia salida A: 16 MHz / 64 / 255 / 2 = 490.196Hz
  • Duty cycle salida A: 180 / 255 = 70.6%
  • Frecuencia salida B: 16 MHz / 64 / 255 / 2 = 490.196Hz
  • Duty cycle salida B: 50 / 255 = 19.6%
El PWM de corrección de fase divide la frecuencia por dos, comparado con el modo PWM rápido (fast PWM), porque el timer va hacia arriba y hacia a abajo. Sorprendentemente, la frecuencia es dividida por 255 en vez de por 256, y los cálculos del el ciclo de trabajo no añaden uno como para el PWM rápido. Ver la explicación más adelante en "Off-by-one".

Variando el límite superior del timer: PWM rápido

Ambos modos, PWM rápido y PWM de corrección de fase, tienen un modo adicional que otorga el control sobre la frecuencia de salida. En este modo, el timer cuenta desde 0 hasta OCRA (El valor del registro de salida comparado A), en vez de de 0 a 255. Esto da mucho más control sobre la frecuencia de salida y los modos previos. (Para un mayor control de la frecuencia, utiliza el Timer 1 de 16-bits).
Notese que en este modo solo la salida B puede ser utilizada para PWM; OCRA no puede ser utilizada como máximo valor y valor de comparación de PWM. Sin embargo, hay un modo de Casos-Especiales "Conmutar OCnA en coincidencia comparada" esto conmutará la salida A al final del ciclo de trabajo, generando un 50% fijo de ciclo de trabajo y la mitad de la frecuencia en este caso. Los ejemplos utilizarán este modo.
En el siguiente diagrama, el timer se resetea cuando coincide con OCRnA, produciendo una frecuencia de salida más rápida para OCnB que en diagramas anteriores. Notese como OCnA conmuta cada vez que el timer se resetea.

Modo PWM rápido (Fast PWM) con límite OCRA

El siguiente fragmento de código configura el PWM rápido en los pines 3 y 11 (Timer 2). Utilizando OCR2A como el valor superior del timer. Los bits del modo de generación de onda WGW se cambian a 111 para el PWM rápido con el OCRA controlando el límite superior. El límite superior del OCR2A es puesto arbritariamente a 180, y el registro de comparación OCR2B es ajustado arbritariamente a 50. El modo OCR2A está configuado para "Conmutar en coincidencia comparada" cambiando los bits del COM2A a 01.
pinMode(3, OUTPUT);
  pinMode(11, OUTPUT);
  TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
  TCCR2B = _BV(WGM22) | _BV(CS22);
  OCR2A = 180;
  OCR2B = 50;
En el Arduino Duemilanove, estos valores producen:
  • Frecuencia salida A: 16 MHz / 64 / (180+1) / 2 = 690.6Hz
  • Duty cycle salida A: 50%
  • Frecuencia salida B: 16 MHz / 64 / (180+1) = 1381.2Hz
  • Duty cycle salida B: (50+1) / (180+1) = 28.2%
Note que en este ejemplo, el timer va de 0 a 180, que tarda 181 ciclos de reloj, entonces la frecuencia de salida es dividida por 181. La salida A tiene la mitad de la frecuencia que la salida B por que la conmutación en modo de Comparación de Coincidencia (Compare Match mode) conmuta la salida A por cada ciclo completo del timer.

Variando el límite superior: PWM corrección de fase (phase-correct PWM)

De manera similar, el timer puede ser configurado en modo de corrección de fase para resetear cuando alcance elOCRnA.
PWM corrección de fase con límite OCRA
El siguiente fragmento de código configura PWM corrección de fase en los pines 3 y 11 (Timer 2), utilizando elOCR2A como el valor superior del timmer. Los bits del modo de generación de onda WGW son ajustados a 101 para el PWM de corrección de fase con el OCRA controlando el límite superior. El límite superior del OCR2A es arbitrariamente ajustado a 180, y el registro comparado OCR2B se ajusta arbritariamente a 50. El modo OCR2A está configuado para "Conmutar en coincidencia comparada" cambiando los bits del COM2A a 01.
pinMode(3, OUTPUT);
  pinMode(11, OUTPUT);
  TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20);
  TCCR2B = _BV(WGM22) | _BV(CS22);
  OCR2A = 180;
  OCR2B = 50;
En el Arduino Duemilanove, estos valores producen:
  • Frecuencia salida A: 16 MHz / 64 / 180 / 2 / 2 = 347.2Hz
  • Duty cycle salida A: 50%
  • Frecuencia salida B: 16 MHz / 64 / 180 / 2 = 694.4Hz
  • Duty cycle salida B: 50 / 180 = 27.8%
Observese que en este ejemplo, el timer va de 0 a 180 y vuelve hacia el 0, lo que requiere 360 ciclos de reloj. Entonces, todo es dividido por 180 o 360, al contrario que en el caso del PWM rápido, en el que todo se divide por 181; ver más adelante para más detalles.
Desplazado-en-uno (Off-by-one)
Puedes haber observado que el PWM rápido (fast PWM) y el PWM de corrección de fase (phase-correct PWM) parecen desfasados en 1 uno con respecto al otro, dividiendo por 256 contra 255 y añadien do 1 en varios lugares. La documentación es un poco oscura en esto, por lo que intentaré explicarlo con algo más de detalle.
Imagina que el timer está configurado en modo PWM rapido y está ajustado para contar hasta un valor de OCRnAde 3. El timer tendrá los valores 012301230123... observa que hay cuatro ciclos de reloj en cada ciclo de timer. Entonces la frecuencia será dividida por 4, no por 3. El ciclo de trabajo será un múltiplo de 25%, desde que la salida esté levantada para 0, 1, 2, 3 o 4 de los cuatro. De la misma manera, ssi el timer cuenta hasta 255, existirán 256 ciclos de reloj en cada ciclo de timer, y el ciclo de trabajo será un múltiplo de 1/256. Para resumir PWM rápido divide por N+1 donde N en el máximo valor del timer (sea OCRnA o 255).
Ahora considera el modo PWM de corrección de fase contando hasta un valor de OCRnA de 3. Los valores del timer seran 012321012321... aquí hay 6 ciclos de reloj en cada ciclo de timer (012321). Entonces la frecuencia será dividida por 6. El ciclo de trabajo será un múltiplo de 33%, desde que la salida puede estar levantada para 0, 2, 4 o 6 de los ciclos. De la misma manera, si el timer cuenta hasta 255 y vuelve hacia abajo, habrá 510 ciclos de reloj en cada ciclo de timer, y el ciclo de trabajo será un múltiplo de 1/255. Para resumir el PWM de corrección de fase divide por 2N, donde N es el valor máximo del timer.
La segunda diferencia importante en la temporización es que el PWM rapido mantiene la salida en alto para un ciclo más que el valor del registro de salida comparado. El motivo de esto es que para el PWM rápido contando hasta 255, el ciclo de trabajo puede ser de 0 a 256, pero el valor del registro de salida comparado solo puede ser de 0 a 255. ¿que pasa con el valor perdido? El PWM mantiene la salida en alto para N+1 ciclos cuando el registro de salida comparado es N por lo que el valor del registro de salida comparado de 255 es el 100% del ciclo de trabajo, pero el valor del registro de salida comparado de 0 no es el 0% del ciclo de trabajo sino un 1/256 del ciclo de trabajo. Esto es diferente del PWM de corrección de fase, donde el valor de un registro de 255 es 100% del ciclo de trabajo y un valor de 0 es un 0% del ciclo de trabajo.

Timers y Arduino

Arduino soporta PWM en varios de sus pins de salida. No es obvio de manera inmediata que timer controla cada salida, pero la siguiente tabla puede aclarar la situación. Esta ofrece a cada salida del timer a un pin de salida en el Arduino (las serigrafiados en la placa), el pin en el chip Atmega, y el nombre y bit del puerto de salida. Por ejemplo la salida del Timer 0 OC0A está conectada al Arduino en el pin 6; utiliza el pin del chip 12 que es también conocido como PD6.
Timer Salida Arduino output Chip pin Pin name
OC0A 6 12 PD6
OC0B 5 11 PD5
OC1A 9 15 PB1
OC1B 10 16 PB2
OC2A 11 17 PB3
OC2B 3 5 PD3
El Arduino realiza alguna inicialización de los timers. El Arduino inicializa el predivisor en los tres timers para dividir el reloj por 64. El Timer 0 es inicializado en modo PWM rápido (fast PWM), mientras que el Timer 1 y el Timer 2 son inicializados en el modo PWM de corrección de fase (Phase Correct PWM). Mira el archivo de código de Arduino wiring.c para más detalles.
El Arduino utiliza el timer 0 internamente para las funciones millis() y delay(), estate atento ya que cambiar la frecuencia de este timer causará que estas funciones sean erroneas. Utilizar las salidas de PWM es seguro si no cambias la frecuencia, pienso.
La función analogWrite(pin, duty_cycle) asigna el pin apropiado al PWM y configura el registro comparado de salida apropiado al ciclo de trabajo (duty cycle) (con el caso especial del ciclo de trabajo 0 en el Timer 0). La función digitalWrite() apaga la salida de PWM si es llamada en un pin del timer. El código relevante esta en wiring_analog.c y wiring_digital.c.
Si tu utilizas analogWrite(5, 0) obtendrás un ciclo de trabajo de 0%, aunque el timer del pin 5 (Timer 0) utiliza PWM rápido. Como puede ser esto, cuando el valor 0 en el PWM rápido produce un ciclo de trabajo de 1/256 como se explicó más arriba? La respuesta es que analogWrite "Hace trampas"; este tiene un código para-casos-especiales para apagar explícitamente el pin cuando es llamado en el Timer 0 con un ciclo de trabajo de 0. Como consecuencia, el ciclo de trabajo de 1/256 no está disponible cuando se utiliza analogWrite en el Timer 0, y habrá un salto en el ciclo de trabajo actual entre los valores 0 y 1.
Otros modelos de Arduino utilizan un procesador AVR diferente con timers similares. El Arduino Mega utiliza elATmega1280 (Ficha), el cual tiene cuatro timers de 16-bit con 3 salidas cada uno y dos timers de 8-bits con dos salidas cada uno. Solo 14 de las salidas PWM están soportadas por la librería de Wiring de Arduino, no obstante algunos de los modelos antiguos de Arduino utilizan el ATmega8 (Ficha), el cual tiene 3 timers pero solo tres salidas PWM: Timer 0 no tiene PWM, Timer 1 es de 16 bits y tiene dos salidas PWM, y el Timer 2 es de 8 bits y tiene una salida PWM.