8 canais de PWM com PIC16F877

Software e Hardware para uC PIC

Moderadores: andre_luis, 51, guest2003, Renie

Mensagempor fabim » 24 Set 2008 17:17

para não haver uma setagem com um delay entre um pino e outro.
não seria conveniente fazer o seguinte. abaixo.

Desta forma se setar todos os pwm´s com o mesmo range, as bordas de descida e subida serão as mesmissimas.

Sei lá, não sei se vem ao caso, é só uma ideóta.

""""""To começando a entender esse maledito C."""""""""""""

Fabim

//#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
#byte saida

#bit pwm_1 = saida.7
#bit pwm_2 = saida.6
#bit pwm_3 = saida.5
#bit pwm_4 = saida.4
#bit pwm_5 = saida.3
#bit pwm_6 = saida.2
#bit pwm_7 = saida.1
#bit pwm_8 = saida.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;}

portb = saida <<<<<<<<<<<<<<<<<<<<<<<<<<<<<


// 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);
}
}
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 » 24 Set 2008 18:23

fabim, acho válido sua ideóta, melhora a questão de tempo, vou adicionar a idéia.

rebelk, a frequencia é de 50Hz, e o duty cycle varia entre 1 e 2ms. É para controlar servos motores mesmo. Chato é uma resolução relativamente boa de 1 a 2ms.

Alessandro
Alessandro
Avatar do usuário
alessandro
Byte
 
Mensagens: 278
Registrado em: 12 Out 2006 19:32

Mensagempor alessandro » 25 Set 2008 12:18

Bom, não sei se estou errado, mas tem algum problema em setarmos o valor de TMR0 dentro do tratamento da interrupção?

No final do tratamento tem um set_timer0(xx) por exemplo, com ele e sem ele da na mesma. Estou louco ou não posso fazer isso?

Alessandro
Alessandro
Avatar do usuário
alessandro
Byte
 
Mensagens: 278
Registrado em: 12 Out 2006 19:32

Mensagempor rebelk » 25 Set 2008 13:14

agora eu entendi , mas acho que desta forma que eu fiz não vai dar certo!
rebelk
Byte
 
Mensagens: 301
Registrado em: 15 Nov 2006 20:16

Mensagempor alessandro » 25 Set 2008 13:55

rebelk estou com um 877 com cristal de 20Mhz.

A int do TMR0 esta ocorrendo a cada 51.2us. Dentro do tratamento a variável serra inclementa até 400.

Como o período é definido por essa variavel, ficaria assim: 400*51.2us = 20.48ms (que é justamente meu período). Assim esta funcionando.

MAS,... achei que 19.53/ms seria uma resolução muito grande, ja que vou variar meu Duty de 1ms a 2ms.

Quanto fui diminuir o tempo da INT0 me deparei com esse problema, em vez de estourar em 255 partindo do 0, tentei setar 128 para que ocorresse na metade do tempo, mas o bom amigo do TMR0 não muda......

Alessandro
Editado pela última vez por alessandro em 25 Set 2008 13:56, em um total de 1 vez.
Alessandro
Avatar do usuário
alessandro
Byte
 
Mensagens: 278
Registrado em: 12 Out 2006 19:32

Mensagempor fabim » 25 Set 2008 13:56

cara eu estava olhando um projetinho que eu fiz a 5 anos e lembrei de uma tecnica que eu usava.
ASM....
1 byte.
eu não contava como valor eu contava como bits, e ia girando para direita.

1 / 8 = 12,5% de incremento

pode usar 16 bits tambem, 24, 32 que seja.

vou passar a ideóta que eu usava e funcionava rapido pra purrete conseguia frequencias bem rapidas...

00000001 12,5%
00000011 25%
00000111 37,5%
00001111 50%
00011111 62,5%
00111111 75%
01111111 87,5%
11111111 100%

Eu fazia a varredura na int, de 8 bytes rigando para direita e testanto, setando um byte, no final eu carregava a porta com o byte que continha o resultado dos testes.

Rotacionado 8 vezes + 1, no maim eu carregava as variaveis novamente

imagine o seguinte.
B1,B2,B3,B4,B5,B6,B7,B8
Cada um para um pino de saida do pwm..

quando tu cair na int do timer zero fica assim.

B1 = B1 >> 1;
if B1.0=1{ Valor_port.0 = 1} else { valor_port.0 = 0}

B2 = B2 >> 1;
if B2.0=1{ Valor_port.1 = 1} else { valor_port.1 = 0}
.............
.............
B8 = B8 >> 1;
if B8.0=1{ Valor_port.7 = 1} else { valor_port.7 = 0}


e para todos os outros.
no final de tudo
PORTB = Valor_port

