Search code examples
arduinospi

Received data are correct on scope not in program


I use Arduino shiftIn instruction to receive data from a MLX90316.
It uses one wire for MOSI an MISO.
Data consists of 4 bytes (2+2)
First 2 bytes are an angle value. Second 2 bytes ares the reversed value of angle (1st complement)
First 2 bytes are correct on scope and program (0x00 and 0x22)
Second 2 bytes are correct on scope (0xFF and 0XDD), but are frequently (not always) wrong in program (0xFF and 0xDC or 0xFF and 0xCC...)

In fact only last byte is sometimes wrong when I print it.

I have tried to change Serial speed to 9600 bps (same result)
I have tried to change delayMicroseconds value before last shiftIn (same result)

/* MLX90316 Rotary Position Sensor */

int readAngle();

int pinSS = 10;  // Green Wire
int pinDATA = 11; // Yellow Wire
int pinSCK = 13; // Grey Wire

unsigned int r1=0;
unsigned int r2=0;
unsigned int r3=0;
unsigned int r4=0;

int angle;

void setup(){
  delayMicroseconds(16000);  // 16ms slave start-up
  pinMode(pinSS,OUTPUT);  // Pin Slave Select
  pinMode(pinSCK, OUTPUT); // Pin Clock
  digitalWrite(pinSS, HIGH); // de-select chip
  Serial.begin(115200);
  Serial.println("MLX90316 Rotary Position Sensor");
}

void loop() {
  angle = readAngle();
  Serial.println("");
  Serial.print("r1=");Serial.println(r1,HEX);
  Serial.print("r2=");Serial.println(r2,HEX);
  Serial.print("r3=");Serial.println(r3,HEX);
  Serial.print("r4=");Serial.println(r4,HEX);

  delay(10000);
}

int readAngle() {
  // Start with clock LOW
  digitalWrite(pinSCK, LOW);

  // Data pin in write mode
  pinMode(pinDATA, OUTPUT);

  // take the SS pin low to select the chip:
  digitalWrite(pinSS, LOW);
  delayMicroseconds(30);

  // Send START bytes
  shiftOut(pinDATA, pinSCK, MSBFIRST, 0xAA);
  delayMicroseconds(25);
  shiftOut(pinDATA, pinSCK, MSBFIRST, 0xFF);
  delayMicroseconds(30);  // 30 us between START bytes and DATA

  // Data pin in read mode
  pinMode(pinDATA, INPUT_PULLUP);

  // Receive data
  r1 = shiftIn(pinDATA, pinSCK, MSBFIRST);
  delayMicroseconds(25);
  r2 = shiftIn(pinDATA, pinSCK, MSBFIRST);
  delayMicroseconds(25);
  r3 = shiftIn(pinDATA, pinSCK, MSBFIRST);
  delayMicroseconds(30);
  r4 = shiftIn(pinDATA, pinSCK, MSBFIRST);

  // take the SS pin high to de-select the chip:
  delayMicroseconds(5);
  digitalWrite(pinSS, HIGH);
}

I expect the output of 0x00 0x22 0xFF 0xDD


Solution

  • I don't know which Arduino you are using but the shift in and out functions are very simple software implementations.

    uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
        uint8_t value = 0;
        uint8_t i;
    
        for (i = 0; i < 8; ++i) {
            digitalWrite(clockPin, HIGH);
            if (bitOrder == LSBFIRST)
                value |= digitalRead(dataPin) << i;
            else
                value |= digitalRead(dataPin) << (7 - i);
            digitalWrite(clockPin, LOW);
        }
        return value;
    }
    

    The MLX90316 allows only a clock speed of 145kHz, so this direct port access could be too fast and the data is not ready when you read it. But since you have a scope available you could simply check that timing and write your own shiftIn if necessary.

    I think this "version" should work for your chip (untested)

    uint8_t shiftIn2(uint8_t dataPin, uint8_t clockPin) {
        uint8_t value = 0;
        uint8_t i;
    
        for (i = 0; i < 8; ++i) {
            digitalWrite(clockPin, HIGH);
            delayMicroseconds(4);
            digitalWrite(clockPin, LOW);
            value |= digitalRead(dataPin) << (7 - i);
            delayMicroseconds(4);           
        }
        return value;
    }