Ir ao conteúdo
  • Cadastre-se

PIC PIC 18F4550 com Bússola Eletrônica HMC5883L


Ir à solução Resolvido por Mario Henrique Luchiari,

Posts recomendados

Olá, pessoal! Estou com problemas para conseguir ler os registradores de um módulo bússola eletrônica.

Estou usando compilador Mikroc, PIC 18F4550 e o módulo HMC5883L.

A comunicação é I2C mas a instrução de leitura I2C1_Rd trava o código.

Não consigo descobrir se o módulo está sendo inicializado corretamente ou se apenas não estou conseguindo ler o registrador do módulo. 

Será que alguém pode me ajudar? Vou postar meu código anexo para darem uma olhada, é só alterar a extensão de .txt para .c.

 

Desde já obrigado!

//Habilitar as bibliotecas:
// I2C, Conversions, C_String e C_Math
// RB0 -> SDA e RB1 -> SDL

// LCD module connections
sbit LCD_RS at RE0_bit;
sbit LCD_EN at RE1_bit;
sbit LCD_D4 at RD0_bit;
sbit LCD_D5 at RD1_bit;
sbit LCD_D6 at RD2_bit;
sbit LCD_D7 at RD3_bit;

sbit LCD_RS_Direction at TRISE0_bit;
sbit LCD_EN_Direction at TRISE1_bit;
sbit LCD_D4_Direction at TRISD0_bit;
sbit LCD_D5_Direction at TRISD1_bit;
sbit LCD_D6_Direction at TRISD2_bit;
sbit LCD_D7_Direction at TRISD3_bit;
// End LCD module connections

signed int tmp;

void HMC5883L_Inicio(){
   PORTB.F4 = 1;    //Indica entrada no modo inicialização
   I2C1_Start();
   I2C1_Wr( 0x3C );
   I2C1_Wr( 0x00 );
   I2C1_Wr( 32 );
   I2C1_Wr( 0x00 ); //Continuo
   I2C1_Stop();
   Delay_ms(500);
   PORTB.F4 = 0;    //Indica saída do modo inicialização
   Delay_ms(500);
   PORTB.F4 = 0;
   
}
void HMC5883_Leitura(){
   PORTB.F5 = 1;    //Indica entrada no modo leitura
   I2C1_Start();
   I2C1_Wr( 0x3C );
   I2C1_Wr( 0x03 );
   I2C1_Repeated_Start();
   I2C1_Wr( 0x3D );
   tmp = I2C1_Rd(1)<<8 | I2C1_Rd(0);
   I2C1_Stop();
   Delay_ms(500);
   PORTB.F5 = 0;    //Indica saída do modo leitura
}

void main() 
{
    ADCON1 = 0x0F;         //desativa o canal analogico
    CMCON = 7;             //desativa os comparadores analogicos
    Lcd_Init();            //inicia o display
    Lcd_Cmd(_LCD_CLEAR);          //Limpa a tela do LCD
    Lcd_Cmd(_LCD_CURSOR_OFF);     //Desliga o cursor piscante
    Lcd_Out(1,1,"HMC5883L-Simples");
    TRISB.F4 = 0; TRISB.F5 = 0; TRISB.F6 = 0; TRISB.F7 = 0;
    Delay_ms(500);
    I2C1_Init(100000);     //Inicializa o módulo I2C
    PORTB.F4 = 0; PORTB.F5 = 0; PORTB.F6 = 0; PORTB.F7 = 0;
    Delay_ms(50);
    HMC5883L_Inicio();     //Inicializa o módulo HMC5883
   while(1)
   {
      HMC5883_Leitura();   //Lê o módulo
      Delay_ms(500);
   }
}

Mario

hmc5883-Simples.txt

Link para o comentário
Compartilhar em outros sites

  • Membro VIP

Amigo Mário, que tal se você googlasse hmc5883l c code example ? (lembrando que não necessariamente direcionado ao mikroc) . Óbvio d+, já o fizeste e prefere alguma orientação personalizada? Está simulando ou já está montado? Neste caso, confira as conexões, alimentações, circuitos e etc...

  • Curtir 2
Link para o comentário
Compartilhar em outros sites

