32 programa de controlador de servo motor usando ATMEGA32

Programa de controlador de 32 servomotores usando ATMEGA32

Las aplicaciones de automatización a menudo requieren controlar varios actuadores (como muchos motores) al mismo tiempo. Para el control simultáneo y sincrónico de muchos actuadores, es necesario controlarlos y operarlos de forma independiente. Los ingenieros utilizan tantos controladores (microcontroladores o ASIC) como actuadores necesarios para funcionar de forma independiente. Sin embargo, cuando un controlador es capaz de operar múltiples actuadores (lo cual es posible cuando tiene una gran cantidad de pines de entrada/salida) y tiene una velocidad de reloj alta (donde, debido a la alta velocidad de reloj del controlador, los actuadores parecen estar controlados de forma independiente al mismo tiempo), es preferible utilizar un único controlador para gestionar varios actuadores. Esto reduce el tamaño y la complejidad en el lado del hardware, reduciendo el costo total de producción del sistema integrado específico. Sin embargo, un sistema de este tipo requiere un software complejo instalado dentro del controlador.

En este proyecto, se diseñó una aplicación integrada de propósito general para controlar 32 servomotores (de forma independiente y simultánea) mediante la popular placa ATMega32 AVR. Aquí se diseña un programa C complejo para mover 32 servos a velocidades predeterminadas a posiciones predefinidas por el ATMega32.

Los servos pueden moverse a 10 posiciones (rangos) diferentes en 20 milisegundos. Ocupan cada posición en un subperíodo de 2 milisegundos, es decir, para asumir una posición predefinida tardan como máximo 2 milisegundos o menos. El conjunto de estas posiciones preestablecidas (10 posiciones definidas individualmente para 32 servos) continúa repitiéndose en intervalos de tiempo de 20 milisegundos hasta que el sistema permanece encendido. Dentro de cada subintervalo de 2 milisegundos, los servos se mueven a la posición preestablecida y permanecen allí hasta que se completa el subintervalo de 2 milisegundos. Para ello se detienen en momentos de tiempo concretos (que obviamente siempre son 2 milisegundos o menos). De manera similar, un servo puede alcanzar y descansar en su posición preestablecida en 1,2 milisegundos y otro puede alcanzar y descansar en su posición preestablecida en 1,5 milisegundos. Los servos continúan recibiendo pulsos PWM hasta que es necesario moverlos y luego, cuando es necesario detenerlos en una posición predefinida (dentro del subrango de 2 milisegundos), el pulso PWM se detiene.

Los servos pueden asumir hasta 100 posiciones preestablecidas diferentes en cada subintervalo de 2 milisegundos. Supongamos que un servo puede girar de 0 a 180 grados proporcionando una señal PWM de 1 milisegundo a 2 milisegundos, luego puede moverse a 100 posiciones con una diferencia de 1,8 grados cada una.

Al alcanzar cada posición (rango) dentro de un subintervalo de 2 milisegundos, los servos pueden alcanzar posiciones preestablecidas a diferentes velocidades. Para la variación de velocidad, se definen posiciones temporales en el código donde el pulso PWM se detiene durante 10 microsegundos. Cuanto mayor sea el número de posiciones temporales (pasos de 10 microsegundos o zonas muertas), menor será la velocidad de rotación. Sin embargo, el servo alcanzará la posición preestablecida en un subintervalo de 2 milisegundos. Si solo hay una posición temporal configurada para alcanzar la posición preestablecida, el servo girará a su velocidad de rotación máxima (ya que se le suministrará un pulso PWM continuo) para alcanzar esa posición preestablecida (ángulo) y se detendrá cuando alcance su posición. posición preestablecida. posición (rango) en un instante de tiempo dentro de un subintervalo de 2 milisegundos. Si hay 10 posiciones temporales configuradas para lograr la posición preestablecida (ángulo), habrá 10 pasos o zonas muertas de 10 microsegundos, lo que reducirá la velocidad de rotación del servo en consecuencia. De esta forma hay variación de velocidad donde el servo gira a su máxima velocidad hasta la posición predefinida cuando hay 1 paso (o zona muerta o posición temporal) y gira lentamente a medida que hay más pasos (hasta 10).

Esto es similar a las aplicaciones de automatización industrial y se puede utilizar para controlar brazos robóticos o alas de un dron. El programa C que se presenta aquí es un programa de propósito general que se puede modificar codificando los valores de velocidad y posición de servos individuales para adaptarse a cualquier tipo de aplicación de automatización. El programa C presentado aquí fue desarrollado y compilado utilizando AVR Studio.

También se proporciona una modificación del diseño anterior donde se controlan 30 servos (mediante 30 pines de entrada/salida del AVR ATMega32) y se utilizan 2 pines de entrada/salida para la comunicación en serie con un dispositivo externo. Aquí el dispositivo externo conectado vía UART envía los valores al ATMega32 para controlar y cambiar la secuencia del servo en tiempo real. El dispositivo externo (otro controlador o PC) se puede conectar vía RS-232, Bluetooth o cualquier interfaz usando el protocolo UART.