observe que se fizer isto em ASM, vai demorar poucos uS para fazer isto tudo dentro da int, ja em C vai ser um pouco mais demorado.
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 ze » 25 Set 2008 15:41

baseado na tec do fabin que tal resolução de 256 (ou +):
8 variáveis de tempo + 8 de controle de ton

Código: Selecionar todos
#define setbit(var,bitno) ((var) |= 1 << (bitno))
#define clrbit(var,bitno) ((var) &= ~(1 << (bitno)))

char ton[8], cton[8]

dentro da interrupt 50Hz:
// coloque um break point aqui pra ver quanto tempo o trem abaixo vai levar
for (i=0;i<8;i++)
   {
   if (ton[i]<cton[i]) clrbit(PORTB,i);
   else setbit(PORTB,i)
   ton[i]++; //em algum mom vai ter que zerar. num sei quando...pregui...   
   }
// coloque um break point aqui pra ver quanto tempo o trem acima vai levar


o tempo em 1 pode ser controlado pelas variáveis cton[7..0]. desculpe, mas só defequei isso sem testar. qquer dia testo no proteus e faço as devidas correções.
este é só o espírito (sw). incorpore-o no seu corpo (hw) se quiser. melhore...talvez fique mais bão com variávies int. talvez tenha erro. talvez...talvez....
com um bom compilador (hitech-c) pode levar o mesmo tempo do asm.(gera as mesmas instruções que seriam feitas a mão! quiçá melhor!).
abs
Avatar do usuário
ze
Dword
 
Mensagens: 1655
Registrado em: 05 Jun 2007 14:32

Mensagempor fabio_ » 28 Out 2008 16:30

vc já resolveu o problema?

Acabei de fazer um PIC controlar 4 saídas PWM por software com frequencia de 200Hz e ciclo ativo com quantos steps desejar de 0 a 100% usando apenas um timer. Acredito que dá para colocar mais PWMs sem problemas pois todas as saídas ficaram bem estáveis.
Fiz a rotina em asm, se interessar é só falar que mando pra vc.

falow.
fabio_
Byte
 
Mensagens: 177
Registrado em: 13 Jun 2007 12:08

Mensagempor Rota » 28 Out 2008 18:41

Fabio_
se voce puder enviar essa subrotina agradeço.
obrigado
marcelo
porao8@yahoo.com.br
Rota
Byte
 
Mensagens: 116
Registrado em: 16 Nov 2006 21:29

Mensagempor rosiley » 03 Nov 2008 15:11

Pra que não fique todo mundo pedindo, não seria mais fácil disponibilizar o código aqui, e os interessados baixarei e fazer as suas modificações, senão vai ficar um tal manda pra mim.Isto é a minha opinião.
rosiley
Nibble
 
Mensagens: 51
Registrado em: 01 Jul 2008 19:38
Localização: rio das ostras

Mensagempor _blackmore_ » 04 Nov 2008 01:32

concordo com o que o rosiley disse. . eu tb já ia pedir!

abrax!
Ouça mais classic rock
_blackmore_
Dword
 
Mensagens: 1397
Registrado em: 28 Set 2008 13:26

Mensagempor alessandro » 13 Nov 2008 07:59

Preciso dar mais uma mexida na minha rotina, os 8 canais estão funcionando, mas estão com pouca resolução, estou usando a varredura de bit a bit em váriáveis de 16 bits.

Mas cá entre nós, talvez ainda não tenha entendido direito ou estou fazendo algo incompleto, mas uma variável de 16 bits com opção 65.536 valores, estou usando a mesma variável para deslocar bits em 16 posições somente, não é certa forma um disperdício de memória? é isso mesmo? Acabei fazendo isso, descolo esses 16 bits enquanto faço a varredura nos pinos da porta respectiva. Ou estou fazendo 'CACA"?

Alessandro
Alessandro
Avatar do usuário
alessandro
Byte
 
Mensagens: 278
Registrado em: 12 Out 2006 19:32

Mensagempor fabio_ » 14 Nov 2008 09:45

Desculpe a demora mas eu estava muito ocupado esses dias.

Bom, segue rotina que fiz para geração de vários PWMs por software utilizando apenas 1 timer.

A rotina abaixo é para apenas 2 saídas com 16 steps do ciclo ativo, porém já utilizei com 4 saídas e funcionou perfeitamente
Acredito que dá para utilizar com mais saídas pois a rotina ficou pequena e não dará para perceber alguma diferença em função do tempo de execução.

Outra vantagem é que vc pode determinar qualquer valor do ciclo ativo em qualquer step, basta alterar a tabela.

