STM32 i SPI

Tu możesz pisać o swoich problemach z pisaniem programów w języku C/C++ dla STM.
Awatar użytkownika
Marcin
User
User
Posty: 309
Rejestracja: środa 09 wrz 2015, 19:30
Lokalizacja: Królewskie miasto Sandomierz

STM32 i SPI

Postautor: Marcin » czwartek 16 kwie 2020, 13:07

Hej

Miałem napisać coś o wyświetlaczu LCD, jednak ten z którym ostatnio walczyłem komunikował się z mikrokontorlerem z użyciem interfejsu SPI. Postanowiłem więc przed opisem LCD trochę czasu poświęcić na SPI. Interfejs ten jest dość szeroko stosowany, często opisywany, więc zbytnio nie będę poświęcał uwagi na jego opis. Po krótkim ogólnym wstępie opisze SPI w STM32. W opisie samego STM zagłębię się w rejestry, będzie to przydatne do zrozumienia kodu, który będzie w przykładach obsługi SPI. Oczywiście użytkownik w czasie swojej pracy nie musi tak nisko schodzić w struktury rejestrów, może zdać się na to co mu oprogramowanie CubeMx wygeneruje, a lwią część wykona za niego. Do zrozumienia samego SPI warto jednak zajrzeć pod maskę i popatrzeć co się tam dzieje.


Ogólnie o SPI
SPI (Serial Peripheral Interface) to szeregowy interfejs komunikacyjny pozwalający na wymianę danych pomiędzy dwoma lub więcej układami. O czym warto nadmienić, jest to interfejs synchroniczny, więc do przesyłania danych potrzebna jest synchronizacja układów na magistrali sygnałem zegarowym, może pracować także jako full duplex, czyli w jednym momencie możliwe jest zarówno nadawanie jak i odbiór danych.

Schemat połączeń układów na magistrali SPI przedstawiony jest poniżej.
SPI_Schemat.PNG

Magistrala SPI wymaga jednego układu nadrzędnego (Master) zarządzającego magistralą. Pozostałe układy to układy podrzędne (Slave)

W skład SPI wchodzą trzy podstawowe linie sygnałowe:

MOSI – Master Output Slave Input, linia którą master, czyli układ nadrzędny wysyła dane do układów podrzędnych (slave). Układy podrzędne odbierają dane przychodzące na tej linii.

MISO – Master Input Slave Output, linia którą układy slave, czyli układy podrzędne wysyłają dane do układu nadrzędnego. Układ nadrzędny na tej linii odbiera dane z układów podrzędnych.

CLK – sygnał zegarowy, sygnał generowany przez master służący do synchronizacji układów na magistrali.

W SPI nie ma potrzeby krzyżowania linii nadawczej i odbiorczej jak w przypadku UART, gdzie linia TX jednego układu jest łączona z linią RX drugiego układu. Zgodnie z zasadą działania w interfejsie SPI linia nadawcza dla mastera jest linią odbiorczą dla slave i odwrotnie linia nadawcza dla slave jest linią odbiorczą dla mastera.

W magistrali SPI jest jeszcze czwarta linia, oznaczona jako SS (Slave Select) lub CS (Chip Select) Służy ona do aktywacji interfejsu komunikacyjnego w konkretnym układzie podrzędnym, do którego master ma wysłać dane. Linia ta jest aktywna w stanie niskim. Oznacza to, że normalnym stanem na liniach SS jest stan wysoki, układ nadrzędny przed rozpoczęciem transmisji wybiera konkretny układ podrzędny opuszczając stan (logiczne 0) na określonym pinie podpiętym do wybranej linii SS połączonej z żądanym układem. Po zakończeniu transmisji master na linii SS ponownie wystawia stan wysoki "odpinając” wcześniej wybrany układ podrzędny od interfejsu.


Transmisja
SPI jak na początku wspomniałem to interfejs szeregowy, więc transmitowany jest kolejno bit po bicie. Układ nadrzędny opuszczając linię SS aktywuje interfejs SPI w układzie podrzędnym, następnie zaczyna transmisję do układu podrzędnego przesyłając bit po bicie linią MOSI, w czasie transmisji slave może odesłać swoje dane wystawiając je na linię MISO. Do synchronizacji transmisji pomiędzy układami służy sygnał zegarowy generowany przez układ nadrzędny. Po zakończeniu transmisji master podnosi linię SS odpinając układ podrzędny od interfejsu. Poniżej przykładowa ramka danych.
SPI_Ramka.PNG