Prototipo de controlador multiservo basado en AVR ATmega32

Fig. 1: Prototipo de servocontrolador múltiple basado en AVR ATmega32

Componentes necesarios

Imagem dos componentes necessários para o controlador servo múltiplo baseado em AVR ATmega32

Fig. 2: Imagen de los componentes necesarios para el servocontrolador múltiple basado en AVR ATmega32

Diagrama de circuito -

El controlador de servomotor para 32 servos tiene el siguiente diagrama de circuito:

Diagrama de circuito do servocontrolador múltiplo baseado em AVR ATmega32

Fig. 3: Diagrama de circuito del servocontrolador múltiple basado en AVR ATmega32

El controlador de servomotor para controlar 30 servos con comunicación serial UART para operación remota mediante un dispositivo externo tiene el siguiente diagrama de circuito:

Diagrama de circuito do servocontrolador múltiplo baseado em AVR ATmega32

Fig. 4: Diagrama de circuito del servocontrolador múltiple basado en AVR ATmega32

Conexiones de circuito

El circuito utilizado en este controlador de servomotor es muy simple. Un servomotor tiene tres terminales: VCC, Tierra y Señal. El pin VCC está conectado a un suministro de 5 V y conectado a tierra común. El pin de señal del servo está conectado al GPIO del controlador para proporcionar una señal PWM. Los servos utilizados en este circuito son SG90 que pueden girar 180 grados (90 grados en cualquier dirección). Al proporcionar un pulso alto de 1 ms, el servo gira a -90 grados. Al proporcionar un pulso alto de 1,5 ms, se mueve a 0 grados y al proporcionar un pulso alto de 2 ms, se mueve a 90 grados. Entonces, para una entrada PWM de 1 ms a 2 ms, el servo gira de -90 grados a 90 grados.

Diagrama de pinos do servo motor

Fig. 5: Diagrama de pines del servomotor

El AVR ATMega32 tiene 32 pines de entrada/salida de uso general desde los cuales la señal PWM se puede suministrar a 32 servos. Viene en un paquete PDIP de 40 pines o TQFP/MLF de 44 pines. El paquete PDIP de 40 pines se utiliza porque el circuito está diseñado en una placa de pruebas. AVR ATMega32 tiene el siguiente diagrama de pines:

Fig. 6: Diagrama de pines AVR ATMega32

Los servos están conectados en orden alfabético de puertos de la siguiente manera:
PUERTO: S1-S8
PUERTO: S9-S16
PUERTO: S17-S24
PUERTO: S25-S32

Para alta velocidad de funcionamiento, se selecciona la frecuencia máxima para el AVR. Se conecta un oscilador de cristal de 16 MHz entre los pines 12 y 13. Se utilizan dos condensadores de 22 pF para resonar con la inductancia del cristal que están conectados en serie a tierra.

Los servos, así como el ATMega32, se suministran con 5 V CC para su funcionamiento. La fuente de alimentación CC del circuito se elige teniendo en cuenta la corriente total consumida por todos los servos conectados al AVR. La corriente nominal de la fuente de CC debe ser mayor para soportar las corrientes de carga máxima de todos los servos. Una regla general para encontrar la corriente de carga máxima para servos estándar es 1 A para servos de 3 a 4 kg. Los servos SG90 utilizados en este circuito pesan 14,7 g (Fuente: Hoja de datos del servo SG90). Por lo tanto, cada servo consume una corriente máxima de unos 15 miliamperios. El paquete PDIP del ATMega32 consume 200 miliamperios de corriente. Por lo tanto, el requisito de corriente total se puede calcular sumando la corriente máxima consumida por todos los servos y el controlador AVR de la siguiente manera:

Corriente Total = Corriente consumida por el AVR ATMega32 + Corriente consumida por los Servos

Corriente total = 32X15 mA + 200 mA

Corriente total = 680 mA

Por lo tanto, la alimentación CC debe ser de 5 V y al menos 680 mA.

En la versión del circuito operada de forma remota, se retiran dos servos de los pines 14 y 15 y se conecta un dispositivo externo (controlador o PC) al ATMega32 a través de UART.

Fig. 7: Imagen que muestra el circuito controlador del servocontrol múltiple basado en AVR ATMega32

Cómo funciona el circuito

