Página 1 de 2

8 canais de PWM com PIC16F877

MensagemEnviado: 23 Set 2008 14:39
por alessandro
Olá pessoal,

Estou precisando realizar um controle em 8 servos motores. Como não conheço nenhum modelo com tanta saída PWM, pensei em fazer isso por software.

Anteriormente havia implementado a mesma idéia para 3 canais para realizar um controle de LED´s RGB e funcionou satisfatóriamente.

Acontece que a mesma idéia para 8 canais esta dando problemas quanto à estabilidade dos canais. Esse micocontrolador fará somente esse controle, não fará mais nada, o codigo que ficará rodando será somente para atualiar as saidas.

A ideia anterior estava fazendo da seguinte forma:

Setei o TIMER2 para gerar uma interrupção à ~2,2ms, sempre que o tramamento da interrupção era feito, setava as saidas de PWM para nivel alto. Depois disso realiza a contagem do TIMER1 e verificava se ele ultrapassou o valor de cada canal, quando acontecia isso, seta o devido canal (saida do respectivo pino) em nivel baixo.

Resumo, o TIMER2 era o cilco de 2ms, e a leitura de TIMER1 o DutyCycle. Mas parece que para muitos canais ando perdendo algo no caminho e acabo atrasando um pouco a atualização de cada canal....

Alguém tem uma idéia melhor ou sugestão?


Alessandro

MensagemEnviado: 23 Set 2008 16:42
por RobL
Conheço um AN da Atmel que pode lhe servir:
http://www.atmel.com/dyn/resources/prod ... oc8020.pdf

MensagemEnviado: 23 Set 2008 16:54
por RobL
Esquecí, o código aqui:
http://www.atmel.com/dyn/resources/prod ... AVR136.zip

Acrecento que a Atmel tem chips dedicados para 6 canais como o ATmega48 outros com 7 canais e 10 canais como o AT90PWM3.
Não sei se a MC tem algum com estas possibilidades.

MensagemEnviado: 23 Set 2008 17:24
por tcpipchip
nao é mais facil por 8 pics de 8 pinos, via i2c, e usar PWM por Hardware deles ?

MensagemEnviado: 23 Set 2008 17:30
por fabim
http://bp1.blogger.com/_F1DrDNRkivc/R0z ... 320/16.bmp
literalmente

qual a frequencia minima, e qual o valor de steps ??
Tipo de 0 a 10...

MensagemEnviado: 23 Set 2008 17:40
por alessandro
Vou dar uma lida nos artigos relacionados nos links.

Cada canal tem um periodo de 22ms, os steps ou a resolução de incremento ou decremento pode ser na casa de 10%, ou seja, 100%=2ms (full duty cycle).

8 PIC´s, é uma alternativa boa agrupar chips, se não conseguir encontrar uma forma de deixa-los estáveis vou repensar essa alternativa. Havia pensado em agrupar chip´s assim se conseguisse deixar estável os canais com um PIC, colocaria o controle de LCD, botões, etc em outro, só para não atrasar os scan e sincronismos nos canais.



Alessandro

MensagemEnviado: 23 Set 2008 17:43
por fabim
de uma olhada no site do mikroe, exemplo do mikrobasic.
o cara fez esse sistema para 8 servomotores.

acho que vai dar certinho para o que você quer, o exemplo é para 18f, mais fica facil portar para 16f "se for o caso".

Fabim

MensagemEnviado: 23 Set 2008 17:52
por alessandro
fabim escreveu:de uma olhada no site do mikroe, exemplo do mikrobasic.
o cara fez esse sistema para 8 servomotores.

acho que vai dar certinho para o que você quer, o exemplo é para 18f, mais fica facil portar para 16f "se for o caso".

Fabim


Rapaz, é isso ai mesmo.

Só que não mexo com assembler faz no mínimo uns 5 anos.... agora complicou, queria ver somente que alternativa ele usou para chegar nisso, estou olhando um aquivo aqui disponibilizado pelo site, mas esta dificil.

Alessandro

MensagemEnviado: 23 Set 2008 18:20
por rebelk
vê se isso da para vc ?

// progama pwm po soft
// para alterar a frequencia do pwm é só alterar o cristal