Olá meu caro .if! Já "googlei" até cansar... kkkk

Está montado.

Verifiquei as conexões, resistores pull-up, já coloquei outro módulo (no caso o MPU6050) e o hardware funcionou. Este módulo funciona no Arduino, então o módulo está operacional.

Quero fazer funcionar no PIC pois estou montando um veículo autônomo que o utilizaria como referência de direção. Já testei com o Mplab, CCS e nada funcionou. Estou intrigado, quando tento fazer a leitura do registro 0x03 ele trava aí.

Coloquei um led que acende na entrada da função de leitura (HMC5883_Leitura()) e apaga na saída, fiz o mesmo na função de inicialização (HMC5883L_Inicio()). O led da inicialização indica que ele entra e sai o da de leitura fica aceso. Quando comento a função I2C1_Rd a função funciona e o led apaga.

Estou a pesquisar mas está difícil...

Obrigado pela dica e pela atenção.

Um grande abraço.

 

Montagem.jpg

Link para o comentário
Compartilhar em outros sites

  • Membro VIP
2 horas atrás, Mario Henrique Luchiari disse:

quando tento fazer a leitura do registro 0x03 ele trava aí.

Então eis seu norte. O que está no registro 0x03?

 

2 horas atrás, Mario Henrique Luchiari disse:

Este módulo funciona no Arduino,

com qual programa? Use-o como base.

 

2 horas atrás, Mario Henrique Luchiari disse:

Quero fazer funcionar no PIC pois estou montando um veículo autônomo que o utilizaria como referência de direção

não justifica PIC. Basta remover o mc (geralmente um atmega) do arduino e colocar no seu sistema.

 

Tenta também:

I2C1_Start();
I2C1_Start(); //+ de 1
I2C1_Repeated_Start();
I2C1_Repeated_Start(); //+ de 1

Também pode tentar reduzir velocidade

 

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Boa noite .if!

Obrigado por retornar.

Esse registro 0x03 é o registro de leitura dos eixos. Teoricamente é pra posicionar nele e fazer leituras consecutivas (6) para extrair os 3 eixos (6 bytes). Pegar o MSB, deslocar 8 posições e sobrepor com o LSB. Depois é só tratar isto como uma variável de 16 bits.

Já olhei a biblioteca do Arduino e tentei adaptar mas, ainda não tenho conhecimento pra isso, por enquanto... 

Eu tenho mais experiência com PIC, fico frustrado quando algo funciona no ATMEL e não no PIC. Meu hardware está praticamente pronto, por isso gostaria muito que funcionasse.

Repetir o Start é uma dica interessante, já fiz algumas tentativas neste sentido, vou tentar mais. Pode ser que a temporização esteja comprometendo a comunicação I2C e isso dê certo. 

O interessante é que eu coloco outros módulos como o BMP180, MPU6050 (como já disse) e funciona muito bem... Esse bandido tá me zoando e isso já tá virando ponto de vista e honra!!!

Fiz a pesquisa que você citou no post anterior, até encontrei um código do CCS e estava testando até agora, é bem interessante mas ainda não deu resultado.

A briga continua...

Um forte abraço e mais uma vez, obrigado.

Link para o comentário
Compartilhar em outros sites

  • Membro VIP
4 horas atrás, Mario Henrique Luchiari disse:

biblioteca do Arduino e tentei adaptar mas, ainda não tenho conhecimento pra isso

publique-a.

 

4 horas atrás, Mario Henrique Luchiari disse:

Esse registro 0x03 é o registro de leitura dos eixos. Teoricamente é pra posicionar nele e fazer leituras consecutivas (6) para extrair os 3 eixos (6 bytes). Pegar o MSB, deslocar 8 posições e sobrepor com o LSB. Depois é só tratar isto como uma variável de 16 bits.

Percebo agora no seu fonte que não vi onde você mostra o resultado e como/onde/porquê/quando você sabe que deu errado. Confio que aquela sequencia de comandos está coerente com o que diz o datasheet..

 

4 horas atrás, Mario Henrique Luchiari disse:

encontrei um código do CCS e estava testando até agora, é bem interessante mas ainda não deu resultado.

