STM32 - ARM Cortex M

STM32 – HAL Kütüphanesi ile ADXL345 Kullanımı

Herkese merhabalar , bu yazımızda HAL Kütüphanesi ile ADXL345 İvme Sensörünün kodlarını açıklayarak yazacağız. ADXL345 den gelen verileri I2C ile okuyacağız.

ADXL345 Datasheet:
[button text=”ADXL345″ new_tab=”yes” size=”medium” icon=”” url=”https://www.analog.com/media/en/technical-documentation/data-sheets/adxl345.pdf”]

1- Kütüphaneyi ekliyoruz.
#include "main.h"

 

2- Gerekli olan değişkenleri tanımlıyoruz.
uint8_t i;
uint8_t data_rec[6];
int16_t x,y,z;
float xg, yg, zg;

i                       :  sensörün adresini hafızada tutar.
data_rec[6]   :
x, y, z              : Hareket değerlerini tutmak için.
xg, yg, zg       :  Hareket ivmelerinin “G kuvvet ” değerlerini tutmak için.

 

3- Sistemin kendi fonksiyonlarını tanımladık.
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);

 

4- ADXL345’e ait özel fonksiyonlarını tanımladık.
void Scan_I2C_Address();
void ADXL_Init();
void ADXL_Write(uint8_t memAddress, uint8_t value);
void ADXL_Read(uint8_t memAddress, uint8_t numberOfBytes);

Scan_I2C_Address  :  I2C iletişiminde kullanacağım cihazların adreslerini bulan fonksiyon.
ADXL_Init                 :  ADXL345’e ait yapıları başlatan fonksiyon.
ADXL_Write             :  ADXL345’in üzerine veri yazacağımızda kullancağım fonksiyon.
ADXL_Read              :  ADX345 üzerinden veri okuyacağımız zaman kullanacağımız fonksiyon.

 

5- Main içerisinde gerekli olan fonksiyonları çağırıyoruz.
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();Scan_I2C_Address();
ADXL_Init();

 

6- While içerinde yapmamız gerekenler;
a- Adresi okuma
b- Hareket yönlerinin değerlerini okuma
c- Hareket yönlerindeki “G Kuvvet” değerlerini okuma.

ADXL_Read(0x32, 6);
x = (data_rec[1] << 8) | data_rec[0];
y = (data_rec[3] << 8) | data_rec[2];
z = (data_rec[5] << 8) | data_rec[4];
xg = x * .0078;// convert the x value g
yg = y * .0078;// convert the y value g
zg = z * .0078;// convert the z value g
7- Sistemin kendi fonksiyonlarının detaylarını kendi cihazınıza göre ayarlayın.
8- void Scan_I2C_Address();   Fonksiyonu.
void Scan_I2C_Address(){
for(i=0; i<=255; i++){
if(HAL_I2C_IsDeviceReady(&hi2c1, i, 1, 10) == HAL_OK){
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_11);
break;}}
}
Burada i 0 dan 255’e kadar dönecek ve if koşulu da slave cihazın adresini kontrol edecek , mesela slave cihazın adresi 166 “Decimal : A6”  , bu durumda i değeri 166 ya geldiğinde if koşulu “True” dönecek ve içeriye girecek. İçeriye giridiğinde led yanacak ve direkt olarak döngüyü break ile sonlandıracak bu sonlandırma işleminde i : 166 değerinde  kalacaktır yani döngü sonlanınca i değeride en son olarak 166 sayısını alıp bünyesinde saklayacaktır , bu sayede ben adresimi bulup  en yukarıya #define olarak tanımladım.
9- void ADXL_Read(uint8_t reg, uint8_t numberOfBytes);   Fonksiyonu.
void ADXL_Read(uint8_t reg, uint8_t numberOfBytes){
HAL_I2C_Mem_Read(&hi2c1, ADXL_ADDRESS, memAddress, 1, data_rec, numberOfBytes, 100);
}