//#include <16f84a.h>
#include <16f876a.h>
//#device adc=10 // Configura o compilador para conversor A/D de 10 bits
#use delay(clock=10000000)
#fuses XT,NOWDT,PUT,NOPROTECT

#define pwm_1 pin_b7
#define pwm_2 pin_b6
#define pwm_3 pin_b5
#define pwm_4 pin_b4
#define pwm_5 pin_b3
#define pwm_6 pin_b2
#define pwm_7 pin_b1
#define pwm_8 pin_b0



int8 set_pwm_1=10;
int8 set_pwm_2=20;
int8 set_pwm_3=30;
int8 set_pwm_4=40;
int8 set_pwm_5=50;
int8 set_pwm_6=60;
int8 set_pwm_7=70;
int8 set_pwm_8=70;

int8 serra=0;

//**********************************************************************
//**********************************************************************


#int_timer0
void trata_t0 ()
{

if(serra < set_pwm_1 ){output_high(pwm_1);}
else{ output_low(pwm_1);}

//////////////////////////////////

if(serra < set_pwm_2 ){output_high(pwm_2); }
else { output_low(pwm_2);}

////////////////////////////////////

if(serra < set_pwm_3 ){output_high(pwm_3);}
else{ output_low(pwm_3);}

//////////////////////////////////

if(serra < set_pwm_4 ){output_high(pwm_4); }
else { output_low(pwm_4);}

////////////////////////////////////

if(serra < set_pwm_5 ){output_high(pwm_5);}
else{ output_low(pwm_5);}

//////////////////////////////////

if(serra < set_pwm_6 ){output_high(pwm_6); }
else { output_low(pwm_6);}

////////////////////////////////////

if(serra < set_pwm_7 ){output_high(pwm_7);}
else{ output_low(pwm_7);}

//////////////////////////////////

if(serra < set_pwm_8 ){output_high(pwm_8); }
else { output_low(pwm_8);}


serra++;
if(serra >= 100)
{
serra=0;
}

set_timer0(190); // recarrega o timer 0 + - 100 HERTS
}

//**********************************************************************
//**********************************************************************

main()
{

set_timer0(190); // carrega o timer 0 com 155
setup_timer_0 ( RTCC_INTERNAL );// configura o timer 0 para clock interno
enable_interrupts ( global | int_timer0 ); // habilita interrupções

// setup_ADC_ports (RA0_RA1_RA3_ANALOG);
// setup_adc(ADC_CLOCK_INTERNAL );


while (true)
{
set_pwm_1++;
if( set_pwm_1 > 100 ){ set_pwm_1 = 0; }

set_pwm_2++;
if( set_pwm_2 > 100 ){ set_pwm_2 = 0; }

set_pwm_3++;
if( set_pwm_3 > 100 ){ set_pwm_3 = 0; }

set_pwm_4++;
if( set_pwm_4 > 100 ){ set_pwm_4 = 0; }

set_pwm_5++;
if( set_pwm_5 > 100 ){ set_pwm_5 = 0; }

set_pwm_6++;
if( set_pwm_6 > 100 ){ set_pwm_6 = 0; }

set_pwm_7++;
if( set_pwm_7 > 100 ){ set_pwm_7 = 0; }

set_pwm_8++;
if( set_pwm_8 > 100 ){ set_pwm_8 = 0; }
delay_ms(50);
}
}

MensagemEnviado: 23 Set 2008 18:24
por alessandro
rebelk escreveu:vê se isso da para vc ?

// progama pwm po soft
// para alterar a frequencia do pwm é só alterar o cristal

//#include <16f84a.h>
#include <16f876a.h>
//#device adc=10 // Configura o compilador para conversor A/D de 10 bits
#use delay(clock=10000000)
#fuses XT,NOWDT,PUT,NOPROTECT

#define pwm_1 pin_b7
#define pwm_2 pin_b6
#define pwm_3 pin_b5
#define pwm_4 pin_b4
#define pwm_5 pin_b3
#define pwm_6 pin_b2
#define pwm_7 pin_b1
#define pwm_8 pin_b0



int8 set_pwm_1=10;
int8 set_pwm_2=20;
int8 set_pwm_3=30;
int8 set_pwm_4=40;
int8 set_pwm_5=50;
int8 set_pwm_6=60;
int8 set_pwm_7=70;
int8 set_pwm_8=70;

