Skip to main content
deleted 746 characters in body
Source Link
R1S8K
  • 283
  • 3
  • 21

================================================================================

Update: The problem that I couldn't read the first 3 configuration registers by one call and use the automatic pointer feature in the HMC5883L, I have to do it one by one.

But for reading the 6 data registers. I can read them by one function call.

The new question now is: Can I read the configuration registers automatically, whether I want to read the first 2 or 3 registers which are:

00 Configuration Register A Read/Write 01 Configuration Register B Read/Write 02 Mode Register Read/Write

Or I have to do it one by one?

The next 6 registers are the data registers. These I can read them automatically, no problem.

================================================================================

Update: The problem that I couldn't read the first 3 configuration registers by one call and use the automatic pointer feature in the HMC5883L, I have to do it one by one.

But for reading the 6 data registers. I can read them by one function call.

The new question now is: Can I read the configuration registers automatically, whether I want to read the first 2 or 3 registers which are:

00 Configuration Register A Read/Write 01 Configuration Register B Read/Write 02 Mode Register Read/Write

Or I have to do it one by one?

The next 6 registers are the data registers. These I can read them automatically, no problem.

added 746 characters in body
Source Link
R1S8K
  • 283
  • 3
  • 21

================================================================================

Update: The problem that I couldn't read the first 3 configuration registers by one call and use the automatic pointer feature in the HMC5883L, I have to do it one by one.

But for reading the 6 data registers. I can read them by one function call.

The new question now is: Can I read the configuration registers automatically, whether I want to read the first 2 or 3 registers which are:

00 Configuration Register A Read/Write 01 Configuration Register B Read/Write 02 Mode Register Read/Write

Or I have to do it one by one?

The next 6 registers are the data registers. These I can read them automatically, no problem.

================================================================================

Update: The problem that I couldn't read the first 3 configuration registers by one call and use the automatic pointer feature in the HMC5883L, I have to do it one by one.

But for reading the 6 data registers. I can read them by one function call.

The new question now is: Can I read the configuration registers automatically, whether I want to read the first 2 or 3 registers which are:

00 Configuration Register A Read/Write 01 Configuration Register B Read/Write 02 Mode Register Read/Write

Or I have to do it one by one?

The next 6 registers are the data registers. These I can read them automatically, no problem.

Source Link
R1S8K
  • 283
  • 3
  • 21

Working with Wire core library

I'm studying the Wire library code driver utility/twi.h to learn how twi buffering and ISR management work.

What I did actually, is that I copied the header/source files from the Arduino path F:\Program Files\Arduino\hardware\arduino\avr\libraries\Wire\src\utility and put them in the Arduino custom libraries and changed the .c to .cpp and it worked.

I actually ran through different i2c libraries for avr and this code library is one of the best IMHO.

My test code is to initialize the HMC5883L, and read back those configuration setting from the first SFRs of the HMC5883L.

I'm using Serial.prints inside/outside the ISR to debug my coding. It is working pretty much OK. I'm not sure if this could cause some problems.

But, because I don't have a complete understanding of how twi works, I'm trying to understand things now as displaying the content that is going in the ISR.

I think the initialize is working ok, but the read procedure may not as it should be.

Here's my code, for application code in the Arduino IDE, and the code in the library.

Arduino IDE code:

#include "twi.h"    
////////////////////////////////////////////////////////////////////////////
// HMC5883L
#define HMC5883L                    0x1E
#define CONFIG_REG_A                0x00  //Read/Write
#define CONFIG_REG_B                0x01  //Read/Write
#define MODE_REGISTER               0x02  //Read/Write
#define DATA_OUTPUT_X_MSB_REGISTER  0x03  //Read
#define DATA_OUTPUT_X_LSB_REGISTER  0x04  //Read
#define DATA_OUTPUT_Z_MSB_REGISTER  0x05  //Read
#define DATA_OUTPUT_Z_LSB_REGISTER  0x06  //Read
#define DATA_OUTPUT_Y_MSB_REGISTER  0x07  //Read
#define DATA_OUTPUT_Y_LSB_REGISTER  0x08  //Read
#define STATUS_REGISTER             0x09  //Read
#define IDENTIFICATION_REGISTER_A   0x10  //Read
#define IDENTIFICATION_REGISTER_B   0x11  //Read
#define IDENTIFICATION_REGISTER_C   0x12  //Read
#define DECLINATION_ANGLE 3.46

