quinta-feira, novembro 05, 2015

Usando o nRF24L01+ com o MSP430: Um Sensor a Bateria - Parte 2

Continuando a nossa prova de conceito, vamos acrescentar o sensor de temperatura.



Hardware

A ligação do DS18B20 é trivial: terra, alimentação (3.3V) e o sinal de dados (DQ, conectado a P1.1 na Launchpad).


Software

Aqui as coisas ficam mais "interessantes" (leia-se complicadas). O protocolo usado pelo sensor (OneWire) possui alguns tempos críticos da ordem de microsegundos. Para complicar, o MSP430 está rodando a 1MHz, portanto cada ciclo consome 1 microsegundo.

O compilador da IAR possui uma pseudo função __delay_cycles que gera código que consome "com precisão" um certo número de ciclos. Entretanto, operações comuns (como a chamada de rotina) consome vários ciclos (e portanto microsegundos). As primeiras tentativas não deram certo. Uma observação com osciloscópio mostrou grandes divergências. Infelizmente, o meu osciloscópio é um pouco limitado para este tipo de análise.  O resultado final foi obtido pelo ajuste empírico dos tempos. Talvez eu tente revisar isto depois usando um Bus Pirate.

Abaixo as rotinas de comunicação com o sensor. O projeto completo pode ser baixado dos arquivos do blog (arquivo MSP_NRF24_2.zip).
#define delayMicroseconds(n)  __delay_cycles(n)   // 1MHz = 1uS por ciclo

// Macros para comunicação com o DS18B20
// Usadas para maior velocidade
#define setDQ_LOW()  { P1DIR |= DS18B20; P1REN &= ~DS18B20; P1OUT &= ~DS18B20; }
#define setDQ_HIGH() { P1DIR &= ~DS18B20; P1REN |= DS18B20; P1OUT |= DS18B20;  }

// Leitura da temperatura
// Retorna temperatura em décimos de grau
// Considera que temos apenas um sensor conectado
// e ele está com a configuração padrão
// Ref: AN162 da Maxim
static unsigned leTemperatura(void)
{
  unsigned valor;
  
  if (!OW_Reset())
    return 0;
  OW_WriteByte (0xCC);  // Skip ROM
  OW_WriteByte (0x44);  // Start conversion
  delay(8);             // aguarda fim da conversao
  
  OW_Reset();
  OW_WriteByte (0xCC);  // Skip ROM
  OW_WriteByte (0xBE);  // Read ScratchPAD
  valor = OW_ReadByte();                 // LSB
  valor = (OW_ReadByte() << 8) + valor;  // MSB
  OW_Reset();           // Nao queremos o resto

  valor = (valor * 100) / 16;
  return (valor+5)/10;
}

// OneWire reset
// Retorna true se tem um sensor conectado
static byte OW_Reset(void)
{
  byte resposta;
  
  setDQ_LOW();
  delayMicroseconds(500);  // comanda reset
  setDQ_HIGH();
  delayMicroseconds(70);      // aguarda sensor responder
  resposta = P1IN & DS18B20;  // le a resposta
  delayMicroseconds(500);     // aguarda fim da resposta
  return resposta == 0;
}

// OneWire Read Byte
static byte OW_ReadByte(void)
{
  byte i, resp;
  for (i = 0; i < 8; i++)
  {
    // Pulso de 1uS abre janela para resposta
    setDQ_LOW();
    setDQ_HIGH();
    // Dá um tempo para sensor colocar a resposta e a lê
    delayMicroseconds(8);
    resp = resp >> 1;
    if ((P1IN & DS18B20) != 0)
    {
      resp = resp | 0x80;
    }
    // Aguarda o final da janela
    delayMicroseconds(50);
  }
  
  return resp;
}

// OneWire Write Byte
static void OW_WriteByte(byte valor)
{
  byte i;
  for (i = 0; i < 8; i++)
  {
    // Low inicia a janela
    setDQ_LOW();
    if (valor & 0x01)
    {
      // Volta o nivel alto se for "1"
      setDQ_HIGH();
      // Manter o bit até o final da janela
      delayMicroseconds(90);
    }
    else
    {
      // Manter o bit até o final da janela
      delayMicroseconds(90);
      // Voltar ao repouso
      setDQ_HIGH();
    }
    // Passar para o próximo bit
    valor = valor >> 1;
  }
}

Nenhum comentário: