32 programa de controlador de servo motor usando ATMEGA32

As aplicações de automação geralmente exigem o controle de vários atuadores (como muitos motores) ao mesmo tempo. Para o controle simultâneo e síncrono de muitos atuadores, eles precisam ser controlados e operados de forma independente. Os engenheiros usam tantos controladores (microcontroladores ou ASICs) quanto o número de atuadores necessário para operar de forma independente. No entanto, quando um controlador é capaz de operar múltiplos atuadores (o que é possível quando possui um grande número de pinos de entrada/saída) e possui alta velocidade de clock (onde, devido à alta velocidade de clock do controlador, os atuadores parecem ser controlados independentemente no mesmo tempo), é preferível usar um único controlador para gerenciar vários atuadores. Isso reduz o tamanho e a complexidade do lado do hardware, reduzindo o custo geral de produção do sistema embarcado específico. No entanto, tal sistema requer um software complexo instalado dentro do controlador.

Neste projeto, um aplicativo embarcado de uso geral foi projetado para controlar 32 servo motores (independentemente e simultaneamente) pela popular placa ATMega32 AVR. Um programa C complexo é projetado aqui para mover 32 servos em velocidades predeterminadas para posições predefinidas pelo ATMega32.

Os servos podem se mover para 10 posições diferentes (faixas) em um intervalo de 20 milissegundos. Eles ocupam cada posição dentro de um subperíodo de 2 milissegundos, ou seja, para assumir uma posição predefinida, demoram no máximo 2 milissegundos ou menos. O conjunto destas posições predefinidas (10 posições definidas individualmente para 32 servos) continua se repetindo em intervalos de tempo de 20 milissegundos até que o sistema permaneça ligado. Dentro de cada subintervalo de 2 milissegundos, os servos se movem para a posição predefinida e permanecem lá até que o subintervalo de 2 milissegundos seja concluído. Para isso, eles são parados em instantes de tempo específicos (que obviamente são sempre 2 milissegundos ou menos). Da mesma forma, um servo pode alcançar e descansar em sua posição predefinida em 1,2 milissegundos e outro pode alcançar e descansar em sua posição predefinida em 1,5 milissegundos. Os servos continuam recebendo pulso PWM até que tenham que ser movidos e então quando precisam ser parados em uma posição pré-definida (dentro do subintervalo de 2 milissegundos), o pulso PWM é interrompido.

Os servos podem assumir até 100 posições predefinidas diferentes em cada subintervalo de 2 milissegundos. Suponha que um servo possa girar de 0 a 180 graus ao fornecer um sinal PWM de 1 milissegundo a 2 milissegundos, então ele pode se mover para 100 posições com uma diferença de 1,8 graus cada.

Ao atingir cada posição (faixa) dentro de um subintervalo de 2 milissegundos, os servos podem alcançar posições predefinidas em velocidades diferentes. Para variação de velocidade, são definidas posições temporárias no código onde o pulso PWM é parado por 10 microssegundos. Quanto maior for o número de posições temporárias (passos ou zonas mortas de 10 microssegundos), menor será a velocidade de rotação. No entanto, o servo alcançará a posição predefinida dentro de um subintervalo de 2 milissegundos. Se houver apenas uma posição temporária definida para atingir a posição predefinida, o servo irá girar em sua velocidade máxima de rotação (já que um pulso PWM contínuo será fornecido a ele) para atingir aquela posição predefinida (ângulo) e irá parar quando atingir sua posição predefinida. posição (faixa) em um instante de tempo dentro de um subintervalo de 2 milissegundos. Se houver 10 posições temporárias definidas para atingir a posição predefinida (ângulo), haverá 10 passos ou zonas mortas de 10 microssegundos, o que reduzirá a velocidade de rotação do servo de acordo. Desta forma, há variação de velocidade onde o servo gira em sua velocidade máxima até a posição predefinida quando há 1 passo (ou zona morta ou posição temporária) e gira lentamente à medida que há mais passos (até 10).

Isto é semelhante às aplicações de automação industrial e pode ser usado para controlar braços robóticos ou asas de um drone. O programa C apresentado aqui é um programa de uso geral que pode ser modificado codificando a velocidade e os valores de posição de servos individuais para se adequar a qualquer tipo de aplicação de automação. O programa C apresentado aqui foi desenvolvido e compilado usando o AVR Studio.

