Programowe SPI proszę o sprawdzeni

Wszystko o co chcesz zapytać na temat mikrokontrolerów ARM pozostałych firmy: problemy z pisaniem programu, problemy sprzętowe, niejasności w DS czy AN itp.
Regulamin forum
Tutaj, w temacie postu, należy na początku w nawiasach prostokątnych zaznaczyć jakiego ARM problem będzie dotyczył.
StaryAnoda

Programowe SPI proszę o sprawdzeni

Postautor: StaryAnoda » piątek 09 cze 2017, 20:30

Hej

Możecie sprawdzić czy ta obsługa SPI zarówno odbiór jak i nadawanie jest poprawne ?
Czy dobrze rozumiem według tego rysunku dane są odbierane i wysyłane od najbardziej znaczącego do najmniej ?
Oraz czy odpowiednio próbkuję dane na odpowiednim zboczu.
Test.png


Kod: Zaznacz cały

uint8_t SPI_Read_and_Write(uint8_t byte)
{
   uint8_t cnt = 0x80, Read_Byte = 0,i = 0;

   DIGITAL_IO_SetOutputLow(&CSB);
   DIGITAL_IO_SetOutputLow(&SCL);

   while(cnt)
   {
      if(byte & cnt)
      {
         DIGITAL_IO_SetOutputHigh(&SDA);
      }
      else
      {
         DIGITAL_IO_SetOutputLow(&SDA);
      }
      DIGITAL_IO_SetOutputHigh(&SCL);
      for(I = 100; I; I--)
      {
      }
      DIGITAL_IO_SetOutputLow(&SCL);
      cnt >>=1;
   }

   for (i = 0; i < 8; i++)
   {
      DIGITAL_IO_SetOutputHigh(&SCL);
      if(DIGITAL_IO_GetInput(&SDO))
      {
         Read_Byte |= 0x01 << i;
      }
      DIGITAL_IO_SetOutputLow(&SCL);
   }
   DIGITAL_IO_SetOutputHigh(&CSB);

   return Read_Byte;
}


Pytam ponieważ próbuję skomunikować się z czujnikiem ale z niewiadomych powodów nie mogę wolę sie upewnić.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1123
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: Programowe SPI proszę o sprawdzeni

Postautor: Antystatyczny » piątek 09 cze 2017, 21:01

Potencjalny problem widzę tu:

Kod: Zaznacz cały

Read_Byte |= 0x01 << i;


Do zmiennej Read_Byte wpisujesz tak naprawdę 0x02, a pewnie wolałbyś wpisać 0x01, a następnie całość przesunąć w lewo o jedno miejsce. Proponuję taki kod:

Kod: Zaznacz cały

Read_Byte |= 0x01;
Read_Byte <<= 1;
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

StaryAnoda

Re: Programowe SPI proszę o sprawdzeni

Postautor: StaryAnoda » piątek 09 cze 2017, 21:13

Ok dzięki dokonałem zmian:

Próbuję okiełznać ten czujnik:
https://cdn-shop.adafruit.com/datasheets/BST-BME280_DS001-10.pdf

Mój kod wygląda następująco:

Kod: Zaznacz cały

while(1U)
   {
      Send_String("BMP280 ID NUMBER: ");
      Send_Int(SPI_Read_And_Write(0xD0));
      Send_String("\r\n");
      for(I = 1000000; I; I--)
      {
      }
   }

uint8_t SPI_Read_And_Write(uint8_t Byte)
{
   uint8_t Cnt = 0x80, Read_Byte = 0, I = 0, A = 0;

   DIGITAL_IO_SetOutputLow(&CSB);
   DIGITAL_IO_SetOutputLow(&SCL);

   while(Cnt)
   {
      if(Byte & Cnt)
      {
         DIGITAL_IO_SetOutputHigh(&SDA);
      }
      else
      {
         DIGITAL_IO_SetOutputLow(&SDA);
      }
      DIGITAL_IO_SetOutputHigh(&SCL);
      for(I = 10; I; I--)
      {
      }
      DIGITAL_IO_SetOutputLow(&SCL);
      Cnt >>=1;
   }

   for (A = 0; A < 8; A++)
   {
      DIGITAL_IO_SetOutputHigh(&SCL);

      for(I = 10; I; I--)
      {
      }
      if(DIGITAL_IO_GetInput(&SDO))
      {
         Read_Byte |= 0x01;
         Read_Byte <<= 1;
      }
      DIGITAL_IO_SetOutputLow(&SCL);
   }
   DIGITAL_IO_SetOutputHigh(&CSB);

   return Read_Byte;
}


