domingo, 25 de julho de 2021

MSP430: gerando PWM - Parte III

Continuando o que foi iniciado no post anterior, vamos ver os demais trechos de código para fazer funcionar os PWMs utilizando o MSP430.

4. ROTINA DE FALHA
A rotina de falha para qual o microcontrolador é desviado no caso de falha de configuração dos clocks de sistema utilizando os valores pré-calibrados de DCO nada mais é do que um loop infinito para evitar que o mesma vá para algum estado inesperado.

void FaultRoutine(void) {
    while(1);                              
}

5. TIMER
Como explicado na primeira parte, a minha idéia é utilizar o bloco 0 para estabelecer a freqüência de dois sinais PWM (TACCR0), e usar os outros dois módulos para gerenciar o duty-cycle de maneira independente desses dois sinais (TACCR1 e TACCR2).
Não vou entrar em detalhes de como a coisa funciona pois isso está na primeira parte bem detalhado. 
Antes de mais nada, vamos escolher a frequencia do PWM que queremos gerar. Para facilitar, vamos gerar um sinal de 1kHz. Com isso em mente, vamos calcular qual deverá ser o valor do registrador TA1CCR0 para que ele seja resetado a cada 1mS (e com isso gerar um sinal de 1kHz). 

A primeira coisa a ser configurada deve ser a fonte de clock para esse periférico. Inicialmente, levando em conta que configuramos o clock do dispositivo conforme post anterior, deveremos ter o SMCLK com 1MHz oriundos do DCO interno.  Para facilitar, vamos configurar o timer para que o SMCLK seja a sua fonte de clock e sem dividir (ou dividir esse clock por 1). Além disso, vamos também configurar ele para que trabalho no modo "up", conforme explicado na primeira parte desse tutorial. Dessa forma, o TA1CCR0 irá contar do valor 0 (zero) até o valor configurado, quando será resetado e iniciará nova contagem.

    TA1CTL = TASSEL_2 + MC_1 + ID_0;
    // TACTL = 0x0200 + 0x0010 = 0x0210
    // TASSEL_2 -> SMCLK como fonte de clock para o timer
    // MC_1        -> up mode: timer conta até o valor de CCR0
    // ID_0        -> entrada de clock dividida por 1

Sabendo que o clock que alimentará o sistema terá uma frequencia de 1MHz (período de 1uS), vamos calcular qual deverá ser o valor do registrado TA1CCR0 para que o mesmo gere uma frequencia de 1kHz, ou seja, que ele estoure a cada 1mS.

TA1CCR0 = 1mS/1uS = 1000

Levando em conta que a contagem se inicia em 0 e não em 1, então o valor inicial do TA1CCR0 deverá ser 1000-1=999. Além disso, vamos configurar os outros dois blocos de comparação/captura no modo reset/set, conforme já explicado na primeira parte desse tutorial.
    TA1CCR0 = PWM_periodo-1;// PWM Period
    TA1CCTL1 = OUTMOD_7;     // CCR1 reset/set
    TA1CCTL2 = OUTMOD_7;     // CCR2 reset/set

    TA1CCR1 = 500;           // CCR1 PWM duty cycle
    TA1CCR2 = 900;          // CCR2 PWM duty cycle

Fazendo TA1CCR1 = 500, teremos um duty cycle perto dos 50% (lembre-se de que o total é de 1000, valor inicial do TA1CCR0), então cada ciclo deverá ter perto de 500uS ou 0,5mS. Para o TA1CCR2, o duty cycle deverá ser de 90%.

6. CÓDIGO COMPLETO
Abaixo disponibilizo o código completo da aplicação. Ao contrário do que disse na parte anterior, eu acabei deixando  omicrocontrolado em Low Power Mode (LPM0).

#include "msp430G2553.h"
void FaultRoutine(void);        //
void ConfigWDT(void);            // Configuração do Watchdog
void ConfigClocks(void);        // Configuração dos Clocks dos sistemas
void ConfigPins(void);
void ConfigTimerA1(void);
void ConfigPWM(unsigned int value1, unsigned int value2);

#define PWM_periodo        1000

void main(void){

    ConfigWDT();        // Configura WatchDog Timer
    ConfigClocks();        // Configura Clocks do Dispositivo
    ConfigPins();        // Configura Pinos do Dispositivo
    ConfigTimerA1();    // Configura Timer A1
//    ConfigPWM();
    _BIS_SR(LPM0_bits); // entra no modo LPM0
}

void FaultRoutine(void) {
    while(1);                              // TRAP
 }

void ConfigWDT(void) {
 WDTCTL = WDTPW + WDTHOLD;                 // Desliga Watchdog
 }

void ConfigClocks(void){

    if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF)     // verifica se os valores de calibração do Clock é válido
        FaultRoutine();                                 // se não for válido então entra nessa rotina

    BCSCTL1 = CALBC1_1MHZ;                                // Configuração da faixa DCO
    DCOCTL  = CALDCO_1MHZ;                             // Configuração dos DCO step e Modulo

    BCSCTL3 |= LFXT1S_2;                                 // LFXT1 = VLO
    IFG1 &= ~OFIFG;                                      // Reseta OSCFault flag
    BCSCTL2 |= SELM_0 + DIVM_0 + DIVS_0;                 // SELM_0 = seleciona Master Clock como sendo DCO; MCLK = DCO, SMCLK = DCO
 }

void ConfigPins(void){
    // PxDIR -> direção do pino (1=saída; 0=entrada)

    P2DIR |= (BIT2+BIT4); // P2.2 e P2.4 como output
    P2SEL2 = 0;
    P2SEL |= (BIT2+BIT4); // P2.2 para TA1.1 e P2.4 para TA1.2
//    P2REN = 1;
}

void ConfigTimerA1(void){
    TA1CCR0 = PWM_periodo-1;// PWM Period
    TA1CCTL1 = OUTMOD_7;     // CCR1 reset/set
    TA1CCTL2 = OUTMOD_7;     // CCR2 reset/set

    TA1CCR1 = 500;           // CCR1 PWM duty cycle
    TA1CCR2 = 900;          // CCR2 PWM duty cycle

}

void ConfigPWM(unsigned int value1, unsigned int value2){
    TA1CCR1 = value1;           // CCR1 PWM duty cycle
    TA1CCR2 = value2;              // CCR2 PWM duty cycle
}

7. RESULTADOS
A seguir temos imagens das capturas realizados nos pinos em que os PWMs foram configurados (clique para ampliar). No primeira figura, vemos que o valor do período ficou bem próximo aos 1mS (1000uS) e o duty cycle para o primeiro PWM também ficou bem próximo aos 50% (0,5mS ou 50uS) desejados e o do segundo perto dos 90% (0,9mS ou 900us)

Captura dos PWMs gerados: período para o duty cycle de 90%.


Captura dos PWMs gerados: período para o duty cycle de 50%.




Nenhum comentário:

Postar um comentário