التعرف على الإيماءات باستخدام APDS-9960

الصورة

عند قراءة التعليقات على مقالتي السابقة حول APDS-9960 ، حيث كان الأمر يتعلق بالتعرف على الألوان ومستوى الضوء ، أصبح هناك شيئان واضحان بالنسبة لي: 1) موضوع التعرف على الإيماءات مثير للاهتمام و 2) لم يتم الكشف عن هذا الموضوع.

في الواقع ، إذا تناولت وصف APDS-9960 ، فعندئذٍ دون النظر إلى الإيماءات ، يبدو الوصف غير مكتمل إلى حد ما. لذلك وجدت بعض الوقت الحر لاستكشاف هذا الموضوع أيضًا.

في هذه المقالة ، استرعي انتباهك إلى نظرة عامة على إمكانات التعرف على الإيماءات التي يوفرها مستشعر APDS-9960.

ستنظر المقالة في آلية إعداد المستشعر وجمع البيانات ومعالجتها وعرضها. يمكنك أن ترى بنفسك مدى سهولة العمل مع الإيماءات باستخدام APDS-9960.

كما في المرة الأخيرة ، سيصاحب المقال رمز ، كل ما يحدث سيتم وصفه بالتفصيل. النسخة الكاملة من الكود متاحة في نهاية المقال.

ملاحظة صغيرة فورًا: لا يحتوي APDS-9960 على آلية تلقائية مدمجة للكشف عن الإيماءات ؛ هذا هو ، مثل هذا هنا ، قرأت ، يعني ، التسجيل ، وهناك بالفعل لفتة معالجة - هذا ليس في APDS-9960 ؛ وهذا يعني أنه يجب عليك كتابة خوارزمية تفسير الإيماءات الخاصة بك ، والتي سنفعلها لاحقًا.

بشكل عام ، هذا جيد وغير جيد على حد سواء. ليس في الحقيقة - لأنه يمكن أن يعقد دراسة هذا المستشعر للمبتدئين ، ولكنه جيد ، لأنه ، إلى جانب بيانات التقريب ، يمكنك ، عن طريق التنقيح ، حتى الخروج بإيماءاتك الخاصة من مختلف الأنواع وأي شيء.

ولكن ، نظرًا لأن هذه المقالة تحتوي على وظيفة نظرة عامة فقط ، فإننا نقتصر على إيماءات UP-DOWN-LEFT-RIGHT الأساسية فقط.

حسنًا ، لنبدأ.

النظرية


سأسمح لنفسي ببعض العتاد.

للحصول على المعلومات اللازمة حول حركة واتجاه الحركة ، يستخدم APDS-9960 LED IR وأربعة صمامات ضوئية ، والتي ، كما هو موضح في الشكل أدناه ، تكشف الإشارات في نطاق الأشعة تحت الحمراء القريبة (NIR).

الصورة

وظيفة IR LED (LED) لها وظيفة الإضاءة الخلفية ، وتسجيل الصمامات الثنائية الضوئية (UDLR) الضوء المنعكس من "العائق".

توجد الثنائيات الضوئية على المستشعر بطريقة ، اعتمادًا على اتجاه حركة "العائق" ، سيتلقى الثنائي الضوئي المقابل معظم إشارة الأشعة تحت الحمراء المنعكسة عند الإدخال وجزءًا أصغر عند الإخراج. في الوقت نفسه ، تخبرنا الوثائق الموجودة على APDS-9960 بشكل لا لبس فيه أنه يمكنك تفسير اتجاه الحركة عن طريق قياس ومقارنة السعة وفرق الطور للإشارات من الثنائيات الضوئية UDLR.

الصورة

تدرب


للعمل مع APDS-9960 ، وكذلك في المرة الأخيرة ، سنستخدم STM32VLDISCOVERY. لم يتغير الاتصال أيضًا.

تكوين APDS-9960

نجعل الإعداد الأولي لجهاز الاستشعار.

مثل هذا:

APDS9960_init
void APDS9960_init(void) { i2c1_write(APDS9960_CONTROL, DEFAULT_PGAIN); i2c1_write(APDS9960_GPENTH, DEFAULT_GPENTH); i2c1_write(APDS9960_GEXTH, DEFAULT_GEXTH); i2c1_write(APDS9960_GCONF2, DEFAULT_GGAIN); i2c1_write(APDS9960_GPULSE, DEFAULT_PULSE_LENGTH); i2c1_write(APDS9960_PPULSE, DEFAULT_PULSE_LENGTH); } 