Uma modificação do projeto acima também é fornecida onde 30 servos são controlados (por 30 pinos de entrada/saída do AVR ATMega32) e 2 pinos de entrada/saída são utilizados para comunicação serial com um dispositivo externo. Aqui o dispositivo externo conectado via UART envia os valores para o ATMega32 para controlar e alterar a sequência de servos em tempo real. O dispositivo externo (outro controlador ou PC) pode ser conectado através de RS-232, Bluetooth ou qualquer interface utilizando o protocolo UART.

Protótipo de servocontrolador múltiplo baseado em AVR ATmega32

Fig. 1: Protótipo de Múltiplo Servo Controlador baseado em AVR ATmega32

Componentes necessários

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

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

Diagrama de circuito

O controlador do servo motor para 32 servos possui o seguinte diagrama de circuito –

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

Fig. 3: Diagrama de circuito do servocontrolador múltiplo baseado em AVR ATmega32

O controlador do servo motor para controle de 30 servos com comunicação serial UART para operação remota por dispositivo externo possui o seguinte diagrama de circuito –

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

Fig. 4: Diagrama de circuito do servocontrolador múltiplo baseado em AVR ATmega32

Conexões de Circuito

O circuito usado neste controlador de servo motor é muito simples. Um servo motor possui três terminais – VCC, Terra e Sinal. O pino VCC é conectado à alimentação de 5V e aterrado com o terra comum. O pino de sinal do servo é conectado ao GPIO do controlador para fornecer sinal PWM. Os servos usados ​​neste circuito são o SG90 que pode girar 180 graus (90 graus em qualquer direção). Ao fornecer pulso alto de 1 ms, o servo gira para -90 graus. Ao fornecer pulso alto de 1,5 ms, ele se move para 0 grau e ao fornecer pulso alto de 2 ms, ele se move para 90 graus. Portanto, para uma entrada PWM de 1 ms a 2 ms, o servo gira de -90 graus a 90 graus.

Diagrama de pinos do servo motor

Fig. 5: Diagrama de pinos do servo motor

O AVR ATMega32 possui 32 pinos de entrada/saída de uso geral a partir dos quais o sinal PWM pode ser fornecido a 32 servos. Ele vem em pacote PDIP de 40 pinos ou TQFP/MLF de 44 pinos. O pacote PDIP de 40 pinos é usado porque o circuito é projetado em uma placa de ensaio. AVR ATMega32 tem o seguinte diagrama de pinos –

Fig. 6: Diagrama de pinos do AVR ATMega32

Os servos são conectados em ordem alfabética das portas da seguinte forma –
PORTA: S1-S8
PORTOB: S9-S16
PORTO: S17-S24
PORTO: S25-S32

Para alta velocidade de operação, a frequência máxima é selecionada para o AVR. Um oscilador de cristal de 16 MHz é conectado entre os pinos 12 e 13. Dois capacitores de 22pF são usados ​​para ressoar com a indutância do cristal que são conectados em série ao terra.

Os servos assim como o ATMega32 são fornecidos com 5V DC para operação. A fonte de alimentação CC do circuito é escolhida levando-se em consideração a corrente total consumida por todos os servos conectados ao AVR. A corrente nominal da fonte CC deve ser maior para suportar as correntes de pico de carga de todos os servos. Uma regra prática para encontrar a corrente de pico de carga para servos padrão é 1A para servos de 3 a 4 Kg. Os servos SG90 usados ​​neste circuito pesam 14,7 g (Fonte – Folha de Dados do Servo SG90). Portanto, cada servo consome corrente máxima de cerca de 15 miliamperes. O pacote PDIP do ATMega32 consome 200 miliamperes de corrente. Portanto, o requisito de corrente total pode ser calculado somando a corrente máxima consumida por todos os servos e pelo controlador AVR como segue –

Corrente Total = Corrente consumida pelo AVR ATMega32 + Corrente consumida pelos Servos

Corrente Total = 32X15 mA + 200 mA

Corrente total = 680 mA

Portanto, a alimentação DC deve ser de 5V e pelo menos 680 mA.

Na versão do circuito operada remotamente, dois servos são removidos dos pinos 14 e 15 e um dispositivo externo (controlador ou PC) é conectado ao ATMega32 via UART.