Publique-a. Qual resultado deu e qual esperava obter?

 

Depois de destravar mas obter resultado inconsistente, tenta

tmp = ((signed int)I2C1_Rd(1)<<8) | (signed int)I2C1_Rd(0);//pois tmp é signed int

Link para o comentário
Compartilhar em outros sites

Boa noite .if!

Obrigado por suas considerações. Vamos lá...

  1. Vou postar aqui o sketch do Arduino que funciona para darmos uma olhada. Já fiz a sequência de acionamento dos registradores conforme está nela, na esperança que desse resultado. Não funcionou. É a pasta Teste_Mhl.zip em anexo.
  2. Você tem razão, ainda não enviei para o LCD na função secundária HMC5883_Leitura(), seria o próximo passo se ela não travasse naquele comando de leitura. Sim a sequência de comandos está coerente com o datasheet e com vários outros programas que encontrei nos sites, embora nenhum deles tenha funcionado. Tenho dúvidas sobre esta sequência e sobre a sequência de inicialização. Porém, estas estão iguais às que tenho visto em outros exemplos. Estou encanado com estas sequências desde o começo...
  3. Este é o código do CCS que eu estou debatendo há dias. O original envia dados pela UART, adaptei este para escrever no LCD. Segue anexo o arquivo HMC5883L-Base.zip com o código. Esperava que ele indicasse os valores dos eixos, azimute e tal. Em vez disso, ele sempre dá uma mensagem de que o sensor não foi detectado, como se o sensor não estivesse lá mas, se eu remover o sensor o programa não passa da mensagem "Inicializando...". Acredito que, de alguma forma, o sensor esteja sendo lido. 

A batalha continua...

Um forte abraço,

Mario

 

Teste_Mhl.zip HMC5883L-Base.zip

Link para o comentário
Compartilhar em outros sites

  • Membro VIP

Olá. Não baixei pra analisar pois tem que dar muitos cliques, criar pasta, extrair ..aff 😁. Publique a parte que interessa do fonte e já filtrada por você. Use os botões "code" e se precisar do botão "spoiler" é porque é muito grande o que pode desmotivar. Por isso de novo... apenas a parte condizente com sua problemática. Publique também o esquema pois pode ter sido alguma cagadinha como p.ex. falta de pullup, pino errado e etc.

 

Boa @Mario Henrique Luchiari !

Me fez lembrar que coincidentemente já passe por isso, era exatamente problema no i2c. A diferença era que era empresa de renome: Microchip. Perdi 1 tempáço tentando fazer a comunicação. Como você, também aprendi detalhes adicionais sobre i2c. Dias depois descobri que havia um erro no ... DATASHEET! do mcp9600... A página que indicava os pinos os mencionara trocados 1 pelo outro! Troquei e deu certo. Com simultâneas lágrimas de raiva e alegria nos olhos - cada lado de um tipo,  avisei-os achando que ia receber um mínimo "thank you" no email ou um brinde a minha escolha (minha preferência) mas os f d p sequer me deram bola. Apenas corrigiram o d.s. sem mencionar nada disso na errata...🤬

 

13 horas atrás, Mario Henrique Luchiari disse:

reservei um tempinho para dizer que resolvi o problema. Eram os registradores que estavam errados, inclusive o endereço I2C.

 

Me permiti registrar "no passado" pra que minha publicação não seja a última ok?

Parabéns pelo empenho! E pelo retorno: "thank you!" 😁

 

Link para o comentário
Compartilhar em outros sites

  • 2 semanas depois...
  • Solução

Boa tarde, .if e pessoal do clube!

Desculpem a minha demora em continuar postando. Estou com uma carga grande de trabalho. A carga ainda está grande mas, reservei um tempinho para dizer que resolvi o problema. Eram os registradores que estavam errados, inclusive o endereço I2C. Isso eu desconfiava desde o começo...

 

Em primeiro lugar o módulo que comprei é uma enganação. Tomem cuidado! Vem apresentado como HMC5883 mas na verdade é QMC5883. A diferença? É brutal! Até o datasheet do QMC é chato de achar. Quando comparar os dois, verá que está tudo diferente. Acertei o endereço e os registradores e pufff! O trem funcionou!

 