void hmc5883l_init(void);
void init_read(void);
void hmc5883l_print_serial(void);

void setup() {
  Serial.begin(9600);
  twi_init();
  hmc5883l_init();
  init_read();
}

void loop() {
  hmc5883l_print_serial();
}

void hmc5883l_init(void){
  uint8_t err,HMC5883L_ini[] = {CONFIG_REG_A,0x78,0x20,0x00};//
    err = twi_writeTo(HMC5883L,HMC5883L_ini,4,1,1);
    if(err != 0){Serial.print("error of init is");Serial.println(err);}
    Serial.println();
}

void init_read(void){
    uint8_t err,len,dat[3];
    err = twi_writeTo(HMC5883L,0x00,1,0,1);
    if(err != 0){Serial.print("error of init is");Serial.println(err);}
    Serial.println();   
    
    len = twi_readFrom(HMC5883L,dat,3,1);
    Serial.print("length of read op:              ");
    Serial.println(len);
    Serial.println();        
    
    Serial.print("config settings:                ");
    Serial.print(dat[0],HEX);Serial.print("\t");
    Serial.print(dat[1],HEX);Serial.print("\t");
    Serial.println(dat[2],HEX);    
}

void hmc5883l_print_serial(void){
  int16_t x,z,y;
  uint8_t data[6],len,err, dst_reg[] = {0x03};
  float heading,heading_in_degrees,declination_angle_YANBU;

  err = twi_writeTo(HMC5883L,dst_reg,1,0,1);
  if(err != 0){Serial.print("error of init is");Serial.println(err);}
  Serial.println();
     
  len = twi_readFrom(HMC5883L,data,6,1);
  Serial.print("len is: ");Serial.println(len);
  x = data[0] << 8 | data[1];
  z = data[2] << 8 | data[3];
  y = data[4] << 8 | data[5];
  
  heading = atan2(y,x);
  declination_angle_YANBU = ((3.0 + (52.0 / 60.0)) / (180 / M_PI));  // in yanbu city it's 
  heading += declination_angle_YANBU;
  
  if (heading < 0){heading += 2 * PI;}
  if (heading > 2 * PI){heading -= 2 * PI;} 
  
  heading_in_degrees = heading * 180 / M_PI;
  Serial.println(heading_in_degrees);

  //Serial.print("x-axis\t");Serial.print("z-axis\t");Serial.println("y-axis");
  Serial.print(x);Serial.print("\t");
  Serial.print(z);Serial.print("\t");
  Serial.println(y);
}

And this is the core library:

In this part, I've put Serial.prints inside the ISR cases to display what's going from the start of the tx/rx buffers. Also, I've cut the slave half of the code because it's not necessary in this problem, as I'm only dealing with HMC5883L as a slave and I'm the master.

#include <math.h>
#include <stdlib.h>
#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <compat/twi.h>
#include "Arduino.h" // for digitalWrite

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif

#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

#include "pins_arduino.h"
#include "twi.h"

static volatile uint8_t twi_state;
static volatile uint8_t twi_slarw;
static volatile uint8_t twi_sendStop;           // should the transaction end with a stop
static volatile uint8_t twi_inRepStart;         // in the middle of a repeated start

static void (*twi_onSlaveTransmit)(void);
static void (*twi_onSlaveReceive)(uint8_t*, int);

static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_masterBufferIndex;
static volatile uint8_t twi_masterBufferLength;

static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_txBufferIndex;
static volatile uint8_t twi_txBufferLength;

static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_rxBufferIndex;

static volatile uint8_t twi_error;

void twi_init(void)
{
  // initialize state
  twi_state = TWI_READY;
  twi_sendStop = true;      // default value
  twi_inRepStart = false;
  
  // activate internal pullups for twi.
  digitalWrite(SDA, 1);
  digitalWrite(SCL, 1);

  // initialize twi prescaler and bit rate
  cbi(TWSR, TWPS0);
  cbi(TWSR, TWPS1);
  TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;

  // enable twi module, acks, and twi interrupt
  TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
}

