collapse

* Posts Recentes

Amplificador - Rockboard HA 1 In-Ear por almamater
[Ontem às 19:13]


O que é isto ? por KammutierSpule
[26 de Março de 2024, 19:35]


Bateria - Portátil por almamater
[25 de Março de 2024, 22:14]


Emulador NES em ESP32 por dropes
[13 de Março de 2024, 21:19]


Escolher Osciloscópio por jm_araujo
[06 de Fevereiro de 2024, 23:07]


TP4056 - Dúvida por dropes
[31 de Janeiro de 2024, 14:13]


Leitura de dados por Porta Serie por jm_araujo
[22 de Janeiro de 2024, 14:00]


Distancia Cabo por jm_araujo
[08 de Janeiro de 2024, 16:30]


Meu novo robô por josecarlos
[06 de Janeiro de 2024, 16:46]


Laser Engraver - Alguém tem? por almamater
[16 de Dezembro de 2023, 14:23]

Autor Tópico: PWM, Potenciômetro e Servo; Com timer0, modo CTC e rodando assíncrono  (Lida 10902 vezes)

0 Membros e 1 Visitante estão a ver este tópico.

Offline Pinout

  • Mini Robot
  • *
  • Mensagens: 22
Re: PWM, Potenciômetro e Servo; Com timer0, modo CTC e rodando assíncrono
« Responder #15 em: 12 de Dezembro de 2014, 18:05 »
Excelente. Mais conhecimento agregado.

Obrigado pelos esclarecimentos.
A wise person listens and takes in more instruction;
A man of understanding acquires skillful direction

Offline Pinout

  • Mini Robot
  • *
  • Mensagens: 22
Re: PWM, Potenciômetro e Servo; Com timer0, modo CTC e rodando assíncrono
« Responder #16 em: 12 de Dezembro de 2014, 19:25 »
Código melhorado, com range de 206 (teoricamente o range do servo é de 180, mas o tower pro 9g tem um pouco mais) seguindo o que o jm_araujo e o Njay me esclareceram:

Código: [Seleccione]
/*
 *
 * Created: 10/12/2014 09:29:27
 */
#include <avr/io.h>
#include <avr/interrupt.h>

#define SERVO (1 << PD6)

void ADC_init() { // inicia ADC em modo contínuo
    ADCSRA = (7 << ADPS0); // prescaler de 128
    ADMUX = (1 << REFS0); // tensão de referência a própria alimentação do mcu
    ADCSRB &= ~(7 << ADTS0); // modo contínuo
    ADCSRA |= (1 << ADATE); // habilita modo contínuo
    ADCSRA |= (1 << ADEN); // habilita ADC
    ADCSRA |= (1 << ADSC); // inicializa conversões - faz uma primeira conversão
}

void CONFIG_servo() {
    DDRD = (SERVO); // seta pino como saída
    PORTD &= ~SERVO; // assegura nível lógico 0 para o pino
}

void timer0_init(void) {
    TCCR0A = (1<<WGM01); // modo CTC
    TCCR0B = (1<<CS01); // prescaler 1:8
    OCR0A = 19; // interrupção à cada 0.01ms - (((Fosc / prescaler) / ((1/tempo alvo) * 1000)) -1), ou seja ((( 16Mhz / 8) / ((1/0.01) * 1000)) -1)
    TIMSK0 = (1<<OCIE0A); // habilita interrupções no timer
    sei(); // habilita interrupções globais
}

int main(void) {
    CONFIG_servo();
    ADC_init();
    timer0_init();
    while(1);
    return 1;
}

ISR(TIMER0_COMPA_vect) {
    static uint16_t pulseCount; // variável static, utilizada como contador
    pulseCount++; // incrementa o contador à cada 0.1ms
    if(pulseCount == 2000) { // atende à condição à cada 20ms - frequência de 50Hz
        PORTD ^= SERVO; // inverte estado lógico do pino (sempre coloca em nível lógico 1)
    } else if(pulseCount >= (2047 + (ADC / 5))) { // verifica valor do contador, compara com potenciômetro e, quanto iguais, limpa o pino
        PORTD ^= SERVO; // inverte estado lógico do pino (sempre coloca nível lógico 0)
        pulseCount -= 2000; // atribui ao contador valor para frequência de 50Hz subtraindo o que foi gerado no dutty cycle
    }
}


« Última modificação: 27 de Janeiro de 2015, 11:29 por Pinout »
A wise person listens and takes in more instruction;
A man of understanding acquires skillful direction

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.598
    • Tróniquices
