Página 1 de 2
8 canais de PWM com PIC16F877

Enviado:
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

Enviado:
23 Set 2008 16:42
por RobL

Enviado:
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.

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

Enviado:
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...

Enviado:
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

Enviado:
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

Enviado:
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

Enviado:
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);
}
}

Enviado:
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

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

Enviado:
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

Enviado:
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

Enviado:
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

Enviado:
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
}