void twi_disable(void)
{
  // disable twi module, acks, and twi interrupt
  TWCR &= ~(_BV(TWEN) | _BV(TWIE) | _BV(TWEA));

  // deactivate internal pullups for twi.
  digitalWrite(SDA, 0);
  digitalWrite(SCL, 0);
}

uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop)
{
  uint8_t i;

  // ensure data will fit into buffer
  if(TWI_BUFFER_LENGTH < length){
    return 0;
  }

  // wait until twi is ready, become master receiver
  while(TWI_READY != twi_state){
    continue;
  }
  twi_state = TWI_MRX;
  twi_sendStop = sendStop;
  // reset error state (0xFF.. no error occured)
  twi_error = 0xFF;

  // initialize buffer iteration vars
  twi_masterBufferIndex = 0;
  twi_masterBufferLength = length-1;  // This is not intuitive, read on...

  twi_slarw = TW_READ;
  twi_slarw |= address << 1;

  if (true == twi_inRepStart) {
    twi_inRepStart = false;         // remember, we're dealing with an ASYNC ISR
    do {
      TWDR = twi_slarw;
    } while(TWCR & _BV(TWWC));
    TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE);  // enable INTs, but not START
  }
  else
    // send start condition
    TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);

  // wait for read operation to complete
  while(TWI_MRX == twi_state){
    continue;
  }

  if (twi_masterBufferIndex < length)
    length = twi_masterBufferIndex;

  // copy twi buffer to data
  for(i = 0; i < length; ++i){
    data[i] = twi_masterBuffer[i];
  }
    
  return length;
}

uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop)
{
  uint8_t i;

  // ensure data will fit into buffer
  if(TWI_BUFFER_LENGTH < length){
    return 1;
  }

  // wait until twi is ready, become master transmitter
  while(TWI_READY != twi_state){
    continue;
  }
  twi_state = TWI_MTX;
  twi_sendStop = sendStop;
  // reset error state (0xFF.. no error occured)
  twi_error = 0xFF;

  // initialize buffer iteration vars
  twi_masterBufferIndex = 0;
  twi_masterBufferLength = length;
  
  // copy data to twi buffer
  for(i = 0; i < length; ++i){
    twi_masterBuffer[i] = data[i];
  }
  
  // build sla+w, slave device address + w bit
  twi_slarw = TW_WRITE;
  twi_slarw |= address << 1;
  
  // if we're in a repeated start, then we've already sent the START
  // in the ISR. Don't do it again.
  //
  if (true == twi_inRepStart) {
    // if we're in the repeated start state, then we've already sent the start,
    // (@@@ we hope), and the TWI statemachine is just waiting for the address byte.
    // We need to remove ourselves from the repeated start state before we enable interrupts,
    // since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning
    // up. Also, don't enable the START interrupt. There may be one pending from the 
    // repeated start that we sent outselves, and that would really confuse things.
    twi_inRepStart = false;         // remember, we're dealing with an ASYNC ISR
    do {
      TWDR = twi_slarw;             
    } while(TWCR & _BV(TWWC));
    TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE);  // enable INTs, but not START
  }
  else
    // send start condition
    TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA); // enable INTs

  // wait for write operation to complete
  while(wait && (TWI_MTX == twi_state)){
    continue;
  }
  
  if (twi_error == 0xFF)
    return 0;   // success
  else if (twi_error == TW_MT_SLA_NACK)
    return 2;   // error: address send, nack received
  else if (twi_error == TW_MT_DATA_NACK)
    return 3;   // error: data send, nack received
  else
    return 4;   // other twi error
}

void twi_reply(uint8_t ack)
{
  if(ack){
    TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
  }else{
      TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
  }
}

void twi_stop(void)
{
  // send stop condition
  TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO);

  while(TWCR & _BV(TWSTO)){
    continue;
  }

  // update twi state
  twi_state = TWI_READY;
}

/* 
 * Function twi_releaseBus
 * Desc     releases bus control
 * Input    none
 * Output   none
 */
void twi_releaseBus(void)
{
  // release bus
  TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);

  // update twi state
  twi_state = TWI_READY;
}