Em segundo lugar descobri o endereço na marra. Como? Fiz um programa que testa endereço por endereço até o módulo, que é escravo, responder. Deu certo! Aí que eu fui me tocar que o chip podia ser outro. Pesquisei e descobri que quando o chip tem a inscrição DA5883 é QMC5883 e quando tem L883 é HMC5883L. Vejam nas fotos.

image.png.9891519419277d4dc4169757e31e5928.pngimage.png.a8b58391940302f75a34d64544919c6e.png  

 

Em terceiro, acabei aprendendo muito sobre I2C, a ponto de colocar num osciloscópio para analisar o protocolo! Cheguei à conclusão que, quando o endereço está errado e o chip não responde, a linha do clock que é a amarela só completa a primeira parte (observe que esta possui (quase) três partes sutilmente separadas). Esta primeira parte é o envio do endereço do mestre para o escravo. Quando não obtém resposta do escravo, a sequência termina. A linha azul é a linha de dados, que é o SDA.

A sequência desta foto está completa. Ela é de uma comunicação bem sucedida entre mestre e escravo com endereço 0x1A.

image.png.aaf5d19c8f904fca3f4ca69d2ac64263.png

 

Quem tiver interesse no código vou postar aqui e considerar esse tópico encerrado!

 

Muito obrigado, .if! você ajudou bastante! 

 

Atenção! Este código é para o QMC5883 e não para o HMC5883.

/*Apesar de ter requisitado o HMC5883 e na placa do módulo estar escrito isso,
o módulo possui o CI QMC5883 cujos endereços de registradores diferem do HMC.
Após muita luta e pesquisa, consegui descobrir isso. Este código faz o QMC
funcionar de maneira básica, apenas indicando os valores dos eixos X, Y e Z no
display LCD 16 x 2 selecionados por um botão no PORTB.F2. A sequência apresenta
X, Y, Z e por último a situação de todos os registradores, de 0x00 a 0x0D.
Reconheço que consegui com a ajuda, a graça e pela glória de Deus!
             Versão: e                  São Carlos, 26 de novembro de 2020.
------------------------------------------------------------------------------*/
//Habilitar as bibliotecas:
// I2C, Conversions, C_String e C_Math
// RB0 -> SDA e RB1 -> SDL

#define QMC5883_ADDRESS    0x1A
#define DATA_OUTPUT_REG    0x00         // São de 0x00 a 0x05
#define STATUS_REG         0x06
#define TEMPL_OUTPUT_REG   0x07
#define TEMPH_OUTPUT_REG   0x08
#define CONTROL1_REG       0x09
#define CONTROL2_REG       0x0A
#define SETRESET_REG       0x0B
#define IDENTIFICATION_REG 0x0D

#define botao PORTB.F2

// LCD module connections
sbit LCD_RS at RE0_bit;
sbit LCD_EN at RE1_bit;
sbit LCD_D4 at RD0_bit;
sbit LCD_D5 at RD1_bit;
sbit LCD_D6 at RD2_bit;
sbit LCD_D7 at RD3_bit;

sbit LCD_RS_Direction at TRISE0_bit;
sbit LCD_EN_Direction at TRISE1_bit;
sbit LCD_D4_Direction at TRISD0_bit;
sbit LCD_D5_Direction at TRISD1_bit;
sbit LCD_D6_Direction at TRISD2_bit;
sbit LCD_D7_Direction at TRISD3_bit;
// End LCD module connections

signed int tmp, tmpx, tmpy, tmpz, tmpr;
char tmp1, tmp2, valor, trava, eixo, trava2, hexa;
char txt[7], *hexindic = "0x00";


