EA871 – LAB. DE PROGRAMAÇÃO BÁSICA DE SISTEMAS DIGITAIS EXPERIMENTO 12 – TIMERS (II): PERIFÉRICOS Prof. Antonio Quevedo e Profa. Wu Shin-Ting INTRODUÇÃO Além dos timers integrados ao núcleo, o nosso MCU tem ainda diversos periféricos de temporização, como os módulos TPM (Timer/PWM) (Cap. 31 de [1]), PIT (Periodic Interrupt Timer) (Cap. 32 de [1]) e LPTMR (Low-Power Timer) (Cap. 33 de [1]). O primeiro é um temporizador de baixo consumo de potência e o segundo, é constituído de um contador LPTPM de 16 bits, também de baixo consumo, programável para operar numa das 3 funções: Input Capture, Output Compare e PWM (pulse-width modulation) (Cap. 12 de [3]). Na primeira função, Input Capture, as transições nos sinais do seu pino de entrada podem ser capturadas e os instantes destas ocorrências armazenados. Na função Output Compare ele gera um nível de sinal no seu pino de saída quando o valor do contador fique igual a um valor pré-definido. E, na função PWM, ele gera no seu pino de saída um sinal modulado por largura de pulso (PWM, pulse width modulation) [6]. O módulo PIT consiste de contadores de 32 e 64 bits com a função específica de disparar interrupções condicionadas a intervalos de tempo programados. E LPTMS são contadores de 16 bits de baixo consumo de energia. Figura 1: Pontos de conexão dos componentes externos Neste experimento, vamos estudar os dois módulos de temporização TPM e PIT através de quatro programas. No primeiro programa vamos controlar as piscadas do led azul do kit FRDM-KL25 com uso do PIT. No segundo programa vamos gerar sinais na frequência audível com o módulo TPM operando no modo Output Compare e alimentar um transdutor piezoelétrico através dos pinos “F” (1 de H7 no esquemático [3]), “G” (2 de H7 no esquemático [3]) e “H” (3 de H7 no esquemático [3]) da placa auxiliar (Figura 1), e no terceiro programa vamos estimar a frequência de um sinal de entrada, via pino “H” (3 de H7 no esquemático [3]), com o módulo TPM operando no modo Input Capture. Para verificar a precisão do nosso procedimento, o módulo PIT é utilizado para gerar um sinal de frequência programável no pino “E” (1 de H6 no esquemático [3]) da placa auxiliar. Finalmente, veremos uma aplicação do modo PWM no controle de velocidade de um cooler através do header H6 do esquemático [3]. Neste caso, a fonte de alimentação é conectada aos pinos “A” (5 de H6 no esquemático [3]) e “B” (4 de H6 no esquemático [3]), e o cooler aos pinos “C” (3 de H6 no esquemático [3]) e “D” (2 de H6 no esquemático [3]). EXPERIMENTO 1. PIT: O módulo PIT (Periodic Interrupt Timer) contém dois contadores de 32 bits que podem operar separadamente ou ser encadeados para formar um contador de 64 bits. Quando operado separadamente, cada contador n conta de forma decrescente a partir do valor setado no registrador PIT_LDVALn até 0, quando o valor é recarregado automaticamente (Seção 32.4.1.1 de [1]). Como todos os módulos que vimos até agora, é necessário ativar a fonte de relógio do módulo (clock gate), o módulo e o(s) contador(es) do módulo a ser(em) utilizado(s). Segundo a Tabela 5-2 em [1], o clock destes contadores é o bus clock, cuja frequência, segundo a Seção 5.4 em [1], é a frequência do relógio do sistema dividido pelo valor setado em SIM_CLKDIV1[OUTDIV4]. Vimos ainda nos experimentos anteriores que a frequência do relógio do sistema é 20.971520MHz. Quando o mecanismo de interrupção de um contador n é habilitado, a sua bandeira de interrupção PIT_TFLGn[TIF] é levantada e, se o módulo é apropriadamente habilitado no controlador NVIC (Seção B3.4 em [2]), a interrupção é atendida pelo núcleo com a chamada da sua rotina de serviço. Neste caso, é fundamental identificar o vetor de interrupção do módulo PIT na Tabela 3-7 de [1]. E, para evitar que se gere novos eventos após o atendimento, deve-se abaixar a bandeira escrevendo 1 no bit (w1c). O código no Anexo 1 ilustra a configuração do contador 0 do módulo PIT para gerar interrupções periódicas. Para verificar visualmente estas interrupções foi usado o led azul do kit FRDM-KL25, alternando os seus estados. Execute e complete o código no lugar onde há ???? e com os comentários para cada uma das suas instruções de configuração do MCU indicando explicitamente os bits setados. 2. TPM: O módulo TPM (Timer/PWM) contém 2 a 8 canais compartilhando um mesmo contador de 16 bits que pode contar de forma crescente (up) ou crecente-decrescente (up-down) (Seção 31.4.3 de [1]). Diferente do PIT, o TPM dispõe de um relógio independente para este contador, que é selecionável pelos campos SIM_SOPT2[TPMSRC] e SIM_SOPT2[PLLFLLSEL]. De acordo com as Seções 31.4.2 e 31.4.3 de [1], o período de contagem máxima do TPM depende, além da frequência do relógio selecionado, dos valores setados em TPMx_MOD[MOD] e em TPMx_SC[PS]. Quando ocorre estouro na contagem, a bandeira TPMx_SC[TOF] é levantada. Se o seu bit TPMx_ SC[TOIE]=1 e o controlador NVIC é adequadamente configurado (Seção B3.4 em [2]), o fluxo de controle é desviado para a sua rotina de serviço. A principal característica do módulo TPM é que cada um dos seus canais pode ser configurado para operar em uma das três seguintes funções: Output Compare, Input Capture e PWM (Seção 31.3.4em [1]). O evento levantador da bandeira de interrupção TPMx_CnSC[CHF] de cada canal n depende, portanto, da sua função. a. Output Compare: Quando o valor do contador TPMx_CNT[COUNT] chegar ao valor setado no registrador TPMx_CnV[VAL] do canal n, é colocado o valor pré-definido no pino de saída do canal. Este é o evento levantador da bandeira de interrupção desta função (Seção 31.4.5 em [1]). O código no Anexo 2 ilustra a configuração do canal 1 do módulo TPM1 para operar com a função Output Compare, de forma que, quando os valores dos registradores se igualam, o valor no pino de saída do canal (pino 44/F da Figura 1) é alternado. Para visualizar esta alternância foi usado o led verde do kit FRDM-KL25. b. Input Capture: Quando o tipo de borda pré-configurado for detectado no sinal do pino de entrada do canal n, o valor do contador TPMx_CNT[COUNT] é capturado no registrador TPMx_CnV[VAL]. Este é o evento levantador da bandeira de interrupção desta função (Seção 31.4.4 em [1]). O código no Anexo 3 ilustra a configuração do canal 0 do módulo TPM1 para operar com a função Input Capture. O módulo PIT foi configurado para gerar no PTB1 (pino 44/F da Figura 1) um trem de pulsos que alimenta o pino de entrada do canal (pino 43/E da Figura 1). A cada borda de subida é levantada a bandeira de interrupção e, como a interrupção do canal é habilitada e setada no controlador NVIC, é chamada a sua rotina de serviço. Com os valores de contadores capturados, é possível estimar o período do sinal gerado pelo contador 0 do PIT. Para visualizar os instantes de captura foi usado o led azul do kit FRDM-KL25. Para execução correta do código, é necessário conectar os pinos E e F da Figura 1. c. PWM (Pulse Width Modulation): Quando o valor do contador TPMx_CNT[COUNT] chega ao valor setado no registrador TPMx_CnV[VAL] do canal n, o sinal no pino de saída do canal é alternado. Este é o evento levantador da bandeira de interrupção desta função (Seção 31.4.6 em [1]). O nível inicial do sinal é pré-configurado. A relação da largura do nível alto em relação ao período do sinal é conhecido como ciclo de trabalho. O código no Anexo 3 ilustra a configuração do canal 0 do módulo TPM1 para operar com a função PWM, de forma que, quando se variam os valores do registrador TPMx_CnV[VAL], as larguras dos pulsos no pino de saída do canal (pino 43/E da Figura 1) são alteradas. Para visualizar estas variações foi usado o led vermelho do kit FRDM-KL25. Execute e complete os códigos dos Anexos 2, 3 e 4 no lugar onde há ???? e com os comentários para cada uma das suas instruções de configuração do MCU indicando explicitamente os bits setados e a programação dos pinos utilizados. 3. Acionamento de Periféricos: Podemos aplicar os sinais gerados pelos contadores PIT e TPM para acionar dispositivos físicos. a. Um transdutor piezoelétrico transforma sinais elétricos em sinais de áudio [5]. Sabendo que as características elétricas do buzzer disponível no nosso almoxarifado são compatíveis com as do nosso MCU [7] (a tensão 3.3V, correspondente ao nível lógico 1é suficiente para gerar pressão necessária sobre o material piezoelétrico do buzzer), ligue o seu conector no header H7 e execute o programa do Anexo 2. O que aconteceu? Sabendo que a faixa de frequências audíveis é [20,20000]Hz, re-configure o PIT com uma das frequências das notas musicais listadas numa das propostas de problema do item 4. b. O cooler é um ventilador CC que opera na faixa de 6V-13V DC com uma corrente mínima de 0.07A, muito acima dos valores que o nosso MCU conseguiria suprir [6]. Portanto, foi implementado na placa auxiliar uma interface elétrica entre ele e o MCU (esquemático em [3]). O circuito é constituído de um transistor de Darlington TIP31/TIP122, que proporciona um elevado ganho de corrente, e um diodo 1N4004/1N4007 para proteger o transistor das correntes reversas. Ligue a fonte de tensão nos pinos 4 e 5 do header H6 e o cooler nos pinos 2 e 3 de mesmo header. Observe que os pinos 3 e 4 são curto-circuitados. Execute o programa do Anexo 4. O que aconteceu? Explique sucintamente os efeitos observados no cooler. 4. Desafio: Inclua no relatório a solução de UM dos seguintes problemas. Não se esquecem de documentar detalhadamente os códigos e acrescentar alguns principais resultados dos testes: a. Tomando como base o programa do Anexo 1 ou Anexo 2, implemente um programa que reproduz o som das notas musicais digitadas por um usuário através do “Terminal”. As 7 notas musicais são: A (lá, 440Hz), B (si, 493,883Hz), C (dó, 261,625Hz), D (ré, 293,665Hz), E (mi, 329,628Hz), F (fá, 349,228Hz) e G (sol, 391,995,736Hz) [7]. Ecoe as letras digitadas no “Terminal”. b. Tomando como base o programa do Anexo 4, implemente um programa que controla a rotação do cooler através do “Terminal”. O valor digitado deve ser ecoado no próprio “Terminal”. A rotação varia de 0 a 100% da rotação máxima. Quando a rotação estiver entre 0 a 20% o led azul acende. Entre 21 a 80% acende o led verde. Acima disso acende o led vermelho do kit FDRM-KL25. c. Tomando como base os programas do Anexo 3, implemente um programa que contabilize os intervalos de tempo que um usuário pressiona qualquer uma das 3 botoeiras, NMI, PTA5 e PTA12, da placa auxiliar. Logo que soltar a botoeira, o intervalo de tempo contabilizado deve ser exibido de forma centrada na primeira linha do LCD. REFERÊNCIAS Todas as referências podem ser encontradas nos links abaixo ou ainda na página do curso. [1] KL25 Sub-Family Reference Manual – Freescale Semiconductors (doc. Number KL25P80M48SF0RM), Setembro 2012. ftp://ftp.dca.fee.unicamp.br/pub/docs/ea871/ARM/KL25P80M48SF0RM.pdf [2] ARMv6-M Architecture Reference Manual – ARM Limited. ftp://ftp.dca.fee.unicamp.br/pub/docs/ea871/ARM/ARMv6-M.pdf [3] A. Quevedo. Descrição da placa auxiliar, com esquemático ftp://ftp.dca.fee.unicamp.br/pub/docs/ea871/ARM/DescricaoDoHardware/.pdf [4] Entenda o PWM , PNCA Robótica e Eletrônica. http://www.pnca.com.br/index.php? option=com_content&view=article&id=67:pwm&catid=42:saiba-mais&Itemid=150 [5] Specification for Piezo Electric Sounder ftp://ftp.dca.fee.unicamp.br/pub/docs/ea871/datasheet/ piezo-buzzer-datasheet.pdf [6] DC Fan Motor – San Ace ftp://ftp.dca.fee.unicamp.br/pub/docs/ea076/datasheet/ SANCooler92.pdf [7] Tabela de Frequências, Períodos e Comprimentos de Onda http://www2.eca.usp.br/prof/iazzetta/tutor/acustica/introducao/tabela1.html Revisado em Fevereiro de 2014 Revisado em Julho de 2014 com base nas sugestões dos monitores da disciplina, Rose Landeira, João Henrique Stange Hoffmam e Eduardo Carlassara. ANEXO 1: LISTAGEM DO PRIMEIRO PROGRAMA #include "derivative.h" #define GPIO_PIN(x) ((1)<<(x)) ///< obtem o bit do pino x /** * \fn InitGPIO (void) * \brief Inicializa o pino conectado ao ledR do FRDM para verificação visual. */ void InitGPIO(void) { SIM_SCGC5 |= SIM_SCGC5_PORTD_MASK;///< PORTD=SIM_SCGC5[12]=1 (Clock gate de PORTD habilitado) PORTD_PCR1 |= (PORT_PCR_ISF_MASK | ///< ISF=PORTB_PCR1[14]: w1c (limpa a pendência) PORT_PCR_MUX(0x1) ); ///< MUX=PORTB_PCR1[10:8]=0b011 (TPM1_CH1) GPIOD_PDDR |= GPIO_PDDR_PDD(GPIO_PIN(1)); ///< GPIOD_PDDR[1]=1 (pino de saída) GPIOD_PSOR |= GPIO_PSOR_PTSO(GPIO_PIN(1)); ///< GPIOD_PSOR[1]=1 (seta 1 no pino) } /** * \fn InitPIT (void) * \brief Configura o timer 0 do PIT para que gere um sinal periódido de ????s. */ void InitPIT(void) { SIM_SCGC6 |= SIM_SCGC6_PIT_MASK; ///< TPM1=SIM_SCGC6[23]=1 (Clock gate de PIT habilitado) /* * Clock do PIT é bus clock (Tabela 5-2 de KL-25 Sub-Family Reference Manual) * frequência de bus clock =(clock do núcleo)/(SIM_CLKDIV1[OUTDIV1]*SIM_CLKDIV1[OUTDIV4]) * (Figura 5-1 de KL-25 Sub-Family Reference Manual) * = ???? * frequência do núcleo = 20.971520 MHz * Periodo_{PIT} = PIT_LDVAL0[TSV]/(frequência de bus clock) * = ??? */ //OUTDIV1=SIM_CLKDIV1[31:28] é carregado no reset com 0000 (dividido por 1) SIM_CLKDIV1 &= SIM_CLKDIV1_OUTDIV4(0x0); PIT_TCTRL0 &= ~(PIT_TCTRL_TEN_MASK | PIT_TCTRL_TIE_MASK ); PIT_LDVAL0 = PIT_LDVAL_TSV(0xA00000); PIT_TFLG0 |= PIT_TFLG_TIF_MASK; PIT_TCTRL0 |= (PIT_TCTRL_TEN_MASK | PIT_TCTRL_TIE_MASK ); PIT_TCTRL0 &= ~PIT_TCTRL_CHN_MASK; PIT_MCR &= ~(PIT_MCR_FRZ_MASK | PIT_MCR_MDIS_MASK ); } /** * \fn enableNVIC (void) * \brief ???? */ void enableNVIC(void) { NVIC_ISER |= 1 << (38-16); NVIC_ICPR |= 1 << (38-16); NVIC_IPR5 |= NVIC_IP_PRI_22(0x40); } /** * \fn PIT_IRQHandler (void) * \brief ???? */ void PIT_IRQHandler(void) { GPIOD_PTOR |= GPIO_PTOR_PTTO(GPIO_PIN(1)); ///< GPIOD_PTOR[1]=1 (alterna o valor) PIT_TFLG0 |= PIT_TFLG_TIF_MASK; } int main(void) { InitPIT(); InitGPIO(); enableNVIC(); for(;;) { } } ANEXO 2: LISTAGEM DO SEGUNDO PROGRAMA #include "derivative.h" #define GPIO_PIN(x) ((1)<<(x)) ///< obtem o bit do pino x /** * \fn InitGPIO (void) * \brief Inicializa o pino conectado ao ledR do FRDM para verificação visual. */ void InitGPIO(void) { SIM_SCGC5 |= SIM_SCGC5_PORTB_MASK; ///< PORTB=SIM_SCGC5[10]=1 (Clock gate de PORTB habilitado) PORTB_PCR19 |= (PORT_PCR_ISF_MASK | ///< ISF=PORTB_PCR1[14]: w1c (limpa a pendência) PORT_PCR_MUX(0x1) ); ///< MUX=PORTB_PCR1[10:8]=0b011 (TPM1_CH1) GPIOB_PDDR |= GPIO_PDDR_PDD(GPIO_PIN(19)); ///< GPIOB_PDDR[19]=1 (pino de saída) GPIOB_PSOR |= GPIO_PSOR_PTSO(GPIO_PIN(19)); ///< GPIOB_PSOR[19]=1 (seta 1 no pino) } /** * \fn InitTPM (void) * \brief Configura o canal 1 do módulo TPM1 com a função "Output Compare", * de forma que a igualdade seja satisfeita a cada ???? segundos. */ void InitTPM(void) { SIM_SCGC5 |= SIM_SCGC5_PORTB_MASK; PORTB_PCR1 |= (PORT_PCR_ISF_MASK | PORT_PCR_MUX(0x3) ); SIM_SCGC6 |= SIM_SCGC6_TPM1_MASK; SIM_SOPT2 |= SIM_SOPT2_TPMSRC(0b01); SIM_SOPT2 &= ~SIM_SOPT2_PLLFLLSEL_MASK; /* * Veja a relação dos campos dos registradores na Figura 31-1 de KL-25 Sub-Family Reference Manual * (1) Configuração do módulo TPM1 * Periodo_{UP} = (MOD+1) * (PS/(frequência de source clock)) (CPWMS=0) * Periodo_{UP-DOWN} = (MOD) * (PS/(frequência de source clock)) * (1/(1+CPWMS)) (CPWMS=1) * Fórmula geral: * Periodo_{TPMx} ~ (MOD) * (PS/(frequência de source clock)) * (1/(1+CPWMS)) * frequência de MCGFLLCLK = ???? * * Período_{TPM1} = ???? */ TPM1_SC |= (TPM_SC_TOF_MASK | TPM_SC_CMOD(0x1) | TPM_SC_PS(0x7) ); TPM1_SC &= ~(TPM_SC_DMA_MASK | TPM_SC_TOIE_MASK | TPM_SC_CPWMS_MASK); TPM1_MOD &= TPM_MOD_MOD(16384); TPM1_CNT |= TPM_CNT_COUNT(0x0) ; TPM1_CONF &= ~(TPM_CONF_CROT_MASK | TPM_CONF_CSOO_MASK | TPM_CONF_CSOT_MASK | TPM_CONF_GTBEEN_MASK | TPM_CONF_DOZEEN_MASK); TPM1_CONF |= TPM_CONF_DBGMODE(0b11); /* * (2) Configurção do canal 1: ???? * */ TPM1_C1SC |= (TPM_CnSC_CHF_MASK | TPM_CnSC_MSA_MASK | TPM_CnSC_ELSA_MASK); TPM1_C1SC &= ~(TPM_CnSC_CHIE_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSB_MASK | TPM_CnSC_DMA_MASK); TPM1_C1V |= TPM_CnV_VAL(0x0); } int main(void) { short tempo = 0; InitTPM(); InitGPIO(); for(;;) { if (TPM1_STATUS & (TPM_STATUS_CH1F_MASK)) { ///< C1V == MOD? tempo++; if (!(tempo % 5)) { GPIOB_PTOR |= GPIO_PTOR_PTTO(GPIO_PIN(19)); ///< GPIOB_PTOR[19]=1 (alterna o valor) tempo = 0; } TPM1_C1SC |= TPM_CnSC_CHF_MASK; } } } ANEXO 3: LISTAGEM DO TERCEIRO PROGRAMA #include "derivative.h" #define GPIO_PIN(x) ((1)<<(x)) ///< obtem o bit do pino x unsigned short valor; /** * \fn InitGPIO (void) * \brief Inicializa o pino conectado ao ledB (para verificação visual do sinal) * e a PTB1 (a alimentar o pino PTB0 do canal 0 de TPM1). */ void InitGPIO(void) { SIM_SCGC5 |= (SIM_SCGC5_PORTB_MASK | ///< PORTB=SIM_SCGC5[10]=1 (Clock gate de PORTB habilitado) SIM_SCGC5_PORTD_MASK); ///< PORTD=SIM_SCGC5[12]=1 (Clock gate de PORTD habilitado) PORTB_PCR1 |= (PORT_PCR_ISF_MASK | ///< ISF=PORTB_PCR1[14]: w1c (limpa a pendência) PORT_PCR_MUX(0x1)); ///< MUX=PORTA_PCR1[10:8]=0b001 (PTB1) PORTD_PCR1 |= (PORT_PCR_ISF_MASK | ///< ISF=PORTB_PCR1[14]: w1c (limpa a pendência) PORT_PCR_MUX(0x1)); ///< MUX=PORTA_PCR19[10:8]=0b001 (PTD1) GPIOB_PDDR |= GPIO_PDDR_PDD(GPIO_PIN(1)); ///< GPIOB_PDDR[1]=1 (saída) GPIOD_PDDR |= GPIO_PDDR_PDD(GPIO_PIN(1)); ///< GPIOD_PDDR[1]=1 (saída) } /** * \fn InitPIT (void) * \brief Configura o timer 0 do PIT para que gere um sinal periódido de ???? segundos. */ void InitPIT(void) { SIM_SCGC6 |= SIM_SCGC6_PIT_MASK; ///< TPM1=SIM_SCGC6[23]=1 (Clock gate de PIT habilitado) /* * Periodo_{PIT} =PIT_LDVAL0[TSV]/(frequência de bus clock) * = ???? */ //OUTDIV1=SIM_CLKDIV1[31:28] é carregado no reset com 0000 (dividido por 1) SIM_CLKDIV1 &= SIM_CLKDIV1_OUTDIV4(0x0); ///< OUTDIV1=SIM_CLKDIV1[18:16]=0b000 (dividido por 1) PIT_TCTRL0 &= ~(PIT_TCTRL_TEN_MASK | PIT_TCTRL_TIE_MASK ); PIT_LDVAL0 = PIT_LDVAL_TSV(0x80000); PIT_TFLG0 |= PIT_TFLG_TIF_MASK; PIT_TCTRL0 |= (PIT_TCTRL_TEN_MASK | PIT_TCTRL_TIE_MASK ); PIT_TCTRL0 &= ~PIT_TCTRL_CHN_MASK; PIT_MCR &= ~(PIT_MCR_FRZ_MASK | PIT_MCR_MDIS_MASK ); } /** * \fn InitTPM (void) * \brief Configura o canal 0 do módulo TPM1 com a função "Input Capture", * de forma que a borda de ???? do sinal de entrada gere um evento. */ void InitTPM(void) { SIM_SCGC5 |= SIM_SCGC5_PORTB_MASK; PORTB_PCR0 |= (PORT_PCR_ISF_MASK | PORT_PCR_MUX(0x3) ); SIM_SCGC6 |= SIM_SCGC6_TPM1_MASK; SIM_SOPT2 |= SIM_SOPT2_TPMSRC(0b01); SIM_SOPT2 &= ~SIM_SOPT2_PLLFLLSEL_MASK; /* * Periodo_{TPM} = MOD * (PS/(frequência de source clock)) * (1/(1+CPWMS)) * = ???? */ TPM1_SC |= (TPM_SC_TOF_MASK | TPM_SC_CMOD(0x1) | TPM_SC_PS(0x6) ); TPM1_SC &= ~(TPM_SC_DMA_MASK | TPM_SC_TOIE_MASK | TPM_SC_CPWMS_MASK); TPM1_MOD &= TPM_MOD_MOD(32758); TPM1_CNT |= TPM_CNT_COUNT(0x0) ; TPM1_CONF &= ~(TPM_CONF_CROT_MASK | TPM_CONF_CSOO_MASK | TPM_CONF_CSOT_MASK | TPM_CONF_GTBEEN_MASK | TPM_CONF_DOZEEN_MASK); TPM1_CONF |= TPM_CONF_DBGMODE(0b11); /* * (2) Configurção do canal 0: ???? */ TPM1_C0SC |= (TPM_CnSC_CHF_MASK | TPM_CnSC_CHIE_MASK | TPM_CnSC_ELSA_MASK ); TPM1_C0SC &= ~(TPM_CnSC_MSB_MASK | TPM_CnSC_MSA_MASK | TPM_CnSC_ELSB_MASK | TPM_CnSC_DMA_MASK); TPM1_C0V |= TPM_CnV_VAL(0x0); } /** * \fn enableNVIC (void) * \brief ???? */ void enableNVIC(void) { NVIC_ISER |= (1 << (38-16) | 1 << (34-16)); NVIC_ICPR |= (1 << (38-16) | 1 << (34-16));; NVIC_IPR5 |= NVIC_IP_PRI_22(0x40); NVIC_IPR4 |= NVIC_IP_PRI_18(0x80); } /** * \fn PIT_IRQHandler (void) * \brief Rotina de Serviço para ????. Gera trem de pulsos no PTB1 e alterna o ledB para verificação visual */ void PIT_IRQHandler(void) { GPIOB_PTOR |= GPIO_PTOR_PTTO(GPIO_PIN(1)); ///< GPIOB_PTOR[1]=1 (alterna o valor) GPIOD_PTOR |= GPIO_PTOR_PTTO(GPIO_PIN(1)); ///< GPIOD_PTOR[1]=1 (alterna o valor) PIT_TFLG0 |= PIT_TFLG_TIF_MASK; } /** * \fn FTM1_IRQHandler (void) * \brief Rotina de serviço para ???? */ void FTM1_IRQHandler(void) { valor = TPM1_C0V; TPM1_C0SC |= TPM_CnSC_CHF_MASK; } int main(void) { unsigned short inicio, fim, amostras; unsigned int soma, media; double periodo; InitTPM(); InitGPIO(); InitPIT(); enableNVIC(); /* * Estimativa do período com base na média de 1000 amostras de intervalos * entre 2 bordas de subida */ valor = 0; soma = 0; amostras = 0; while (valor == 0); ///< ???? Para quê? inicio = valor; for(;;) { fim = valor; if (fim > inicio) { amostras++; soma += (fim-inicio); inicio = fim; } else if (fim < inicio) { amostras++; soma += ((0xFFFF-inicio) + fim); inicio = fim; } if (amostras == 1000) { media = (unsigned int)(0.001*soma); /* * periodo estimado = ???? segundos (adicione a instrução no código) */ amostras = 0; ///< seta brkp nesta instrução } } } ANEXO 4: LISTAGEM DO QUARTO PROGRAMA #include "derivative.h" #define GPIO_PIN(x) ((1)<<(x)) ///< obtem o bit do pino x #define COUNTER_OVF 16384 /** * \fn InitGPIO (void) * \brief Inicializa o pino conectado ao ledR do FRDM para verificação visual. */ void InitGPIO(void) { SIM_SCGC5 |= SIM_SCGC5_PORTB_MASK; ///< PORTB=SIM_SCGC5[10]=1 (Clock gate de PORTB habilitado) PORTB_PCR18 |= (PORT_PCR_ISF_MASK | ///< ISF=PORTB_PCR1[14]: w1c (limpa a pendência) PORT_PCR_MUX(0x1) ); ///< MUX=PORTB_PCR1[10:8]=0b011 (TPM1_CH1) GPIOB_PDDR |= GPIO_PDDR_PDD(GPIO_PIN(18)); ///< GPIOB_PDDR[18]=1 (pino de saída) GPIOB_PSOR |= GPIO_PSOR_PTSO(GPIO_PIN(18)); ///< GPIOB_PSOR[18]=1 (seta 1 no pino) } /** * \fn InitTPM (void) * \brief Configura o canal 0 do módulo TPM1 com a função "PWM", * de forma que a igualdade seja satisfeita a ???? duty cycle. */ void InitTPM(void) { SIM_SCGC5 |= SIM_SCGC5_PORTB_MASK; PORTB_PCR0 |= (PORT_PCR_ISF_MASK | PORT_PCR_MUX(0x3) ); SIM_SCGC6 |= SIM_SCGC6_TPM1_MASK; SIM_SOPT2 |= SIM_SOPT2_TPMSRC(0b01); SIM_SOPT2 &= ~SIM_SOPT2_PLLFLLSEL_MASK; /* * Periodo_{TPMx} ~ (MOD) * (PS/(frequência de source clock)) * (1/(1+CPWMS)) * = ???? */ TPM1_SC |= (TPM_SC_TOF_MASK | TPM_SC_CMOD(0x1) | TPM_SC_PS(0x7) ); TPM1_SC &= ~(TPM_SC_DMA_MASK | TPM_SC_TOIE_MASK | TPM_SC_CPWMS_MASK); TPM1_MOD &= TPM_MOD_MOD(COUNTER_OVF); TPM1_CNT |= TPM_CNT_COUNT(0x0) ; TPM1_CONF &= ~(TPM_CONF_CROT_MASK | TPM_CONF_CSOO_MASK | TPM_CONF_CSOT_MASK | TPM_CONF_GTBEEN_MASK | TPM_CONF_DOZEEN_MASK); TPM1_CONF |= TPM_CONF_DBGMODE(0b11); /* * (2) Configurção do canal 0: */ TPM1_C0SC |= (TPM_CnSC_CHF_MASK | ///< CHF=TPM1_C1SC[7]: w1c (limpa a pendência do canal) TPM_CnSC_MSB_MASK | ///< MSB=TPM1_C1SC[5]=1 TPM_CnSC_ELSB_MASK ); ///< ELSB=TPM1_C1SC[3]=1 TPM1_C0SC &= ~(TPM_CnSC_CHIE_MASK | ///< CHIE=TPM1_C1SC[6]=0 (desabilita interrupção) TPM_CnSC_MSA_MASK | ///< MSA=TPM1_C1SC[4]=0 TPM_CnSC_ELSA_MASK | ///< ELSA=TPM1_C1SC[2]=0 TPM_CnSC_DMA_MASK); ///< DMA=TPM1_C1SC[0]=0 (desabilita DMA) TPM1_C0V = TPM_CnV_VAL((short)(1.1*COUNTER_OVF)); ///< (> COUNTER_OVF????) (Seção 31.4.6} } int main(void) { int count = 0; short CnV; InitTPM(); InitGPIO(); while (!(TPM1_STATUS & TPM_STATUS_TOF_MASK)); ///< ???? Para quê? (Seção 31.4.8.2) GPIOB_PCOR |= GPIO_PCOR_PTCO(GPIO_PIN(18)); ///< GPIOB_PCOR[18]=1 (seta 0 no pino) for(;;) { count++; if (TPM1_STATUS & TPM_STATUS_TOF_MASK) { GPIOB_PCOR |= GPIO_PCOR_PTCO(GPIO_PIN(18)); ///< GPIOB_PCOR[18]=1 (seta 0 no pino) TPM1_C0SC |= TPM_CnSC_CHF_MASK; ///< baixa a bandeira de igualdade TPM1_SC |= TPM_SC_TOF_MASK; ///< baixa a bandeira de overflow } else if (TPM1_STATUS & TPM_STATUS_CH0F_MASK) { GPIOB_PSOR |= GPIO_PSOR_PTSO(GPIO_PIN(18)); ///< GPIOB_PSOR[18]=1 (seta 1 no pino) TPM1_C0SC |= TPM_CnSC_CHF_MASK; ///< baixa a bandeira de igualdade TPM1_SC |= TPM_SC_TOF_MASK; ///< baixa a bandeira de overflow } if (count == 0x100000) { TPM1_C0V = TPM_CnV_VAL((short)(0.8*COUNTER_OVF)); ///< 80% while (!(TPM1_STATUS & TPM_STATUS_TOF_MASK)); GPIOB_PCOR |= GPIO_PCOR_PTCO(GPIO_PIN(18)); } else if (count == 0x200000) { TPM1_C0V = TPM_CnV_VAL((short)(0.6*COUNTER_OVF)); ///< 60% while (!(TPM1_STATUS & TPM_STATUS_TOF_MASK)); GPIOB_PCOR |= GPIO_PCOR_PTCO(GPIO_PIN(18)); } else if (count == 0x300000) { TPM1_C0V = TPM_CnV_VAL((short)(0.4*COUNTER_OVF)); ///< 40% while (!(TPM1_STATUS & TPM_STATUS_TOF_MASK)); GPIOB_PCOR |= GPIO_PCOR_PTCO(GPIO_PIN(18)); } else if (count == 0x400000) { TPM1_C0V = TPM_CnV_VAL((short)(0.2*COUNTER_OVF)); ///< 20% while (!(TPM1_STATUS & TPM_STATUS_TOF_MASK)); GPIOB_PCOR |= GPIO_PCOR_PTCO(GPIO_PIN(18)); } else if (count == 0x500000) { TPM1_C0V = TPM_CnV_VAL((short)(0.02*COUNTER_OVF)); ///< 2% while (!(TPM1_STATUS & TPM_STATUS_TOF_MASK)); GPIOB_PCOR |= GPIO_PCOR_PTCO(GPIO_PIN(18)); } else if (count == 0x600000) { TPM1_C0V = TPM_CnV_VAL((short)(1.1*COUNTER_OVF)); ///< 100% while (!(TPM1_STATUS & TPM_STATUS_TOF_MASK)); GPIOB_PCOR |= GPIO_PCOR_PTCO(GPIO_PIN(18)); count = 0; } } }