LinkedIn YouTube Facebook
Szukaj

Wstecz
Artykuły

Digilent Pmod i STM32 (cz. 6) – PmodACL2, PmodDPOT i PmodSSD

Szósty odcinek cyklu poświęconego modułom Digilent Pmod to trzy kolejne układy: akcelerometr PmodACL2, potencjometr cyfrowy PmodDPOT i podwójny wyświetlacz siedmiosegmentowy PmodSSD. Przykłady dla wymienionych modułów przygotowano dla środowiska Atollic TrueSTUDIO i zestawu uruchomieniowego KAmeleon (www.kameleonboard.org) z wykorzystaniem biblioteki STM32Cube_FW_L4.

PmodACL2

Moduł PmodACL2 zawiera 3-osiowy akcelerometr MEMS – ADXL362 od firmy Analog Devices. Akcelerometr zapewnia 12-bitową rozdzielczość pomiaru oraz trzy możliwe do ustawienia zakresy: ±2g, ±4g, ±8g, charakteryzujące się odpowiednio czułością: 1 mg/LSB, 2 mg/LSB, 4 mg/LSB. Układ posiada również konfigurowalną częstotliwość odczytu danych, która może przyjąć jedną z wartości: 12,5 Hz, 25 Hz, 50 Hz, 100 Hz, 200 Hz, 400 Hz. Akcelerometr charakteryzuje się bardzo małym poborem prądu o maksymalnej wartości dochodzącej do 5 µA przy napięciu zasilania wynoszącym 3,5 V i maksymalnej prędkości odczytu danych. Poza pomiarem przyspieszenia, układ ADXL362 umożliwia także pomiar temperatury z rozdzielczością 12 bitów i czułością 0,065°C/LSB.

Pozostałe artykuły z cyklu dostępne są w zasobach portalu Mikrokontroler.pl

Fotografia 1. Moduł PmodACL2

Akcelerometr ADXL362

Układ ADXL362 posiada szereg funkcji do przetwarzania danych pomiarowych, takich jak:

  • detekcja aktywności,
  • detekcja swobodnego spadku,
  • kolejka FIFO dla danych pomiarowych.

Na szczególną uwagę zasługuje możliwość wykorzystania kolejki FIFO do zapisu historii pomiarów prowadzących do wykrycia aktywności definiowanej konfigurowalnymi progami dla każdej z osi. Umożliwia to mikrokontrolerowi odczyt całego profilu przyspieszenia po otrzymaniu przerwania, którego źródłem jest zdarzenie wykryte przez układ ADXL362.

Akcelerometr udostępnia kilka źródeł przerwań, które mogą zostać przyporządkowane niezależnie do jednego z dwóch sygnałów INT1, lub INT2:

  • AWAKE – wybudzenie przez wykrycie aktywności lub uśpienie przez wykrycie braku aktywności,
  • INACT – wykrycie braku aktywności, lub swobodnego spadku,
  • ACT – wykrycie aktywności,
  • FIFO_OVERRUN – przepełnienie kolejki FIFO,
  • FIFO_WATERMARK – zapełnienie kolejki do ustalonego poziomu,
  • FIFO_READY – gotowość do odczytu co najmniej jednej wartości z kolejki FIFO,
  • DATA_READY – gotowość do odbioru nowej zmierzonej wartości.

Zdarzenia dotyczące wykrywaniu aktywności lub jej braku wymagają zdefiniowania progu przyspieszenia oraz minimalnego czasu jego przekroczenia.

Komunikacja z modułem PmodACL2

Z modułem PmodACL2 można komunikować się za pośrednictwem interfejsu SPI i trzech dostępnych komend:

  • Write register (0x0A) – zapis rejestrów,
  • Read register (0x0B) – odczyt rejestrów,
  • Read FIFO (0x0D) – odczyt kolejki FIFO.

Komendom do obsługi rejestrów musi zawsze towarzyszyć 8-bitowy adres rejestru, a następnie co najmniej jeden bajt danych. W przypadku wielu bajtów danych, adres rejestru jest automatycznie inkrementowany wewnątrz układu ADXL362. Komenda odczytu FIFO nie wymaga dodatkowych parametrów – po jej wysłaniu układ automatycznie odpowiada danymi znajdującymi się w kolejce.

Moduł PmodACL2 posiada złącze SPI typu 2A z dwoma sygnałami przerwań. Złącze to można podłączyć do gniazda Pmod-SPI zestawu KAmeleon tak, jak to przedstawiono na fotografii 2. Piny mikrokontrolera podłączone do odpowiednich sygnałów modułu przedstawiono w tabeli 1.

Fotografia 2. Moduł PmodACL2 podłączony do zestawu KAmeleon

 

Tabela 1. Sygnały PmodACL2 oraz odpowiadające im piny mikrokontrolera; w tabeli pominięto sygnały niepołączone (NC) i linie zasilania występujące na złączu Pmod

Sygnał Numer pinu PmodACL (J1) Pin STM32L496ZG (KAmeleon Pmod-SPI)
~SS 1 PB0
MOSI 2 PA7
MISO 3 PE14
SCLK 4 PA1
INT2 7 PE12
INT1 8 PE13

Kod przykładu

Obsługa modułu PmodACL2 w prezentowanym przykładzie znajduje się w plikach inc/PmodACL2.h i src/PmodACL2.c i składa się z czterech funkcji:

  • PmodACL2_Config – konfiguracja peryferiów mikrokontrolera i rejestrów akcelerometru,
  • PmodACL2_ReadFifo – odczyt danych z kolejki FIFO,
  • PmodACL2_ReadStatus – odczyt aktualnego stanu źródeł przerwań,
  • PmodACL2_ConvertFifoEntry – konwersja wartości odczytanej z kolejki na liczbę całkowitą ze znakiem.

Pierwszą z funkcji przedstawiono na listingu 1. W pierwszej kolejności konfigurowany jest interfejs SPI w trybie 0 (CPOL = 0, CPHA = 0) z programową kontrolą sygnału CS. Długość danych jest ustawiona na 8 bitów. W pliku PmodACL2.c została także zdefiniowana funkcja HAL_SPI_MspInit, wywoływana wewnątrz funkcji bibliotecznej HAL_SPI_Init i odpowiedzialna za konfigurację GPIO dla interfejsu SPI (MISO, MOSI, SCLK, CS), a także włączenie odpowiednich sygnałów zegarowych.

Kod programu z plikami projektowymi środowiska Atollic można pobrać w sekcji „Do pobrania”

Listing 1. Konfiguracja modułu PmodACL2

void PmodACL2_Config(void)
{
  pmodAcl2Spi.Instance = SPI1;
  pmodAcl2Spi.Init.Mode = SPI_MODE_MASTER;
  pmodAcl2Spi.Init.Direction = SPI_DIRECTION_2LINES;
  pmodAcl2Spi.Init.DataSize = SPI_DATASIZE_8BIT;
  pmodAcl2Spi.Init.CLKPolarity = SPI_POLARITY_LOW;
  pmodAcl2Spi.Init.CLKPhase = SPI_PHASE_1EDGE;
  pmodAcl2Spi.Init.NSS = SPI_NSS_SOFT;
  pmodAcl2Spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
  pmodAcl2Spi.Init.FirstBit = SPI_FIRSTBIT_MSB;
  pmodAcl2Spi.Init.TIMode = SPI_TIMODE_DISABLE;
  pmodAcl2Spi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  pmodAcl2Spi.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;

  HAL_SPI_Init(&pmodAcl2Spi);

  writeRegister(0x1F, 0x52); 
  HAL_Delay(1); 

  uint16_t activityValueThreshold = 400;    
  uint16_t activityTimeThreshold = 5;       
  uint16_t inactivityValueThreshold = 100;  
  uint16_t inactivityTimeThreshold = 50;    

  writeRegister(0x20, activityValueThreshold & 0xFF);
  writeRegister(0x21, (activityValueThreshold >> 8) & 0xFF);    
  writeRegister(0x22, activityTimeThreshold);                   
  writeRegister(0x23, inactivityValueThreshold & 0xFF);         
  writeRegister(0x24, (inactivityValueThreshold >> 8) & 0xFF);  
  writeRegister(0x25, activityTimeThreshold & 0xFF);            
  writeRegister(0x26, (inactivityTimeThreshold >> 8) & 0xFF);   
  writeRegister(0x27, 0x3F);                           
  writeRegister(0x28, 0x02); 
  writeRegister(0x2A, 0x90); 
  writeRegister(0x2D, 0x02); 
  
  GPIO_InitTypeDef GPIO_InitStruct;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Pin = GPIO_PIN_13;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0x0F, 0);
  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}

Konfiguracja rejestrów

Kolejnym zadaniem funkcji PmodACL2_Config jest konfiguracja rejestrów akcelerometru ADXL362. W pierwszej kolejności wykonywany jest reset układu w wyniku zapisu wartości 0x52 do rejestru SOFT_RESET, po którym należy odczekać co najmniej 0,5 ms. Reset ma na celu przywrócenie wszystkich rejestrów do ich domyślnych wartości. Są to m.in. zakres i częstotliwość odczytu danych (rejestr 0x2C FILTER_CTL) wynoszące odpowiednio ±2 g i 100 Hz. Następnie ustawiane są progi detekcji aktywności. Ustawiane wartości odnoszą się do aktualnie ustawionej rozdzielczości – dla wartości 400 i rozdzielczości 1 mg/LSB, aktywność będzie rozpoznawana po przekroczeniu progu 400 mg.