Próbuję odczytać ID i otrzymuje 6.
Skąd mam wiedzieć jaki jest rzeczywiście adres tego układu.
0x60 to adres po resecie.
Ostatnio zmieniony piątek 09 cze 2017, 21:15 przez StaryAnoda, łącznie zmieniany 1 raz.

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1123
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: Programowe SPI proszę o sprawdzeni

Postautor: Antystatyczny » piątek 09 cze 2017, 21:15

Wygląda na to, że czytasz odwrotnie. 0x60 to 0b0110 0000, a Ty odczytujesz 0x06, czyli 0b0000 0110. Dokładna odwrotność...
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Awatar użytkownika
xor
User
User
Posty: 159
Rejestracja: poniedziałek 05 wrz 2016, 21:44

Re: Programowe SPI proszę o sprawdzeni

Postautor: xor » piątek 09 cze 2017, 22:04

Próbuję odczytać ID i otrzymuje 6.
Skąd mam wiedzieć jaki jest rzeczywiście adres tego układu.
0x60 to adres po resecie.


To nie jest adres tylko identyfikator układu, czy raczej typu czujnika. Jest zawsze ten sam.

Ja mam uwagę, która możliwe, ale niekoniecznie odnosi się do uzyskanego wyniku.
Zdaje się, że próbujesz zastosować tryb "11", ale nie jestem pewien, czy go faktycznie ustawiasz (uwielbiam analizować fragmenty programu wyrwane z całości :roll: ). Być może za pierwszym obrotem pętli jest to ustawione, ale za drugim i kolejnych wydaje mi się możliwym, że nie, albowiem po wyjściu z funkcji rspi_ead_and_write pozostawiasz SCL jako LOW, a jak mówi pismo święte na stronie 32: "The automatic selection between mode ‘00’ and ‘11’ is determined by the value of SCK after the CSB falling edge." a na łobrasku na następnej stronie wyraźnie widać, że SCL po opadającym zboczu CSB w tym trybie jest HIGH.

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1123
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: Programowe SPI proszę o sprawdzeni

Postautor: Antystatyczny » piątek 09 cze 2017, 22:14

Przeanalizowałem diagram czasowy i wydaje mi się, że taka forma będzie poprawna:

Kod: Zaznacz cały

uint8_t SPI_Read_And_Write(uint8_t Byte)
{
   uint8_t Cnt = 0x80, Read_Byte = 0;

   DIGITAL_IO_SetOutputLow(&CSB);
   

   while(Cnt)
   {
      Read_Byte <<= 1;

      DIGITAL_IO_SetOutputLow(&SCL);
      if(Byte & Cnt)
      {
         DIGITAL_IO_SetOutputHigh(&SDA);
      }
      else
      {
         DIGITAL_IO_SetOutputLow(&SDA);
      }
      
      if(DIGITAL_IO_GetInput(&SDO))
      {
         Read_Byte |= 0x01;
      }      

      DIGITAL_IO_SetOutputHigh(&SCL);//slave reads bit at rising edge
      
            Cnt >>=1;
   }
   DIGITAL_IO_SetOutputHigh(&CSB);

   return Read_Byte;
}

"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

StaryAnoda

Re: Programowe SPI proszę o sprawdzeni

Postautor: StaryAnoda » sobota 10 cze 2017, 10:59

Ok pytań ciąg dalszy:

Chcę używać trybu CPOL = CPHA = '1'

Zgodnie z tym rysunkiem:

Test.png


Według tego rysunku próbkowanie jest ustawione na zboczę opadające.
xor przed pętlą główną ustawiłem SCK na HIGH.

Mój kod wygląda następująco:

Kod: Zaznacz cały

/*
 * main.c
 *
 *  Created on: 2017 Jun 09 12:18:56
 *  Author: StaryKatoda
 */




#include <DAVE.h>                 //Declarations from DAVE Code Generation (includes SFR declaration)
#include <stdio.h>

/**

 * @brief main() - Application entry point
 *
 * <b>Details of function</b><br>
 * This routine is the application entry point. It is invoked by the device startup code. It is responsible for
 * invoking the APP initialization dispatcher routine - DAVE_Init() and hosting the place-holder for user application
 * code.
 */

uint32_t I;

void Send_Char(char B);
void Send_String(char * String);
void Send_Int(int Value);

uint8_t SPI_Read_And_Write(uint8_t Byte);


int main(void)
{
   DAVE_STATUS_t status;

   status = DAVE_Init();           /* Initialization of DAVE APPs  */
   DIGITAL_IO_SetOutputHigh(&SCL);
   if(status != DAVE_STATUS_SUCCESS)
   {
      /* Placeholder for error handler code. The while loop below can be replaced with an user error handler. */
      XMC_DEBUG("DAVE APPs initialization failed\n");

      while(1U)
      {

      }

   }

   /* Placeholder for user application code. The while loop below can be replaced with user application code. */
   while(1U)
   {

      Send_String("BMP280 ID NUMBER: ");
      Send_Int(SPI_Read_And_Write(0xD0));
      Send_String("\r\n");
      Send_String("Temp_msb value: ");
      Send_Int(SPI_Read_And_Write(0xFA));
      Send_String("\r\n");



      for(I = 1000000; I; I--)
      {
      }
      DIGITAL_IO_SetOutputHigh(&LED_0);
      DIGITAL_IO_SetOutputLow(&LED_1);
      for(I = 1000000; I; I--)
      {
      }
      DIGITAL_IO_SetOutputLow(&LED_0);
      DIGITAL_IO_SetOutputHigh(&LED_1);
   }
}

void Send_Char(char B)
{
   UART_Transmit(&UART_0, &B, 1);
}


void Send_String(char * String)
{
   while(*String)Send_Char(*String++);
}

void Send_Int(int Value)
{
   char Bufor[50];
   sprintf(Bufor, "%i", Value);
   Send_String(Bufor);
}

uint8_t SPI_Read_And_Write(uint8_t Byte)
{
   uint8_t Cnt = 0x80, Read_Byte = 0, I = 0, A = 0;

   DIGITAL_IO_SetOutputLow(&CSB);

   while(Cnt)
   {
      DIGITAL_IO_SetOutputHigh(&SCL);
      if(Byte & Cnt)
      {
         DIGITAL_IO_SetOutputHigh(&SDA);
      }
      else
      {
         DIGITAL_IO_SetOutputLow(&SDA);
      }
      DIGITAL_IO_SetOutputLow(&SCL);
      Cnt >>=1;
   }

   for (A = 0; A < 8; A++)
   {
      DIGITAL_IO_SetOutputHigh(&SCL);
      if(DIGITAL_IO_GetInput(&SDO))
      {
         Read_Byte |= 0x01;
         Read_Byte <<= 1;
      }
      DIGITAL_IO_SetOutputLow(&SCL);
   }
   DIGITAL_IO_SetOutputHigh(&SCL);
   DIGITAL_IO_SetOutputHigh(&CSB);

   return Read_Byte;
}


Analizator stanów logicznych ustawiłem w taki sposób:

Test2.png


I tutaj mam pytanie dlaczego program ustawia przy próbkowaniu zbocze narastające a nie opadające
(Jest tu jakaś nieścisłość chyba. W nocie CPOL = CPHA = '1' próbkuję na opadającym)

Test3.png


I tak widać, że Nadawanie jest chyba w porządku od strony mikrokontrolera, odbieranie zawodzi co widać tutaj:

Test4.png
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Awatar użytkownika
xor
User
User
Posty: 159
Rejestracja: poniedziałek 05 wrz 2016, 21:44

Re: Programowe SPI proszę o sprawdzeni