Re: PWM, Potenciômetro e Servo; Com timer0, modo CTC e rodando assíncrono
« Responder #17 em: 12 de Dezembro de 2014, 20:35 »
E agora o teu próximo desafio é: como suportar vários servos ao mesmo tempo :)


p.s.: A biblioteca de suporte ao GCC para AVR trás uma macro que faz o "1 << XX", chamada _BV() (ex.: _BV(PD6)) . "BV" penso que vem de "Bit Value". O help da libc para AVR tem tudo o que existe para AVR, é documentação imprescindível para quem programa a este nível, logo a seguir à datasheet.
« Última modificação: 12 de Dezembro de 2014, 20:39 por Njay »

Offline Pinout

  • Mini Robot
  • *
  • Mensagens: 22
Re: PWM, Potenciômetro e Servo; Com timer0, modo CTC e rodando assíncrono
« Responder #18 em: 12 de Dezembro de 2014, 21:00 »
Opa! Esse já está pronto também...

Código: [Seleccione]
/*
 *
 * Created: 11/12/2014 11:15:59
 */

#include <avr/io.h>
#include <avr/interrupt.h>

#define POT_STEER (1 << PC0);
#define POT_GEAR (1 << PC1);

#define SERVO_STEER (1 << PD5)
#define SERVO_GEAR (1 << PD6)

volatile uint16_t servo_steer_pulse;
volatile uint16_t servo_gear_pulse;

void ADC_init() {
ADCSRA = (7 << ADPS0); // prescaler de 128
ADMUX = (1 << REFS0); // tensão de referência a própria alimentação do mcu
ADCSRA |= (1 << ADEN); // habilita ADC
ADCSRA |= (1 << ADSC); // faz primeira conversão e habilita conversões
}

void getPulseSteer() {
ADMUX &= 0XC0; // limpa canais
ADMUX |= (uint8_t) 0; // seleciona canal 0 do ADC (PC0)
ADCSRA |= (1 << ADSC); // realiza conversão
while(ADCSRA & (1 << ADSC)); // espera conclusão da conversão
servo_steer_pulse = ADC; // atribui valor do ADC à variável relativa ao servo
}

void getPulseGear() {
ADMUX &= 0XC0;
ADMUX |= (uint8_t) 1; // seleciona canal 1 do ADC (PC1)
ADCSRA |= (1 << ADSC);
while(ADCSRA & (1 << ADSC));
servo_gear_pulse = ADC;
}

void CONFIG_servos() {
DDRD = (SERVO_STEER) | (SERVO_GEAR); // Seta o pino como saída
PORTD &= ~SERVO_STEER;
PORTD &= ~SERVO_GEAR;
}

void timer0_init(void) {
TCCR0A = (1<<WGM01); // Timer no modo CTC
TCCR0B = (1<<CS01); // Prescaler em 1:8
OCR0A = 19; // interrupção à cada 0.01ms - (((Fosc / prescaler) / ((1/tempo alvo) * 1000)) -1), ou seja ((( 16Mhz / 8) / ((1/0.01) * 1000)) -1) = 19
TIMSK0 = (1<<OCIE0A); // Habilita interrupções no timer
}

void timer2_init(void) {
TCCR2A = (1<<WGM21); // Timer no modo CTC
TCCR2B = (1<<CS21); // Prescaler em 1:8
OCR2A = 19; // interrupção à cada 0.01ms - (((Fosc / prescaler) / ((1/tempo alvo) * 1000)) -1), ou seja ((( 16Mhz / 8) / ((1/0.01) * 1000)) -1) = 19
TIMSK2 = (1<<OCIE2A); // Habilita interrupções no timer
}

int main(void) {
CONFIG_servos();
ADC_init();
timer0_init();
timer2_init();
sei(); // Habilita interrupções globais
while(1) {
getPulseSteer();
getPulseGear();
}
return 1;
}

ISR(TIMER0_COMPA_vect) {
static uint16_t counterSteer;
counterSteer++; // Incrementa o contador milisSteer em um milisSteeregundo
if(counterSteer == 2000) { // implementa em 20ms
PORTD ^= SERVO_STEER; // inverte nível lógico do pino ha cada 20ms/50Hz
} else if(counterSteer >= (2047 + (servo_steer_pulse / 5))) {
PORTD ^= SERVO_STEER; // inverte nível lógico do pino, segundo o valor do potenciômetro, gerando o dutty cycle
counterSteer -= 2000;
}
}