Pontos a serem considerados:
1 - Quantidade de Steps do ciclo ativo. Eles podem ser determinados por qualquer fonte, conversor A/D, entrada serial, entrada digital, etc
2 - A frequencia de interrupção do timer. É ela que determina a frequencia do PWM.

Espero ter ajudado.



; ---------------------------------------------------------------------------------
; Variáveis

STEP_PWM1 ; step do PWM1
STEP_PWM2 ; step do PWM2

PWM1 ; ciclo ativo da saída PWM1
PWM2 ; ciclo ativo da saída PWM2
AUX_PWM ; auxiliar para geração

; ---------------------------------------------------------------------------------
; Definições

#DEFINE OUT_PWM1 PORTD,4 ; saída PWM1
#DEFINE OUT_PWM2 PORTD,5 ; saída PWM2
#DEFINE OUT_PWM3 PORTD,6 ; saída PWM3

; ---------------------------------------------------------------------------------
; Interrupção do TIMER 2

INT_TIMER2 ; Base de tempo de 50 useg.

BCF PIR1,TMR2IE

AT_OUT_PWM1 ; atualiza ciclo ativo da saída PWM1

MOVLW D'100'
SUBWF PWM1,W
BTFSS ZERO ; PWM1 = 100? ( ciclo ativo em 100% )
GOTO $+3 ; não
BSF OUT_PWM1 ; sim
GOTO AT_OUT_PWM2
MOVF PWM1,W
BTFSS ZERO ; PWM1 = 0 ?
GOTO $+2 ; não
GOTO $+4 ; sim
BSF OUT_PWM1 ; saída PWM1 = 5V
DECFSZ PWM1,F ; decrementa PWM1. PWM1 = 0 ?
GOTO $+2 ; não
BCF OUT_PWM1 ; sim, então saída PWM1 = 0V

AT_OUT_PWM2 ; idem PWM1

MOVLW D'100'
SUBWF PWM2,W
BTFSS ZERO ; PWM2 = 100? ( ciclo ativo em 100% )
GOTO $+3 ; não
BSF OUT_PWM2 ; sim
GOTO T_PWM
MOVF PWM2,W
BTFSS ZERO ; PWM2 = 0 ?
GOTO $+2 ; não
GOTO $+4 ; sim
BSF OUT_PWM2 ; saída PWM2 = 5V
DECFSZ PWM2,F ; decrementa PWM2. PWM2 = 0 ?
GOTO $+2 ; não
BCF OUT_PWM2 ; sim, então saída PWM2 = 0V

; ---------------------------------------------------------------------------------
; Gera Período das Saídas PWM

; F = 200hz => T = 5mseg. então, 100 decrementos x 50 useg. = 5 mseg

T_PWM

DECFSZ AUX_PWM,F ; decrementa AUX_PWM. AUX_PWM = 0 ?
GOTO SAI_INT ; não
MOVLW D'100' ; sim, recarrega para novo período
MOVWF AUX_PWM

; ---------------------------------------------------------------------------------
; Atualiza Saídas PWM a Cada Início de Período

MOVF STEP_PWM1,W ; atualiza a saída PWM1
CALL TABELA_PWM
MOVWF PWM1

MOVF STEP_PWM2,W ; atualiza a saída PWM2
CALL TABELA_PWM
MOVWF PWM2

GOTO SAI_INT

; ---------------------------------------------------------------------------------
; Tabela Ciclo Ativo X %

TABELA_PWM

RETLW D'00' ; step 0 => ciclo ativo = 0%
RETLW D'05' ; step 1 => ciclo ativo = 5%
RETLW D'10' ; step 2 => ciclo ativo = 10%
RETLW D'15' ; step 3 => ciclo ativo = 15%
RETLW D'20' ; step 4 => ciclo ativo = 20%
RETLW D'25' ; step 5 => cilclo ativo = 25%
RETLW D'30' ; step 6 => ciclo ativo = 30%
RETLW D'35' ; step 7 => ciclo ativo = 35%
RETLW D'40' ; step 8 => ciclo ativo = 40%
RETLW D'45' ; step 9 => ciclo ativo = 45%
RETLW D'50' ; step 10 => ciclo ativo = 50%
RETLW D'60' ; step 11 => ciclo ativo = 60%
RETLW D'70' ; step 12 => ciclo ativo = 70%
RETLW D'80' ; step 13 => ciclo ativo = 80%
RETLW D'90' ; step 14 => ciclo ativo = 90%
RETLW D'100' ; step 15 => ciclo ativo = 100%
fabio_
Byte
 
Mensagens: 177
Registrado em: 13 Jun 2007 12:08

Anterior

Voltar para PIC

Quem está online

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

x