Implementacja interfejsu SPI nie narzuca kolejności przesyłania danych. SPI pozwala na konfigurację kolejności przesyłanych bitów, od najstarszego do najmłodszego bitu lub odwrotnie. Nie precyzuje też sygnału zegarowego, pozwala określić polaryzacje sygnału zegarowego jak i wybór zbocza (narastającego lub opadającego) na których będą próbkowane dane na liniach wejściowych (MOSI oraz MISO) Kolejność przesyłanych bitów oraz określenie parametrów sygnału zegarowego należy dobrać zgodnie ze specyfikacją układów które będą podpięte do magistrali.

Myślę że tyle wystarczy wstępu o SPI, przejdźmy teraz do implementacji SPI w STM32.


SPI w STM32
Po krótkim opisie przyszedł czas na przedstawienie SPI jaki znajdziemy w STM32, pod ręką mam STM32G071KB, jednak w innych STMach SPI będzie bardzo podobne. SPI które jest w STMach może pracować w czterech trybach, full-duplex master, half-duplex master, full-duplex slave oraz half-duplex slave. STM32G071KB zawiera dwa interfejsy SPI.

Na wstępie spójrzmy na schemat blokowy, który będzie nam pomocny do zrozumienia dalszego opisu SPI. Nie jest to skomplikowany układ, znajdziemy w nim kontroler kontrolujący pracę SPI, generator sygnału zegarowego oraz część nadawczą i odbiorczą w skład której wchodzą bufory oraz rejestry przesuwne połączone z nadajnikami.
SPI_BockDiagram.PNG



Tryb pracy
Na początek określmy tryb pracy naszego mikrokontrolera. Masz mikrokontroler może być układem nadrzędnym (master) inicjującym komunikację i zarządzającym komunikacją na magistrali, lub rzadziej układem podrzędnym (slave) Za wybór trybu master lub slave odpowiada bit MSTR znajdujący się w rejestrze SPIx_CR1.

Konfiguracja pozwala pracę w różnych trybach, ja jednak skupię się na najprostszej konfiguracji najczęściej spotykanej i używanej, nie będę się zagłębiał w liczenie CRC, tryby half-duplex czy multi-master, w którym na jednej magistrali może być więcej niż jeden układ nadrzędny.


Sygnał zegarowy
Sygnał zegarowy (SCK) służący do synchronizacji komunikacji między układami master oraz slave, generowany jest przez układ master. Pochodzi on z bloku “baud rate generator”, jest to dzielnik częstotliwości, który dzieli częstotliwość dostarczaną przez fPCLK, dla omawianego mikrokontrolera (STM32G071KB) maksymalna częstotliwość z jaką mogą być transmitowane dane to fPCLK/2, a więc 32Mb/s. Jest to dość duża wartość, nie wszystkie układy na magistrali SPI mogą z tak dużą prędkością pracować, a czasem konstrukcja urządzania uniemożliwia transfer danych z taką prędkością. Dzielnik częstotliwości pozwala na wybór częstotliwości zegara taktującego magistralę, do wyboru mamy podział fPCLK przez wartości 2, 4, 8, 16, 32, 128, 256. Wybór dzielnika dokonujemy w SPI control register 1 (SPIx_CR1, bity BR[2:0]: Baud rate control)

SPI pozwala nam na określenie polaryzacji sygnału zegarowego oraz wybór zbocza zegara (narastającego lub opadającego) na którym będą odczytywane transmitowane bity. Konfiguracja ta związana jest ze specyfikacją układów jakie współpracują na magistrali SPI. Za wybór polaryzacji sygnału zegarowego odpowiada bit CPOL, za wybór zbocza odpowiada bit CPHA, oba bity znajdują się w rejestrze SPIx_CR1. Samą zasadę wyboru polaryzacji zegara i zbocza najlepiej pokazuje dokumentacja od ST
SPI_CLOCK.PNG