int8 serra=0;

//**********************************************************************
//**********************************************************************


#int_timer0
void trata_t0 ()
{

if(serra < set_pwm_1 ){output_high(pwm_1);}
else{ output_low(pwm_1);}

//////////////////////////////////

if(serra < set_pwm_2 ){output_high(pwm_2); }
else { output_low(pwm_2);}

////////////////////////////////////

if(serra < set_pwm_3 ){output_high(pwm_3);}
else{ output_low(pwm_3);}

//////////////////////////////////

if(serra < set_pwm_4 ){output_high(pwm_4); }
else { output_low(pwm_4);}

////////////////////////////////////

if(serra < set_pwm_5 ){output_high(pwm_5);}
else{ output_low(pwm_5);}

//////////////////////////////////

if(serra < set_pwm_6 ){output_high(pwm_6); }
else { output_low(pwm_6);}

////////////////////////////////////

if(serra < set_pwm_7 ){output_high(pwm_7);}
else{ output_low(pwm_7);}

//////////////////////////////////

if(serra < set_pwm_8 ){output_high(pwm_8); }
else { output_low(pwm_8);}


serra++;
if(serra >= 100)
{
serra=0;
}

set_timer0(190); // recarrega o timer 0 + - 100 HERTS
}

//**********************************************************************
//**********************************************************************

main()
{

set_timer0(190); // carrega o timer 0 com 155
setup_timer_0 ( RTCC_INTERNAL );// configura o timer 0 para clock interno
enable_interrupts ( global | int_timer0 ); // habilita interrupções

// setup_ADC_ports (RA0_RA1_RA3_ANALOG);
// setup_adc(ADC_CLOCK_INTERNAL );


while (true)
{
set_pwm_1++;
if( set_pwm_1 > 100 ){ set_pwm_1 = 0; }

set_pwm_2++;
if( set_pwm_2 > 100 ){ set_pwm_2 = 0; }

set_pwm_3++;
if( set_pwm_3 > 100 ){ set_pwm_3 = 0; }

set_pwm_4++;
if( set_pwm_4 > 100 ){ set_pwm_4 = 0; }

set_pwm_5++;
if( set_pwm_5 > 100 ){ set_pwm_5 = 0; }

set_pwm_6++;
if( set_pwm_6 > 100 ){ set_pwm_6 = 0; }

set_pwm_7++;
if( set_pwm_7 > 100 ){ set_pwm_7 = 0; }

set_pwm_8++;
if( set_pwm_8 > 100 ){ set_pwm_8 = 0; }
delay_ms(50);
}
}




A ideia é bem parecida que a usada por mim, só que acabei usando dois TIMER´s.
Vou testar essa alternativa e retorno com o resultado.


Alessandro

MensagemEnviado: 23 Set 2008 20:28
por rebelk
ok , vê ai!

MensagemEnviado: 24 Set 2008 09:56
por alessandro
Fiz a conversão para o 877 e funcionou corretamente. A idéia é mais simples que a usei anteriormente mas muito funcional. Então modifiquei algumas coisas como: passei para o Timer1 a int para ter mais resolução e alguns valores. Mas não sei o que esta acontecendo, fiz uns testes no Proteus e o bixo não quer gerar interrupção de jeito algum. Estou tentando ver o que errei, veja o codigo abaixo:

#include <16F877.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,NODEBUG
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6,rcv=PIN_C7)

#define pwm_1 pin_b7
#define pwm_2 pin_b6
#define pwm_3 pin_b5
#define pwm_4 pin_b4
#define pwm_5 pin_b3
#define pwm_6 pin_b2
#define pwm_7 pin_b1
#define pwm_8 pin_b0



int16 set_pwm_1=1000;
int16 set_pwm_2=1000;
int16 set_pwm_3=1000;
int16 set_pwm_4=1000;
int16 set_pwm_5=1000;
int16 set_pwm_6=1000;
int16 set_pwm_7=1000;
int16 set_pwm_8=1000;

int16 serra=0;

//**********************************************************************
//**********************************************************************