Burada  I2C protokolü ile cihazınımızın hafızasına direkt olarak işlem yapıyoruz.  Cihazın hafızasındaki belli bölgelerde işlemler yapacağız bu işlemleri hafızadaki Register’lar üzerinden yazacağız.
HAL_I2C_Mem_Read   ;  Bu fonksiyon ile hafızaya direkt olarak erişeceğiz. Aşağıda bu fonksiyoonun içeriği hakında bilgiler verilmiştir..
&hi2c1                                 :  Kartımızda  Hal kütüphanesi ile  hangi I2C portlarını kullanıysak onu seçiyoruz.
ADXL_ADDRESS            :  ADXL345 cihazın adresi.
memAddress                     :  Cihazın hangi Registerına işlem yapılacksa o Registerın adresi buraya yazılır.
1                                           :  Burada okuyacağınız Dahili Bellek adresinin boyutunu giriyoruz. (Bizim okuyacağımız adresin boyu 1 byte)
data_rec                             :  Alınan adres bu diziye saklanır .
numberOfBytes                :  Gönderilen verinin boyutu.
100                                      :  Timout.

10- void ADXL_Write(uint8_t memAddress, uint8_t value)  Fonksiyonu.
void ADXL_Write(uint8_t memAddress, uint8_t value){
uint8_t data[2]={0};
data[0] = memAddress;
data[1] = value;
HAL_I2C_Master_Transmit(&hi2c1, ADXL_ADDRESS, data, 2, 100);
}

data[2]={0}                                   :  Bu diziyi direkt cihaza göndereceğiz ve içerisine aşağıdaki verileri koyacağız.
data[0] = memAddress               :  0. index’e cihazın hangi Register’a gideceğini yazdık.
data[1] = value                              :   1. index’e cihaza gidecek değeri yazdık.

HAL_I2C_Master_Transmit    ;  Gönderici cihaz Master konumuda olduğu için “Master Gönderici”  fonksiyonunu kullandık.
&hi2c1                             :  Kartımızda  Hal kütüphanesi ile  hangi I2C portlarını kullanıysak onu seçiyoruz.
ADXL_ADDRESS        :  ADXL345 cihazın adresi.
data                                 :  Oluşturduğumuz dizi.
2                                       :  Gönderilen verinin boyutu.
100                                  :  Timout.

 

11- ADXL_Init()  Fonksiyonu.
void ADXL_Init(){
ADXL_Read(0x00, 1);      // 0x00 Adresini oku ve geri dönen değer 0xE5 ise Çalışmaya başla
ADXL_Write(0x2D, 0);     // Tüm verileri resetle
ADXL_Write(0x2D, 0x08);  // Şu anlık sadece ölçü modunda başlattık.
ADXL_Write(0x31, 0x01);  // Şu anlık sadece +-4g yapacağız.
}

001000  :  0x08 dir.

0x2D için;

Link : Dökümandan oku.
Auto Sleep : Otomatik Uyuklama.
Measure     :  Ölçüm yapar.
Sleep           :  Cihazı uyku moduna geçirir.
Wake up     :  Uyku modunda çalımaların hızını kontrol eder. Biz burda 0 yaparak 8Hz olarak ayarladık.

0x31 için;

 

0   0  :  +-2g
0   1   :  +-4g
1    0  :  +-8g
1    1   :  +-16g

D2,D3,D4,D5,D6,D7 verilerini Dökümandan okuyabilirsiniz.
0x2D ve 0x31 Registerındaki işlemleri daha iyi anlamak için dökümanı inceleyin ve açıklamları okuyun.


Şimdi While içerisine yazdığımız kodları detaylı inceleyelim.

a) ADXL_Read(0x32, 6); Bu fonksiyon okuma işlemi yapıyor peki nerden deyi okuyor? 0x32 Registeri bizim yön verilerimizi tutar ama sadece bu register değil , 0x32 – 0x36 arasındaki tüm registerlar bu değerleri tutar bizde bu yüzden 6 yazdık . Kısacası 0x32 adresinden başla 6 bytlık bir okuma yap buda 36 ya kadar tüm verileri oku demek bunlar oku ve verileri ADXL_Read fonksiyonu içerisindeki kodlar sayesinde ” data_rec ” adlı dizide tut.

b) Burada dikkat edilmesi gereken olaylar  var;
1) x,y,z değişkenleri uint16_t cinsinden tanımlandı çünü 8 bit + 8bit toplamda 16 bitlik bir alana ihtiyacımız var .
2) x,y,z  yönünde yapılan değerler yön olduğu için (+) ve (-) olarak hafızada tutulur.
3) Okunan x,y,z değerleri tek tek (+) yönde 8 bit  , (-) yönde 8 bit toplamda 16 bitlik bir veri oluşturur.
4)  Aşağıda görülen çizimde siyahlar(x) , kırmızılar(y) ve morlar(z) dir. Buradaki tablonun sol tarfına eksi(-) değerleri sağ tarafına  artı(+) değerlerini kaydettik.

 