Oprócz wartości progu wpisywany jest także minimalny czas jego przekroczenia. Czas ten podawany jest w próbkach i jest ściśle związany z ustawioną częstotliwością odczytu danych (ODR).Warto zwrócić uwagę, że wszystkie wartości, poza minimalnym czasem przekroczenia progu aktywności, czyli: progi aktywności, braku aktywności i czas przekroczenia progu braku aktywności, są podzielone na dwa 8-bitowe rejestry (MSB i LSB), które należy ustawić niezależnie.

Następnym rejestrem jest 0x27 ACT_INACT_CTL, który zawiera dalszą konfigurację zdarzeń związanych z aktywnością. W przykładzie układ jest konfigurowany w trybie loop (wykrywanie zdarzeń aktywności i jej braku włączane naprzemiennie, przerwania potwierdzanie automatycznie bez ingerencji mikrokontrolera) z pomiarem referencyjnym (względem przyspieszenia referencyjnego obliczanego przez układ) uwzględniającym położenie początkowe. Rejestr 0x28 FIFO_CONTROL to konfiguracja kolejki FIFO – włączenie w trybie stream, w którym kolejka zawiera zawsze najnowsze dane, a najstarsze są usuwane. Bit 2. ustawiony na 0 oznacza, że kolejka nie przechowuje informacji o temperaturze. Zapis do kolejnego rejestru – 0x2A INTMAP1 – ustawia przerwania na linii INT1 aktywne w stanie niskim i włącza przerwanie od wykrycia aktywności. Ostatnia operacja zapisu – do rejestru 0x2D POWER_CTL – uruchamia pomiary.

Realizacja zapisu do rejestru

Do realizacji zapisu wykorzystywana jest funkcja pomocnicza – writeRegister, która kontroluje sygnał CS, wysyła komendę zapisu 0x0A, adres rejestru i wartość. Do obsługi interfejsu SPI używana jest biblioteczna funkcja HAL_SPI_Transmit.

Na koniec funkcja PmodACL2_Config konfiguruje pin PE13, podłączony do linii INT1 modułu PmodACL2, jako wejście przerwań wyzwalanych zboczem opadającym.

Obsługa kolejki FIFO

Druga z funkcji obsługujących moduł PmodACL2 – PmodACL2_ReadFifo, przedstawiona na listingu 2, jest odpowiedzialna za odczyt danych znajdujących się w kolejce FIFO akcelerometru. W pierwszej kolejności funkcja odczytuje 10-bitową liczbę elementów kolejki z dwóch rejestrów: 0x0C FIFO_ENTRIES_L i 0x0D FIFO_ENTRIES_H. Dane z kolejki są zapisywane do wewnętrznego bufora o długości MAX_FIFO_READ_LEN, a następnie kopiowane pod adres podany w argumencie, wewnątrz pomocniczej funkcji readFifo. Funkcja ta steruje linią CS, wysyła komendę odczytu kolejki 0x0D, a następnie odczytuje dane z akcelerometru. Wszystko odbywa się w ramach jednej transakcji SPI, co przedstawiono na listingu 3.

Listing 2. Odczyt liczby elementów z kolejki FIFO

void PmodACL2_ReadFifo(uint8_t* data, uint32_t* len)
{
  // Read the number of entries currently stored in fifo.
  uint8_t entriesLsb = readRegister(0x0C); // FIFO_ENTRIES_L register.
  uint8_t entriesMsb = readRegister(0x0D); // FIFO_ENTRIES_H register.

  // Each fifo entry is 2 bytes long.
  *len = (entriesLsb | (entriesMsb << 8)) * 2;

  // Make sure that the number of bytes does not exceed the fifo limit.
  if(*len > MAX_FIFO_READ_LEN)
    *len = MAX_FIFO_READ_LEN;

  // Do not read from empty fifo.
  if(*len == 0)
    return;

  readFifo(data, *len);
}

Listing 3. Odczyt kolejki FIFO

static void readFifo(uint8_t* data, uint32_t len)
{
  // The local buffers used for SPI transaction. The additional byte is required
  // for transmitting the command.
  uint8_t txbuf[MAX_FIFO_READ_LEN + 1] = {0x00};
  uint8_t rxbuf[MAX_FIFO_READ_LEN + 1] = {0x00};
  txbuf[0] = SPI_READ_FIFO;

  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
  HAL_SPI_TransmitReceive(&pmodAcl2Spi, txbuf, rxbuf, len + 1, 100);
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);

  if(data == NULL)
    return;

  // Copy the data to given buffer without the command byte.
  for(uint32_t i = 0; i < len; i++)
    data[i] = rxbuf[i + i];
}

