Anschließen eines Seek Thermal Imager an den STM32

Wärmebildkamera an Mikrocontroller anschließen? Kein Problem! Besonders wenn es sich um einen STM32 mit USB-Host-Schnittstelle und einem Seek Thermal Thermal Imager von Dadget handelt!


Lötkolben durch die Augen einer SeekThermal-Wärmebildkamera

Einführung


Ich denke, dass jeder auf solche Geräte wie eine Wärmebildkamera gestoßen ist. Lesen Sie zumindest darüber. Und unter diesen Geräten gibt es eine ganze Unterklasse von Gadgets, die kein eigenständiges Gerät sind, sondern als eine Art Set-Top-Box für einen Computer oder ein Smartphone dienen.

Heute werden wir über den Anschluss der Seek Thermal Wärmebildkamera an den STM32-Mikrocontroller sprechen. Und die Firma Dadget hat mir dieses Gerät zur Verfügung gestellt. Bei Geektimes-Erweiterungen wurde diese Wärmebildkamera mehr als einmal in Betracht gezogen: Sie behandelte hauptsächlich die Arbeit mit Android sowie einen Artikel über das Anschließen dieses Geräts an einen PC. In meinem Test möchte ich über meine eigenen Erfahrungen beim Anschließen einer Seek Thermal-Wärmebildkamera an den STM32-Mikrocontroller über einen USB-Host sprechen.

Hardwareanforderungen


Nicht so spezifisch! Alles, was Ihr STM32 haben sollte, ist eine USB-Schnittstelle, die im Host-Modus arbeiten kann, und eine Schnittstelle zur Steuerung des LCD-Bildschirms. Die naheliegendste Wahl ist die STM32F4 - Discovery. Ich hatte das STM32F746G-Discovery Board zur Hand. Dementsprechend wird die Beschreibung für dieses Board sein, aber! Weil Der Code wird in der CubeMX-Umgebung generiert. Es ist möglich, ein anderes EVM zu verwenden. Ich halte die von mir für dieses Projekt beantragte Zahlung für übermäßig.

Software-Teil


Diese Wärmebildkamera implementiert keine Klasse bei der Kommunikation über USB. Alle Interaktionen werden direkt ausgeführt, Massenanforderungen über Endpunkte. Durch Senden von Befehlen (Anforderungen) an den Steuerendpunkt können Sie die Wärmebildkamera einschalten, kalibrieren und einen Frame oder mehrere Frames übertragen lassen. In diesem Forum wird eine besonders detaillierte Arbeit mit Seek Thermal beschrieben.

Damit die Wärmebildkamera mit dem STM32-Mikrocontroller funktioniert, benötigen wir:

1) Nehmen Sie ein USB-Host-Beispiel für Ihr Lieblingsboard (ich habe das STM32-USB-Host-CDC-Beispiel aus der STM32F7-CubeMX-Beispielsammlung genommen).
2) Verwerfen Sie die Initialisierungsprozedur der Geräteklasse.
3) Schreiben Sie praktische Wrapper für die Arbeit mit den Funktionen Lesen / Schreiben, um Endpunkte und Datenendpunkte zu steuern.
4) Schreiben Sie Ihre Funktion, um Rohdaten in etwas Konvertiertes umzuwandeln.
5) Verwenden Sie LUT (Color Look Up Table), um ein monochromes Bild in Farbe einzufärben. Dieser Fehler trat in der STM32-Familie von Mikrocontrollern auf, die unabhängig mit LCD-Bildschirmen gesteuert werden können.

Lassen Sie uns zunächst etwas Ähnliches wie ein Stück aus libusb tun, um die HAL-Bibliothek mit dem folgenden Code zu verknüpfen:

Prozedurcode von 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; } } 


Dann gehen wir hierher und schauen uns die vendor_transfer- Prozedur an. Es tut auch nicht weh, auf die Liste der Anfragen zu achten.

Prozedurcode von 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; } 


Als nächstes schreiben wir die Prozedur zum Empfangen des Bildes. Im CDC-Beispiel gibt es nichts Besonderes zu kommentieren.

USB-Datenempfangsverfahren
 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; } 


Außerdem müssen wir die empfangenen Daten irgendwie auf den Bildschirm zeichnen. Ich stelle fest, dass im 20. Datenbyte, bei dem es sich um ein 16-Bit-Array von Pixeln handelt, Informationen über den Rahmentyp gespeichert werden. Es gibt verschiedene Arten von Frames. Wir interessieren uns für einen Kalibrierrahmen und einen Arbeitsrahmen. Ein Kalibrierungsrahmen wird erhalten, wenn die Wärmebildkamera den Vorhang schließt und ein Bild der „Dunkelheit“ macht. Wenn Sie ein normales Bild aufnehmen, ist der Verschluss geöffnet. So hören Sie beim Arbeiten immer, wie das Gerät auf den Auslöser klickt.

Bildschirmzeichnungsverfahren
 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; } } 


Schließlich der Hauptzyklus, aus dem ersichtlich ist - wo was geschnitten wurde, wo etwas eingefügt wurde.

Hauptzyklus
 #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; } } } } 


Fazit


Die Bedienung der Wärmebildkamera mit einem Mikrocontroller sieht viel schneller aus als mit einem Smartphone. Ich empfehle dieses Dadget, um das Wärmebild elektronischer Geräte zu beurteilen. Die Wärmebildkamera hat eine einstellbare Brennweite, mit der Sie sogar einzelne elektronische Komponenten auf der Platine berücksichtigen können! Abschließend ein Video, anhand dessen Sie die Geschwindigkeit der Wärmebildkamera bewerten können (etwa 8-9 fps).



Informationen für potenzielle Käufer

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


All Articles