El controlador de servomotor diseñado aquí tiene un conjunto predefinido de posiciones de servo (10 posiciones por servo) en el código de firmware. Cuando se enciende el circuito, el controlador lee una matriz donde están predefinidas la posición actual, la posición final y la velocidad de rotación de cada servo. En consecuencia, se determinan los instantes de tiempo dentro del primer subintervalo de 2 milisegundos para que cada servo se detenga. Los instantes de tiempo para detener los servos se ordenan en orden ascendente y el instante de tiempo con el valor más pequeño se carga en el registro de comparación de un temporizador. Los pulsos PWM se activan para todos los servos. La señal PWM para cada servo se apaga a medida que se genera la interrupción del temporizador respectivo. El ancho máximo de la señal PWM puede ser de 2 milisegundos. Los servos giran a su posición preestablecida (pista) y se detienen para descansar en esa posición. Cada servo tiene su propia posición final (ángulo) y velocidad (determinada por el número de zonas muertas o posiciones temporales definidas para ese servo). Una vez que se completa el subintervalo de 2 milisegundos, todos los servos alcanzaron y se detuvieron en sus primeras posiciones preestablecidas. Las nuevas posiciones actuales de los servos se actualizan en el controlador. Las posiciones finales del primer ciclo pasan a ser las posiciones actuales del segundo ciclo.

Ahora las posiciones y velocidades finales para el segundo subintervalo de 2 milisegundos se cargan en la matriz. En consecuencia, se determinan los instantes de tiempo dentro del segundo subintervalo de 2 milisegundos para que cada servo se detenga. Nuevamente, los instantes de tiempo para detener los servos se ordenan en orden ascendente y el instante de tiempo con el valor más pequeño se carga en el registro de comparación del temporizador. Los pulsos PWM se activan para todos los servos. La señal PWM para cada servo se apaga a medida que se genera su respectiva interrupción del temporizador. Esto se repite para cada uno de los ocho subintervalos restantes de 2 milisegundos.

Una vez que se completen los 10 ciclos (subintervalos de 2 milisegundos), se completará el ciclo de 20 milisegundos. Ahora nuevamente, el ciclo comienza en el primer subintervalo de 2 milisegundos y continúa durante los ciclos restantes. El bucle de 10 ciclos para 10 posiciones preestablecidas para cada servo continúa repitiéndose hasta que el sistema permanece encendido. Si el sistema se apaga y se reinicia, el ciclo de 10 ciclos comienza desde el primer ciclo.

Imagem mostrando o funcionamento do Controle de Servo Múltiplo baseado em AVR ATMega32

Fig. 8: Imagen que muestra el funcionamiento del servocontrol múltiple basado en AVR ATMega32

Guía de programación

El firmware de este controlador de servomotor se desarrolló en C integrado para AVR ATMega32. Para escribir y compilar código se utilizan winavr y el bloc de notas del programador.

El funcionamiento de este circuito se basa en interrupciones del temporizador. El AVR ATMega32 tiene tres temporizadores: Timer0 (que es un temporizador de 8 bits), Timer1 (que es un temporizador de 16 bits) y Timer2 (que es un temporizador de 8 bits). Timer1 y Timer2 se utilizan para codificar este sistema.

Timer1 es un contador de 16 bits formado por dos registros de 8 bits: TCNT1H y TCNT1L. Se utiliza para contar el tiempo y generar interrupciones en los instantes de tiempo deseados para apagar los respectivos servopulsos. Timer1 puede funcionar en cinco modos:

1) Modo Normal

2) Borrar el cronómetro en el modo Comparar coincidencia (CTC)

3) Modo PWM rápido

4) Modo PWM con corrección de fase

5) Modo PWM con corrección de fase y frecuencia

Aquí, el Temporizador1 funciona en modo CTC (Borrar temporizador en comparación de coincidencias).

Valores de bits do registro TCNT1 no AVR ATMega32

Fig. 9: Valores de bits del registro TCNT1 en el AVR ATMega32

Los siguientes registros están asociados con el temporizador 1:

1) TCCR1A (registro de control de temporizador/contador 1 A)

2) TCCR1B (registro de control B del temporizador/contador 1)

3) TCNT1 (Registro 1 del temporizador/contador, es de 16 bits – TCNT1H y TCNT1L)

4) OCR1A (registro de comparación de salida de 1 A, 16 bits – OCR1AH ​​​​y OCR1AL)

5) OCR1B (Registro de comparación de salida 1 B, es de 16 bits – OCR1BH y OCR1BL)

6) ICR1 (Registro de captura de entrada 1, es de 16 bits – ICR1H e ICR1L)

7) TIMSK (Registro de máscara de interrupción de temporizador/contador)

8) TIFR (Registro de señalización de interrupción del temporizador)

El temporizador 1 tiene dos registros de control: TCCR1A y ​​TCCR1B. Tiene un registro Flag – TIFR que informa al controlador del estado del Timer 1. TCCR1A es un registro de 8 bits con los siguientes valores de bits –

Valores de bits do registro TCCR1A no AVR ATMega32

Fig. 10: Valores de bits del registro TCCR1A en el AVR ATMega32

TCCR1B también es un registro de 8 bits con los siguientes valores de bits:

Valores de bits do registro TCCR1B no AVR ATMega32

Fig. 11: Valores de bits del registro TCCR1B en el AVR ATMega32