Aşağıdaki kod ile eksi değerleri 8 bit sola kaydırıp kayıt ettik ve diğerlerini olduğu gibi (veya) ile yanına yapıştırdık.

x = (data_rec[1] << 8) | data_rec[0];
y = (data_rec[3] << 8) | data_rec[2];
z = (data_rec[5] << 8) | data_rec[4];

Bu şekilde 8 bitlik  eksi ve artı yönleri bir diziye kayıt ettik bu saaten sonra bu diziyi nerde kullanmak istersek kullanabiliriz.

 

c) Son olarak x,y,z yönlerinin “g” yi bulmak kalıyor.

Biz (+-4g) olarak işlem yapıyoruz, bu tabloda çıkış çözünürlüğümüz 11 bit ve g yi hesaplarken kullanacağımız bilgiler de ” Sensitivity Deviation from Ideal” kısmındaki sarı ile çizilmiş bölgede yer alıyor. Burada “7.8” değerinin yanında mg işareti var bunu grama çevirirsek “0.0078 ” gram olarak buluruz. Hadi bunu formüle sokalım ve “g”  değerini elde edelim.

xg = x * .0078;
yg = y * .0078;
zg = z * .0078;

İlk başta bulduğumuz x,y,z değerini “0.0078” ile çarparak x,y ve z ye ait “g” değerlerini bulabiliriz.


 

Tüm Kod:

#include “main.h”
#define ADXL_ADDRESS 0xA6 // 0x53 << 1
I2C_HandleTypeDef hi2c1;
uint8_t i;
uint8_t data_rec[6];
int16_t x,y,z;
float xg, yg, zg;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
void Scan_I2C_Address();
void ADXL_Init();
void ADXL_Write(uint8_t memAddress, uint8_t value);
void ADXL_Read(uint8_t memAddress, uint8_t numberOfBytes);
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_I2C1_Init();
  Scan_I2C_Address();
  ADXL_Init();
  while (1)
  {
  ADXL_Read(0x32, 6);
  x = (data_rec[1] << 8) | data_rec[0];
  y = (data_rec[3] << 8) | data_rec[2];
  z = (data_rec[5] << 8) | data_rec[4];
  xg = x * .0078;// convert the x value g
  yg = y * .0078;// convert the y value g
  zg = z * .0078;// convert the z value g
  HAL_Delay(10);
  }
}
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}
static void MX_I2C1_Init(void)
{
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 100000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }
}
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOD, GPIO_PIN_11, GPIO_PIN_RESET);
  /*Configure GPIO pin : PD11 */
  GPIO_InitStruct.Pin = GPIO_PIN_11;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
}
void Scan_I2C_Address()
{
for(i=0; i<=255; i++)
{
if(HAL_I2C_IsDeviceReady(&hi2c1, i, 1, 10) == HAL_OK)
{
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_11);
break;
}
}
}
void ADXL_Write(uint8_t memAddress, uint8_t value){
uint8_t data[2]={0};
data[0] = memAddress;
data[1] = value;
HAL_I2C_Master_Transmit(&hi2c1, ADXL_ADDRESS, data, 2, 100);
}
void ADXL_Read(uint8_t memAddress, uint8_t numberOfBytes)
{
HAL_I2C_Mem_Read(&hi2c1, ADXL_ADDRESS, memAddress, 1, data_rec, numberOfBytes, 100);
}
void ADXL_Init()
{
ADXL_Read(0x00, 1);// Read adres: 0x00 and 1 bit if bits = 0xE5 sensor is true work
ADXL_Write(0x2D, 0);// Reset all bits
ADXL_Write(0x2D, 0x08);// Setup for measurement .  Measure bit-1, wake up 0, 0 at 8 hz
ADXL_Write(0x31, 0x01);//Set to Range +- 4g  >> +- 4g range
// And ok ready to read for value
}
void Error_Handler(void)
{
}
#ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif

 

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Başa dön tuşu