ISR(TIMER2_COMPA_vect) {
static uint16_t counterGear;
counterGear++; // Incrementa o contador milisSteer em um milisSteeregundo
if(counterGear == 2000) { // implementa em 20ms
PORTD ^= SERVO_GEAR; // inverte nível lógico do pino ha cada 20ms/50Hz
} else if(counterGear >= (2047 + (servo_gear_pulse / 5))) {
PORTD ^= SERVO_GEAR; // inverte nível lógico do pino, segundo o valor do potenciômetro, gerando o dutty cycle
counterGear -= 2000;
}
}

Tá lá, com dois servos. Nesse eu não consegui fugir de colocar algo no while(1), li que quando utiliza-se mais de um canal ADC, colocar no modo contínuo gera atraso na leitura, então fiz a captura do valor do ADC, no modo 'single', que tive que fazer no loop infinito, já que também gera atrasos chamar funções nas ISR. Mas está bem rápido e num nível bom de precisão.

Quero só dar um agradecimento formal ao moderador senso, que fez o blog https://hekilledmywire.wordpress.com/category/avr/avr-tutorials/ que me ajudou muito a esclarecer os pontos básicos da programação para AVR, com o winavr. E já aproveitar para fazer um pedido para que ele continue com os tutoriais.

Njay, tenho essa biblioteca baixada, vou dar mais atenção a ela. =)
« Última modificação: 27 de Janeiro de 2015, 11:30 por Pinout »
A wise person listens and takes in more instruction;
A man of understanding acquires skillful direction

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.598
    • Tróniquices
Re: PWM, Potenciômetro e Servo; Com timer0, modo CTC e rodando assíncrono
« Responder #19 em: 12 de Dezembro de 2014, 21:47 »
Assim é batota, heheheh. O desafio é implementar usando apenas um timer, e para N servos ;)

"essa bibliioteca" já vem com o winavr, "faz parte" do GCC (o compilador).

Já que tás à vontade com interrupções, podes fazer a leitura de vários canais de ADC "em background", usando a interrupção do ADC. É assim que costumo fazer quando preciso de ler vários canais; a interrupção vai armazenando o resultado das conversões num array indexado por canal, e eu depois a qualquer momento e em qualquer sitio do programa uso o valor que estiver no array (com atenção à sincronização, se for caso disso).

Offline Pinout

  • Mini Robot
  • *
  • Mensagens: 22
Re: PWM, Potenciômetro e Servo; Com timer0, modo CTC e rodando assíncrono
« Responder #20 em: 12 de Dezembro de 2014, 21:51 »
Entendo o jeito que fala, seria implementar um modo contínuo, mas fazendo com o ADC configurado como single, passando para o ADC os canais que quer ler, num loop for, por exemplo, mas resetando o contador para que as leituras se façam infinitamente. Assim daria pra fazer sem utilizar o loop infinito do main. É verdade, vou fazer desse jeito. =)

O exemplo com os dois servos que te passei era o que eu queria fazer desde o começo. Tenho um projeto em que controlo dois servos remotamente e com potenciômetros. Esse arquivo aí veio antes dos de um servo só, daí hoje melhorei bastante o código, depois de postar aqui.. hahah

Vou fazer com o mesmo timer, mas dependendo da aplicação não me servirá, pois preciso do servo fixo na posição que eu lhe enviar, mas todo conhecimento agrega, mais tarde testo um exemplo com isso.

« Última modificação: 12 de Dezembro de 2014, 22:27 por Pinout »
A wise person listens and takes in more instruction;
A man of understanding acquires skillful direction

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.598
    • Tróniquices
Re: PWM, Potenciômetro e Servo; Com timer0, modo CTC e rodando assíncrono
« Responder #21 em: 12 de Dezembro de 2014, 22:18 »
Faço isso do ADC no código do DiffTrike, aqui está a ISR do ADC (o link é directo para a ISR):

https://github.com/vnevoa/DiffTrike/blob/MarkIV_RasPi_NJAY/Electronics/PowerBridge/sw/main.c#L447

O programa é um pouco complexo e tem lá mais umas coisas, mas sempre dá para ter uma ideia geral. No inicio do ISR lê-se logo o ADC (o ISR ocorre para indicar o fim de uma conversão) e vê-se qual o canal que foi lido (vendo o canal selecionado no mux), depois aponta-se o mux para o próximo canal a ler (no inicio de cada case do switch), guarda-se o valor lido do ADC na posição certa do array (isto é feito aqui no meio antes de dar inicio a nova conversão para dar um tempo ao mux para estabilizar no novo canal) e dá-se inicio a nova conversão (que terminará em novo ISR). O ADC não está em modo continuo, a inicialização do ADC tá logo a seguir à ISR. O AVR aqui é um ATtiny26 (2KB de flash!) mas o ADC é quase igual ao que tás a usar (presumo que tás com um ATmega).