Para configurar Timer1 en modo CTC, WGM13 y WGM12 en TCCR1B deben ser 0 y 1 respectivamente, mientras que WGM11 y WGM10 en TCCR1A deben ser 0.

Tabela mostrando condições para seleção do modo CTC para o temporizador 1

Figura 12: Tabla que muestra las condiciones para seleccionar el modo CTC para el temporizador 1

Además, como hay un oscilador de cristal de 16 MHz conectado al ATMega32, el período de tiempo del oscilador de cristal es el siguiente:

Período de tiempo = 1/Frecuencia

= 1/(16X10^6)

= 0,0625 microsegundos

Con esta F_CPU en modo normal, Timer1 solo podía contar 4,096 milisegundos a escala completa (0,0625 microsegundos X 2^16). Por lo tanto, es necesario realizar un escalado previo. La preescala está determinada por los bits CS12, CS11 y CS10 del registro TCCR1B según la siguiente tabla:

Se escribe un valor de 0x0A en TCCR1B mientras que TCCR1A se deja en 0x00. Esto configura Timer1 en modo CTC, haciendo que WGM13, WGM12 en TCCR1B sean 0 y 1 respectivamente, mientras que WGM11 y WGM10 en TCCR1A sean 0. Esto también selecciona un preescalador de E /S /8 clk . Por lo tanto, Timer1 tiene una velocidad de reloj de 2 MHz (16/8). Cada recuento del temporizador ahora corresponde al período de tiempo calculado a continuación:

Período de tiempo = 1/Frecuencia

= 1/(2X10^6)

= 0,5 microsegundos

Ahora Timer1 puede contar hasta 32,768 milisegundos (0,5 microsegundos X 2^16). Para generar interrupciones dentro del subintervalo de 2 milisegundos, solo necesita contar hasta 4000 (0,5 microsegundos X 4000 = 2 milisegundos). Cuando el registro TCCR1B se escribe con bits de preescalador de reloj distintos de cero, se inicia el temporizador/contador. Por lo tanto, este registro se escribe después de ordenar y escribir los valores de posición mínimos en el Registro de comparación de salida de 16 bits (OCR1A).

ATMega32 tiene un registro TIMSK que tiene bits de control de interrupción para todos sus temporizadores/contadores, incluido el temporizador 1. TIMSK tiene los siguientes valores de bits:

Valores de bits do registro TIMSK no AVR ATMega32

Fig. 13: Valores de bits del registro TIMSK en el AVR ATMega32

Los bits TICIE1, OCIE1A, OCIE1B y TOIE1 del registro TIMSK están asociados con el temporizador 1. TIMSK está configurado en 0x90, lo que establece OCIE2 y OCIE1A en 1. Configurar OCIE1A en alto habilita la interrupción de comparación de salida del temporizador/contador1. . El instante de tiempo para detener la señal PWM se carga en el registro OCR1A. Cuando el recuento de TCNT1 coincide con el valor cargado en OCR1A, se genera una interrupción. Cada vez que se genera la interrupción, la señal PWM al sevo respectivo se apaga.

Timer2 es un registro de 8 bits (TCNT2) que se utiliza para generar interrupciones cada 2 milisegundos (para indicar el final de subintervalos de 2 milisegundos). Puede funcionar en cuatro modos:

1) modo normal

2) Borrar el cronómetro en el modo Comparar coincidencia (CTC)

3) Modo PWM rápido

4) Modo PWM con corrección de fase

Para habilitar el temporizador 2, OCIE2 se configuró en 1 en el registro TIMSK. El temporizador 2 tiene los siguientes registros asociados:

1) TCCR2 (registro de control de temporizador/contador 2)

2) TCNT2 (registro temporizador/contador 2)

3) OCR2 (Registro de comparación de salida 2)

4) ASSR (Registro de estado asíncrono)

5) TIMSK (Registro de máscara de interrupción de temporizador/contador)

6) TIFR (Registro de señalización de interrupción del temporizador)

El temporizador 2 está controlado por el registro TCCR2. TCCR2 tiene los siguientes valores de bits:

Valores de bits do registro TCCR2 no AVR ATMega32

Fig. 14: Valores de bits del registro TCCR2 en el AVR ATMega32

Se escribe un valor de 0x0D en TCCR2. El modo de funcionamiento del Temporizador 2 se selecciona escribiendo en los bits WGM21 y WGM20 del TCCR2. La selección del modo se realiza según la siguiente tabla:

Al escribir 0x0D en TCCR2, WGM20 se establece en 0 y WGM21 en 1, por lo que el temporizador 2 funciona en modo CTC. Como se hizo para el temporizador 1, el escalado previo también se realiza para el temporizador 2. Para el temporizador 2, el escalado previo está determinado por los bits CS22, CS21 y CS20 del registro TCCR2 de acuerdo con la siguiente tabla:

Al escribir 0x0D en TCCR2, CS22, CS21 y CS20 de TCCR2 se configuran en 1, 0 y 1 respectivamente. Por lo tanto, Timer2 tiene una velocidad de reloj de 125 KHz (16/128 MHz). Cada recuento del temporizador ahora corresponde al período de tiempo calculado a continuación:

Período de tiempo = 1/Frecuencia

= 1/(125X10^3)

= 8 microsegundos

Durante 2 milisegundos, el temporizador 2 ahora necesita contar hasta 250 (8 microsegundos x 25

0 = 2 milisegundos). Por lo tanto, se escribe un valor de 250 en OCR2. Cuando el temporizador 2 cuenta hasta 250 y coincide con el valor escrito en OCR2, se genera la interrupción. Esto indica el final de un subintervalo (uno de diez ciclos para las posiciones de los servos).

El registro TIFR tiene los siguientes valores de bits:

Valores de bits do registro TIFR no AVR ATMega32

Fig. 15: Valores de bits del registro TIFR en el AVR ATMega32

El temporizador 1 en modo CTC se compara con el registro OCR1A. Al generar una interrupción, OCF1A en TIFR se establece en Alto. Al verificar el estado de OCF1A en TIFR, se monitorea la interrupción generada por el Temporizador 1. El temporizador 2 en modo CTC se compara con el registro OCR2. Cuando esto genera una interrupción, OCF2 se establece en Alto. Al verificar el estado de OCF2 en TIFR, se monitorea la interrupción generada por el Temporizador 2.

El código C desarrollado para este circuito AVR sigue un algoritmo resumido en el siguiente diagrama de flujo:

Fluxograma do algoritmo usado pelo código AVR para controle de múltiplos servos

Fig. 16: Diagrama de flujo del algoritmo utilizado por el código AVR para controlar múltiples servos

El código C sigue el siguiente algoritmo:

1) Cargue y clasifique las posiciones de los servos (ángulos) para el primer subintervalo de 2 milisegundos –

Para el primer subintervalo de 2 milisegundos, la posición actual, la posición final y la velocidad de rotación de los servos se almacenan en una matriz bidimensional servo_positions(ns+1)(3) . El tamaño de la matriz es una unidad mayor que la cantidad de servos conectados al AVR ATMega32. Como hay 32 servos conectados al controlador, la constante ns se inicializa a 32.

La matriz almacena tres bytes para cada servo. El primer byte es el valor de velocidad, el segundo byte es la posición final (ángulo final) del servo y el tercer byte es la posición actual (ángulo actual) del servo.

Las posiciones finales se mencionan como valores que deben cargarse en el Temporizador 1 para detener la señal PWM cuando se genera la interrupción del Temporizador 1 para el servo respectivo. El servo gira a -90 grados cuando se le suministra un pulso PWM de 1 ms. Gira a 0 grados cuando se suministra un pulso PWM de 1,5 ms y gira a 90 grados cuando se suministra un pulso PWM de 2 ms. Para una señal PWM de al menos 1 ms, el temporizador 1 debe contar hasta 2000, ya que 2000 conteos del temporizador 1 equivalen a 1 milisegundo en la configuración del registro de control. Por lo tanto, las posiciones final y actual de los servos pueden estar entre 2000 y 4000, ya que se puede proporcionar una señal PWM de mínimo 1 milisegundo y máximo 2 milisegundos para girar el servo entre -90 y 90 grados. Por tanto, cualquier valor, para la posición actual y final de los servos, inferior a 2.000 o superior a 4.000, es irrelevante. Por lo tanto, la posición final y actual del servo siempre debe establecerse entre 2000 y 4000.

El código fue desarrollado para rotar el servo en 100 posiciones (ángulos) diferentes entre -90 y 90 grados. Todas las posiciones adyacentes están espaciadas 1,8 grados desde el ángulo del servo. Dado que el recuento del Temporizador 1 para cada posición del servo puede estar entre 2000 y 4000, con 100 posiciones posibles (ángulos), el recuento del Temporizador 1 se incrementa en un factor de 20 para cualquier siguiente posición del servo (ángulo). Por ejemplo, para mover el servo a la posición final de -90 grados, la posición final definida en el código debe ser 2000. Para mover el servo a una posición más alta, es decir, -88,2 grados (-90+1,8 grados), la posición final la posición definida en el código debe ser 2020 y así sucesivamente. Los valores de posición final o actual necesarios para girar los servos en algunos ángulos estándar se enumeran en la siguiente tabla:

La velocidad está controlada por el primer byte de la matriz servo_positions(ns+1)(3) . El valor mencionado en esta variable define el número de ángulos temporales que se alcanzan antes de alcanzar el ángulo final (posición final). Después de cada posición temporal se inserta una zona muerta de 10 microsegundos (equivalente a la cuenta de 20 del temporizador 1). Para cada servo que se detendrá a continuación, el valor en el índice respectivo en la matriz Cycle_speed(temperature) se restablece a 0 y se compara con el valor de velocidad en la matriz servo_positions(ns+1)(3) en un bucle while. El valor de la variable speed_cycle se incrementa en cada posición temporal y se compara con el valor de velocidad del rango servo_positions(ns+1)(3) . Una vez que el valor del índice respectivo en la matriz Cycle_speed(temperature) se vuelve igual al valor de velocidad de la matriz servo_positions(ns+1)(3) , se alcanza la posición final. El valor de velocidad de la matriz servo_positions(ns+1)(3) se utiliza para asignar valor a otros pasos variables. El valor asignado a los escalones es igual al número de puestos temporales. La diferencia entre la posición final y la posición actual se calcula y se divide en pasos para ingresar posiciones temporales antes de alcanzar la posición final (ángulo). Si el valor del paso es 1, se suministrará una señal PWM continua al servo sin ninguna zona muerta, luego el servo girará a la velocidad máxima para alcanzar la posición final desde la posición actual. Si el valor del paso es mayor, habrá la misma cantidad de posiciones temporales y zonas muertas antes de llegar a la posición final, por lo que la velocidad de rotación para alcanzar la posición final se reducirá en consecuencia. La variable de paso se inicializa en 5, pero para cada posición del servo en cada subintervalo de 2 milisegundos, se asigna igual al valor de velocidad respectivo de la matriz servo_positions(ns+1)(3) . El valor de velocidad siempre debe establecerse en un número entero positivo entre 1 y 10 para evitar errores de posición como valores negativos o valores de posición fuera de rango. Por lo tanto, un valor de velocidad de 1 indica la velocidad de rotación más alta del servo respectivo, mientras que un valor de velocidad de 10 indica la velocidad de rotación más baja del servo respectivo.

1) Identificación de instantes de tiempo únicos en los que uno o más servos deben detenerse dentro de un subintervalo de 2 milisegundos.

Las posiciones finales de todos los servos se ordenan en orden ascendente y los índices correspondientes a los servos que deben detenerse uno tras otro se almacenan en una matriz definida como order(ns+1) . El tamaño de la matriz de pedidos es uno mayor que el número de servidores. No se utiliza el índice 0 de la matriz de pedidos .

Puede haber situaciones en las que dos o más servos tengan la misma posición final y deban detenerse al mismo tiempo. Así, la diferencia entre posiciones finales consecutivas después de la clasificación se calcula en un bucle y para cada diferencia distinta de cero, una variable ' instantes ' se incrementa en uno. Esto proporciona el número exacto de cuándo se debe generar la interrupción del Temporizador 1. La diferencia entre posiciones finales consecutivas después de la clasificación se almacena en una variable Temp2 y su valor se carga en el registro OCR1A. Las posiciones finales en las que se deben generar las interrupciones del temporizador 1 se almacenan en otra matriz: timer_inst .

2) Para cada instante de tiempo, genere la interrupción del Temporizador 1 y actualice las salidas de la puerta –

Para cada instante, los valores del puerto (señales PWM de los pines GPIO) se almacenan en una matriz Port_Values . De esta manera, si dos o más servos tienen la misma posición final y deben ser detenidos en el mismo instante de tiempo, se genera una única interrupción en ese instante de tiempo en comparación con los valores del array timer_inst, y todos los servos con esa posición final se detienen donde interrumpen (y en ese instante) asignando a los puertos los valores almacenados para el índice respectivo en el array Port_Values . El valor_puerto respectivo se escribe usando una 'instrucción de cambio' en el código y esto se repite para todos los servos.

La operación BIT Wise OR se utiliza para actualizar repetidamente los valores en la matriz ports_value sin cambiar los valores anteriores. La operación AND bit a bit con valor_puerto invertido se utiliza para desactivar los pines (bits) que están ALTOS en la variable valores_puerto para detener los servos respectivos. Todos los "valores de puerto" se escriben y asignan a todos los PUERTOS en todo momento del temporizador. Aunque no hay ningún valor de puerto (cero) en ningún momento para un puerto determinado, los pines del PUERTO no se cambian debido a la operación BIT Wise AND invertida.

Inicialmente, todas las señales PWM se activan y se suministra al menos 1 milisegundo de señal PWM. Luego, los servos se detienen uno tras otro mientras se genera su respectiva interrupción del Temporizador 1 dentro del subintervalo de 2 milisegundos. En cada interrupción, el temporizador 1 se borra y se carga con el valor de la diferencia con la siguiente posición final consecutiva de la matriz ordenada.

Fig. 17: Captura de pantalla del código AVR para controlar múltiples servomotores

