توصيل جهاز التصوير الحراري بجهاز STM32

توصيل جهاز التصوير الحراري بوحدة التحكم الدقيقة؟ لا مشكلة! خاصة إذا كان STM32 مزودًا بواجهة USB Host وجهاز تصوير حراري من Dadget!


لحام الحديد من خلال عيون التصوير الحراري SeekThermal

مقدمة


أعتقد أن الجميع قد عثروا على أدوات مثل التصوير الحراري ، حسنًا ، على الأقل اقرأ عنها. ومن بين هذه الأجهزة ، هناك فئة فرعية كاملة من الأدوات التي ليست جهازًا مستقلاً ، ولكنها تعمل كنوع من أجهزة فك التشفير لجهاز كمبيوتر أو هاتف ذكي.

سنتحدث اليوم عن توصيل جهاز التصوير الحراري الحراري Seek بجهاز التحكم الدقيق STM32. وقد زودتني شركة Dadget بهذا الجهاز. في توسعات Geektimes ، تم اعتبار هذا التصوير الحراري أكثر من مرة: فقد غطى بشكل أساسي عمله مع Android ، بالإضافة إلى مقالة حول توصيل هذا الجهاز بجهاز الكمبيوتر. في مراجعتي ، أريد أن أتحدث عن تجربتي الخاصة في توصيل جهاز التصوير الحراري Seek إلى متحكم متحكم STM32 عبر مضيف USB.

متطلبات الأجهزة


ليس محددًا جدًا! كل ما يجب أن يكون لديك STM32 هو واجهة USB قادرة على العمل في وضع المضيف وبعض الواجهة للتحكم في شاشة LCD. الخيار الأكثر وضوحًا هو أخذ STM32F4 - Discovery. كان لدي لوحة STM32F746G-Discovery في متناول اليد. تبعا لذلك ، سيكون الوصف لهذه اللوحة ، ولكن! لأن يتم إنشاء الرمز في بيئة CubeMX ، فمن الممكن استخدام EVM آخر. أنا أعتبر أن الدفع المقدم من قبلي مبالغ فيه لهذا المشروع.

جزء البرنامج


لا يطبق هذا التصوير الحراري أي فئة عند الاتصال عبر USB. يتم تنفيذ جميع التفاعلات مباشرة ، والطلبات المجمعة من خلال نقاط النهاية. عن طريق إرسال أوامر (طلبات) إلى نقطة نهاية التحكم ، يمكنك تشغيل التصوير الحراري ومعايرته وجعله يرسل إطارًا أو عدة إطارات. يتم وصف العمل التفصيلي بشكل خاص مع Seek Thermal في هذا المنتدى .

وبالتالي ، لكي يعمل جهاز التصوير الحراري مع وحدة التحكم الدقيقة STM32 ، نحتاج إلى:

1) خذ أي مثال لمضيف USB للوحة المفضلة لديك (أخذت مثال STM32 USB Host CDC من مجموعة عينات STM32F7 CubeMX) ؛
2) التخلص من إجراء التهيئة لفئة الجهاز ؛
3) كتابة أغلفة ملائمة للعمل مع وظائف القراءة / الكتابة للتحكم في نقاط النهاية ونقاط نهاية البيانات ؛
4) اكتب وظيفتك لتحويل البيانات الخام إلى شيء معروض ؛
5) استخدم LUT (جدول البحث عن اللون) لتلوين صورة أحادية اللون في اللون. ظهر هذا الأمر في عائلة STM32 من وحدات التحكم الدقيقة ، والتي يمكن التحكم فيها بشكل مستقل مع شاشات LCD.

للبدء ، لنفعل شيئًا مشابهًا لقطعة من libusb ، والتي ستساعدنا على ربط مكتبة HAL بالرمز التالي:

رمز الإجراء من libusb
int libusb_control_transfer(libusb_device_handle* dev_handle, uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned char* data, uint16_t wLength, unsigned int timeout) { hUSBHost.Control.setup.b.bmRequestType = request_type; hUSBHost.Control.setup.b.bRequest = bRequest; hUSBHost.Control.setup.b.wValue.w = wValue; hUSBHost.Control.setup.b.wIndex.w = wIndex; hUSBHost.Control.setup.b.wLength.w = wLength; int status; do { status = USBH_CtlReq(&hUSBHost, data, wLength); } while (status == USBH_BUSY); if (status != USBH_OK) { hUSBHost.RequestState = CMD_SEND; return 0; } else { return wLength; } } 


ثم نذهب هنا ونلقي نظرة على إجراء vendor_transfer . أيضا ، لا يضر أن تولي اهتماما لقائمة الطلبات بناء الطلب .

رمز إجراء vendor_transfer
 int vendor_transfer(bool direction, uint8_t req, uint16_t value, uint16_t index, uint8_t * data, uint8_t size, int timeout) { int res; uint8_t bmRequestType = (direction ? LIBUSB_ENDPOINT_IN : LIBUSB_ENDPOINT_OUT) | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_INTERFACE; uint8_t bRequest = req; uint16_t wValue = value; uint16_t wIndex = index; uint8_t * aData = data; uint16_t wLength = size; if (!direction) { // to device #ifdef LOG_DEBUG USBH_UsrLog("ctrl_transfer(0x%x, 0x%x, 0x%x, 0x%x, %d)", bmRequestType, bRequest, wValue, wIndex, wLength); printf(" ["); for (int i = 0; i < wLength; i++) { printf(" %02x", data[i]); } printf(" ]\n"); #endif res = libusb_control_transfer(handle, bmRequestType, bRequest, wValue, wIndex, aData, wLength, timeout); #ifdef LOG_DEBUG if (res != wLength) { USBH_UsrLog("Bad returned length: %d\n", res); } #endif } else { // from device #ifdef LOG_DEBUG USBH_UsrLog("ctrl_transfer(0x%x, 0x%x, 0x%x, 0x%x, %d)", bmRequestType, bRequest, wValue, wIndex, wLength); #endif res = libusb_control_transfer(handle, bmRequestType, bRequest, wValue, wIndex, aData, wLength, timeout); #ifdef LOG_DEBUG if (res != wLength) { USBH_UsrLog("Bad returned length: %d\n", res); } printf(" -> ["); for (int i = 0; i < res; i++) { printf(" %02x", data[i]); } printf(" ]\n"); #endif } return res; } 


بعد ذلك ، نكتب الإجراء الخاص باستلام الصورة. لا يوجد شيء خاص للتعليق عليه ، تجسس في مثال CDC.

إجراء استقبال بيانات USB
 int CAM_ProcessReception(USBH_HandleTypeDef *phost) { USBH_URBStateTypeDef URB_Status = USBH_URB_IDLE; uint16_t length = 0; uint8_t data_rx_state = CDC_RECEIVE_DATA; size = FRAME_WIDTH * FRAME_HEIGHT; int bufsize = size * sizeof(uint16_t); int bsize = 0; while (data_rx_state != CDC_IDLE) { switch(data_rx_state) { case CDC_RECEIVE_DATA: USBH_BulkReceiveData (phost, &rawdata[bsize], 512, InPipe); data_rx_state = CDC_RECEIVE_DATA_WAIT; break; case CDC_RECEIVE_DATA_WAIT: URB_Status = USBH_LL_GetURBState(phost, InPipe); /*Check the status done for reception*/ if(URB_Status == USBH_URB_DONE ) { length = USBH_LL_GetLastXferSize(phost, InPipe); bsize+= length; if(((bufsize - length) > 0) && (bsize < bufsize)) //TODO { data_rx_state = CDC_RECEIVE_DATA; } else { data_rx_state = CDC_IDLE; } #if (USBH_USE_OS == 1) osMessagePut ( phost->os_event, USBH_CLASS_EVENT, 0); #endif } break; default: break; } } return data_rx_state; } 


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

إجراء رسم الشاشة
 void BSP_LCD_DrawArray(uint32_t Xpos, uint32_t Ypos, uint32_t width, uint32_t height, uint8_t bit_pixel, uint8_t *pbmp) { uint32_t index = 0; uint32_t index2 = 0; // uint32_t address; //uint32_t input_color_mode = 0; //uint32_t Color; static int pixel; static int calib_pixel=0; uint8_t Component; static int v; uint8_t frame_type; frame_type = *(__IO uint8_t *) (pbmp + 20); switch (frame_type) { case 6: calib_pixel = (*(uint16_t*)pbmp); minpixel = calib_pixel; //calib_pixel = bswap_16(calib_pixel); break; case 3: /* Convert picture to ARGB8888 pixel format */ for(index=0; index < height; index++) { for(index2=0; index2 < width; index2++) { pixel = (*(uint16_t*)pbmp); //pixel = bswap_16(pixel); //v = pixel - calib_pixel; //v += 0x8000; if (maxpixel < pixel) maxpixel = pixel; if (minpixel > pixel) minpixel = pixel; if (pixel < 0) { pixel = 0; } if (pixel > 0xFFFF) { pixel = 0xFFFF; } v = map(pixel, 6000, 13000, 0, 255); //v = (v - MAX) * 255 / (MIN - MAX); if (v < 0) v = 0; if (v > 255) v = 255; BSP_LCD_DrawPixel(index2+270, index+100, (0xFF << 24) | (uint8_t)v << 16 | (uint8_t)v << 8 | (uint8_t)v); pbmp += 2; } } break; case 4: break; } } 


وأخيرًا ، الدورة الرئيسية ، التي يمكن رؤيتها منها - حيث تم قطع ما ، وأين تم إدخال شيء ما.

الدورة الرئيسية
 #define DELAY1 10 #define USB_PIPE_NUMBER 0x81 #define FRAME_WIDTH 208 #define FRAME_HEIGHT 156 uint8_t OutPipe, InPipe; uint8_t usb_device_state; uint8_t rawdata[FRAME_HEIGHT*FRAME_WIDTH*2]; uint8_t data[64]; USBH_StatusTypeDef status; uint8_t transf_size; int size; int main(void) { /* Enable the CPU Cache */ CPU_CACHE_Enable(); /* STM32F7xx HAL library initialization: - Configure the Flash ART accelerator on ITCM interface - Configure the Systick to generate an interrupt each 1 msec - Set NVIC Group Priority to 4 - Low Level Initialization */ HAL_Init(); /* Configure the System clock to have a frequency of 200 MHz */ SystemClock_Config(); /* Init CDC Application */ CDC_InitApplication(); /* Init Host Library */ USBH_Init(&hUSBHost, USBH_UserProcess, 0); /* Add Supported Class */ //USBH_RegisterClass(&hUSBHost, USBH_CDC_CLASS); /* Start Host Process */ USBH_Start(&hUSBHost); /* Run Application (Blocking mode) */ while (1) { /* USB Host Background task */ USBH_Process(&hUSBHost); if (hUSBHost.gState == HOST_CHECK_CLASS) { switch (usb_device_state) { case 1: status = USBH_Get_StringDesc(&hUSBHost,hUSBHost.device.DevDesc.iManufacturer, data , 64); if (status == USBH_OK) { USBH_UsrLog("## Manufacturer : %s", (char *)data); HAL_Delay(1000); usb_device_state = 1; } break; case 2: status = USBH_Get_StringDesc(&hUSBHost, hUSBHost.device.DevDesc.iProduct, data , 64); if (status == USBH_OK) { USBH_UsrLog("## Product : %s", (char *)data); HAL_Delay(1000); usb_device_state = 2; } break; case 0: InPipe = USBH_AllocPipe(&hUSBHost, 0x81); status = USBH_OpenPipe(&hUSBHost, InPipe, 0x81, hUSBHost.device.address, hUSBHost.device.speed, USB_EP_TYPE_BULK, USBH_MAX_DATA_BUFFER); if (status == USBH_OK) usb_device_state = 3; break; case 3: HAL_Delay(1); const uint8_t data0[2] = {0x00, 0x00}; vendor_transfer(0, SET_OPERATION_MODE, 0, 0, data0, 2); vendor_transfer(0, SET_OPERATION_MODE, 0, 0, data0, 2); vendor_transfer(0, SET_OPERATION_MODE, 0, 0, data0, 2); data[0] = 0x01; vendor_transfer(0, TARGET_PLATFORM, 0, 0, data, 1); data[0] = 0x00; data[1] = 0x00; vendor_transfer(0, SET_OPERATION_MODE, 0, 0, data); transf_size = vendor_transfer(1, GET_FIRMWARE_INFO, 0, 0, data, 4); transf_size = vendor_transfer(1, READ_CHIP_ID, 0, 0, data, 12); const uint8_t data1[6] = { 0x20, 0x00, 0x30, 0x00, 0x00, 0x00 }; vendor_transfer(0, SET_FACTORY_SETTINGS_FEATURES, 0, 0, data1, 6); transf_size = vendor_transfer(1, GET_FACTORY_SETTINGS, 0, 0, data, 64); const uint8_t data2[6] = { 0x20, 0x00, 0x50, 0x00, 0x00, 0x00 }; vendor_transfer(0, SET_FACTORY_SETTINGS_FEATURES, 0, 0, data2, 6); transf_size = vendor_transfer(1, GET_FACTORY_SETTINGS, 0, 0, data, 64); const uint8_t data3[6] = { 0x0c, 0x00, 0x70, 0x00, 0x00, 0x00 }; vendor_transfer(0, SET_FACTORY_SETTINGS_FEATURES, 0, 0, data3, 6); transf_size = vendor_transfer(1, GET_FACTORY_SETTINGS, 0, 0, data, 24); const uint8_t data4[6] = { 0x06, 0x00, 0x08, 0x00, 0x00, 0x00 }; vendor_transfer(0, SET_FACTORY_SETTINGS_FEATURES, 0, 0, data4, 6); vendor_transfer(1, GET_FACTORY_SETTINGS, 0, 0, data, 12); const uint8_t data5[2] = { 0x08, 0x00 }; vendor_transfer(0, SET_IMAGE_PROCESSING_MODE, 0, 0, data5, 2); vendor_transfer(1, GET_OPERATION_MODE, 0, 0, data,2); const uint8_t data6[2] = { 0x08, 0x00 }; vendor_transfer(0, SET_IMAGE_PROCESSING_MODE, 0, 0, data6, 2); const uint8_t data7[2] = { 0x01, 0x00 }; vendor_transfer(0, SET_OPERATION_MODE, 0, 0, data7, 2); vendor_transfer(1, GET_OPERATION_MODE, 0, 0, data, 2); USBH_UsrLog("SeeK Thermal Init Done.\n"); size = FRAME_WIDTH * FRAME_HEIGHT; int bufsize = size * sizeof(uint16_t); status = CDC_IDLE; usb_device_state = 4; break; case 4: //while(1 ){ // request a frame data[0] = (uint8_t)(size & 0xff); data[1] = (uint8_t)((size>>8)&0xff); data[2] = 0; data[3] = 0; if (status == CDC_IDLE) vendor_transfer(0, 0x53, 0, 0, data, 4); status = CAM_ProcessReception(&hUSBHost); if (status == CDC_IDLE) BSP_LCD_DrawArray(10, 10, FRAME_WIDTH, FRAME_HEIGHT, 16, rawdata); usb_device_state = 4; break; } } } } 


الخلاصة


يبدو تشغيل التصوير الحراري باستخدام وحدة التحكم الدقيقة أسرع بكثير من الهاتف الذكي. أوصي بهذه الأداة لتقييم الصورة الحرارية للأجهزة الإلكترونية. يحتوي جهاز التصوير الحراري على طول بؤري قابل للتعديل ، والذي يسمح لك حتى بالنظر في المكونات الإلكترونية الفردية على اللوحة! في الختام ، فيديو يمكنك من خلاله تقييم سرعة التصوير الحراري (في مكان ما حول 8-9 إطارًا في الثانية)



معلومات للمشترين المحتملين

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


All Articles