Postautor: xor » sobota 10 cze 2017, 11:08

Proponuję wywalić sterowanie CSB poza funkcję SPI_Read_and_Write co umożliwi transmisje blokowe a jest to w trybie NORMAL nie tylko porządane ale wręcz niezbędne. Zresztą w transmisji "pojedynczej" mieszanie CSB miedzy adresem a zapisem/odczytem danej może powodować błędy (chyba).
To uwagi do funkcji Antystatycznego, bo to co ty robisz z tą funkcją IMHO kwalifikuje się do lekkiego naprostowania (ale to później bo teraz muszę wyjść)

StaryAnoda

Re: Programowe SPI proszę o sprawdzeni

Postautor: StaryAnoda » sobota 10 cze 2017, 13:36

Hejka

Xor nic to nie dało, dalej takie same objawy.
W sensie wyciągniecie pinu CSB poza funkcję.

Awatar użytkownika
xor
User
User
Posty: 159
Rejestracja: poniedziałek 05 wrz 2016, 21:44

Re: Programowe SPI proszę o sprawdzeni

Postautor: xor » sobota 10 cze 2017, 17:58

I tutaj mam pytanie dlaczego program ustawia przy próbkowaniu zbocze narastające a nie opadające
(Jest tu jakaś nieścisłość chyba. W nocie CPOL = CPHA = '1' próbkuję na opadającym)


W tym trybie ustawianie jest na "leading edge", próbkowanie na "trailing edge", ale zauważ, że w trybie nieaktywnym zegar jest HIGH, a więc "leading edge" to zbocze opadające, a "trailing" to rosnące.
Uwaga na marginesie: W drugim trybie tego czujnika "00" ustawianie jest na "trailing" próbkowanie na "leading", zegar w stanie idle jest LOW, a więc próbkowanie też będzie na zboczu rosnącym.

Wracając do programu: Niepotrzebnie gmerasz a funkcji spi_read_write. Funkcja ma za zadanie wysłać i jednocześnie odczytać jeden bajt. Prosta funkcja z jasno zdefiniowanym, łatwym do zrozumienia działaniem. Koniec, kropka. Jej kod wygląda dobrze, nawet analiza gołym okiem, bez testowania wskazuje, że prawdopodobnie będzie działać bezbłędnie. Dodawanie do niej dalszych funkcji jest niecelowe, komplikuje ją, naraża na trudne do wyłapania błędy.
Dalszy kod można konstruować na bazie tej funkcji, a mógłby wyglądać tak:

Kod: Zaznacz cały

#define ID_REG 0xD0

int main()
{
   uint8_t byte;

   ...
   
   //ustawienie trybu "11" (automagicznie przez czujnik)
   DIGITAL_IO_SetOutputHigh(&SCL);
   //aktywacja magistrali
   DIGITAL_IO_SetOutputLow(&CSB);
   //transmisja adresu rejestru
   (void)SPI_Read_And_Write(ID_REG);
   //odczytanie id z czujnika
   byte = SPI_Read_And_Write(0);
   //funkcja SPI_Read_And_Write pozostawia SCL w stanie HIGH więc następny wiersz niepotrzebny
   //DIGITAL_IO_SetOutputHigh(&SCL);
   //dezaktywacja magistrali
   DIGITAL_IO_SetOutputHigh(&CSB);
   
   //dalej wyświetlenie wartości byte
   ...
   
}

uint8_t SPI_Read_And_Write(uint8_t Byte)
{
   uint8_t Cnt = 0x80, Read_Byte = 0;


   while(Cnt)
   {
      Read_Byte <<= 1;

      DIGITAL_IO_SetOutputLow(&SCL);
      if(Byte & Cnt)
      {
         DIGITAL_IO_SetOutputHigh(&SDA);
      }
      else
      {
         DIGITAL_IO_SetOutputLow(&SDA);
      }
     
      if(DIGITAL_IO_GetInput(&SDO))
      {
         Read_Byte |= 0x01;
      }     

      DIGITAL_IO_SetOutputHigh(&SCL);//slave reads bit at rising edge
     
            Cnt >>=1;
   }

   return Read_Byte;
}