Las posiciones finales y actuales de los servos siempre deben estar entre 2000 y 4000. Es por esto que, por seguridad, el código está desarrollado de tal manera que el usuario debe establecer un valor entre 0 y 100 para las posiciones finales de los servos. Los valores ingresados ​​por el usuario para las posiciones finales de los servos se multiplican por 20 (unidad definida para incrementar el conteo del Temporizador 1) y se suman a 2000 en el código para derivar los instantes de interrupción reales del Temporizador 1. Los valores ​​de las posiciones actuales y los extremos de los ángulos de servo estándar que el usuario debe ingresar en las matrices servo_positions(ns+1)(3) y servo_tracks(10)(2)(ns+1) se enumeran a continuación mesa -

4) Repita subintervalos de 2 milisegundos 10 veces y repita todo el programa infinitamente –

El temporizador 2 genera una interrupción cada 2 milisegundos. Cuando genera una interrupción, es una indicación de que el subintervalo actual de 2 milisegundos ha finalizado. Luego se apagan todos los servos (escribiendo 0x00 en todos los puertos) como medida de precaución. El número de subintervalos de 2 milisegundos se rastrea mediante una variable de ciclos . Esta variable también se incrementa en uno cuando el temporizador 2 genera la interrupción.

Cuando han finalizado 10 subintervalos de 2 milisegundos, ha transcurrido un período de 20 milisegundos. Dado que se supone que los servos funcionan a la frecuencia de señal de 50 Hz (1/50 = 20 milisegundos), las posiciones de los servos ahora se repiten desde la posición en el primer subintervalo de 2 milisegundos. Esto se repite hasta que el sistema permanece encendido.

Tenga en cuenta que en este circuito las señales PWM para 10 secuencias de ángulo de servo para 32 servos están codificadas en el código de firmware. El usuario final no siempre tiene acceso al código de firmware. Así, también se presenta una modificación del circuito anterior donde se eliminan 2 servos en los pines 14 y 15 y se puede conectar un dispositivo externo (otro controlador o PC) al ATMega32 vía UART.

El circuito modificado puede recibir valores de fuentes externas y operar servos en tiempo real por parte de un operador humano. Una subrutina de interrupción ISR (USART_RXC_vect) está definida en el código para recibir información de la fuente externa. Este ISR está comentado en el código original (donde se controlan 32 servos). Disponiendo adecuadamente las posiciones secuenciales, también se pueden preprogramar secuencias complejas. Esto está limitado únicamente por la RAM del microcontrolador para almacenar las variables de datos. El programa presentado aquí contiene un programa secuencial de 10 pasos, que balancea los brazos dentro de límites predefinidos. Esta secuencia se llama pista y la posición junto con la velocidad se definen en la matriz de pista (tridimensional). Se puede programar un microcontrolador externo para pasar las secuencias en el formato predefinido de acuerdo con la entrada humana en tiempo real o la entrada humana en tiempo real se puede pasar a través de una aplicación terminal en una PC en el formato predefinido.

UART se utiliza para actualizar las posiciones de los servos en tiempo real desde el mundo exterior. Esto reducirá los 32 canales a 30 canales, ya que UART utiliza dos pines TX y RX para el procesamiento de datos. Esto es útil en sistemas multiprocesador. El procesador principal envía datos a este servocontrolador dependiendo de entradas o retroalimentación en tiempo real. Esto también es útil para el control inalámbrico de servomotores mediante Bluetooth o cualquier otra transmisión en serie inalámbrica.

Código fuente del proyecto

###

 //Programa para
 #incluir
 #incluir
 #incluir


 #definir ns 32

 #definir nt 8




 posiciones_servo de caracteres sin firmar (ns+1)(3)= {{0,0,0}, //ns=no.of servos

 //Speed Final Position Present Position for initialization only

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},



 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},



 {1,1,100},
 