Offline Pinout

  • Mini Robot
  • *
  • Mensagens: 22
Re: PWM, Potenciômetro e Servo; Com timer0, modo CTC e rodando assíncrono
« Responder #22 em: 12 de Dezembro de 2014, 22:22 »
Vou dar uma olhada. Do jeito que eu falei, não tem como fazer, não daria pra fugir do loop infinito do main, sem travar o programa.
Tentei também implementar a leitura do canal ADC dentro das ISR's dos respectivos timers, mas fica lento demais, chega a travar.
Vou ver essa página que me passou. E sim, estou usando o Atmega328p. Um arduino uno e uma outra placa que também tem um atmega328p.
« Última modificação: 12 de Dezembro de 2014, 22:35 por Pinout »
A wise person listens and takes in more instruction;
A man of understanding acquires skillful direction

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.598
    • Tróniquices
Re: PWM, Potenciômetro e Servo; Com timer0, modo CTC e rodando assíncrono
« Responder #23 em: 12 de Dezembro de 2014, 22:31 »
Uma conversão normal do ADC demora mais de 10us, por isso não dá para fazer isso e esperar dentro de um a ISR.
Não há nada de fundamentalmente errado em fazer coisas no loop principal, e é o que eu faço muitas vezes. Mas quando queremos optimizar é mais flexível tirar o maior proveito do hardware, deixá-lo fazer a maior fatia possível do trabalho, e isto implica usar ISRs para as coisas que demoram algum tempo. Há uma frase que não sei quem foi o autor, que disse algo como "quando usamos interrupções, é o trabalho que vem à procura do CPU e não o contrário" :)

Offline Pinout

  • Mini Robot
  • *
  • Mensagens: 22
Re: PWM, Potenciômetro e Servo; Com timer0, modo CTC e rodando assíncrono
« Responder #24 em: 13 de Dezembro de 2014, 00:12 »
Njay, implementei um código, colocando tudo em interrupções, mas ocorre que quando eu inicializo o timer0 e o timer2 (cada um com sua respectiva interrupção) e mais a interrupção do ADC, algo acontece e não funciona. Caso eu não inicialize um dos timers funciona, mas se inicializo os dois (faço isso através de chamada das funções  timer0_init(); e timer2_init(); na função main). Testei comentando o timer0_init() aí funciona, depois comentando só o timer2_init, também funciona, mas os dois timers, consequentemente com os dois pots e os dois servos, não funciona. Me faz pensar se existe algum limite de interrupções simultâneas, ou se a interrupção do ADC faz uso de algum dos timers de 8bits (mascaradamente).

Sabe o que pode ser?
A wise person listens and takes in more instruction;
A man of understanding acquires skillful direction

Offline Pinout

  • Mini Robot
  • *
  • Mensagens: 22
Re: PWM, Potenciômetro e Servo; Com timer0, modo CTC e rodando assíncrono
« Responder #25 em: 13 de Dezembro de 2014, 00:15 »
Njay, segue o código:


Código: [Seleccione]
/*
 *
 * Created: 11/12/2014 11:15:59
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>

#define SERVO_STEER (1 << PD6)
#define SERVO_GEAR (1 << PD5)

volatile uint16_t servoSteerPulse;
volatile uint16_t servoGearPulse;

volatile uint8_t selectedPot = 0;

void ADC_init() {
    ADCSRA = (7 << ADPS0); // prescaler de 128
    ADMUX = (1 << REFS0); // tensão de referência a própria alimentação do mcu
    ADCSRA |= (1 << ADIE); // habilita interrupção por conversão concluída
    ADCSRA |= (1 << ADEN); // habilita ADC
    ADCSRA |= (1 << ADSC); // inicializa conversões - faz uma primeira conversão
}

void CONFIG_servos() {
    DDRD = (SERVO_STEER) | (SERVO_GEAR); // Seta o pino como saída
    PORTD &= ~SERVO_STEER;
    PORTD &= ~SERVO_GEAR;
}

void timer0_init(void) {
    TCCR0A = (1<<WGM01); // Timer no modo CTC
    TCCR0B = (1<<CS01); // Prescaler em 1:8
    OCR0A = 19; // interrupção à cada 0.01ms - (((Fosc / prescaler) / ((1/tempo alvo) * 1000)) -1), ou seja ((( 16Mhz / 8) / ((1/0.01) * 1000)) -1) = 19
    TIMSK0 = (1<<OCIE0A); // Habilita interrupções no timer
}

void timer2_init(void) {
    TCCR2A = (1<<WGM21); // Timer no modo CTC
    TCCR2B = (1<<CS21); // Prescaler em 1:8
    OCR2A = 19; // interrupção à cada 0.01ms - (((Fosc / prescaler) / ((1/tempo alvo) * 1000)) -1), ou seja ((( 16Mhz / 8) / ((1/0.01) * 1000)) -1) = 19
    TIMSK2 = (1<<OCIE2A); // Habilita interrupções no timer
}

int main(void) {
    CONFIG_servos();
    ADC_init();
    timer0_init();
    //timer2_init();
    sei(); // Habilita interrupções globais
    while(1);
    return 1;
}

ISR(ADC_vect) {
    if(!selectedPot) {
        servoSteerPulse = ADC; // atribui valor do ADC à variável
        selectedPot = 1; // altera valor do pot da vez       
    } else {
        servoGearPulse = ADC;
        selectedPot = 0;
    }
        ADMUX &= 0XF0; // limpa canais do ADC
        ADMUX |= selectedPot; // configura novo canal para conversão
        ADCSRA |= (1 << ADSC); // realiza a conversão
}

ISR(TIMER0_COMPA_vect) {
    static uint16_t counterSteer;
    counterSteer++; // Incrementa o contador
    if(counterSteer == 2000) { // implementa em 20ms
        PORTD ^= SERVO_STEER; // inverte nível lógico do pino ha cada 20ms/50Hz
    } else if(counterSteer >= (2047 + (servoSteerPulse / 5))) {
        PORTD ^= SERVO_STEER; // inverte nível lógico do pino, segundo o valor do potenciômetro, gerando o dutty cycle
        counterSteer -= 2000;
    }
}

ISR(TIMER2_COMPA_vect) {
    static uint16_t counterGear;
    counterGear++; // Incrementa o contador
    if(counterGear == 2000) { // implementa em 20ms
        PORTD ^= SERVO_GEAR; // inverte nível lógico do pino ha cada 20ms/50Hz
    } else if(counterGear >= (2047 + (servoGearPulse / 5))) {
        PORTD ^= SERVO_GEAR; // inverte nível lógico do pino, segundo o valor do potenciômetro, gerando o dutty cycle
        counterGear -= 2000;
    }
}

eu estou comentando a função timer2_init();, para funcionar, mas se descomentar ela e comentar a timer0_init(); também funciona, porém, com as duas juntas não funciona. Não sei, poderia ser falta de energia também, mas o código antigo funcionava com os dois simultaneamente e usando a mesma fonte de energia.

« Última modificação: 27 de Janeiro de 2015, 11:31 por Pinout »
A wise person listens and takes in more instruction;
A man of understanding acquires skillful direction

Offline Njay

  • Mini Robot
  • *
  • Mensagens: 3.598
    • Tróniquices
Re: PWM, Potenciômetro e Servo; Com timer0, modo CTC e rodando assíncrono
« Responder #26 em: 13 de Dezembro de 2014, 00:47 »
À partida não vejo nenhum problema óbvio. Não verifiquei a programação dos timers, é melhor re-verificares o nome dos registos e dos bits, eu tinha a ideia de que eles não era assim tão "iguais".

Também podes experimentar fazer

static uint16_t counterGear = 1000;

A ideia aqui é "des-sincronizar" as interrupções dos timers para não ocorrerem ao "mesmo tempo", o que vai fazer uma delas atrasar-se.

Também se pode estar a dar o caso de que não há CPU power suficiente para executar tudo; estás a ter 2 interrupções de timer a cada 10us, isto pode já ser muita carga no CPU e ele não consegue fazer tudo a tempo. A 16MHz são 160 instruções, 80 instruções por interrupção, mais o resto das coisas que ele tem para fazer (ADC ISR). Tenta aumentar o periodo das interrupções, por exemplo para ~50us para experimentar.

Offline Pinout

  • Mini Robot
  • *
  • Mensagens: 22
Re: PWM, Potenciômetro e Servo; Com timer0, modo CTC e rodando assíncrono
« Responder #27 em: 13 de Dezembro de 2014, 11:35 »
São bem parecidos mesmo, praticamente os mesmos, com exceção de que muda-se de 0 para 2 nos nomes. Não é erro neles pois eles funcionam separadamente,
e quando coloco o ADC sem interrupção, funciona com os dois perfeitamente. Vou continuar a pesquisar o que pode ser.
A wise person listens and takes in more instruction;
A man of understanding acquires skillful direction