Pomijam tu konieczność ustawiania lub zerowania najstarszego bitu adresu rejestru, bo w tym przypadku, przypadkowo bit jest ustawiony jak należy.

StaryAnoda

Re: Programowe SPI proszę o sprawdzeni

Postautor: StaryAnoda » sobota 10 cze 2017, 21:14

Ok

Dzięki za pomoc, odebrałem poprawny identyfikator urządzenia.

StaryAnoda

Re: Programowe SPI proszę o sprawdzeni

Postautor: StaryAnoda » niedziela 11 cze 2017, 17:04

Dobra teraz chciałem się zapytać czy dobrze odbieram tą temperaturę:

Tutaj wykrywam podłączony czujnik:

Kod: Zaznacz cały

   DIGITAL_IO_SetOutputHigh(&SCL);
   DIGITAL_IO_SetOutputLow(&CSB);
   SPI_Read_And_Write(0xD0);

   if(SPI_Read_And_Write(0) == 0x60)
   {
      Send_String(" Znaleziono BME280!!! ");
   }
   else
   {
      Send_String(" Nie znaleziono BME280!!! ");
   }
   DIGITAL_IO_SetOutputHigh(&CSB);


Tutaj ustawiam w rejestrze ctrl_meas bity 7 i 5 (czyli oversampling *16) oraz bity 1 i 0 (czyli normal mode)

Kod: Zaznacz cały

   DIGITAL_IO_SetOutputHigh(&SCL);
   DIGITAL_IO_SetOutputLow(&CSB);
   SPI_Read_And_Write(0b01110100); // 0xF4
   SPI_Read_And_Write(0b10100011); // 0xA3
   DIGITAL_IO_SetOutputHigh(&CSB);


Tutaj odbieram dane Compensation parameter storage:
Zgodnie z tabelą:
Tsessss.png


Kod: Zaznacz cały

uint16_t dig_t1;
int16_t dig_t2;
int16_t dig_t3;

DIGITAL_IO_SetOutputHigh(&SCL);
      DIGITAL_IO_SetOutputLow(&CSB);
      SPI_Read_And_Write(0b10001001);
      dig_t1 = SPI_Read_And_Write(0);
      dig_t1 = dig_t1 << 8;
      DIGITAL_IO_SetOutputHigh(&CSB);

      DIGITAL_IO_SetOutputHigh(&SCL);
      DIGITAL_IO_SetOutputLow(&CSB);
      SPI_Read_And_Write(0b10001000);
      dig_t1 = dig_t1 |SPI_Read_And_Write(0);
      DIGITAL_IO_SetOutputHigh(&CSB);
      //
      DIGITAL_IO_SetOutputHigh(&SCL);
      DIGITAL_IO_SetOutputLow(&CSB);
      SPI_Read_And_Write(0b10001011);
      dig_t2 = SPI_Read_And_Write(0 );
      dig_t2 = dig_t2 << 8;
      DIGITAL_IO_SetOutputHigh(&CSB);

      DIGITAL_IO_SetOutputHigh(&SCL);
      DIGITAL_IO_SetOutputLow(&CSB);
      SPI_Read_And_Write(0b10001010);
      dig_t2 = dig_t2 |SPI_Read_And_Write(0);
      DIGITAL_IO_SetOutputHigh(&CSB);
      //
      DIGITAL_IO_SetOutputHigh(&SCL);
      DIGITAL_IO_SetOutputLow(&CSB);
      SPI_Read_And_Write(0b10001101);
      dig_t3 = SPI_Read_And_Write(0 );
      dig_t3 = dig_t3 << 8;
      DIGITAL_IO_SetOutputHigh(&CSB);

      DIGITAL_IO_SetOutputHigh(&SCL);
      DIGITAL_IO_SetOutputLow(&CSB);
      SPI_Read_And_Write(0b10001100);
      dig_t3 = dig_t3 |SPI_Read_And_Write(0);
      DIGITAL_IO_SetOutputHigh(&CSB);


Tutaj odbieram 3 bajty zawierające temperaturę :
memory.png


