terça-feira, fevereiro 07, 2012

Display Gráfico Nokia 5110 - Parte 8: MSP430

Para encerrarmos nossas experiências com o display Nokia 5110, vamos conectá-lo ao Launchpad da Texas, que utiliza um microcontrolador da família MSP430.


O modelo específico que vamos usar é o MSP430G2231 que possui 2K de memória Flash, 128 bytes de Ram e 10 pinos de E/S digital. Os 2K de Flash, em uma arquitetura Von-Neuman, permitem colocar facilmente a nossa matriz de caracteres. Já os 128 bytes de Ram impedem de manter uma imagem da tela na memória.

Hardware

Como a Launchpad trabalha com 3,6V vamos abusar ligeiramente o display e conectá-lo diretamente. Vamos usar a USI (Universal Serial Interface) para gerar os sinais DIN e SCK, os demais sinais serão gerados por E/S digital. Ficamos portanto com as seguintes conexões entre o display e a Launchpad:
  • Vcc -> Vcc
  • Gnd -> Gnd
  • SCE -> P1.4
  • RST -> P1.0
  • D/C -> P1.1
  • DIN -> P1.6
  • SCK -> P1.5
  • LED -> Vcc através de um resistor de 100R
Software

Para sofisticar um pouco, vamos colocar duas imagens e uma barra indicando a leitura de temperatura feita pelo sensor interno do MSP430. O software foi feito usando o compilador IAR.

Como de costume, a parte mais complicada é iniciar corretamente os periféricos:
  • O watchdog precisa ser desligado (ou reiniciado periodicamente), já que é ativado no reset.
  • As entradas e saídas digitais precisam ter a direção programa, o resto das configurações podem ficar nos valores de reset.
  • A USI é bastante flexível, o que significa que tem vários registradores de configuração. Em primeiro lugar, precisamos acionar os pinos de SDO e SCLK, selecionar operação SPI master, habilitar a saída e deixar a operação suspensa.Isto é feito no registrador USICTL0.
  • O registrador USICTL1 deve ser programado com zero, para ativar o modo SPI.
  • O clock para a USI foi programado para SMCLK/8 no registrador USICKCTL.
  • A contagem de bits (USICNT) é iniciada com zero e a operação da USI é liberada no USICTL0.
  • Embora o MSP430G2231 possa gerar internamente um clock de até 16MHz, vamos trabalhar com 1MHz que é o valor calibrado na fábrica.
  • A programação do ADC para leitura da temperatura foi copiada do exemplo da Texas.
  • Para fazer a leitura periodicamente vamos usar o TimerA, alimentado pela base de tempo de 12KHz do MSP430.
  1. void IniciaHw (void)  
  2. {  
  3.   // Programa entradas e saídas digitais  
  4.   P1SEL = 0;  
  5.   P1DIR = PIN_SCE | PIN_RESET | PIN_DC | PIN_SDIN | PIN_SCLK;  
  6.   P1OUT = PIN_SCE;  
  7.       
  8.   // Programa a USI  
  9.   USICTL0 = USIPE6 | USIPE5 | USIMST | USIOE | USISWRST ;  
  10.   USICTL1 = 0;  
  11.   USICKCTL = USIDIV_2 | USISSEL_2;  // Clock = SMCLK/8 = 1/16 MHZ = 62,5 KHz, ativo low  
  12.   USICNT = 0;  
  13.   USICTL0 &= ~USISWRST;  
  14.     
  15.   // retira o reset do LCD  
  16.   P1OUT = PIN_SCE | PIN_RESET;  
  17.   
  18.   // Ligar o DCO, vamos trotar a 1MHz  
  19.   BCSCTL1 = CALBC1_1MHZ;  
  20.   DCOCTL  = CALDCO_1MHZ;  
  21.   BCSCTL2 = 0;  
  22.     
  23.   // Configura o ADC para ler o sensor de temperatura  
  24.   ADC10CTL1 = INCH_10 + ADC10DIV_3;         // Temp Sensor ADC10CLK/4  
  25.   ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE;  
  26.     
  27.   // Configura ACLK p/ 6 KHz  
  28.   BCSCTL1 |= DIVA_1;                        // ACLK/2  
  29.   BCSCTL3 |= LFXT1S_2;                      // ACLK = VLO / 2  
  30. }  
Feita a programação da USI, para enviar um byte basta colocá-lo em USISRL e carregar 8 em USICNT. A USI se encarrega de gerar o clock e deslocar os bits. O fim da transmissão é sinalizado pelo bit USIIFG no registrador USICTL1.
  1. // Envia um byte para o controlador do display  
  2. // dc:   LCD_CMD ou LCD_DAT  
  3. // data: byte a enviar  
  4. void LcdWrite(byte dc, byte data)  
  5. {  
  6.   // Seleciona dado ou comando  
  7.   if (dc == LCD_DAT)  
  8.     P1OUT |= PIN_DC;  
  9.   else  
  10.     P1OUT &= ~PIN_DC;  
  11.     
  12.   // Seleciona o display  
  13.   P1OUT &= ~PIN_SCE;  
  14.     
  15.   // Envia o byte  
  16.   USICTL0 |= USIOE;  
  17.   USISRL = data;  
  18.   USICNT = 8;  
  19.   while ((USICTL1 & USIIFG) == 0)  
  20.     ;  
  21.   
  22.   // Desliga a seleção  
  23.   P1OUT |= PIN_SCE;  
  24. }  