Fig. 7: Imagem mostrando o circuito controlador do controle servo múltiplo baseado em AVR ATMega32

Como funciona o circuito

O controlador do servo motor projetado aqui possui um conjunto predefinido de posições de servo (10 posições por servo) no código do firmware. Quando o circuito é ligado, o controlador lê uma matriz onde a posição atual, a posição final e a velocidade de rotação de cada servo são predefinidas. Conseqüentemente, os instantes de tempo dentro do primeiro subintervalo de 2 milissegundos para cada servo parar são determinados. Os instantes de tempo para parar os servos são classificados em ordem crescente e o instante de tempo com o menor valor é carregado no registro de comparação de um temporizador. Os pulsos PWM são ativados para todos os servos. O sinal PWM para cada servo é desligado conforme a respectiva interrupção do temporizador é gerada. A largura máxima do sinal PWM pode ser de 2 milissegundos. Os servos giram para sua posição predefinida (pista) e param para descansar nessa posição. Cada servo possui sua própria posição final (ângulo) e velocidade (determinada pelo número de zonas mortas ou posições temporárias definidas para aquele servo). Assim que o subintervalo de 2 milissegundos for concluído, todos os servos alcançaram e pararam em suas primeiras posições predefinidas. As novas posições atuais dos servos são atualizadas no controlador. As posições finais do primeiro ciclo passam agora a ser posições atuais do segundo ciclo.

Agora as posições e velocidades finais para o segundo subintervalo de 2 milissegundos são carregadas na matriz. Conseqüentemente, os instantes de tempo dentro do segundo subintervalo de 2 milissegundos para cada servo parar são determinados. Novamente, os instantes de tempo para parar os servos são classificados em ordem crescente e o instante de tempo com o menor valor é carregado no registro de comparação do temporizador. Os pulsos PWM são ativados para todos os servos. O sinal PWM para cada servo é desligado conforme sua respectiva interrupção do temporizador é gerada. Isso se repete para cada um dos oito subintervalos restantes de 2 milissegundos.

Assim que os 10 ciclos (subintervalos de 2 milissegundos) forem concluídos, o loop de 20 milissegundos será concluído. Agora, novamente, o loop começa no primeiro subintervalo de 2 milissegundos e continua nos ciclos restantes. O loop de 10 ciclos para 10 posições predefinidas para cada servo continua se repetindo até que o sistema permaneça ligado. Se o sistema for desligado e reiniciado, o ciclo de 10 ciclos começa a partir do primeiro ciclo.

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

Fig. 8: Imagem mostrando o funcionamento do controle servo múltiplo baseado em AVR ATMega32

Guia de programação

O firmware deste controlador de servo motor foi desenvolvido em C embarcado para AVR ATMega32. Para escrever e compilar o código, são usados ​​​​o winavr e o bloco de notas do programador.

O funcionamento deste circuito é baseado em interrupções de temporizador. O AVR ATMega32 possui três temporizadores – Timer0 (que é um temporizador de 8 bits), Timer1 (que é um temporizador de 16 bits) e Timer2 (que é um temporizador de 8 bits). Timer1 e Timer2 são utilizados na codificação deste sistema.

O Timer1 é um contador de 16 bits formado por dois registradores de 8 bits – TCNT1H e TCNT1L. É utilizado para contar o tempo e gerar interrupções nos instantes de tempo desejados para desligar os respectivos pulsos do servo. O Timer1 pode operar em cinco modos –

1) Modo Normal

2) Limpar temporizador no modo Compare Match (CTC)

3) Modo PWM rápido

4) Modo PWM com correção de fase

5) Modo PWM com correção de fase e frequência

Aqui, o Timer1 é operado no modo CTC (Clear Timer on Compare Match).

Valores de bits do registro TCNT1 no AVR ATMega32

Fig. 9: Valores de bits do registro TCNT1 no AVR ATMega32

Os seguintes registros estão associados ao Timer 1 –

1) TCCR1A (registro de controle do temporizador/contador 1 A)

2) TCCR1B (registro de controle B do temporizador/contador 1)

3) TCNT1 (Registro de Temporizador/Contador 1, é de 16 bits – TCNT1H e TCNT1L)

4) OCR1A (Registro de comparação de saída 1 A, é de 16 bits – OCR1AH ​​e OCR1AL)