Format I długość ramki
SPI pozwala określić kolejność transmisji: od najstarszego do najmłodszego bitu lub odwrotnie (MSB -> LSB lub LSB -> MSB) jak i długość ramki. Do wyboru mamy długość ramki od 4 do 16 bitów. Kolejność wysyłania bitów określmy bitem LSBFIRST w rejestrze konfiguracyjnym SPIx_CR1, natomiast długość ramki określają bity DS[3:0] w rejestrze konfiguracyjnym 2 (SPIx_CR2 bity DS[3:0]: Data size)


Sterowanie pinem SS
Aby aktywować interfejs w układzie podrzędnym master na czas transmisji musi ustawić na linii SS logiczne zero. Możemy to zrobić programowo, na określony pin, który służy jako linia SS ustawić logiczne zero. Możemy też polegać na sprzętowym zarządzaniu sygnałem SS, wówczas w czasie transmisji SPI samo zajmie się zarządzaniem pinem SS (w dokumentacji oznaczony jako NSS) konfiguracja zarzadzaniem sygnałem SS (w dokumentacji ST NSS) jest w rejestrze kontrolnym 1 (SPIx_CR1, bit SSM)


Transmisja danych
Przyjrzyjmy się teraz części nadawczo odbiorczej, jak pokazuje schemat blokowy składa się on dwóch bliźniaczych par w skład których wchodzi bufor 4x8bit tworzący kolejkę FIFO oraz rejestr przesuwny. Jedna para to część nadawcza, druga jest częścią odbiorczą. Obie te części nie są dostępne bezpośrednio dla użytkownika, użytkownik / programista ma do dyspozycji tylko rejestr DR, do którego zapisuje dane do wysłania jak i z którego odczytuje odebrane dane. SPI po załadowaniu przez użytkownika rejestru DR nową wartością przerzuca ją w tle do bufora nadawczego TXFIFO, który kolejkuje dane do wysłania. Dane w buforze oczekują na gotowość rejestru przesuwnego na przyjęcie kolejnego bajta danych do finalnej wysyłki na magistralę SPI. Zgodnie z zasadą kolejki FIFO do rejestru przesuwnego trafiają dane w kolejności jakiej były ładowane do bufora, a więc również w kolejności jakiej były zapisywane do rejestru DR. Po zwolnieniu rejestru przesuwnego w części nadawczej pobierany jest z kolejki TXFIFO kolejny bajt danych, który bit po bicie jest wystawiany na magistralę SPI. Dane odczytywane przez SPI również są kolejkowane w buforze odbiorczym RXFIFO, odczyt rejestru DR zwraca najstarszą wartość z kolejki bufora odbiorczego.

Statusy buforów są sygnalizowane poprzez flagi w rejestrze "SPI status register", flaga dla bufora nadawczego (TXE) jest ustawiania w momencie, kiedy poziom załadowania bufora jest mniejszy lub równy połowie swojej pojemności. W przeciwnym razie flaga jest kasowana a bufor uważany jest za pełny. Bufor odbiorczy ma również swoja flagę (RXNE) jednak próg, przy którym flaga jest ustawiana jest konfigurowalny, w konfiguracji SPI możemy zdecydować czy flaga ma być ustawiana kiedy zapełnienie bufora odbiorczego osiągnie ¼ jego pojemności (8bitów) czy ½ pojemności (16bitów) To jest ważne ze względu na długość ramki, jaką wysyłamy przez SPI. Domyślnie flaga RXNE dla bufora odbiorczego jest ustawiania po osiągnięciu połowy swojej pojemności, co przy ramce 8 bitów będzie problematyczne, o czym wspomnę pokazując przykładowe kody.

Tu jedna mała uwaga odnośnie bufora nadawczego, z reguły wysyłamy przez SPI słowa o długości 8 bitów, bufor nadawczy ma całkowitą pojemność 32 bity, zapisaliśmy 3 bajty, więc jest jeszcze miejsce na jeden bajt danych. Dlaczego flaga TXE już informuje, że bufor jest pełny? Tu przypomnę, że długość ramki wysyłanej przez SPI jest konfigurowalna i może sięgać 16bitów, czyli połowę wielkości bufora, a nie ma możliwości konfiguracji progu zgłaszania zapełnienia bufora, jak przypadku bufora odbiorczego. A więc załadowanie dwóch słów 16bitowych zapełnia bufor w 100%.