ISR(TWI_vect)
{
  switch(TW_STATUS){
    // All Master
    case TW_START:     // sent start condition
    case TW_REP_START: // sent repeated start condition
      // copy device address and r/w bit to output register and ack
      TWDR = twi_slarw;
      Serial.print("mt add sent is:                 0x");
      Serial.println(twi_slarw, HEX);
      twi_reply(1);
      break;

    // Master Transmitter
    case TW_MT_SLA_ACK:  // slave receiver acked address
    case TW_MT_DATA_ACK: // slave receiver acked data
      // if there is data to send, send it, otherwise stop 
      if(twi_masterBufferIndex < twi_masterBufferLength){
        // copy data to output register and ack
        Serial.print("mt byte transmitted no:         ");
        Serial.println(twi_masterBufferIndex);
        Serial.print("content of tx byte:             0x");
        Serial.println(twi_masterBuffer[twi_masterBufferIndex], HEX);       
        TWDR = twi_masterBuffer[twi_masterBufferIndex++];
        twi_reply(1);
      }else{
    if (twi_sendStop){
        Serial.println("mt stop");
        twi_stop();
    }
          
    else {
      Serial.println("mt repst");
      twi_inRepStart = true;    // we're gonna send the START
      TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;
      twi_state = TWI_READY;
    }
      }
      break;
    case TW_MT_SLA_NACK:  // address sent, nack received
    Serial.println("mt add sent nack");
      twi_error = TW_MT_SLA_NACK;
      twi_stop();
      break;
    case TW_MT_DATA_NACK: // data sent, nack received
      twi_error = TW_MT_DATA_NACK;
      twi_stop();
      break;
    case TW_MT_ARB_LOST: // lost bus arbitration
      twi_error = TW_MT_ARB_LOST;
      twi_releaseBus();
      break;

    // Master Receiver
    case TW_MR_DATA_ACK: // data received, ack sent
      // put byte into buffer
        Serial.print("mr byte received no:            ");
        Serial.println(twi_masterBufferIndex);      
        Serial.print("content of rx byte:             0x");
        Serial.println(twi_masterBuffer[twi_masterBufferIndex], HEX);       
      twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
    case TW_MR_SLA_ACK:  // address sent, ack received
      // ack if more bytes are expected, otherwise nack
      if(twi_masterBufferIndex < twi_masterBufferLength){
        twi_reply(1);
      }else{
        twi_reply(0);
      }
      break;
    case TW_MR_DATA_NACK: // data received, nack sent
      // put final byte into buffer
        Serial.print("mr last received byte:          ");
        Serial.println(twi_masterBufferIndex);  
        Serial.print("content of last rx byte:        0x");
        Serial.println(twi_masterBuffer[twi_masterBufferIndex], HEX);       
      twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
    if (twi_sendStop)
          twi_stop();
    else {
      twi_inRepStart = true;
      TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;
      twi_state = TWI_READY;
    }    
    break;
    case TW_MR_SLA_NACK:
      twi_stop();
      break;
}

The problem now is:

I get these readings on the Arduino serial monitor. Note: I've wrote my notes on each section.

// initialization
// I think this process is done just fine, the exact bytes are sent
// but each time I open the serial monitor I get like garbage at the first 
// of the window, like this flipped question mark I get it most the time
// Is there something like flush function to clear things?
⸮mt add sent is:                 0x3C
mt byte transmitted no:         0
content of tx byte:             0x0
mt byte transmitted no:         1
content of tx byte:             0x78
mt byte transmitted no:         2
content of tx byte:             0x20
mt byte transmitted no:         3
content of tx byte:             0x0
mt stop


// writing target register which is 0x00, but I get 0xA3 ??!
m
mt byte transmitted no:         0
content of tx byte:             0xA3
mt stop

// Here requesting the 6 bytes data, I send read of HMC5883L which is 0x3D
// but 1st byte should be 0x78 then 2nd should be 0x20, 3rd 0x00
// but the result is different ! it's like they are shifted somehow

// At the end I'm trying to read back the passed array to the function, but 
// I get 0s!!
mt add sent is:                 0x3D
mr byte received no:            0
content of rx byte:             0xA3
mr byte received no:            1
content of rx byte:             0x78
mr last received byte:          2
content of last rx byte:        0x20
length of read op:              3

config settings:                0   0   0