در قسمت قبلی راهاندازی تایمر نگهبان پنجرهای (WWDG) را آموختیم و در این قسمت میخواهیم راهاندازی ماژول شتاب سنج و ژیروسکوپ LSM6DS3 را بررسی کنیم. ماژول LSM6DS3 یک ماژول چندکاره است که درگوشیهای هوشمند نیز بهکار میرود و امکان Tilt یا چرخش صفحه و دیگر قابلیتهای مرتبط با حرکت و چرخش (ازجمله شمارش قدم) را به دستگاه میدهد. LSM6DS3 امکان مدیریت 6 سنسور و ذخیره اطلاعات آنها را فراهم میکند. این ماژول دارای 8 Kbyte حافظه FIFO، رابط I2C و SPI، یک ژیروسکوپ، یک شتاب سنج و یک دماسنج داخلی است.
ما در این بخش قصد داریم ژیروسکوپ و شتاب سنج این ماژول را بهوسیله ارتباط SPI راهاندازی کنیم.
ایجاد پروژه
همانطور که گفته شد ما در اینجا برای ارتباط با ماژول شتاب سنج از رابط SPI استفاده میکنیم. پس در تنظیمات پروژه، پس از تنظیم دیباگ و کلاک، واحد SPI1 را فعال میکنیم و روی حالت Full-Duplex Master تنظیم میکنیم:
برای پایه فعالساز چیپ نیز باید یک پین میکرو را در حالت خروجی تنظیم کنیم که ما برای منظور پین PC0 را انتخاب کردیم. در آخر برای نمایش اطلاعات دریافتی از ماژول روی ترمینال سریال، واحد USART1 را فعال میکنیم. بقیهی تنظیمات را انجام میدهیم و پروژه را ایجاد میکنیم.
نوشتن کد پروژه
در این پروژه برای جلوگیری از سردرگمی و شلوغ شدن فایل اصلی برنامه، مثل بخش LCD فایل کتابخانه بانامهای LSM6DS3.h و LSM6DS3.c میسازیم و مانند همان پروژه به فایلهای برنامه اضافه میکنیم. کدهای مربوط به ریدایرکت را نیز کپی میکنیم و تنظیمات آن را انجام میدهیم. حالا باید کتابخانههای موردنیاز را در LSM6DS3.h اضافه کنیم، آدرس رجیسترهای مورداستفاده را در دیتاشیت پیداکرده و بنویسیم و همچنین توابعی که میخواهیم تعریف کنیم را در اینجا اعلان کنیم:
#include "stdio.h" #include "stdint.h" #include "stm32f1xx_ll_spi.h" #include "stm32f1xx_ll_utils.h" #include "stm32f1xx_ll_gpio.h"
/************** Device Register *******************/ #define LSM6DS3_ACC_GYRO_CTRL1_XL 0X10 #define LSM6DS3_ACC_GYRO_OUTX_L_XL 0X28 #define LSM6DS3_ACC_GYRO_OUTY_L_XL 0X2A #define LSM6DS3_ACC_GYRO_OUTZ_L_XL 0X2C #define LSM6DS3_ACC_GYRO_CTRL2_G 0X11 #define LSM6DS3_ACC_GYRO_OUTX_L_G 0X22 #define LSM6DS3_ACC_GYRO_OUTY_L_G 0X24 #define LSM6DS3_ACC_GYRO_OUTZ_L_G 0X26
/********///Functions: uint8_t converse_SPI(uint8_t); int16_t LSM6DS3_read_reg (uint8_t, uint8_t); void LSM6DS3_write_reg (uint8_t, uint8_t); uint8_t get_accelRange(void); float LSM6DS3_calcAccel(int16_t); float LSM6DS3_readFloatAccelX( void ); float LSM6DS3_readFloatAccelY( void ); float LSM6DS3_readFloatAccelZ( void ); uint16_t get_gyroRange(void); float LSM6DS3_calcGyro(int16_t); float LSM6DS3_readFloatGyroX(void); float LSM6DS3_readFloatGyroY(void); float LSM6DS3_readFloatGyroZ(void); void initialize_LSM6DS3 (void); void print_gyro_uncal(void); void print_acc_uncal(void); void print_gyro_cal(void); void print_acc_cal(void);
اکنون نوبت فایل LSM6DS3.c است. در این فایل متغیرهای مورد نیاز را تعریف میکنیم و رجیسترهای کنترل شتاب سنج و ژیروسکوپ را با توجه به دیتاشیت مقداردهی میکنیم. همچنین باید توابع موردنیاز برای خواندن و چاپ خروجی ماژول را بنویسیم:
#include "LSM6DS3.h" Miss Khabaz, [Sat, 30/07/2022 11:08 AM] #define LSM_Port GPIOC; #define CS_Pin LL_GPIO_PIN_0 uint8_t Read = 0x80; uint8_t Write = 0x00;
تابع مربوط به ارسال و دریافت داده توسط SPI:
/* function to transmit or receive one byte through SPI1 unit */ uint8_t converse_SPI(uint8_t wdata) { while(!LL_SPI_IsActiveFlag_TXE(SPI1)); LL_SPI_TransmitData8(SPI1, wdata); while(!LL_SPI_IsActiveFlag_RXNE(SPI1)); return LL_SPI_ReceiveData8(SPI1); }
تابع نوشتن داده در رجیسترهای ماژول:
/* function to write one byte(data) into the register identified by the Address addr in LSM6DS3 module */ void LSM6DS3_write_reg (uint8_t addr, uint8_t data) { LL_GPIO_ResetOutputPin(LSM_Port, CS_Pin); converse_SPI(Write | addr); converse_SPI(data); LL_GPIO_SetOutputPin(LSM_Port, CS_Pin); }
تابع خواندن داده 1 یا 2 بایتی از رجیسترهای ماژول:
/* function to read one or two bytes(data) from the register identified by the Address addr in LSM6DS3 module */ int16_t LSM6DS3_read_reg (uint8_t addr, uint8_t rcount) { int16_t data; LL_GPIO_ResetOutputPin(LSM_Port, CS_Pin); converse_SPI(Read | addr); data = converse_SPI(0xFF); if(rcount == 1) { LL_GPIO_SetOutputPin(LSM_Port, CS_Pin); return data; } else if(rcount == 2) { data |= converse_SPI(0xFF) << 8; LL_GPIO_SetOutputPin(LSM_Port, CS_Pin); return data; } else return 0; }
تابع محاسبه مقدار کالیبره خروجی شتاب سنج:
/* function to calculated calibrated values for the accelerometer */ float LSM6DS3_calcAccel(int16_t input) { float output = (float)input * 0.061 * get_accelRange() / 1000; return output; }
توابع خواندن و کالیبره کردن پارامترهای x،y و z مربوط بهشتاب سنج:
/* function to read X value of the accelerometer */ float LSM6DS3_readFloatAccelX(void) { float output = LSM6DS3_calcAccel(LSM6DS3_read_reg(LSM6DS3_ACC_GYRO_OUTX_L_XL, 2)); return output; }
/* function to read Y value of the accelerometer */ float LSM6DS3_readFloatAccelY(void) { float output = LSM6DS3_calcAccel(LSM6DS3_read_reg(LSM6DS3_ACC_GYRO_OUTY_L_XL, 2)); return output; }
/* function to read Z value of the accelerometer */ float LSM6DS3_readFloatAccelZ(void) { float output = LSM6DS3_calcAccel(LSM6DS3_read_reg(LSM6DS3_ACC_GYRO_OUTZ_L_XL, 2)); return output; }
تابع محاسیه مقدار کالیبره خروجی ژیروسکوپ:
/* function to calculated calibrated values for the gyroscope */ float LSM6DS3_calcGyro(int16_t input) { uint8_t gyroRangeDivisor = 2; float output = (float)input * 4.375 * (gyroRangeDivisor) / 1000; return output; }
توابع خواندن و کالیبره کردن پارامترهای x،y و z مربوط به ژیروسکوپ:
/* function to read X value of the gyroscope */ float LSM6DS3_readFloatGyroX(void) { float output = LSM6DS3_calcGyro(LSM6DS3_read_reg(LSM6DS3_ACC_GYRO_OUTX_L_G, 2)); return output; }
/* function to read Y value of the gyroscope */ float LSM6DS3_readFloatGyroY(void) { float output = LSM6DS3_calcGyro(LSM6DS3_read_reg(LSM6DS3_ACC_GYRO_OUTY_L_G, 2)); return output; }
/* function to read Z value of the gyroscope */ float LSM6DS3_readFloatGyroZ(void) { float output = LSM6DS3_calcGyro(LSM6DS3_read_reg(LSM6DS3_ACC_GYRO_OUTZ_L_G, 2)); return output; }
تنظیم اولیهی ماژول، رجیستر شناسایی ماژول در این تابع خوانده میشود و شتاب سنج و ژیروسکوپ فعال میشوند:
/* initializing the LSM6DS3 module */ void initialize_LSM6DS3 (void) { /* Enable & Configure Accelerometer */ Miss Khabaz, [Sat, 30/07/2022 11:08 AM] LSM6DS3_write_reg(LSM6DS3_ACC_GYRO_CTRL1_XL, 0xA0); /* Enable & Configure Gyroscope */ LSM6DS3_write_reg(LSM6DS3_ACC_GYRO_CTRL2_G, 0x80); printf("Initialization done!\tWHO_AM_I Register: %x\r\n", LSM6DS3_read_reg(LSM6DS3_ACC_GYRO_WHO_AM_I_REG, 1)); }
تابع چاپ مقادیر کالیبره شدهی شتاب سنج:
/* print the calibrated x,y and z values of the gyroscope */ void print_gyro_cal(void) { printf("Calibrated Gyroscope parameters:\r\n"); printf("\r\n\t X:\t %3.3f DPS", LSM6DS3_readFloatGyroX()); printf("\r\n\t Y:\t %3.3f DPS", LSM6DS3_readFloatGyroY()); printf("\r\n\t Z:\t %3.3f DPS", LSM6DS3_readFloatGyroZ()); printf("\r\n"); }
تابع چاپ مقادیر کالیبره شدهی ژیروسکوپ:
/* print the calibrated x,y and z values of the accelerometer */ void print_acc_cal(void) { printf("Calibrated Accelerometer parameters:\r\n"); printf("\r\n\t X:\t %3.3f g", LSM6DS3_readFloatAccelX()); printf("\r\n\t Y:\t %3.3f g", LSM6DS3_readFloatAccelY()); printf("\r\n\t Z:\t %3.3f g", LSM6DS3_readFloatAccelZ()); printf("\r\n"); }
پس از تکمیل این فایل، به سراغ main.c رفته و LSM6DS3.h را به آن اضافه میکنیم. سپس واحد SPI را فعال میکنیم و ماژول را با تابع نوشتهشده تنظیم میکنیم:
#include "LSM6DS3.h"
LL_SPI_Enable(SPI1); LL_GPIO_SetOutputPin(GPIOC, LL_GPIO_PIN_0); initialize_LSM6DS3();
در آخر نیز در حلقه while(1) توابع مربوط به چاپ خروجی ماژول را صدا میزنیم:
LL_mDelay(250); /* print calibrated values of the Gyroscope and the Accelerometer */ print_acc_cal(); print_gyro_cal();
اگر تمامی مراحل بهدرستی انجامشده باشد، و اتصالات پایههای VDD(3V3), GND, SCL, SDA, CS, SA0 به طرز صحیح به پایههای متناظر آنها در میکرو یعنی 3V3, GND, SPI1_SCK, SPI1_MOSI, PC0, SPI1_MISO صورت گرفته باشد، میبینیم که مقادیر شتاب اندازهگیری شده و تغییرات زاویه روی ترمینال سریال نمایش داده میشود؛
منبع: سیسوگ