5) OCR1B (Registro de comparação de saída 1 B, é de 16 bits – OCR1BH e OCR1BL)

6) ICR1 (Registro de Captura de Entrada 1, é de 16 bits – ICR1H e ICR1L)

7) TIMSK (registro de máscara de interrupção de temporizador/contador)

8) TIFR (Registro de Sinalização de Interrupção do Temporizador)

O Timer 1 possui dois registradores de controle – TCCR1A e TCCR1B. Possui um registrador Flag – TIFR que informa ao controlador o status do Timer 1. O TCCR1A é um registrador de 8 bits com os seguintes valores de bit –

Valores de bits do registro TCCR1A no AVR ATMega32

Fig. 10: Valores de bits do registro TCCR1A no AVR ATMega32

O TCCR1B também é um registro de 8 bits com os seguintes valores de bits –

Valores de bits do registro TCCR1B no AVR ATMega32

Fig. 11: Valores de bits do registro TCCR1B no AVR ATMega32

Para definir o Timer1 no modo CTC, WGM13 e WGM12 no TCCR1B devem ser 0 e 1 respectivamente, enquanto WGM11 e WGM10 no TCCR1A devem ser 0.

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

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

Além disso, como um oscilador de cristal de 16 MHz está conectado ao ATMega32, o período de tempo do oscilador de cristal é o seguinte –

Período de tempo = 1/Frequência

= 1/(16X10^6)

= 0,0625 microssegundo

Com este F_CPU no modo normal, o Timer1 só poderia contar 4,096 milissegundos para escala completa (0,0625 microssegundos X 2 ^ 16). Portanto, ele precisa ser pré-escalado. A pré-escala é determinada pelos bits CS12, CS11 e CS10 do registro TCCR1B de acordo com a tabela a seguir –

Um valor de 0x0A é gravado em TCCR1B enquanto TCCR1A é deixado em 0x00. Isso define o Timer1 no modo CTC, tornando WGM13, WGM12 no TCCR1B em 0 e 1, respectivamente, enquanto WGM11 e WGM10 no TCCR1A em 0. Isso também seleciona um pré-escalador de clkE/S/8. Portanto, o Timer1 conta com um clock de 2 MHz (16/8). Cada contagem do cronômetro agora corresponde ao período de tempo calculado abaixo –

Período de tempo = 1/Frequência

= 1/(2X10^6)

= 0,5 microssegundo

Agora o Timer1 pode contar até 32,768 milissegundos (0,5 microssegundos X 2 ^ 16). Para gerar interrupções dentro do subintervalo de 2 milissegundos, ele só precisa contar até 4.000 (0,5 microssegundo X 4.000 = 2 milissegundos). Quando o registro TCCR1B é escrito com bits de pré-escalador de clock diferentes de zero, o temporizador/contador é iniciado. Portanto, este registro é escrito após classificar e escrever os valores de posição mínima no Registro de comparação de saída de 16 bits (OCR1A).

ATMega32 possui registro TIMSK que possui bits de controle de interrupção para todos os seus temporizadores/contadores, incluindo o temporizador 1. TIMSK possui os seguintes valores de bit –

Valores de bits do registro TIMSK no AVR ATMega32

Fig. 13: Valores de bits do registro TIMSK no AVR ATMega32

Os bits TICIE1, OCIE1A, OCIE1B e TOIE1 do registro TIMSK estão associados ao Timer 1. O TIMSK é definido como 0x90, o que define OCIE2 e OCIE1A como 1. Ao definir OCIE1A como High, ele habilita a interrupção de correspondência de comparação de saída do Timer/Counter1. . O instante de tempo para parar o sinal PWM é carregado no registro OCR1A. Quando a contagem de TCNT1 coincide com o valor carregado no OCR1A, é gerada uma interrupção. Cada vez que a interrupção é gerada, o sinal PWM para o respectivo sevo é desligado.

Timer2 é um registro de 8 bits (TCNT2) que é usado para gerar interrupções a cada 2 milissegundos (para indicar o final de subintervalos de 2 milissegundos). Ele pode operar em quatro modos –

1) Modo normal

2) Limpar temporizador no modo Compare Match (CTC)

3) Modo PWM rápido

4) Modo PWM com correção de fase

Para habilitar o Timer 2, o OCIE2 foi definido como 1 no registro TIMSK. O temporizador 2 possui os seguintes registros associados a ele –

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