W drugiej części zaprezentuję od strony praktycznej konfigurację i wykorzystanie SPI.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Awatar użytkownika
Marcin
User
User
Posty: 309
Rejestracja: środa 09 wrz 2015, 19:30
Lokalizacja: Królewskie miasto Sandomierz

Re: STM32 i SPI

Postautor: Marcin » piątek 08 maja 2020, 22:54

A więc czas na praktyczny przykład użycia SPI. W projekcie użyję układu ADS1256, komunikującego się z mikrokontrolerem przez magistralę SPI. Mikrokontroler (STM32G071KBT6) jest w naszym projekcie masterem, a więc układem nadrzędnym, to on inicjuje komunikację i zarządza magistralą. ADS1256 jest układem podrzędnym. Mikrokontroler komunikuje się z układem ADS przez interfejs SPI2.

Schemat przykładowego układu
SCHEMATIC.PNG


ADS1256

ADS1256.PNG


Na początku trochę informacji o ADS1256 i komunikacji z nim. ADS1256 to 8 kanałowy, 24 bitowy przetwornik analogowo-cyfrowy produkowany przez Texas Instruments. To co dla nas istotne, układ posiada 8 multipleksowanych wejść oraz interfejs SPI do komunikacji z otoczeniem. Opis konfiguracji układu pominę, ponieważ to jest nieistotne w tym artykule, skupmy się natomiast na obsłudze układu, a w zasadzie na komunikacji przez SPI. Po załadowaniu konfiguracji ADC zostaje wprowadzony w tryb STANDBY. Pomiar wyzwalany jest programowo w przerwaniu od TIM17 poprzez wybudzenie przetwornika z trybu STANDBY. Wybudzenie może być poprzedzone zmianą konfiguracji kanałów pomiarowych. ADC sygnalizuje zakończenie pomiaru i gotowość do odczytu wyniku konwersji ustawieniem stanu niskiego na pinie DRDY. Pin DRDY połączony jest z linią EXTI, dzięki czemu w przerwaniu na zboczu opadającym możemy przechwycić moment zakończenia pomiaru i odczytać wynik. Odczyt kończymy ponownym wprowadzaniem ADC w tryb STANDBY.

SPI
Na początku konfiguracja GPIO, włączamy zegar dla GPIOA, pinom odpowiadającym liniom MISO, MOSI oraz CLK przypisujemy funkcje alternatywne. Jako że programowo będziemy zarządzać linią CS pin CS konfigurujemy jako wyjście.

Kolejny etap to konfiguracja i włączenie SPI, włączamy zegar dla SPI2, określamy rozmiar ramki danych, włączamy ustawianie flagi RX po odebraniu 8 bitów, ustawiamy dzielnik zegara dla SPI 32, tryb master, programowe zarządzanie pinem CS, bit CPHA zgodnie z wymaganiami układu ADS1256. Oraz włączamy interfejs SPI.

Kod: Zaznacz cały

// Configure GPIO

RCC->IOPENR |=RCC_IOPENR_GPIOAEN;

LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_0, LL_GPIO_MODE_ALTERNATE);                    //SCK
LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_0, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_0, LL_GPIO_PULL_NO);
LL_GPIO_SetAFPin_0_7(GPIOA, LL_GPIO_PIN_0, LL_GPIO_AF_0);
     

LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_3, LL_GPIO_MODE_ALTERNATE);                    //MISO
LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_3, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_3, LL_GPIO_PULL_NO);
LL_GPIO_SetAFPin_0_7(GPIOA, LL_GPIO_PIN_3, LL_GPIO_AF_0);
     

LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_4, LL_GPIO_MODE_ALTERNATE);                    //MOSI
LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_4, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_4, LL_GPIO_PULL_NO);
LL_GPIO_SetAFPin_0_7(GPIOA, LL_GPIO_PIN_4, LL_GPIO_AF_1);
     

LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_8, LL_GPIO_MODE_OUTPUT);                        //CS

     

// Configure SPI2

RCC->APBENR1 |= RCC_APBENR1_SPI2EN;

SPI2->CR2 = SPI_CR2_FRXTH |                          //RX Buffer set flag
      0x07<<SPI_CR2_DS_Pos;                          //SPI data size
      