ما الذي يحدث هنا؟ دعنا نحصل على حق.

 i2c1_write(APDS9960_CONTROL, DEFAULT_PGAIN); 

PGAIN (التحكم في اكتساب القرب) هي معلمة تتحكم في اكتساب حساسية التقارب. قم بتعيين قيمة 2 ، والتي تقابل أربعة أضعاف الكسب.

 i2c1_write(APDS9960_GPENTH, DEFAULT_GPENTH); i2c1_write(APDS9960_GEXTH, DEFAULT_GEXTH); 

GPENTH (القرب من لفتة الدخول سجل عتبة) - تحدد هذه المعلمة قيمة عتبة القرب لتحديد بداية التعرف على الإيماءات.

يحدد GEXTH (سجل حد الخروج من الإيماءة) ، على التوالي ، قيمة الحد لتحديد نهاية التعرف على الإيماءة.

 i2c1_write(APDS9960_GCONF2, DEFAULT_GGAIN); 

في تسجيل GCONF2 (تكوين الإيماءة 2) ، قمنا صراحةً بتعيين معلمة GGAIN (التحكم في اكتساب الإيماءات) إلى أربعة أضعاف قيمة الكسب.

 i2c1_write(APDS9960_GPULSE, DEFAULT_PULSE_LENGTH); i2c1_write(APDS9960_PPULSE, DEFAULT_PULSE_LENGTH); 

الإضاءة الخلفية بشكل افتراضي ، يتم تعيين قيمة المصدر الحالي لمصباح LED الخلفي IR على 0 ، وهو ما يتوافق مع تيار 100 مللي أمبير ، وهذا يناسبنا تمامًا - لن نقوم بتغييره.

إن إضاءة IR في APDS-9960 عبارة عن سلسلة من النبضات وتتميز بمعلمات التسجيل المقابلة لإيماءات GPULSE (عدد نبضات الإيماءات وطولها): GPLEN (طول نبضة الإيماءات) و GPULSE (عدد نبضات الإيماءات) ، بالإضافة إلى تقريب PPULSE (سجل عدد نبضات التقارب) ): PPLEN (طول النبض التقريبي) و PPULSE (عدد نبضات التقارب) يحددان عدد النبضات وفترة كل نبضة فردية.

حدد أن GPLEN و PPLEN سيأخذان قيمة 2 تساوي 16 μs ، و GPULSE و PPULSE بقيمة 9 ، والتي تقابل 10 نبضات.

كما ترى ، تبين أن الإعداد أكثر تعقيدًا قليلاً من الإعداد المماثل للتعرف على الألوان والإضاءة من مراجعة APDS-9960 السابقة.

قراءة البيانات

ننتقل الآن إلى دورة البرنامج الرئيسية ، حيث نبدأ في تسجيل وتفسير البيانات من الثنائيات الضوئية بين الحين والآخر ، ونتعلم أيضًا كيفية التمييز بين إيماءة من أخرى.

أولاً ، فلنبدأ APDS-9960 بوظائف للعمل مع الإيماءات والتكبير.

 GesturesSet(GESTURES_START); 

وعلى الفور نبدأ في تتبع معلمة GVALID. GVALID (بيانات الإيماءات FIFO) هي معلمة في سجل GSTATUS (سجل حالة الإيماءات) ، والتي ، في حالة غير الصفر ، تُعلمنا أن المستشعر يحتوي على بيانات إيماءات قابلة للاستخدام.

تعلمنا الوثائق أن معلومات الإيماءات موجودة في المخزن المؤقت ، في منطقة ذاكرة الوصول العشوائي ، والتي يبلغ حجمها بشكل عام 32 × 4 بايت.

من الناحية العملية ، يمكن العثور على الحجم الفعلي لهذا المخزن المؤقت من خلال قراءة قيمة سجل GFLVL (مستوى إيماءة FIFO) ، أي وفقًا لملاحظاتي التجريبية التجريبية البحتة ، يتم الحصول على GFLVL * 4. شيء من هذا القبيل:

الصورة

حسنًا ، على النحو التالي من اسم المخزن المؤقت ، يتم ترتيب البيانات الموجودة فيه بالترتيب First In - First Out. أي ، تقريبًا ، "في وقت سابق" وصلت الإشارة من كل من الثنائيات الضوئية ، "أعلى" في GFLVL.

يمكن قراءة البيانات من الثنائيات الضوئية (UDLR) من سجل الإيماءات FIFO المقابل:

- GFIFO_U (بيانات إيماءة FIFO ، UP)
- GFIFO_D (إيماءات FIFO ، لأسفل)
- GFIFO_L (بيانات الإيماءة FIFO ، اليسار)
- GFIFO_R (بيانات إيماءة FIFO ، يمين)

بعد كل قراءة للقيم من هذه السجلات ، يتم إنقاص GFLVL ؛ وبالتالي ، بطريقة جيدة ، من الضروري قراءة المخزن المؤقت بأكمله حتى يصل GFLVL إلى الصفر.

لتحديد الإيماءات ، نحتاج فقط إلى البايتات الأربعة الأولى من هذا المخزن المؤقت ، وليس أكثر. لذلك ، سوف نقرأها فقط.

 GestureUp = i2c1_read(APDS9960_GFIFO_U); GestureDown = i2c1_read(APDS9960_GFIFO_D); GestureLeft = i2c1_read(APDS9960_GFIFO_L); GestureRight = i2c1_read(APDS9960_GFIFO_R); 

التعرف على الإيماءات

لتفسير نوع الإيماءة التي حدثت ، سنجري حسابات بسيطة:

 GestUpDown = GestureUp-GestureDown; GestLeftRight = GestureLeft-GestureRight; 

لتحديد أي من الإيماءات التي حدثت في الوقت الحالي ، ليست قيم GestUpDown و GestLeftRight هي التي تهمنا ، ولكن فقط علامة الرقم الحقيقي ، إذا جاز التعبير.

بمعنى آخر ، مع أخذ القيم السلبية والإيجابية لمتغيري GestUpDown و GestLeftRight كمدخل ، نحدد أي إيماءة مثالية.

يظهر جدول الحقيقة للمتغيرات GestUpDown و GestLeftRight في الشكل أدناه.

الصورة

أعد ضبط GFLVL الآن:

 GesturesSet(GESTURES_STOP); 

... والعودة إلى بداية دورة البرنامج الرئيسية.

والآن الرمز بالكامل:

الرئيسية ج
 #include "stm32f10x.h" #define APDS9960_I2C_ADDR 0x39 #define APDS9960_ENABLE 0x80 #define APDS9960_GSTATUS 0xAF #define APDS9960_GFLVL 0xAE //Gesture FIFO Register (0xFC – 0xFF): #define APDS9960_GFIFO_U 0xFC #define APDS9960_GFIFO_D 0xFD #define APDS9960_GFIFO_L 0xFE #define APDS9960_GFIFO_R 0xFF #define APDS9960_CONTROL 0x8F #define APDS9960_GPENTH 0xA0 #define APDS9960_GEXTH 0xA1 #define APDS9960_GCONF2 0xA3 #define APDS9960_GPULSE 0xA6 #define APDS9960_PPULSE 0x8E #define GESTURES_START 0x01 #define GESTURES_STOP 0x02 #define DEFAULT_GPENTH 40 // Threshold for entering gesture mode #define DEFAULT_GEXTH 30 // Threshold for exiting gesture mode #define DEFAULT_PGAIN 8 // Proximity Gain Control: 4X #define DEFAULT_GGAIN 0x40 // Gesture Gain Control: 4X #define DEFAULT_PULSE_LENGTH 0x89 // 16us, 10 pulses /* Bit fields */ #define APDS9960_PON 0x01 #define APDS9960_AEN 0x02 #define APDS9960_PEN 0x04 #define APDS9960_WEN 0x08 #define APSD9960_AIEN 0x10 #define APDS9960_PIEN 0x20 #define APDS9960_GEN 0x40 #define APDS9960_GVALID 0x01 int GestUpDown = 0; int GestLeftRight = 0; //----------------------------------------------------------------------- uint8_t i2c1_read(uint8_t addr); void i2c1_write(uint8_t addr, uint8_t data); void I2C1_init(void) { I2C_InitTypeDef I2C_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB| RCC_APB2Periph_AFIO , ENABLE); GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_Init(GPIOB, &GPIO_InitStructure); I2C_StructInit(&I2C_InitStructure); I2C_InitStructure.I2C_ClockSpeed = 100000; I2C_InitStructure.I2C_OwnAddress1 = 0x01; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_Init(I2C1, &I2C_InitStructure); I2C_Cmd(I2C1, ENABLE); } //----------------------------------------------------------------------- void APDS9960_init(void) { i2c1_write(APDS9960_CONTROL, DEFAULT_PGAIN); i2c1_write(APDS9960_GPENTH, DEFAULT_GPENTH); i2c1_write(APDS9960_GEXTH, DEFAULT_GEXTH); i2c1_write(APDS9960_GCONF2, DEFAULT_GGAIN); i2c1_write(APDS9960_GPULSE, DEFAULT_PULSE_LENGTH); i2c1_write(APDS9960_PPULSE, DEFAULT_PULSE_LENGTH); } //----------------------------------------------------------------------- uint8_t i2c1_read(uint8_t addr) { uint8_t data; while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, APDS9960_I2C_ADDR<<1, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1, addr); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, APDS9960_I2C_ADDR<<1, I2C_Direction_Receiver); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); data = I2C_ReceiveData(I2C1); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); I2C_AcknowledgeConfig(I2C1, DISABLE); I2C_GenerateSTOP(I2C1, ENABLE); while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); return data; } //----------------------------------------------------------------------- void i2c1_write(uint8_t addr, uint8_t data) { I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, APDS9960_I2C_ADDR<<1, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1, addr); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C1, data); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTOP(I2C1, ENABLE); while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)) {}; } //----------------------------------------------------------------------- void GesturesSet(uint8_t GestSel) { switch (GestSel) { case GESTURES_START: i2c1_write(APDS9960_ENABLE, APDS9960_GEN | APDS9960_PEN | APDS9960_PON); break; case GESTURES_STOP: i2c1_write(APDS9960_ENABLE, APDS9960_PEN | APDS9960_PON); break; default: i2c1_write(APDS9960_ENABLE, APDS9960_GEN | APDS9960_PEN | APDS9960_PON); } } //----------------------------------------------------------------------- int main() { uint8_t GFLVL_buf = 0; uint8_t GSTATUS_buf = 0; uint8_t GestureUp = 0; uint8_t GestureDown = 0; uint8_t GestureLeft = 0; uint8_t GestureRight = 0; I2C1_init(); APDS9960_init(); while (1) { GFLVL_buf = 0; GSTATUS_buf = 0; GestureUp = 0; GestureDown = 0; GestureLeft = 0; GestureRight = 0; GestUpDown = 0; GestLeftRight = 0; GesturesSet(GESTURES_START); GSTATUS_buf = i2c1_read(APDS9960_GSTATUS); if(GSTATUS_buf & APDS9960_GVALID) { GFLVL_buf = i2c1_read(APDS9960_GFLVL); if(GFLVL_buf) { GestureUp = i2c1_read(APDS9960_GFIFO_U); GestureDown = i2c1_read(APDS9960_GFIFO_D); GestureLeft = i2c1_read(APDS9960_GFIFO_L); GestureRight = i2c1_read(APDS9960_GFIFO_R); //Truth table: //UP: GestUpDown(+) | GestLeftRight(+) //DOWN: GestUpDown(-) | GestLeftRight(-) //LEFT: GestUpDown(+) | GestLeftRight(-) //RIGHT: GestUpDown(-) | GestLeftRight(+) GestUpDown = GestureUp-GestureDown; GestLeftRight = GestureLeft-GestureRight; GesturesSet(GESTURES_STOP); } } } } 


أريد أن أشير إلى أن آلية إيماءات APDS-9960 تعمل بشكل جيد للغاية. الاعتراف مستقر ، تعمل المرشحات المدمجة في APDS-9960 UV و IR بشكل جيد.

آمل أن تكون هذه المواد مفيدة لشخص ما. شكرا لكم على اهتمامكم.

Source: https://habr.com/ru/post/ar424947/


All Articles