2) TCNT2 (registro de temporizador/contador 2)

3) OCR2 (Registro de comparação de saída 2)

4) ASSR (registro de status assíncrono)

5) TIMSK (registro de máscara de interrupção de temporizador/contador)

6) TIFR (Registro de Sinalização de Interrupção do Temporizador)

O temporizador 2 é controlado pelo registro TCCR2. TCCR2 tem os seguintes valores de bits –

Valores de bits do registro TCCR2 no AVR ATMega32

Fig. 14: Valores de bits do registro TCCR2 no AVR ATMega32

Um valor de 0x0D é gravado em TCCR2. O modo de operação do Timer 2 é selecionado escrevendo nos bits WGM21 e WGM20 do TCCR2. A seleção do modo é feita de acordo com a tabela a seguir –

Ao escrever 0x0D em TCCR2, WGM20 é definido como 0 e WGM21 como 1, então o Timer 2 opera no modo CTC. Assim como feito para o Timer 1, a pré-escala também é feita para o Timer 2. Para o Timer 2, a pré-escala é determinada pelos bits CS22, CS21 e CS20 do registro TCCR2 de acordo com a tabela a seguir –

Ao escrever 0x0D em TCCR2, CS22, CS21 e CS20 de TCCR2 são definidos como 1, 0 e 1 respectivamente. Portanto, o Timer2 conta com um clock de 125 KHz (16/128 MHz). Cada contagem do cronômetro agora corresponde ao período de tempo calculado abaixo –

Período de tempo = 1/Frequência

= 1/(125X10^3)

= 8 microssegundos

Por 2 milissegundos, o temporizador 2 agora precisa contar até 250 (8 microssegundos x 25

0 = 2 milissegundos). Portanto, um valor de 250 é gravado no OCR2. Quando o Timer 2 conta até 250 e corresponde ao valor escrito em OCR2, a interrupção é gerada. Isto indica o fim de um subintervalo (um dos dez ciclos para posições de servo).

O registro TIFR possui os seguintes valores de bits –

Valores de bits do registro TIFR no AVR ATMega32

Fig. 15: Valores de bits do registro TIFR no AVR ATMega32

O temporizador 1 no modo CTC está comparando com o registro OCR1A. Quando gera interrupção, OCF1A no TIFR é definido como High. Ao verificar o status do OCF1A no TIFR, a interrupção gerada pelo Timer 1 é monitorada. O temporizador 2 no modo CTC está comparando com o registro OCR2. Quando isso gera interrupção, OCF2 é definido como High. Ao verificar o status do OCF2 no TIFR, a interrupção gerada pelo Timer 2 é monitorada.

O código C desenvolvido para este circuito AVR segue um algoritmo resumido pelo seguinte fluxograma –

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

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

O Código C segue o seguinte algoritmo –

1) Carregue e classifique as posições do servo (ângulos) para o primeiro subintervalo de 2 milissegundos –

Para o primeiro subintervalo de 2 milissegundos, a posição atual, a posição final e a velocidade de rotação dos servos são armazenadas em uma matriz bidimensional posições_servo(ns+1)(3). A matriz é dimensionada em uma unidade maior que o número de servos conectados ao AVR ATMega32. Como existem 32 servos conectados ao controlador, a constante ns é inicializado em 32.

A matriz armazena três bytes para cada servo. O primeiro byte é o valor da velocidade, o segundo byte é a posição final (ângulo final) do servo e o terceiro byte é a posição atual (ângulo atual) do servo.

As posições finais são mencionadas como valores que devem ser carregados no Timer 1 para parar o sinal PWM quando a interrupção do Timer 1 para o respectivo servo for gerada. O servo gira para -90 graus quando um pulso PWM de 1 ms é fornecido a ele. Ele gira para 0 grau quando o pulso PWM de 1,5 ms é fornecido e gira para 90 graus quando o pulso PWM de 2 ms é fornecido. Para um sinal PWM de pelo menos 1 ms, o Timer 1 precisa contar até 2.000, pois 2.000 contagens do Timer 1 são equivalentes a 1 milissegundo nas configurações de seus registros de controle. Assim, as posições final e atual dos servos podem estar entre 2.000 e 4.000, pois o sinal PWM de mínimo 1 milissegundo e máximo de 2 milissegundos pode ser fornecido para girar o servo entre -90 a 90 graus. Qualquer valor, para posição atual e final dos servos, menor que 2.000 ou maior que 4.000, é portanto irrelevante. Assim, a posição final e atual do servo deve ser sempre definida entre 2000 e 4000.