Kod: Zaznacz cały

      uint8_t MSB, LSB,XLSB;
      // Read MSB
      DIGITAL_IO_SetOutputHigh(&SCL);
      DIGITAL_IO_SetOutputLow(&CSB);
      SPI_Read_And_Write(0xFA);
      for(I = 1000; I; I--)
      {
      }
      MSB = SPI_Read_And_Write(0);
      DIGITAL_IO_SetOutputHigh(&CSB);

      // Read LSB
      DIGITAL_IO_SetOutputHigh(&SCL);
      DIGITAL_IO_SetOutputLow(&CSB);
      SPI_Read_And_Write(0xFB);
      for(I = 1000; I; I--)
      {
      }
      LSB = SPI_Read_And_Write(0);
      DIGITAL_IO_SetOutputHigh(&CSB);

      // Read XLSB
      DIGITAL_IO_SetOutputHigh(&SCL);
      DIGITAL_IO_SetOutputLow(&CSB);

      SPI_Read_And_Write(0xFC);
      for(I = 1000; I; I--)
      {
      }
      XLSB = SPI_Read_And_Write(0);
      DIGITAL_IO_SetOutputHigh(&CSB);


Tutaj mam pytanie odnośnie konwersji tych trzech bajtów do jednej zmiennej

Kod: Zaznacz cały

int32_t Temp;
Temp = (int32_t)( ( ( (int32_t) MSB <<16 ) | ( (int32_t)LSB <<8 ) | ((int32_t) XLSB ) ) >> 4 );


Czy to przesunięci w prawo o 4 jest potrzebne czy nie ?

Tutaj funkcja zaczerpnięta z noty katalogowej:

ffffffffffffffffffff.png


Kod: Zaznacz cały

int32_t t_fine;
int32_t bmp280_compensate_T_int32(int32_t  adc_T)
{
   int32_t var1, var2, T;
   var1  = ((((adc_T>>3) - ((int32_t)dig_t1 <<1))) * ((int32_t)dig_t2)) >> 11;

   var2  = (((((adc_T>>4) - ((int32_t)dig_t1)) *
         ((adc_T>>4) - ((int32_t)dig_t1))) >> 12) *
         ((int32_t)dig_t3)) >> 14;
   t_fine = var1 + var2;
   T = (t_fine * 5 + 128) >> 8;
   return T;
}


Tutaj wynik całego programu:

Czy macie jakieś uwagi ?
Czy dobrze interpretuję że temperatura wynosi 25.26 St C.

Terminal.png
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Awatar użytkownika
xor
User
User
Posty: 159
Rejestracja: poniedziałek 05 wrz 2016, 21:44

Re: Programowe SPI proszę o sprawdzeni

Postautor: xor » niedziela 11 cze 2017, 18:39

Mam cztery uwagi:
1. Te bloki powinny być zamknięte w funkcjach (nie wiem, może są ale tego nie widać w listingach)
2. Nie stosuj tzw. magic numbers. Zamiast

Kod: Zaznacz cały

   SPI_Read_And_Write(0b01110100); // 0xF4
   SPI_Read_And_Write(0b10100011); // 0xA3

wpisz

Kod: Zaznacz cały

   //gdzieś w pliku .h albo w .c w bloku różnych definicji   
   #define CTRL_MEAS_REG   0xF4
   #define TEMP_OVS_16   ((0b101)<<5)
   #define NORMAL_MODE   (0b11)
   
   //.....
   
   SPI_Read_And_Write(CTRL_MEAS_REG);
   SPI_Read_And_Write(TEMP_OVS_16 + NORMAL_MODE);


Oczywiście przy SPI trzeba pamiętać o bicie R/W (Ty to zrobiłeś ręczno nożnie), proponuję utworzyć odpowiednią funkcję (albo makro) żeby nie zaprzątać sobie głowy czy wyzerować czy ustawić bit. Wtedy pierwsze wywołanie SPI_Read_And_Write może wyglądać np. tak:

Kod: Zaznacz cały

SPI_Read_And_Write(spi_write(CTRL_MEAS_REG));


a funkcje np. tak