SPI2->CR1 = 0x04<<SPI_CR1_BR_Pos |                 //SPI baudrate 2MB/s
      SPI_CR1_CPHA |                                //CPHA=1
      SPI_CR1_SSM | SPI_CR1_SSI |              //Software slave management 
      SPI_CR1_MSTR |                               //Master mode
      SPI_CR1_SPE;                                 //ENABLE SPI


Obsługa wysyłania i odbierania danych

Dane do wysłania ładujemy do rejestru DR, oczywiście upewniając się, że bufor nadawczy jest w stanie przyjąć dane. Status bufora nadawczego wskazuje flaga TXE. Interfejs SPI równolegle z wysyłaniem odbiera dane, gotowość do odczytu odebranych danych sygnalizuje flaga RXNE. Odczyt danych odebranych z bufora odbiorczego realizowany jest przez odczyt rejestru DR.

Kod: Zaznacz cały

uint8_t SPI_SendRead(SPI_TypeDef *SPIx, uint8_t data) {
     
  while (!(SPIx->SR & SPI_SR_TXE));
  *(volatile uint8_t*)&(SPIx->DR) = data;
       
  while (!(SPIx->SR & SPI_SR_RXNE));
  return *(volatile uint8_t*)&SPIx->DR;
       
}


O czym warto wspomnieć, rejestr DR to rejestr 16-bitowy, przy zapisie liczb 8-bitowych ważne jest jawne rzutowanie

Kod: Zaznacz cały

*(volatile uint8_t*)&(SPIx->DR)


Oczekiwanie na odbiór danych wstrzymuje wykonywanie programu do czasu zakończenia transmisji, co ma znaczenie szczególnie przy wolnych transferach. Rozwiązaniem tego problemu może być odczyt odebranych danych w przerwaniach.

Komunikacja z ADS1256
Na początek parę definicji, które będą pojawiać się w kodzie.

Kod: Zaznacz cały

#define SPI                     SPI2
#define ADS_PORT_CS             GPIOB
#define ADS_PIN_CS                 8

#define ADS_CS_LOW                 GPIO_ResetBits(ADS_PORT_CS, ADS_PIN_CS) 
#define ADS_CS_HIGH             GPIO_SetBits(ADS_PORT_CS, ADS_PIN_CS) 

 
//------------------INPUTS----------------------

#define ADS_INPUT_AIN0            0x00
#define ADS_INPUT_AIN1            0x01
#define ADS_INPUT_AIN2            0x02
#define ADS_INPUT_AIN3            0x03
#define ADS_INPUT_AIN4            0x04
#define ADS_INPUT_AIN5            0x05
#define ADS_INPUT_AIN6            0x06
#define ADS_INPUT_AIN7            0x07
#define ADS_INPUT_AIN8            0x08
#define ADS_INPUT_AINCOM        0x09

 

//----------------REGISTERS---------------------

#define STATUS_REG                0x00
#define MUX_REG                    0x01
#define ADCON_REG                0x02
#define DRATE_REG                0x03
#define IO_REG                    0x04
#define OFC0_REG                0x05
#define OFC1_REG                0x06
#define OFC2_REG                0x07
#define FSC0_REG                0x08
#define FSC1_REG                0x09
#define FSC2_REG                0x0A

 

//-----------------COMMANDS---------------------

#define WAKEUP                    0x00
#define RDATA                    0x01
#define RDATAC                    0x03
#define SDATAC                    0x0F
#define RREG                    0x10
#define WREG                    0x50
#define SELFCAL                    0xF0
#define SELFOCAL                0xF1
#define SELFGCAL                0xF2
#define SYSOCAL                    0xF3
#define SYSGCAL                    0xF4
#define SYNC                    0xFC
#define STANDBY                    0xFD
#define RESET                    0xFE

 
typedef union
{
    uint8_t data[4];
    uint32_t out;
} ADC_DATA;


Rozpoczęcie pomiaru

Pomiar jest inicjowany w przerwaniu od licznika TIM17. Wywołujemy funkcję ADS_START_CONVERSION() która jako parametr wejściowy przyjmuje konfigurację wejść, na których będzie wykonywany pomiar


Kod: Zaznacz cały