O código foi desenvolvido para girar o servo em 100 posições (ângulos) diferentes entre -90 e 90 graus. Todas as posições adjacentes são espaçadas em 1,8 graus do ângulo do servo. Como a contagem do Timer 1 para cada posição do servo pode estar entre 2.000 e 4.000, com 100 posições (ângulos) possíveis, a contagem do Timer 1 é incrementada por um fator de 20 para qualquer próxima posição do servo (ângulo). Por exemplo, para mover o servo para a posição final de -90 graus, a posição final definida no código deve ser 2000. Para mover o servo para uma posição acima, ou seja, -88,2 graus (-90+1,8 graus), a posição final definida no código deve ser 2020 e assim por diante. Os valores da posição final ou atual necessários para girar os servos em alguns ângulos padrão estão listados na tabela a seguir –

A velocidade é controlada pelo primeiro byte do posições_servo(ns+1)(3) variedade. O valor mencionado nesta variável define a quantidade de ângulos temporários que são alcançados antes de atingir o ângulo final (posição final). Uma zona morta de 10 microssegundos (equivalente à contagem de 20 do Timer 1) é inserida após cada posição temporária. Para cada servo que deve ser parado em seguida, o valor no respectivo índice em ciclo_velocidade(temperatura) matriz é redefinida para 0 e comparada ao valor de velocidade de posições_servo(ns+1)(3) matriz em um loop while. O valor da variável speed_cycle é incrementado em cada posição temporária e comparado com o valor da velocidade de posições_servo(ns+1)(3) variedade. Uma vez que o valor do respectivo índice em ciclo_velocidade(temperatura) matriz torna-se igual ao valor da velocidade de posições_servo(ns+1)(3) matriz, a posição final é alcançada. O valor da velocidade de posições_servo(ns+1)(3) array é usado para atribuir valor a outra variável passos. O valor atribuído a passos é igual ao número de cargos temporários. A diferença entre a posição final e a posição atual é calculada e dividida por etapas para inserir posições temporárias antes de atingir a posição final (ângulo). Se o valor do passo for 1, o sinal PWM contínuo será fornecido ao servo sem qualquer zona morta, então o servo girará até a velocidade máxima para alcançar a posição final a partir da posição atual. Se o valor do passo for maior, haverá o mesmo número de posições temporárias e zonas mortas antes de atingir a posição final, portanto a velocidade de rotação para atingir a posição final será reduzida de acordo. A variável de passo é inicializada como 5, mas para cada posição do servo em cada subintervalo de 2 milissegundos, ela é atribuída igual ao respectivo valor de velocidade de posições_servo(ns+1)(3) variedade. O valor da velocidade deve ser sempre definido como um número inteiro positivo variando entre 1 e 10 para evitar erros de posição, como valores negativos ou valores de posição fora da faixa. Assim, o valor da velocidade igual a 1 indica a maior velocidade de rotação do respectivo servo enquanto o valor da velocidade igual a 10 indica a menor velocidade de rotação do respectivo servo.

1) Identificação de instantes de tempo únicos quando um ou mais servos precisam ser parados dentro de um subintervalo de 2 milissegundos –

As posições finais de todos os servos são ordenadas em ordem crescente e os índices respectivos aos servos que devem ser parados um após o outro são armazenados em um array definido como ordem(ns+1). O tamanho do ordem array é um maior que o número de servos. O índice 0 do ordem matriz não é usada.

Pode haver situações onde dois ou mais servos podem ter a mesma posição final e precisam ser parados ao mesmo tempo. Assim, a diferença entre posições finais consecutivas após a classificação é calculada em um loop e para cada diferença diferente de zero, uma variável 'instantes'é incrementado em um. Isto fornece o número exato quando a interrupção do Timer 1 deve ser gerada. A diferença entre posições finais consecutivas após a classificação é armazenada em uma variável Temp2 e seu valor é carregado no registro OCR1A. As posições finais nas quais as interrupções do Timer 1 devem ser geradas são armazenadas em outro array – timer_inst .

