8 canais de PWM com PIC16F877

Software e Hardware para uC PIC

Moderadores: andre_luis, 51, guest2003, Renie

8 canais de PWM com PIC16F877

Mensagempor alessandro » 23 Set 2008 14:39

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
Alessandro
Avatar do usuário
alessandro
Byte
 
Mensagens: 278
Registrado em: 12 Out 2006 19:32

Mensagempor RobL » 23 Set 2008 16:42

Conheço um AN da Atmel que pode lhe servir:
http://www.atmel.com/dyn/resources/prod ... oc8020.pdf
RobL
Dword
 
Mensagens: 1546
Registrado em: 20 Fev 2007 17:56

Mensagempor RobL » 23 Set 2008 16:54

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.
RobL
Dword
 
Mensagens: 1546
Registrado em: 20 Fev 2007 17:56

Mensagempor tcpipchip » 23 Set 2008 17:24

nao é mais facil por 8 pics de 8 pinos, via i2c, e usar PWM por Hardware deles ?
Avatar do usuário
tcpipchip
Dword
 
Mensagens: 6560
Registrado em: 11 Out 2006 22:32
Localização: TCPIPCHIPizinho!

Mensagempor fabim » 23 Set 2008 17:30

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...
Mano, ve só.
Sou responsável pelo que escrevo!!! E não pelo que você entende !!!
fabim
Dword
 
Mensagens: 5001
Registrado em: 16 Out 2006 10:18
Localização: aqui uái!!!?

Mensagempor alessandro » 23 Set 2008 17:40

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
Editado pela última vez por alessandro em 23 Set 2008 17:44, em um total de 1 vez.
Alessandro
Avatar do usuário
alessandro
Byte
 
Mensagens: 278
Registrado em: 12 Out 2006 19:32

Mensagempor fabim » 23 Set 2008 17:43

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
Mano, ve só.
Sou responsável pelo que escrevo!!! E não pelo que você entende !!!
fabim
Dword
 
Mensagens: 5001
Registrado em: 16 Out 2006 10:18
Localização: aqui uái!!!?

Mensagempor alessandro » 23 Set 2008 17:52

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
Alessandro
Avatar do usuário
alessandro
Byte
 
Mensagens: 278
Registrado em: 12 Out 2006 19:32

Mensagempor rebelk » 23 Set 2008 18:20

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);
}
}
rebelk
Byte
 
Mensagens: 301
Registrado em: 15 Nov 2006 20:16

Mensagempor alessandro » 23 Set 2008 18:24

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
Alessandro
Avatar do usuário
alessandro
Byte
 
Mensagens: 278
Registrado em: 12 Out 2006 19:32

Mensagempor rebelk » 23 Set 2008 20:28

ok , vê ai!
rebelk
Byte
 
Mensagens: 301
Registrado em: 15 Nov 2006 20:16

Mensagempor alessandro » 24 Set 2008 09:56

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
Alessandro
Avatar do usuário
alessandro
Byte
 
Mensagens: 278
Registrado em: 12 Out 2006 19:32

Mensagempor alessandro » 24 Set 2008 15:28

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
Alessandro
Avatar do usuário
alessandro
Byte
 
Mensagens: 278
Registrado em: 12 Out 2006 19:32

Mensagempor rebelk » 24 Set 2008 17:04

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);
}
}
rebelk
Byte
 
Mensagens: 301
Registrado em: 15 Nov 2006 20:16

50 niveis ,500hz cristal de 24mh

Mensagempor rebelk » 24 Set 2008 17:10

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
}
rebelk
Byte
 
Mensagens: 301
Registrado em: 15 Nov 2006 20:16

Próximo

Voltar para PIC

Quem está online

Usuários navegando neste fórum: Nenhum usuário registrado e 1 visitante

x