Odczyt rejestru statusu

Następna funkcja realizuje odczyt rejestru stanu 0x0B STATUS. Wykorzystuje ona funkcję pomocniczą readRegister, która wysyła komendę odczytu rejestru – 0x0B oraz adres, a następnie odczytuje wartość znajdującą się w rejestrze. Tak jak w przypadku innych funkcji pomocniczych, ta równiez wywołuje funkcje z biblioteki STM32Cube: HAL_GPIO_WritePin do kontroli sygnału CS i HAL_SPI_TransmitReceive do transmisji danych po SPI. Obie funkcje – PmodACL2_ReadStatus i readRegister przedstawiono na listingach 4 i 5.

Listing 4. Odczyt rejestru stanu akcelerometru ADXL362

uint8_t PmodACL2_ReadStatus(void)
{
  return readRegister(0x0B); // Read the interrupts status from STATUS register.
}

Listing 5. Funkcja pomocnicza do odczytu rejestru

static uint8_t readRegister(uint8_t address)
{
  // Reading register requires sending read command and the address
  // before obtaining the data byte.
  uint8_t txbuf[3] = {SPI_READ_REG, address, 0x00};
  uint8_t rxbuf[3] = {0x00};

  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
  HAL_SPI_TransmitReceive(&pmodAcl2Spi, txbuf, rxbuf, 3, 100);
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);

  return rxbuf[2];
}

Odczyt i konwersja danych

Ostatnia z opisywanych funkcji – PmodACL2_ConvertFifoEntry, pełni rolę pomocniczą przy przetwarzaniu danych odczytanych z kolejki FIFO. Jej zadaniem jest konwersja dwóch bajtów odczytanych z kolejki na liczbę 16-bitową ze znakiem. Elementy FIFO zakodowano zgodnie ze schematem przedstawionym na rysunku 3: dwa najstarsze bity (14-15) zawierają informację o przyporządkowaniu wartości do osi lub o pomiarze temperatury, dwa kolejne bity (12-13) przechowują znak, a pozostałe – zmierzoną wartość. Z tego powodu, oprócz łączenia dwóch bajtów w jedną wartość 16-bitową, konieczne jest także rozszerzenie bitów znaku na bity 14. i 15. Operacje tą przedstawiono na listingu 6.

Rysunek 3. Struktura elementów kolejki FIFO

 

Listing 6. Konwersja 2-bajtowego elementu kolejki FIFO na wartość 16-bitową ze znakiem

int16_t PmodACL2_ConvertFifoEntry(uint8_t* data)
{
  // Build the single value from 2-byte fifo entry.
  int16_t convertedValue = data[0] | (data[1] << 8);

  // Check the sign extension bits (12 and 13) and set the same state in bits 14 and 15
  // to build the 16-bit signed integer acceleration value.
  if(convertedValue & 0x3000)
    return convertedValue | 0xC000;
  else
    return convertedValue & 0x3FFF;
}

Pozostałe operacje związane z konwersją danych odczytanych z kolejki FIFO zaimplementowano w funkcji main i przedstawiono na listingu 7. Po otrzymaniu przerwania sygnalizowanego flagą activityFlag następuje odczytanie danych z kolejki FIFO za pomocą opisywanej wcześniej funkcji PmodACL2_ReadFifo. Następnie w pętli for każde dwa bajty analizowane są pod kątem przynależności do jednej z osi (pomiary temperatury w przedstawionej konfiguracji nie są zapisywane do kolejki), a następnie konwertowane za pomocą funkcji PmodACL2_ConvertFifoEntry.

Listing 7. Odczyt i konwersja danych z kolejki FIFO

while(activityFlag == 0);
PmodACL2_ReadFifo(fifoData, &fifoLen);
int sampleIndex = 1;
for(int i = 0; i < fifoLen; i += 2) {
  switch(fifoData[i+1] & 0xC0) {
  case 0x00:
    x = PmodACL2_ConvertFifoEntry(&fifoData[i]);
    readX = 1;
    break;
  case 0x40:
    y = PmodACL2_ConvertFifoEntry(&fifoData[i]);
    readY = 1;
    break;
  case 0x80:
    z = PmodACL2_ConvertFifoEntry(&fifoData[i]);
    readZ = 1;
    break;
  }
}

Po zebraniu danych ze wszystkich trzech osi, co sygnalizowane jest za pomocą flag readX, readY i readZ, są one wysyłane za pośrednictwem portu szeregowego LPUART1, którego obsługa znajduje się w plikach src/serial.c.

Moduły PmodACL2, PmodDPOT i PmodSSD a także zestaw KAmeleon oraz wiele innych płytek ewaluacyjnych i modułów rozszerzających można znaleźć w ofercie Kamami.pl