Kod: Zaznacz cały

   uint8_t spi_write(uint8_t address)
   {
      return address & 0x7f;
   }

   uint8_t spi_read(uint8_t address)
   {
      return address | 0x80;
   }


3. Źle odczytujesz rejestry danych. W trybie NORMAL trzeba je odczytywać jako blok, nie po jednym. Odczytywanie po jednym rejestrze może prowadzić do błędów, jeżeli konwersja zakończy się pomiędzy odczytami (a w trybie NORMAL konwersja wykonuje się automatycznie co ustalony czas). Wtedy z części rejestrów ma się dane z poprzedniej konwersji a z części z bieżącej. Przy odczycie blokowym jest zagwarantowana spójność danych. Jest to opisane w datasheecie.
Dane korekcyjne też odczytujesz po jednym, to nie jest błąd bo dane są niezmienne, ale to niepotrzebnie spowalnia i komplikuje program -> też można odczytać blokiem.
A więc potrzebujesz funkcji blokowego odczytu danych (blokowy zapis też mógłby się przydać, ale to nie jest krytyczne) np. takiej:

Kod: Zaznacz cały

#define DUMMY 0

void spi_read_block(uint8_t *buffer, uint8_t address, uint8_t len)
{
   DIGITAL_IO_SetOutputHigh(&SCL);
   DIGITAL_IO_SetOutputLow(&CSB);
   SPI_Read_And_Write(spi_read(address));
   while(len--)
      *buffer++ = SPI_Read_And_Write(DUMMY);
   DIGITAL_IO_SetOutputHigh(&CSB);
}


wywołanie

Kod: Zaznacz cały

   #define TEMP_REG   0xFA
   #define TEMP_DATA_LEN   3
   uint8_t buffer[TEMP_DATA_LEN];   //po wyjściu z funkcji zawiera kolejno temp_msb, temp_lsb, temp_xlsb

   spi_read_block(buffer, TEMP_REG, TEMP_DATA_LEN);   //odczyt nieskompensowanej temperatury



Co do prawidłowości wyliczeń się nie wypowiadam, bo po to bym nie musiał sobie tym zaprzątać głowy producent czujnika przygotował odpowiedni driver ;-)

StaryAnoda

Re: Programowe SPI proszę o sprawdzeni

Postautor: StaryAnoda » niedziela 11 cze 2017, 22:34

Wielkie dzięki kolego

Jutro naniosę poprawki.

Awatar użytkownika
xor
User
User
Posty: 159
Rejestracja: poniedziałek 05 wrz 2016, 21:44

Re: Programowe SPI proszę o sprawdzeni

Postautor: xor » poniedziałek 12 cze 2017, 08:21

Tutaj mam pytanie odnośnie konwersji tych trzech bajtów do jednej zmiennej

Kod: Zaznacz cały

int32_t Temp;
Temp = (int32_t)( ( ( (int32_t) MSB <<16 ) | ( (int32_t)LSB <<8 ) | ((int32_t) XLSB ) ) >> 4 );



Czy to przesunięci w prawo o 4 jest potrzebne czy nie ?


Tak, jest potrzebne albowiem w XLSB wartość jest przechowywana w 4 starszych bitach, na 4 młodszych bitach jest zawsze zero.
Coś mi jednak nie pasowało w powyższym zapisie więc zajrzałem do źródeł drivera i tam przesunięcia są trochę inne:
MSB<<12
LSB<<4
XLSB>>4
W sumie daje to liczbę 20bitową, tak jak piszą przy okazji funkcji kompensującej temperaturę.

Awatar użytkownika
xor
User
User
Posty: 159
Rejestracja: poniedziałek 05 wrz 2016, 21:44

Re: Programowe SPI proszę o sprawdzeni

Postautor: xor » poniedziałek 12 cze 2017, 09:04

No to jeszcze:
Obrazek z rozmieszczeniem rejestrów jest (chyba) od czujnika BMP, wydawało mi się, że mówimy o BME, jak to w końcu jest?