#INT_TIMER1
void trata_t1()
{

if(serra < set_pwm_1 ){output_high(pwm_1);}
else{ output_low(pwm_1);}

//////////////////////////////////

if(serra < set_pwm_2 ){output_high(pwm_2); }
else { output_low(pwm_2);}

////////////////////////////////////

if(serra < set_pwm_3 ){output_high(pwm_3);}
else{ output_low(pwm_3);}

//////////////////////////////////

if(serra < set_pwm_4 ){output_high(pwm_4); }
else { output_low(pwm_4);}

////////////////////////////////////

if(serra < set_pwm_5 ){output_high(pwm_5);}
else{ output_low(pwm_5);}

//////////////////////////////////

if(serra < set_pwm_6 ){output_high(pwm_6); }
else { output_low(pwm_6);}

////////////////////////////////////

if(serra < set_pwm_7 ){output_high(pwm_7);}
else{ output_low(pwm_7);}

//////////////////////////////////

if(serra < set_pwm_8 ){output_high(pwm_8); }
else { output_low(pwm_8);}


serra++;
if(serra >= 2000)//2k*1us = 2ms - Duty Cycle
{
serra=0;
}

set_timer1(45536); //
}

//**********************************************************************
//**********************************************************************

main()
{
setup_timer_1(T1_INTERNAL);
set_timer1(45536); //20.000 contagens p/ estouro = 20k*1us=20ms
enable_interrupts (GLOBAL); //
enable_interrupts(INT_TIMER1);

while (true)
{
set_pwm_1++;
if( set_pwm_1 > 2000 ){ set_pwm_1 = 0; }

set_pwm_2++;
if( set_pwm_2 > 2000 ){ set_pwm_2 = 0; }

set_pwm_3++;
if( set_pwm_3 > 2000 ){ set_pwm_3 = 0; }

set_pwm_4++;
if( set_pwm_4 > 2000 ){ set_pwm_4 = 0; }

set_pwm_5++;
if( set_pwm_5 > 2000 ){ set_pwm_5 = 0; }

set_pwm_6++;
if( set_pwm_6 > 2000 ){ set_pwm_6 = 0; }

set_pwm_7++;
if( set_pwm_7 > 2000 ){ set_pwm_7 = 0; }

set_pwm_8++;
if( set_pwm_8 > 2000 ){ set_pwm_8 = 0; }
delay_ms(50);
}
}

Alessandro

MensagemEnviado: 24 Set 2008 15:28
por alessandro
No Proteus ele não gerava int alguma, mas na dúvida montei e testei. Na pratica a int é gerada corretamente, mas não sei ainda porque, mas o tempo de ciclo esta totalmente fora...

Alessandro

MensagemEnviado: 24 Set 2008 17:04
por rebelk
Alessandro , qual a frequencia maxima do seu pwm , qual a resoluçãp minima que sua aplicaçao pode suportar , pois este tipo de pwm possui uma frequencia relativamente baixa até uns 200hz ( com cristal de 24mz ) , 1/200hz = 0.005s ( 5ms ) , vc que um periodo de 2ms ( 500 hz ) , 500 hz nao da certo , pois vc não consegui fazer nada com tanta interrupção .

a resolução é 100 ( valor 100 ) b1100100 , vc que 2000 passos , não da!

se vc reduzir a quantidade de passo para 50 , a frequencia salta para 400 hz ( no exemplo abaixo ) , ve ai!

observe que no modo de 100 passos de resoluçao e 50 passos.


// progama pwm po soft
// para alterar a frequencia do pwm é só alterar o cristal

//#include <16f84a.h>
//#include <16f876a.h>
#include <16f877.h>
//#device adc=10 // Configura o compilador para conversor A/D de 10 bits
#use delay(clock=24000000)
#fuses XT,NOWDT,PUT,NOPROTECT

#use fast_io(b)
#byte portb = 0x06

#bit pwm_1 = portb.7
#bit pwm_2 = portb.6
#bit pwm_3 = portb.5
#bit pwm_4 = portb.4
#bit pwm_5 = portb.3
#bit pwm_6 = portb.2
#bit pwm_7 = portb.1
#bit pwm_8 = portb.0

int8 set_pwm_1=10;
int8 set_pwm_2=20;
int8 set_pwm_3=30;
int8 set_pwm_4=40;
int8 set_pwm_5=50;
int8 set_pwm_6=60;
int8 set_pwm_7=70;
int8 set_pwm_8=70;

