جهاز للرؤية الليلية يعتمد على وحدة التصوير الحراري Flir Lepton 3

في وقت سابق ، كتبت مقالًا حول توصيل جهاز فك التشفير للتصوير الحراري بهاتف ذكي Flir One Gen 2. حان الوقت لإزالة وحدة lepton من جهاز فك التشفير هذا وتوصيلها مباشرة بوحدة التحكم الدقيقة عن طريق تجميع جهاز الرؤية الليلية بدقة 160 × 120 بكسل.

لبناء جهاز التصوير الحراري للرؤية الليلية الخاص بك ، ستحتاج إلى:

1) مجلس مع متحكم. أخذت لوحة من صديق صيني مع متحكم STM32F407VGT6. هذه وحدة تحكم جيدة: تردد 168 ميجا هرتز و 192 كيلو بايت من ذاكرة الوصول العشوائي.



2) العرض. أخذت عرضًا بدقة 320 × 240. تأتي هذه الشاشات مع وحدات تحكم مختلفة. حصلت عليه مع وحدة تحكم hx8347d.



3) لوحة لتوصيل lepton 3 على SPI و I2C.



4) اللبتون نفسه 3. العنصر الأصعب والأغلى. للحصول عليه ، اشتريت جهاز تصوير حراري Flir One Gen 2 معيب على موقع ئي باي وأخرجت ليبتون منه. يبدو في التكبير مثل هذا:



يمكنك استبعاد العنصر 3 من هذه القائمة ، ما لم تتمكن ، بالطبع ، من أخذ سرير لبرتون من جهاز تصوير حراري معطل ويمكنك إلغاء تثبيته (وبالمناسبة ، ستكون جهات اتصالها من الأسفل). لسوء الحظ ، المسافة بين الأرجل في اللبتون صغيرة جدًا ، لذلك لم يتم تقديم هذا الخيار لي.

لجمع كل هذا ، ما عليك سوى لحام كل شيء على النحو التالي:



تحتاج أيضًا إلى توصيل الطاقة. لتشغيل لوحة lepton ، أستخدم 5 فولت ، للوحة STM32 3.3 V. للحصول على 5 فولت من البطارية ، أستخدم محول TEL3-0511 (جهد الدخل من 4.5 إلى 9 فولت) ، وأخفض بالفعل 5 فولت على LP2950CZ-3.3 عادي (بالمناسبة ، يسخن حتى 70 درجة. هنا أيضًا ، تحتاج إلى استخدام محول DC / DC ، لكنني لم أشتريه بعد). يأكل ليبتون 3 ، بالمناسبة ، حسنا. عند تشغيله من 6 فولت ، يبلغ الاستهلاك الحالي للجهاز بأكمله حوالي 250 مللي أمبير. عندما يريد lepton النقر فوق الغالق للمعايرة ، يرتفع التيار إلى 500 مللي أمبير.

كل شيء معًا يبدو كالتالي:





للعمل مع لبتون ، تحتاج إلى برنامج. لقد استخدمت CubeMX و Keil 5. في هذه الحالة ، يتم تبسيط جميع روابط البرامج إلى استحالة.

يتم التواصل مع ليبتون على SPI. لم أستخدم I2C ، حيث لم يكن هناك حاجة خاصة لذلك. وفقًا لـ I2C ، يمكنك التحكم في حالة lepton ، وأنماط تشغيلها ، وتمكين / تعطيل وضع المعايرة التلقائية ، وما إلى ذلك. ولكن بالنسبة لجهاز الرؤية الليلية ، فهذا ليس ضروريًا بشكل خاص.

لفك تشفير البيانات ، كتبت وحدة:

الوحدة
leptoncontrol.h
#ifndef LEPTON_CONTROL_H #define LEPTON_CONTROL_H #include <stdbool.h> #include <stdio.h> //   ( ) #define LEPTON_ORIGINAL_IMAGE_WIDTH 160 #define LEPTON_ORIGINAL_IMAGE_HEIGHT 120 //  VoSPI #define VOSPI_FRAME_HEIGHT 60 //  VoSPI #define VOSPI_FRAME_WIDTH 80 //  VoSPI   (164  RAW14  244  RGB) #define VOSPI_PACKAGE_SIZE 164 //   VoSPI   #define VOSPI_PACKAGE_LINE_SIZE 160 //  VOSPI   #define VOSPI_SEGMENT_LINE_AMOUNT 60 void LEPTONCONTROL_Init(void);// void LEPTONCONTROL_CalculateCRC(unsigned short *crc,unsigned char byte);// crc bool LEPTONCONTROL_PushVoSPI(unsigned char data[VOSPI_PACKAGE_SIZE],bool *first_line);//    VoSPI    unsigned short *LEPTONCONTROL_GetRAW14Ptr(void);//      #endif 