{1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},



 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 {1,1,100},

 };


 carácter sin firmar temp_servo_positions(ns+1),temp_inst=0,*temp_inst_addr=&temp_inst,step=5;

 temperatura de carbón sin firmar, temp1, temp2, temp3, intercambio, orden (ns+1);

 caracteres sin firmar posiciones_clasificadas(ns+1),instantes=ns,valores_puerto(ns+1)(5),
 temp_port=1,ciclos=0,velocidad=10,uart=0,pista=0;

 unsigned int timer_inst(ns+1),speed_cycle(ns+1),sort=0;

 servo_tracks(nt)(2)(ns+1)={

 {//Velocidad

 //Posición

 {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8 , 1,2,3,4,5,6,7,8},

 {0,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,
 100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100},

 },

 {

 {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8 , 1,2,3,4,5,6,7,8},
 
{0,1,1,1,1,1,1,1,1, 100,100,100,100,100,100,100,100, 100,100,100,100,100,
 100,100,100, 100,100,100,100,100,100,100,100},

 },

 {

 {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8 , 1,2,3,4,5,6,7,8},

 {0,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 100,100,100,100,100,100,100,100, 100,
 100,100,100,100,100,100,100},

 },

 {

 {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8 , 1,2,3,4,5,6,7,8},

 {0,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1 , 100,100,100,100,100,100,100,100},

 },

 {

 {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8 , 1,2,3,4,5,6,7,8},

 {0,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1 , 1,1,1,1,1,1,1,1},

 },

 {

 {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8 , 1,2,3,4,5,6,7,8},
 
{0,100,100,100,100,100,100,100,100, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1 },

 },

 {

 {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8 , 1,2,3,4,5,6,7,8},

 {0,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1},

 },

 {

 {0,1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8, 1,2,3,4,5,6,7,8 , 1,2,3,4,5,6,7,8},

 {0,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,
 100,100,100,100,100,100,100,100, 1,1,1,1,1,1,1,1},

 }

 };


 void sort_positions(nulo);

 void instantes_port_values(void);




 int principal (vacío)

 {

 PUERTO=0; DDRA=0XFF;

 PUERTO=0; DDRB=0XFF;

 PUERTO=0; DDRC=0XFF;

 PUERTO=0; DDRD=0XFF;



 ordenar_posiciones;

 OCR2=250;

 OCR1AH=timer_inst(1)/256; OCR1AL=timer_inst(1)%256;

 temp_inst=1;


 PUERTO=255; //Encender todos los pulsos de servo

 PUERTOB=255;

 PORTC=255;
 
PUERTO=255;


 //Encender temporizadores

 TCCR2=0X0D; //F/128,Modo CTC

 TCCR1B=0X0A;

 TIMSK=0X90; //OCIE 2.1A


 SREG =0X80;


 mientras(1);

 }


 ISR(TIMER2_COMP_vect)

 {

 ciclos++;


 si(ciclos==1)

 {

 //Precaución: apague todos los pulsos (servos)

 PUERTO=0X00;

 PUERTO=0X00;

 PUERTO=0X00;

 PUERTO=0X00;

 // Consulta de actualización de datos del servidor

 //uart=0;

 //hacer{}mientras((UCSRA&0X20)==0);

 //UCSRA =0X40;

 //UDR=0;

 //hacer{}mientras((UCSRA&0X40)==0);

 }

 de lo contrario si (ciclos == 3)

 {

 // Consulta de actualización de datos del servidor

 //uart=0;

 //hacer{}mientras((UCSRA&0X20)==0);

 //UCSRA =0X40;

 //UDR=1;

 //hacer{}mientras((UCSRA&0X40)==0);

 }

 de lo contrario si (ciclos == 5)

 { //<2mS

 ordenar=ns;

 para(temp=1;temp<=ns;temp++)

 {

 if(posiciones_servo(temp)(2)>posiciones_servo(temp)(1))

 {

 //1.5uS para un servo

 ciclo_velocidad(temperatura)++;

 if(speed_cycle(temp)>=servo_positions(temp)(0))

 {



 servo_positions(temp)(2)-=paso;

 si (posiciones_servo (temperatura) (2) = posiciones_servo(temp)(0))

 {

 servo_positions(temp)(2)+=paso;

 if(posiciones_servo(temp)(2)>posiciones_servo(temp)(1))

 {

 posiciones_servo(temp)(2)=posiciones_servo(temp)(1);

 }

 ciclo_velocidad(temp)=0;

 }

 }

 demás

 {

 clasificar--; //para contar el número de posiciones de servo desiguales

 }

 }

 //Clasificación-orden ascendente según servo_positions(1)

 si(ordenar!=0)

 {

 ordenar_posiciones;

 }

 demás

 {

 pista++;

 pista%=nt;

 para(temp=1;temp<=ns;temp++)

 {

 servo_positions(temp)(0)=servo_tracks(pista)(0)(temp);

 servo_positions(temp)(1)=servo_tracks(track)(1)(temp);

 }

 ordenar_posiciones;

 }

 }

 de lo contrario si (ciclos == 10)

 {

 PUERTO=0XFF;

 PUERTOB=0XFF;

 PUERTO=0XFF;

 PUERTO=0XFF;

 temp_inst=1;

 OCR1AH=timer_inst(temp_inst)/256; OCR1AL=timer_inst(temp_inst)%256;

 TCCR1B=0X0A;

 ciclos=0;

 }

 }


 ISR(TIMER1_COMPA_vect)

 {

 TCCR1B=0X00;
 
PUERTO&=~port_values(temp_inst)(1);

 PORTB&=~port_values(temp_inst)(2);

 PORTC&=~port_values(temp_inst)(3);

 PORTD&=~port_values(temp_inst)(4);


 si(temp_inst=1;temp2--)

 {

 intercambiar=0;

 para(temp1=ns;temp1>=1;temp1--)

 {

 si(intercambiar 

###

Diagramas de circuito

Diagrama de circuito para controlador multiservo independiente basado en ATMega32

Vídeo del proyecto

contenido relacionado

Regresar al blog

Deja un comentario

Ten en cuenta que los comentarios deben aprobarse antes de que se publiquen.