A leitura da temperatura é feita disparando a conversão e deixando o processador parado (low power mode 0) enquanto ela é concluída. A instrução __bis_SR_register liga (coloca em 1) bits do registrador de status (SR). No caso são ligados os bits que param o processador e habilita interrupções. Quando uma interrupção ocorre, este valor do SR é salvo na pilha e o processador é acordado. A rotina de interrupção usa a instrução __bic_SR_register_on_exit para limpar (colocar em 0) bits da cópia do SR que está na pilha. Quando a rotina retorna, é restaurado o SR alterado, o que no caso significa que o processador estará novamente rodando.
  1. // Faz uma leitura de temperatura  
  2. void LeTemperatura (void)  
  3. {  
  4.   ADC10CTL0 |= ENC + ADC10SC;  
  5.   __bis_SR_register(CPUOFF + GIE);    // dorme em LPM0 até concluir leitura  
  6.   temperatura = ADC10MEM;  
  7. }  
  8.   
  9. // Trata interrupção do ADC  
  10. #pragma vector=ADC10_VECTOR  
  11. __interrupt void ADC10_ISR (void)  
  12. {  
  13.   __bic_SR_register_on_exit(CPUOFF);  // Retorna ao modo ativo  
  14. }  
No lado do timer, é usado o modo de "contagem para cima", onde ele conta de 0 até o valor escrito em TACCR0. Ao atingir o valor, uma interrupção é gerada. Novamente é usado o recurso de economia de energia; neste caso paramos mais partes do MSP430 usando o low power mode 3.
  1. // Dorme por 200 mseg  
  2. void Dorme (void)  
  3. {  
  4.   TACCR0 = 1200;                         // 200 ms  
  5.   TACTL = TASSEL_1 | MC_1 | TAIE;        // ACLK, Up mode.    
  6.   __bis_SR_register(LPM3_bits + GIE);    // dorme em LPM3 até timer interromper  
  7. }  
  8.   
  9. // Trata interrupção do timer  
  10. // Timer A0 interrupt service routine  
  11. #pragma vector=TIMERA1_VECTOR  
  12. __interrupt void Timer_A (void)  
  13. {  
  14.   TACTL = TASSEL_1;                       // Para o timer  
  15.   __bic_SR_register_on_exit(LPM3_bits);   // Retorna ao modo ativo  
  16. }  
O nosso programa principal fica assim:
  1. int main( void )  
  2. {  
  3.   // Desliga Watchdog  
  4.   WDTCTL = WDTPW + WDTSSEL + WDTHOLD;  
  5.   
  6.   // Inicia o hardware  
  7.   IniciaHw();  
  8.   LcdInitialise();  
  9.    
  10.   // Mostra a nossa tela inicial  
  11.   LcdClear();  
  12.   LcdWriteString ("DQSoft", 0, 3);  
  13.   LcdWriteImg (invasor1, 30, 16, 11, 8);  
  14.   LcdWriteImg (invasor2, 51, 16, 8, 8);  
  15.   
  16.   // Loop principal  
  17.   for (;;)  
  18.   {  
  19.     Dorme ();  
  20.     LeTemperatura ();  
  21.     LcdBarraHoriz(0, 40, LCD_DX, 0);  
  22.     LcdBarraHoriz(0, 40, (int) (temperatura >> 4), 1);  
  23.   }  
  24. }  
O projeto completo está nos arquivos do blog (veja lá no alto à direita) no arquivo Nokia5110_MSP430.zip.

4 comentários:

Jucimar Antunes Cabral disse...

Bom dia
Qual sua versão do IAR?
Pois não consigo abrir na minha versão (4,2)
Eu teria como modificar para poder abrir?

Daniel Quadros disse...

Acho que usei a 5.30. Não tenho certeza se 4.2 suporta o MSP430G que é usado na Launchpad. De qualquer forma, o jeito é criar um novo workspace / projeto com a sua versão, acertar a configuração para a Launchpad e incluir os meus fontes. Outra opção é baixar a versão atual do site da IAR (http://supp.iar.com/Download/SW/?item=EW430-KS4).

NOME disse...

Ola..Boa Noite..Gostaria de Saber se este projeto com o MSP430..TEM video..? Obrigada!!

Daniel Quadros disse...

Não fiz nenhum vídeo deste projeto com o MSP430. O mais próximo foi um vídeo do display ligado a um Arduino: http://www.youtube.com/watch?feature=player_embedded&v=xO05Rs3qrdo