leptoncontrol.c

 #include "leptoncontrol.h" #include "stm32f4xx_hal.h" static unsigned short RAW14Image[LEPTON_ORIGINAL_IMAGE_HEIGHT*LEPTON_ORIGINAL_IMAGE_WIDTH];//  static unsigned short CRCTable[256];//   CRC16 //     #define RESYNC_TIMEOUT_MS 19 //:   #define NO_SEGMENT -1 //:   #define ERROR_PACKAGE -2 //---------------------------------------------------------------------------------------------------- // //---------------------------------------------------------------------------------------------------- void LEPTONCONTROL_Init(void) { //    CRC unsigned short code; for(long n=0;n<256;n++) { code=((unsigned short)n)<<8; for(unsigned char m=0;m<8;m++) { if(code&(1<<15)) code=(code<<1)^0x1021; else code=code<<1; } CRCTable[n]=code; } } //---------------------------------------------------------------------------------------------------- // crc //---------------------------------------------------------------------------------------------------- void LEPTONCONTROL_CalculateCRC(unsigned short *crc,unsigned char byte) { *crc=CRCTable[(((*crc)>>8)^byte++)&0xFF]^((*crc)<<8); } //---------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------- long LEPTONCONTROL_ReadSegment(unsigned short *raw14_ptr,unsigned char data[VOSPI_PACKAGE_SIZE],bool *first_line) { static long current_package=-1; static long segment=-1; long n; *first_line=false; if ((data[0]&0x0F)==0x0F) return(NO_SEGMENT);//  unsigned short crc=data[2]; crc<<=8; crc|=data[3]; // CRC unsigned short crc16=0; LEPTONCONTROL_CalculateCRC(&crc16,data[0]&0x0F); LEPTONCONTROL_CalculateCRC(&crc16,data[1]); LEPTONCONTROL_CalculateCRC(&crc16,0); LEPTONCONTROL_CalculateCRC(&crc16,0); for(n=4;n<VOSPI_PACKAGE_SIZE;n++) LEPTONCONTROL_CalculateCRC(&crc16,data[n]); if (crc16!=crc) return(ERROR_PACKAGE);// CRC //   unsigned short package=data[0]&0x0F; package<<=8; package|=data[1]; if (package==0) { *first_line=true; current_package=0; } if (package==20) { unsigned char ttt=(data[0]&0x70)>>4;//     20  segment=ttt; } if (current_package<0) return(NO_SEGMENT); if (current_package!=package) { current_package=-1; return(ERROR_PACKAGE); } unsigned short *raw_ptr=raw14_ptr+current_package*VOSPI_PACKAGE_LINE_SIZE/2; for(n=0;n<VOSPI_PACKAGE_LINE_SIZE/2;n++,raw_ptr++) { //    big-endian: ,  unsigned short value=data[n*sizeof(short)+4]; value<<=8; value|=data[n*sizeof(short)+5]; *raw_ptr=value; } current_package++; if (current_package!=VOSPI_FRAME_HEIGHT) return(NO_SEGMENT); current_package=-1; return(segment); } //---------------------------------------------------------------------------------------------------- //    VoSPI    //---------------------------------------------------------------------------------------------------- bool LEPTONCONTROL_PushVoSPI(unsigned char data[VOSPI_PACKAGE_SIZE],bool *first_line) { *first_line=false; static long waitable_segment=1; long segment=LEPTONCONTROL_ReadSegment(RAW14Image+(waitable_segment-1)*VOSPI_FRAME_WIDTH*VOSPI_SEGMENT_LINE_AMOUNT,data,first_line); if (segment==ERROR_PACKAGE) HAL_Delay(RESYNC_TIMEOUT_MS); if (segment==ERROR_PACKAGE || segment==0) waitable_segment=1; if (segment==ERROR_PACKAGE || segment==NO_SEGMENT || segment==0) return(false); if (segment!=waitable_segment) { waitable_segment=1; if (segment!=1) return(false); } waitable_segment++; if (waitable_segment!=5) return(false); waitable_segment=1; return(true); } //---------------------------------------------------------------------------------------------------- //      //---------------------------------------------------------------------------------------------------- unsigned short *LEPTONCONTROL_GetRAW14Ptr(void) { return(RAW14Image); } 


كل العمل مع هذه الوحدة يتكون من تقديم بسيط للبيانات التي تم الحصول عليها بواسطة SPI.

العمل مع الوحدة
 while(1) { //   while(1) { HAL_SPI_Receive(&hspi1,buffer,VOSPI_PACKAGE_SIZE,0x1000); bool first_line=false; unsigned char *buffer_ptr=buffer; LEPTONCONTROL_PushVoSPI(buffer_ptr,&first_line); if (first_line==true) break; } //    lepton3 unsigned char *buffer_ptr=buffer; HAL_SPI_Receive(&hspi1,buffer_ptr,VOSPI_PACKAGE_SIZE*SPI_READ_VOSPI_AMOUNT,0x1000); buffer_ptr=buffer; for(long n=0;n<SPI_READ_VOSPI_AMOUNT;n++,buffer_ptr+=VOSPI_PACKAGE_SIZE) { bool first_line=false; bool res=LEPTONCONTROL_PushVoSPI(buffer_ptr,&first_line); if (res==true) CreateImage();//     } } 


بعد تجميع الإطار ، يتم ببساطة تطبيع قراءات المستشعر ، وتقليصها إلى النطاق [0.255] وعرضها على الشاشة في شكل ظلال رمادية. ومع ذلك ، لا شيء يمنع استخدام أي لوحة لتلوين الصورة.

لعرض الصورة على الشاشة ، أستخدم وحدة FSMC المضمنة في وحدة التحكم هذه في وضع ناقل البيانات 8 بت.

يمكن تنزيل البرنامج الكامل هنا .

فيديو للعمل (للأسف ، قمت بالتصوير على كاميرا قديمة بالجودة المناسبة - ليس لدي كاميرا فيديو معي الآن).


PS بالمناسبة ، يمكنك توصيل جهاز التصوير الحراري Flir One Gen 2 بلوحة التصحيح STM32F407Discovery مباشرة عبر USB. ومع ذلك ، فإن الاتصال غير مستقر - غالبًا ما يتم فقدان التصوير الحراري.


برنامج لمثل هذا الاتصال هنا . ربما يفهم شخص ما هو وكيفية جعل الاتصال مستقرًا.

كما أن وحدة lepton 3 هذه تتصل بسهولة وببساطة مع Raspberry Pi.


في هذه الحالة ، اضطررت إلى تعديل البرنامج من المستودع ، جاعلاً إصداري يعمل مع lepton 3.

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


All Articles