int8 serra=0;

//**********************************************************************
//**********************************************************************


#int_timer0
void trata_t0 ()
{

if(serra < set_pwm_1 ){pwm_1=1;}
else{ pwm_1=0;}

//////////////////////////////////

if(serra < set_pwm_2 ){pwm_2=1; }
else { pwm_2=0;}

////////////////////////////////////

if(serra < set_pwm_3 ){pwm_3=1;}
else{pwm_3=0;}

//////////////////////////////////

if(serra < set_pwm_4 ){pwm_4=1; }
else { pwm_4=0;}

////////////////////////////////////

if(serra < set_pwm_5 ){pwm_5=1;}
else{ pwm_5=0;}

//////////////////////////////////

if(serra < set_pwm_6 ){pwm_6=1; }
else { pwm_6=0;}

////////////////////////////////////

if(serra < set_pwm_7 ){pwm_7=1;}
else{ pwm_7=0;}

//////////////////////////////////

if(serra < set_pwm_8 ){pwm_8=1; }
else { pwm_8=0;}


// serra++; // 100 niveis / passos = 200 hz
//////////////////////////////////////

serra++; //
serra++; // 50 passos / passos = 400 hz


if(serra >= 100)
{
serra=0;
}

set_timer0(256-103); // recarrega o timer 0 + - 200 HERTS
}

//**********************************************************************
//**********************************************************************

main()
{


set_tris_b(0b00000000);
portb=0x00; // limpa port

set_timer0(190); // carrega o timer 0 com 155
setup_timer_0 ( RTCC_INTERNAL );// configura o timer 0 para clock interno
enable_interrupts ( global | int_timer0 ); // habilita interrupções

// setup_ADC_ports (RA0_RA1_RA3_ANALOG);
// setup_adc(ADC_CLOCK_INTERNAL );


while (true)
{
set_pwm_1++;
if( set_pwm_1 > 100 ){ set_pwm_1 = 0; }

set_pwm_2++;
if( set_pwm_2 > 100 ){ set_pwm_2 = 0; }

set_pwm_3++;
if( set_pwm_3 > 100 ){ set_pwm_3 = 0; }

set_pwm_4++;
if( set_pwm_4 > 100 ){ set_pwm_4 = 0; }

set_pwm_5++;
if( set_pwm_5 > 100 ){ set_pwm_5 = 0; }

set_pwm_6++;
if( set_pwm_6 > 100 ){ set_pwm_6 = 0; }

set_pwm_7++;
if( set_pwm_7 > 100 ){ set_pwm_7 = 0; }

set_pwm_8++;
if( set_pwm_8 > 100 ){ set_pwm_8 = 0; }
delay_ms(50);
}
}

50 niveis ,500hz cristal de 24mh

MensagemEnviado: 24 Set 2008 17:10
por rebelk
deleta a interrupçao anterior e copia essa!
//**********************************************************************
//**********************************************************************
#int_timer0
void trata_t0 ()
{

if(serra < set_pwm_1 ){pwm_1=1;}
else{ pwm_1=0;}
//////////////////////////////////
if(serra < set_pwm_2 ){pwm_2=1; }
else { pwm_2=0;}
////////////////////////////////////
if(serra < set_pwm_3 ){pwm_3=1;}
else{pwm_3=0;}
//////////////////////////////////
if(serra < set_pwm_4 ){pwm_4=1; }
else { pwm_4=0;}
////////////////////////////////////
if(serra < set_pwm_5 ){pwm_5=1;}
else{ pwm_5=0;}
//////////////////////////////////
if(serra < set_pwm_6 ){pwm_6=1; }
else { pwm_6=0;}
////////////////////////////////////
if(serra < set_pwm_7 ){pwm_7=1;}
else{ pwm_7=0;}
//////////////////////////////////
if(serra < set_pwm_8 ){pwm_8=1; }
else { pwm_8=0;}
// serra++; // 100 niveis / passos = 200 hz
//////////////////////////////////////

serra++; //
serra++; // 50 passos / passos = 400 hz


if(serra >= 100)
{
serra=0;
}

set_timer0(256-72); // recarrega o timer 0 + - 500 HERTS
}