Diabelnie źle czyta się kod wczytujący dane kalibracyjne. Trudno dojrzeć czy nie ma tam buga. Proponuję to uczytelnić - najpierw odczyt blokowy, potem przepisanie do globalnej struktury (albo do niezależnych zmiennych ale wtedy funkcja musiała by odwoływać się do nich nie przez parametr lecz "bezpośrednio" - tego unikamy jak ognia). Całość w odrębnej funkcji.

Kod: Zaznacz cały

struct coefficients {
   uint_16_t dig_T1;
   int16_t dig_T2;
   int16_t dig_T3;
   /dalej współczynniki dla ciśnienia i wilgotnośći
...
} coeff;

...

void read_calib_data( struct coefficients *coef)
{
   uint8_t buffer[CALIB_DATA_LEN];
   
   spi_read_block(buffer, CALIB_REG, CALIB_DATA_LEN);
   
   coef->dig_T1 = buffer[0] | buffer[1]<<8;
   // ... itd ...
}

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1123
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: Programowe SPI proszę o sprawdzeni

Postautor: Antystatyczny » poniedziałek 12 cze 2017, 09:13

Jakie są przesłanki ku temu, by unikać jak ognia wczytywania pojedynczych zmiennych? Całość i tak jest zawarta w osobnym module, a zmienne można schować korzystając ze "static". Zmienne będą o zasięgu modułu, a funkcja/funkcje korzystające z tych danych będą miały bezpośredni dostęp. Oczywiście rozumiem, że chodzi tu o dane kalibracyjne, które odczytuje się tylko raz.
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Awatar użytkownika
xor
User
User
Posty: 159
Rejestracja: poniedziałek 05 wrz 2016, 21:44

Re: Programowe SPI proszę o sprawdzeni

Postautor: xor » poniedziałek 12 cze 2017, 09:18

Nie ma podstaw do założenia że jest to w odrębnym module. Na razie wygląda to na jeden wielki main.
W przypadku modułu oczywiście jest tak jak mówisz.
Zresztą, nawet w module lepiej to wygląda jak jest przekazywane przez parametr. IMHO.
...ale generalnie zgadzam się z tym co jest napisane w następnych dwóch postach, 0144% zgody :-)
Ostatnio zmieniony poniedziałek 12 cze 2017, 10:00 przez xor, łącznie zmieniany 3 razy.

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1123
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: Programowe SPI proszę o sprawdzeni

Postautor: Antystatyczny » poniedziałek 12 cze 2017, 09:21

Sądzę, że wszystko jest w main jedynie na czas testów, a potem tradycyjnie trafi do osobnego modułu. Prawdę mówiąc nawet nie spojrzałem na nazwę pliku, bo skupiłem się na samych funkcjach. Oczywiście masz rację, że w obecnej formie lepiej by to wyglądało w strukturze.
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1123
Rejestracja: czwartek 03 wrz 2015, 22:02

Re: Programowe SPI proszę o sprawdzeni

Postautor: Antystatyczny » poniedziałek 12 cze 2017, 09:32

Przekazywanie wskaźnika na strukturę z danymi kalibracyjnymi do funkcji, gdy te dane są jedynymi i nigdy nie ulegają podmianie, jest w mojej ocenie zbędnym obciążaniem rejestrów/stosu. Jak już wspomniałem wcześniej, zmienne są ukryte przed światem zewnętrznym i nie ma powodu, by dodatkowo tworzyć wskaźnik na strukturę w wywołaniu funkcji wykorzystującej dane kalibracyjne. Oczywiście dane mogą być w strukturze, to ładnie wygląda i poprawia czytelność, ale do pól można się odwoływać bezpośrednio.
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

StaryAnoda

Re: Programowe SPI proszę o sprawdzeni

Postautor: StaryAnoda » poniedziałek 12 cze 2017, 22:04

Ok dzięki za pomoc koledzy :)

Musi mi się to wszystko przetrawić ;)
Na razie zajmuję się uporządkowaniem biblioteki, podziałem na poszczególne moduły. Za niedługo uruchomię pomiar ciśnienia i wilgotności.
Xor masz rację zrzut ekranu jest z innego czujnika nie wiem jakim prawem miałem go otwartego w przeglądarce. Cały czas mówimy o BME280.


Wróć do „ARM innych firm”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 1 gość