void TIM17_IRQHandler(void)
{
    if(TIM17->SR & TIM_SR_UIF){
        ADS_START_CONVERSION((ADS_INPUT_AIN6 << 4) | ADS1256_MUXN_AIN1);
        TIM17->SR &= ~TIM_SR_UIF;
    }
}


Funkcja ADS_START_CONVERSION przez SPI wysyła przekazaną w argumencie konfigurację wejść, synchronizuje ADC a następnie wybudza go.

Kod: Zaznacz cały

void ADS_START_CONVERSION(uint8_t inputs)
{
   ADS_CS_LOW;                           

   SPI_SendRead(SPI, WREG | MUX_REG);         //send 0x51
   SPI_SendRead(SPI, 0x00);               //send 0x00
   SPI_SendRead(SPI, inputs);               //send 0x61, ADS_INPUT_AIN6 << 4 | ADS1256_MUXN_AIN1

   SPI_SendRead(SPI, SYNC);               //send 0xFC

   SPI_SendRead(SPI, WAKEUP);               //send 0x00

   ADS_CS_HIGH;
}


Analizując funkcję ADS_START_CONVERSION
1. Przed rozpoczęciem transmisji programowo opuszczamy pin CS aktywując interfejs komunikacyjny w ADS1256.
2. Wysyłamy przez SPI 5 bajtów danych: 0x51, 0x00, 0x61, 0xFC, 0x00. Na tym etapie dane odebrane przez SPI są zbędne.
3. Po zakończeniu transmisji programowo podnosimy pin CS.

Dokładnie to co program wykonuje znajdziemy na magistrali, poniżej zarejestrowana transmisja
StartConv.PNG


Odczyt wyniku
Zakończenie pomiaru sygnalizowane jest ustawieniem stanu niskiego na pinie DRDY, pin ten jest połączony z linią EXTI10 (pin PA10) Odczyt danych dokonywany jest w przerwaniu.


Kod: Zaznacz cały

void EXTI4_15_IRQHandler(void)
{
  if (LL_EXTI_IsActiveFallingFlag_0_31(LL_EXTI_LINE_10) != RESET)
  {
    LL_EXTI_ClearFallingFlag_0_31(LL_EXTI_LINE_10);
    if(SPI2->CR1 & SPI_CR1_SPE){
      uint32_t data = ADS_READ_DATA();
      PrintData(data);
   }
}


Funkcja ADS_READ_DATA odbiera dane, które w funkcji PrintData są wyświetlane na LCD. Odczyt poprzedzony jest komendą RDATA (0x01) Po odczycie przetwornik jest ponownie usypiany. Przyjrzyjmy się dokładniej funkcji ADS_READ_DATA.

Kod: Zaznacz cały

uint32_t ADS_READ_DATA(void)
{
   ADC_DATA data;
   data.out = 0;
   
   ADS_CS_LOW;
   SPI_SendRead(SPI, RDATA);                     //send 0x01

   Delay(150);
   
   uint8_t ptr = 2;

   for(uint8_t x=0; x<3; x++)
   {
      data.data[ptr--] = SPI_SendRead(SPI, 0xAA);      //send 3*0xAA, read data from slave
   }
   
   SPI_SendRead(SPI, STANDBY);                     //send 0xFD
   ADS_CS_HIGH;

   return data.out;
}


1. Programowo opuszczamy pin CS aktywując interfejs w ADC.
2. Wysyłamy do ADC komendę RDATA, po wysłaniu RDATA musimy chwilę odczekać, datasheet podaje czas 50 tCLKIN, jest to czas niezbędny do przesunięcia wyniku pomiaru do DOUT.
3. W pętli odczytujemy dane. Master równolegle z wysyłaniem odbiera, nie ma możliwości tylko odczytu danych, do odbioru danych musimy zainicjować transmisję wysyłając dowolną wartość. Z tego powodu podczas odbioru wysyłam do ADC wartość 0xAA.
4. Po odebraniu 3 bajtów wysyłam do układu polecenie STANDBY celem ponownego uśpienia układu.
5. Podnosimy pin CS dezaktywując interfejs w układzie ADS1256.

ReadData.PNG


Wynik przekazany jest do PrintData, który wyświetla odebraną wartość na LCD.
LCD_Print.PNG
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.


Wróć do „Programowanie STM w C/C++”

Kto jest online

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