PIC: PWM via Software
Vários microcontroladores possuem PWM internos, que são fáceis de configurar e, uma vez inicializado continuará funcionando por conta própria. As vezes você precisa de mais canais PWM ou o microcontrolador não possui canal PWM. Neste post mostrarei como implementar via software vários canais PWM utilizando apenas o TIMER1 do microcontrolador.
Através da interrupção do TIMER1, o valor de uma variável é incrementada e comparada com o valor da largura de pulso que você deseja para um determinado sinal. Se ambos valores forem iguais, a saída do sinal será 1( ou 0 ). Quando a variável estourar ( ex.: > 255 ) a saída do sinal será 0 ( ou 1 ).
No código utilizei apenas três canais, para isso criei três "registros" que configuram cada canal.
typedef struct { union { char EN:1; //Habilita/Desabilita char INV:1; //Inverter char:6; char DC:8; //Duty Cycle }; } PWM; PWM PWM1 absolute 0x22; PWM PWM2 absolute 0x24; PWM PWM3 absolute 0x26;
o bit EN, habilita ou desabilita o canal.
o bit INV, inverte o sinal. Se "0" o valor do duty cycle será para o nível baixo. Se "1" o valor do duty cycle será para o nível alto.
o registro DC contém o valor do duty cycle do sinal.
CÓDIGO DA BIBLIOTECA
obs.: clock utilizado é de 16MHz
unsigned Timer1 at TMR1L; typedef struct { union { char EN:1; //Enable char INV:1; //Inverse char:6; char DC:8; //Duty Cycle }; } PWM; PWM PWM1 absolute 0x22; PWM PWM2 absolute 0x24; PWM PWM3 absolute 0x26; void Soft_PWM_Init() { PWM1_Channel_Dir = 0; PWM2_Channel_Dir = 0; PWM3_Channel_Dir = 0; PWM1_Channel = 0; PWM2_Channel = 0; PWM3_Channel = 0; PWM1.EN = 0; PWM2.EN = 0; PWM3.EN = 0; PWM1.INV = 0; PWM2.INV = 0; PWM3.INV = 0; PWM1.DC = 127; PWM2.DC = 127; PWM3.DC = 127; T1CON = 1; Timer1 = 0xFF38; TMR1IE_Bit = 1; GIE_Bit = 1; PEIE_Bit = 1; } void Soft_PWM_Start( char channel ) { ((PWM*)&PWM1)[channel].EN = 1; } void Soft_PWM_Stop( char channel ) { ((PWM*)&PWM1)[channel].EN = 0; } void Soft_PWM_Set_Duty( char channel, char DutyCycle ) { ((PWM*)&PWM1)[channel].DC = DutyCycle; } void Interrupt() { static unsigned count = 0; if( TMR1IF_Bit ) { TMR1IF_Bit = 0; Timer1 = 0xFF38; count++; if( PWM1.EN ) { if( count == PWM1.DC ) { PWM1_Channel = !PWM1.INV; } else if( count > 255 ) { PWM1_Channel = PWM1.INV; } } if( PWM2.EN ) { if( count == PWM2.DC ) { PWM2_Channel = !PWM2.INV; } else if( count > 255 ) { PWM2_Channel = PWM2.INV; } } if( PWM3.EN ) { if( count == PWM3.DC ) { PWM3_Channel = !PWM3.INV; } else if( count > 255 ) { PWM3_Channel = PWM3.INV; } } count &= 0xFF; } }
EXEMPLO:
MikroC PRO PIC
//Habilitar as seguintes bibliotecas: // - ADC //MCU: PIC16F877A //CLOCK: 16MHz sbit PWM1_Channel at RB0_Bit; sbit PWM2_Channel at RB1_Bit; sbit PWM3_Channel at RB2_Bit; sbit PWM1_Channel_Dir at TRISB0_Bit; sbit PWM2_Channel_Dir at TRISB1_Bit; sbit PWM3_Channel_Dir at TRISB2_Bit; //Copie e cole o código da biblioteca aqui!!! void main() { char i; CMCON = 7; ADC_Init(); Soft_PWM_Init(); Soft_PWM_Set_Duty( 0, 90 ); Soft_PWM_Set_Duty( 1, 100 ); Soft_PWM_Set_Duty( 2, 200 ); Soft_PWM_Start( 0 ); Soft_PWM_Start( 1 ); Soft_PWM_Start( 2 ); while(1) { for( i = 0; i < 3; i++ ) { Soft_PWM_Set_Duty( i, ADC_Get_Sample(i) >> 2 ); } } }
Amigo eu preciso criar 6 canais PWM idênticos, mas com defasamento. Vc teria algum exemplo no CCS pra isso?
ResponderExcluircomo que eu vou saber qual é o valor do de frequência do pwm.
ResponderExcluirPor ex gostaria de um pwm 10 kHz
PWM_INIT(10000);
como fica no seu código.
Não tem como. Como esta gerando sinal via hardware, a frequência sera baixa.
ExcluirNem uma base de em que faixa de frequência seria gerado este sinal PWM?
ExcluirO código abaixo possibilita a defasagem de 120 graus entre dois sinais PWM. utilizando filtros passa baixa, é possível tornar os sinais pwm, em senoides de 60Hz.
ResponderExcluirint seno [36] = {127,150,172,192,210,226,239,248,254,255,254,248,239,226,210,192,172,150,127,106,84,64,46,30,17,8,2,0,2,8,17,30,46,64,84,106};
int seno_b [36] = {254,248,239,226,210,192,172,150,127,106,84,64,46,30,17,8,2,0,2,8,17,30,46,64,84,106,127,150,172,192,210,226,239,248,254,255};
//int seno_c [36] = {64,46,30,17,8,2,0,2,8,17,30,46,64,84,106,127,150,172,192,210,226,239,248,254,255,254,248,239,226,210,192,172,150,127,106,84};
void main() {
trise =0;
PWM1_Init(10000); PWM2_init(10000);
PWM1_start(); PWM2_start();
while(1){
PWM1_Set_Duty(seno[0]);PWM2_Set_Duty(seno_b[0]); delay_us(463);
PWM1_Set_Duty(seno[1]);PWM2_Set_Duty(seno_b[1]); delay_us(463);
PWM1_Set_Duty(seno[2]);PWM2_Set_Duty(seno_b[2]); delay_us(463);
PWM1_Set_Duty(seno[3]);PWM2_Set_Duty(seno_b[3]); delay_us(463);
PWM1_Set_Duty(seno[4]);PWM2_Set_Duty(seno_b[4]); delay_us(463);
PWM1_Set_Duty(seno[5]);PWM2_Set_Duty(seno_b[5]); delay_us(463);
PWM1_Set_Duty(seno[6]);PWM2_Set_Duty(seno_b[6]); delay_us(463);
PWM1_Set_Duty(seno[7]);PWM2_Set_Duty(seno_b[7]); delay_us(463);
PWM1_Set_Duty(seno[8]);PWM2_Set_Duty(seno_b[8]); delay_us(463);
PWM1_Set_Duty(seno[9]);PWM2_Set_Duty(seno_b[9]); delay_us(463);
PWM1_Set_Duty(seno[10]);PWM2_Set_Duty(seno_b[10]); delay_us(463);
PWM1_Set_Duty(seno[11]);PWM2_Set_Duty(seno_b[11]); delay_us(450);
PWM1_Set_Duty(seno[12]);PWM2_Set_Duty(seno_b[12]); delay_us(463);
PWM1_Set_Duty(seno[13]);PWM2_Set_Duty(seno_b[13]); delay_us(463);
PWM1_Set_Duty(seno[14]);PWM2_Set_Duty(seno_b[14]); delay_us(463);
PWM1_Set_Duty(seno[15]);PWM2_Set_Duty(seno_b[15]); delay_us(463);
PWM1_Set_Duty(seno[16]);PWM2_Set_Duty(seno_b[16]); delay_us(463);
PWM1_Set_Duty(seno[17]);PWM2_Set_Duty(seno_b[17]); delay_us(463);
PWM1_Set_Duty(seno[18]);PWM2_Set_Duty(seno_b[18]); delay_us(463);
PWM1_Set_Duty(seno[19]);PWM2_Set_Duty(seno_b[19]); delay_us(463);
PWM1_Set_Duty(seno[20]);PWM2_Set_Duty(seno_b[20]); delay_us(463);
PWM1_Set_Duty(seno[21]);PWM2_Set_Duty(seno_b[21]); delay_us(463);
PWM1_Set_Duty(seno[22]);PWM2_Set_Duty(seno_b[22]); delay_us(463);
PWM1_Set_Duty(seno[23]);PWM2_Set_Duty(seno_b[23]); delay_us(463);
PWM1_Set_Duty(seno[24]);PWM2_Set_Duty(seno_b[24]); delay_us(463);
PWM1_Set_Duty(seno[25]);PWM2_Set_Duty(seno_b[25]); delay_us(463);
PWM1_Set_Duty(seno[26]);PWM2_Set_Duty(seno_b[26]); delay_us(463);
PWM1_Set_Duty(seno[27]);PWM2_Set_Duty(seno_b[27]); delay_us(463);
PWM1_Set_Duty(seno[28]);PWM2_Set_Duty(seno_b[28]); delay_us(463);
PWM1_Set_Duty(seno[29]);PWM2_Set_Duty(seno_b[29]); delay_us(463);
PWM1_Set_Duty(seno[30]);PWM2_Set_Duty(seno_b[30]); delay_us(463);
PWM1_Set_Duty(seno[31]);PWM2_Set_Duty(seno_b[31]); delay_us(463);
PWM1_Set_Duty(seno[32]);PWM2_Set_Duty(seno_b[32]); delay_us(463);
PWM1_Set_Duty(seno[33]);PWM2_Set_Duty(seno_b[33]); delay_us(463);
PWM1_Set_Duty(seno[34]);PWM2_Set_Duty(seno_b[34]); delay_us(463);
PWM1_Set_Duty(seno[35]);PWM2_Set_Duty(seno_b[35]); delay_us(463);
}
}
Porque só foi mostrados 2 ondas defasadas em 120 graus???
ExcluirSe em um sistema trifásico é necessário 3 ondas de 120 graus???
Tem como indicar um link, em que esse programa, passa por um filtro e gere na saída ondas em 60 Hz? Acredito que variando os delays é possível obter tambem ondas de 50 Hz, com pouca perda ou distorção, correto?
Agradeço a atenção.
Qual é a frequência máxima que eu consigo com seu código e como que eu mudo para esse valor
ResponderExcluirHá parabéns pelo blog.
Tiago Henrique, voce poderia postar algum exemplo de aplicaçao + codigo, de algum controle PWM com o Arduino UNO Rev 3?
ResponderExcluirBoa Noite!
ResponderExcluirObrigador por dividir os seus conhecimentos conosco.
Poderia dar um exemplo de um filtro passa baixo para criar o senoides de 60Hz.
Qual compilador para o código exemplo.
Atenciosamente
Carlos Roberto
Peço desculpas passei pela informação do compilador fica valendo só o exemplo do filtro passa baixo.
ResponderExcluirAtenciosamente
Carlos Roberto
Eu conseguiria variar de 10Hz a 1.5khz com este programa seu? Obrigado
ResponderExcluirTiago , como faço para um pwm começar em nivel low, e outro right.
ResponderExcluirpwm1.inv =0; pwm2.inv = 1;
Excluirsoft_pwm_init();
......
Este comentário foi removido pelo autor.
ResponderExcluirBom dia, teria como postar em assembler com a defasagem
ResponderExcluir