2) Para cada instante de tempo único, gere a interrupção do Timer 1 e atualize as saídas da porta –

Para cada instante, os valores das portas (sinais PWM dos pinos GPIO) são armazenados em uma matriz Valores_porta . Desta forma, se dois ou mais servos possuem a mesma posição final e devem ser parados no mesmo instante de tempo, uma única interrupção é gerada naquele instante de tempo por comparação com os valores do array timer_inst , e todos os servos com aquela posição final são parados em que interrompem (e naquele instante) atribuindo às portas os valores armazenados para o respectivo índice em Valores_porta variedade. O respectivo port_value é escrito usando uma 'instrução switch' no código e isso é repetido para todos os servos.

A operação BIT Wise OR é usada para atualizar repetidamente os valores na matriz ports_value sem alterar os valores anteriores. A operação AND bit a bit com port_value invertido é usada para desligar os pinos (bits) que estão em ALTO na variável port_values ​​para parar o(s) respectivo(s) servo(s). Todos os “port_values” são escritos e atribuídos a todas as PORTs em todos os instantes do temporizador. Mesmo que não haja port_value (zero) em nenhum instante para uma determinada porta, os pinos da PORT não são alterados devido à operação BIT Wise AND invertida.

Inicialmente, todos os sinais PWM são LIGADOS e pelo menos 1 milissegundo de sinal PWM é fornecido. Então, os servos são parados um após o outro enquanto sua respectiva interrupção do Timer 1 é gerada dentro do subintervalo de 2 milissegundos. A cada interrupção, o Timer 1 é limpo e carregado com o valor da diferença com a próxima posição final consecutiva do array classificado.

Fig. 17: Captura de tela do código AVR para controle de múltiplos servomotores

As posições finais e atuais dos servos devem estar sempre entre 2000 e 4000. É por isso que, por segurança, o código é desenvolvido de forma que o usuário deve definir um valor entre 0 e 100 para as posições finais dos servos. Os valores inseridos para as posições finais dos servos pelo usuário são multiplicados por 20 (unidade definida para incrementar a contagem do Timer 1) e são adicionados a 2.000 no código para derivar os instantes reais de interrupção do Timer 1. Os valores das posições atuais e finais dos ângulos servo padrão que devem ser inseridos pelo usuário no posições_servo(ns+1)(3) e servo_tracks(10)(2)(ns+1) matrizes estão listadas na tabela a seguir –

4) Repita subintervalos de 2 milissegundos por 10 vezes e repita toda a programação infinitamente –

O Timer 2 está gerando interrupção a cada 2 milissegundos. Quando gera uma interrupção, é uma indicação de que o subintervalo atual de 2 milissegundos terminou. Todos os servos são então desligados (gravando 0x00 em todas as portas) como medida de precaução. O número de subintervalos de 2 milissegundos é rastreado por uma variável ciclos. Esta variável também é incrementada em um quando o Timer 2 gera a interrupção.

Quando 10 subintervalos de 2 milissegundos terminarem, um período de 20 milissegundos terá passado. Como se considera que os servos operam na frequência do sinal de 50 Hz (1/50 = 20 milissegundos), agora as posições dos servos são repetidas a partir da posição no primeiro subintervalo de 2 milissegundos. Isso continua se repetindo até que o sistema permaneça ligado.

Observe que neste circuito os sinais PWM para 10 sequências de ângulos de servo para 32 servos são codificados no código do firmware. O usuário final nem sempre tem acesso ao código do firmware. Assim, também é apresentada uma modificação do circuito acima onde 2 servos são removidos nos pinos 14 e 15 e um dispositivo externo (outro controlador ou PC) pode ser conectado ao ATMega32 via UART.

O circuito modificado pode receber valores de fontes externas e operar servos em tempo real por um operador humano. Uma sub-rotina de interrupção ISR(USART_RXC_vect) é definido no código para receber informações da fonte externa. Este ISR está comentado no código original (onde 32 servos são controlados). Através do layout adequado das posições sequenciais, sequências complexas também podem ser pré-programadas. Isto é limitado apenas pela RAM do microcontrolador para armazenar as variáveis ​​de dados. O programa aqui apresentado contém um programa sequencial de 10 passos, que balança os braços dentro de limites pré-definidos. Esta sequência é denominada trilha e a posição junto com a velocidade são definidas na matriz de trilhas (3-Dimensional). Um microcontrolador externo pode ser programado para passar as sequências no formato predefinido de acordo com a entrada humana em tempo real ou a entrada humana em tempo real pode ser passada através de um aplicativo de terminal em um PC no formato predefinido.

