Registro de lectura de 3 hilos SPI del software

0

Actualmente intento leer un registro de un sensor de aceleración (ADXL345). Intento simular el procedimiento SPI con software. Escribí una función readRegister :

uint8_t readRegister(uint8_t reg) {
    uint8_t i, k;
    uint8_t res = 0; 
    GPIOD->ODR &= ~(1<<CS);
    delay_ms(2); 

    // write register address
    GPIOD->DDR |= (1<<SDIO);            // SDIO as output
    for(i=0; i<8; i++) {       
            GPIOD->ODR &= ~(1<<SCLK);      
            if(reg & (1<<7)) {            
                    GPIOD->ODR |= (1<<SDIO);    
            } else {                        
                    GPIOD->ODR &= ~(1<<SDIO); 
            }
            delay_ms(1);                   
            GPIOD->ODR |= (1<<SCLK);       
            delay_ms(1);                   
            reg <<= 1;                      
    }

    // read result
    GPIOD->DDR &= ~(1<<SDIO);           // set SDIO as input
    for(k=0; k<8; k++) {        // loop over data bits
            res <<= 1;                      
            GPIOD->ODR &= ~(1<<SCLK);       
            delay_ms(1);        
            GPIOD->ODR |= (1<<SCLK);   
            if(GPIOD->IDR & (1<<SDIO)) {  
                    res |= 1;                 
            }                        
            delay_ms(1); 
    }

    GPIOD->ODR |= (1<<CS);             
    return res; 
}

La función devuelve 227 (así que 11100011 ) en lugar de 119 ( 01110111 ) a pesar de que le escribí 119 con este código:

void enableADXL345(){
    uint8_t write;
    uint8_t addr;
    uint8_t data;
    uint8_t result;
    uint16_t write_command;
    uint8_t read_command;

    addr = 0x31;
    data = 0x04;
    write_command = (write | addr) << 8 | data; //set SPI 3-wire
    writeCommand(write_command);

    addr = 0x27;
    data = 0x77;
    write_command = (write | addr) << 8 | data; //write something
    writeCommand(write_command);

    read_command = (0b10000000 | addr);
    result = readRegister(read_command); //read something
}

Esta es mi función write :

void writeCommand(uint16_t cmd){
    uint16_t i;

    GPIOD->ODR &= ~(1<<CS);
    delay_ms(2);
    GPIOD->DDR |= (1<<SDIO);  //SDIO as output
    for(i = 0; i < 16; i++){ //start to transfer the register we want to read
        GPIOD->ODR &= ~(1<<SCLK);
        if(cmd & (1<<7)){
            GPIOD->ODR |= (1<<SDIO);
        } else {
            GPIOD->ODR &= ~(1<<SDIO);
        }
        GPIOD->ODR |= (1<<SCLK);
        delay_ms(1);
        cmd <<= 1;
    }
    GPIOD->ODR |= (1<<CS);
}

Este es el SPI-lectura / escritura como se describe en la hoja de datos:

    
pregunta binaryBigInt

1 respuesta

1
  • Como te han dicho en los comentarios, debes manejar la línea del reloj cuando estás leyendo desde el sensor.
  • También debe leer los datos de GPIOD->IDR en lugar de GPIOD->DDR .
  • Este sensor utiliza el modo SPI 3 y no está implementando el protocolo correctamente. La línea SCLK debe estar inactiva ALTA cuando termine de escribir, como esto:

    GPIOD->ODR &= ~(1<<CS);
    delay_ms(2);
    for(i = 0; i < 8; i++){ //start to transfer the register we want to read
      GPIOD->ODR &= ~(1<<SCLK);   // setup data
      if(reg & 128){
          GPIOD->ODR |= (1<<SDIO);
      } else {
        GPIOD->ODR &= ~(1<<SDIO);
      }
      GPIOD->ODR |= (1<<SCLK);  // now capture data
      delay_ms(1);
      reg <<= 1;
    }
    
  • Finalmente, obtienes 0x00 porque estás verificando el estado del bit DDR. Dado que el bit es 0, porque usted lo borró justo antes del bucle while, esta extraña declaración resData = (0<<0); siempre se ejecuta y la función devuelve 0. Debe leer del sensor de esta manera:

    resData = 0;
    GPIOD->DDR &= ~(1<<SDIO); //set SDIO as Input
    while(k > 0){ //read 8 bit
        delay_ms(1);
        GPIOD->ODR &= ~(1<<SCLK);  // allow the sensor to setup data
        delay_ms(1);
        GPIOD->ODR |= (1<<SCLK);  // now capture data
        resData <<= 1;
        resData |= GPIOD->IDR & (1 << SDIO) ? 1 : 0;
        k--;
    }
    return resData;
    
  • No está escribiendo correctamente en el módulo y también tiene un problema de conversión en su código principal. Mira los cambios que he hecho:

    void writeCommand(uint16_t cmd){
      uint8_t i; 
      GPIOD->ODR &= ~(1<<CS); 
      delay_ms(2); 
      GPIOD->DDR |= (1<<SDIO); //SDIO as output 
      for(i = 0; i < 16; i++){ //start to transfer the register we want to read 
        GPIOD->ODR &= ~(1<<SCLK); 
        if(cmd & (1<<15)){ 
          GPIOD->ODR |= (1<<SDIO); 
        } else { 
          GPIOD->ODR &= ~(1<<SDIO); 
        }
        delay_ms(1);
        GPIOD->ODR |= (1<<SCLK); 
        delay_ms(1); 
        cmd <<= 1; 
      } 
    
      GPIOD->ODR |= (1<<CS); 
    }
    

Debes lanzar de esta manera:

    write = 0;  // no real need for ORing though
    write_command = ((write | (uint16_t)addr) << 8) | data; 
    
respondido por el TisteAndii

Lea otras preguntas en las etiquetas