void QMC5883L_Inicio(void){
   PORTB.F4 = 1;     //Indica entrada no modo inicialização
   I2C1_Start();
   I2C1_Wr( QMC5883_ADDRESS );
   I2C1_Wr( CONTROL1_REG );
   //I2C1_Wr( 0x1D ); //OSR 512, Scale Range 8 Gauss, ODR 200Hz, Continuous mode
   I2C1_Wr( 0x0D ); //OSR 512, Scale Range 2 Gauss, ODR 200Hz, Continuous mode
   I2C1_Stop();
   I2C1_Start();
   I2C1_Wr( QMC5883_ADDRESS );
   I2C1_Wr( CONTROL2_REG );
   I2C1_Wr( 0x41 );
   I2C1_Stop();
   PORTB.F4 = 0;     //Indica entrada no modo inicialização
}
signed int QMC5883_Leitura(void){
   PORTB.F5 = 1;    //Indica entrada no modo leitura
   I2C1_Start();
   I2C1_Wr( QMC5883_ADDRESS );
   if(eixo == 3) I2C1_Wr( valor );        //Ler o valor dos registradores
   else I2C1_Wr( DATA_OUTPUT_REG );
   I2C1_Repeated_Start();
   I2C1_Wr( QMC5883_ADDRESS | 1 );
   if(eixo == 3) tmpr = I2C1_Rd(0);       //Ler o valor dos registradores
   else{
      tmpx = I2C1_Rd(1);
      tmpx |= I2C1_Rd(1) << 8;
      tmpy = I2C1_Rd(1);
      tmpy |= I2C1_Rd(1) << 8;
      tmpz = I2C1_Rd(1);
      tmpz |= I2C1_Rd(0) << 8;
   }
   I2C1_Stop();
   PORTB.F5 = 0;    //Indica saída do modo leitura
}
char hexadec(char hexanum){
   if((hexanum >> 4) < 10) hexindic[2] = (hexanum >> 4) +48;
   else hexindic[2] = (hexanum >> 4) +55;
   if((hexanum & 0x0F) < 10) hexindic[3] = (hexanum & 0x0F) +48;
   else hexindic[3] = (hexanum & 0x0F) +55;
   Lcd_Out_CP(hexindic);        //Escreve hexindic depois do texto
}
void main() 
{
    ADCON1 = 0x0F;         //desativa o canal analogico
    CMCON = 7;             //desativa os comparadores analogicos
    TRISB = 0x0F;
    PORTB = 0x00; valor = 0; trava = 0; eixo = 0; trava2 = 0;
    //RBPU_bit = 0;
    
    Lcd_Init();                   //inicia o display
    Lcd_Cmd(_LCD_CLEAR);          //Limpa a tela do LCD
    Lcd_Cmd(_LCD_CURSOR_OFF);     //Desliga o cursor piscante
    Delay_ms(100);
    Lcd_Out(1,1,"QMC5883e-Simples");
    Delay_ms(500);
    
    I2C1_Init(100000);     //Inicializa comunicação I2C
    Delay_ms(50);
    QMC5883L_Inicio();     //Inicializa o módulo QMC5883. Engraçado que sem...
    Delay_ms(50);          //... inicializar pela segunda vez, às vezes falha!
    QMC5883L_Inicio();     //Inicializa o módulo QMC5883 novamente !!!
   while(1)
   {
      QMC5883_Leitura();   //Lê o módulo
      if(!eixo){
         if(trava2){
            Lcd_Out(1,1,"QMC5883e-Simples");
            Lcd_Out(2,1,"                ");
         }
         Lcd_Out(2,5,"X:");
         IntToStr(tmpx, txt);
         Lcd_Out_CP(txt);
         valor = 0;
         trava2 = 0;
      }
      if(eixo == 1){
         Lcd_Out(2,5,"Y:");
         IntToStr(tmpy, txt);
         Lcd_Out_CP(txt);
      }
      if(eixo == 2){
         Lcd_Out(2,5,"Z:");
         IntToStr(tmpz, txt);
         Lcd_Out_CP(txt);
      }
      if(eixo == 3){
         if(!trava2) Lcd_Cmd(_LCD_CLEAR);
         trava2 = 1;
         Lcd_Out(1,3,"Reg:   ");
         hexadec(valor);
         Lcd_Out(2,3,"Valor: ");
         hexadec(tmpr);
         valor++;
         if(valor > 0x0D) valor = 0;
         Delay_ms(1000);
      }
      Delay_ms(500);
      if(!botao && !trava){
         trava = 1;
         eixo++;
         if(eixo > 3) eixo = 0;
      }
      if(botao) trava = 0;
   }
}

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...