UART é usado para atualizar as posições dos servos em tempo real do mundo exterior. Isso reduzirá os 32 canais para 30 canais, já que dois pinos TX e RX são usados ​​pelo UART para processamento de dados. Isso é útil em sistemas com múltiplos processadores. O processador principal emite dados para este servo controlador dependendo das entradas ou feedbacks em tempo real. Isto também é útil para controle sem fio de servomotores usando Bluetooth ou qualquer outra transmissão serial sem fio.

Código-fonte do projeto

###

//Program to
#include  
#include 
​#include 


#define ns 32

#define nt 8


                          

unsigned char servo_positions(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},

                                     };                                  


unsigned char temp_servo_positions(ns+1),temp_inst=0,*temp_inst_addr=&temp_inst,step=5;

unsigned char temp,temp1,temp2,temp3,swap,order(ns+1);

unsigned char sorted_positions(ns+1),instants=ns,port_values(ns+1)(5),
temp_port=1,cycles=0,speed=10,uart=0,track=0;

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

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

                                    {//Speed

//Position

{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(void);

void instants_port_values(void);




int main(void)

{

    PORTA=0;    DDRA=0XFF;

    PORTB=0;    DDRB=0XFF;

    PORTC=0;    DDRC=0XFF;

    PORTD=0;    DDRD=0XFF;

   

sort_positions ;

OCR2=250;

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

temp_inst=1;


PORTA=255;    //Turn ON All servo pulses

PORTB=255;    

PORTC=255;    

PORTD=255;


    //Turn ON timers

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

    TCCR1B=0X0A;

    TIMSK =0X90;   //OCIE 2,1A


SREG =0X80;


    while(1);

}


ISR(TIMER2_COMP_vect)

{

    cycles++;


    if(cycles==1)

    {

        //Precautionary:turn Off all pulses(servos)

        PORTA=0X00;

        PORTB=0X00;

        PORTC=0X00;

PORTD=0X00;

//Servo data update inquiry

//uart=0;

//do{}while((UCSRA&0X20)==0);

//UCSRA =0X40;

//UDR=0;

//do{}while((UCSRA&0X40)==0);  

    }

else if(cycles==3)

    {

        //Servo data update inquiry

//uart=0;

//do{}while((UCSRA&0X20)==0);

//UCSRA =0X40;

//UDR=1;

//do{}while((UCSRA&0X40)==0);  

    }

else if(cycles==5)

    { //<2mS

        sort=ns;

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

        {

            if(servo_positions(temp)(2)>servo_positions(temp)(1))

            {

                //1.5uS for one servo

                speed_cycle(temp)++;

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

                {

                   

servo_positions(temp)(2)-=step;

if(servo_positions(temp)(2)=servo_positions(temp)(0))

                {

                    servo_positions(temp)(2)+=step;

if(servo_positions(temp)(2)>servo_positions(temp)(1))

{

    servo_positions(temp)(2)=servo_positions(temp)(1);

}

                    speed_cycle(temp)=0;

                }

            }

            else

            {

                sort--;  //to count no.of unequal servo positions

            }

        }

        //Sorting-Ascending Order according to servo_positions(1)

        if(sort!=0)

        {

            sort_positions ;

        }

else

{

    track++;

track%=nt;

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

{

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

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

}

sort_positions ;

}

    }

    else if(cycles==10)

    {

        PORTA=0XFF;

        PORTB=0XFF;

        PORTC=0XFF;

        PORTD=0XFF;

        temp_inst=1;

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

        TCCR1B=0X0A;

        cycles=0;

}

}


ISR(TIMER1_COMPA_vect)

{

    TCCR1B=0X00;

PORTA&=~port_values(temp_inst)(1);

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

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

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


    if(temp_inst=1;temp2--)

    {

        swap=0;

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

        {

            if(swap

###

 

Diagramas de circuito

Diagrama de circuito para controlador independente de múltiplos servos baseado em ATMega32

Vídeo do projeto

Related Content

Back to blog

Leave a comment

Please note, comments need to be approved before they are published.