W tym poście chciałbym opisać sposób obsługi czujnika PAJ7620 do odczytywania gestów.
Podłączenie:
Czujnik można podłączyć przez 5 pinów:
- VCC - zasilanie 2,8 do 3.3V.
- GND - masa.
- SDA - I2C linia danych.
- SCL - I2C linia zegara.
- INT - opcjonalnie do podłączenia. Na pinie pojawia się sygnał niski, który informuje o odczytaniu jednego z dostępnych dla tego czujnika gestów.
Testy Arduino:
Do szybkich testów czujnika wykorzystałem platformę Arduino z układem Arduino Uno oraz biblioteką RavEng PAJ7620 (link github)
Podłączenie PAJ7620 do Arduino Uno:
- VCC - 3V3
- GND - GND
- SDA - A4
- SCL - A5
- INT - Pin 2
Podczas instalacji czujnika należy pamiętać o odpowiednich parametrach mechanicznych które można znaleźć w dokumentacji. W przypadku błędnej instalacji np. przez umieszczenie białego obiektu zbyt blisko czujnika jego działanie może zostać zaburzone lub nawet całkowicie zahamowane.
Skorzystałem z przykładu obsługującego przerwanie:
- #include "RevEng_PAJ7620.h"
- #define INTERRUPT_PIN 2
- volatile bool isr = false;
- RevEng_PAJ7620 sensor = RevEng_PAJ7620();
- void setup()
- {
- pinMode(INTERRUPT_PIN, INPUT);
- Serial.begin(115200);
- Serial.println("PAJ7620 Test Demo: Recognize 9 gestures using interrupt callback.");
- if( !sensor.begin() ) // Returns 0 if sensor connect fail
- {
- Serial.print("PAJ7620 I2C error - halting");
- while(true) {}
- }
- Serial.println("PAJ7620 Init OK.");
- Serial.println("Please input your gestures:");
- attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), interruptRoutine, FALLING);
- }
- void loop()
- {
- Gesture gesture;
- if (isr == true) {
- isr = false;
- gesture = sensor.readGesture();
- switch (gesture) {
- case GES_FORWARD: {
- Serial.print(" GES_FORWARD");
- break;
- }
- case GES_BACKWARD: {
- Serial.print(" GES_BACKWARD");
- break;
- }
- case GES_LEFT: {
- Serial.print(" GES_LEFT");
- break;
- }
- case GES_RIGHT: {
- Serial.print(" GES_RIGHT");
- break;
- }
- case GES_UP: {
- Serial.print(" GES_UP");
- break;
- }
- case GES_DOWN: {
- Serial.print(" GES_DOWN");
- break;
- }
- case GES_CLOCKWISE: {
- Serial.print(" GES_CLOCKWISE");
- break;
- }
- case GES_ANTICLOCKWISE: {
- Serial.print(" GES_ANTICLOCKWISE");
- break;
- }
- case GES_WAVE: {
- Serial.print(" GES_WAVE");
- break;
- }
- case GES_NONE: {
- Serial.print(" GES_NONE");
- break;
- }
- }
- Serial.print(", Gesture Code: ");
- Serial.println(gesture);
- if (isr == true) {
- Serial.println(" --> Interrupt during event processing");
- }
- }
- }
- // Called then interrupt pin is set high
- void interruptRoutine()
- {
- isr = true;
- Serial.print("Interrupt! -- ");
- }
Powyższy przykład działa w oparciu o odebrania przerwania z pinu, które jest generowane sygnałem niski gdy czujnik rozpozna jeden z 9 gestów. Na tej podstawie w oknie terminala wyświetlana będzie informacja o tym, który gest został odebrany.
Może się zdarzyć, że po którymś włączeniu zasilania, bez programowania kontrolera czujnik PAJ nie będzie działał, mimo iż komunikacja z układem będzie wyglądała na poprawną. Prawdopodobnie wynika to z czasu potrzebnego na inicjalizację czujnika przed wysyłaniem mu danych. Rozwiązałem to przez dodanie opóźnienia przed jego uruchomieniem. Wartość 500 - 1000 ms jest wystarczająca.
- Serial.println("PAJ7620 Test Demo: Recognize 9 gestures using interrupt callback.");
- delay(1000);
- if( !sensor.begin() )
- //...
Dodatkowo dołożyłem opóźnienie podczas inicjalizacji czujnika w bibliotece RavEng:
- uint8_t RevEng_PAJ7620::begin(TwoWire *chosenWireHandle)
- {
- // Reasonable timing delay values to make algorithm insensitive to
- // hand entry and exit moves before and after detecting a gesture
- gestureEntryTime = 0;
- gestureExitTime = 200;
- wireHandle = chosenWireHandle; // Save selected I2C bus for our use
- delayMicroseconds(700); // Wait 700us for PAJ7620U2 to stabilize
- // Reason: see v0.8 of 7620 documentation
- wireHandle->begin(); // Start the I2C bus via wire library
- /* There's two register banks (0 & 1) to be selected between.
- * BANK0 is where most data collection operations happen, so it's default.
- * Selecting the bank is done here twice for a reason. When the 7620 turns
- * on, the I2C bus is sleeping. When you first read/write to the bus
- * the 7620 wakes up, but it sometimes misses that first message.
- * Running the 7620 on an arduino with the USB power, a single call here
- * usually works, but as soon as you use an external power bus it often
- * fails to properly initialize and begin returns an error.
- */
- selectRegisterBank(BANK0); // This is done twice on purpose
- selectRegisterBank(BANK0); // Default operations on BANK0
- //Modyfikacja
- delayMicroseconds(200);
- Serial.println("selectRegisterBank_Bank0");
- selectRegisterBank(BANK0);
- //Koniec modyfikacji
- if( !isPAJ7620UDevice() ) {
- return 0; // Return false - wrong device found
- }
- initializeDeviceSettings(); // Set registers up
- setGestureMode(); // Specifically set to gesture mode
- return 1;
- }
W przypadku pierwszego uruchomienia czujnika może wystąpić błędne działanie układu, wynikające (prawdopodobnie) ze zmian w rejestrach konfiguracyjnych. W takim przypadku warto odłączyć zasilanie układu na chwilę po czym ponownie uruchomić płytkę.
Programowanie:
Poniżej znajduje się zmodyfikowany projekt z biblioteki dla Arduino opisanej powyżej.
Według mnie najrozsądniejszym sposobem komunikacji z czytnikiem jest oczekiwanie na przerwanie wygenerowane przez zmianę stanu na pinie INT. Oszczędza to trochę czasu i kłopotu potrzebnego na niepotrzebne odpytywanie czujnika.
W obsłudze przerwania podobnie jak w przypadku dla Arduino ustawiana będzie jedynie flaga informująca o konieczności odczytania danych z czujnika.
Wartości jakie muszą być wgrane do rejestrów w celu poprawnej konfiguracji można znaleźć pod tym linkiem https://github.com/acrandal/RevEng_PAJ7620/wiki w dokumencie PAJ7620U2 Datasheet: v0.8.pdf.
Inicjalizacja interfejsu I2C:
- static void MX_I2C1_Init(void)
- {
- /* USER CODE BEGIN I2C1_Init 0 */
- /* USER CODE END I2C1_Init 0 */
- /* USER CODE BEGIN I2C1_Init 1 */
- /* USER CODE END I2C1_Init 1 */
- hi2c1.Instance = I2C1;
- hi2c1.Init.ClockSpeed = 100000;
- hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
- hi2c1.Init.OwnAddress1 = 0;
- hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
- hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
- hi2c1.Init.OwnAddress2 = 0;
- hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
- hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
- if (HAL_I2C_Init(&hi2c1) != HAL_OK)
- {
- Error_Handler();
- }
- /* USER CODE BEGIN I2C1_Init 2 */
- /* USER CODE END I2C1_Init 2 */
- }
Inicjalizacja czujnika:
- uint8_t PAJ7620_Init(void)
- {
- HAL_StatusTypeDef opResoult = 0;
- uint8_t readData1 = 0;
- uint8_t readData2 = 0;
- if(PAJ7620_SelectBank(0) != 0) { return 1; }
- if(PAJ7620_SelectBank(0) != 0) { return 2; }
- if(PAJ7620_ReadRegister(0,1, &readData1) != 0) { return 3; }
- if(PAJ7620_ReadRegister(1,1, &readData2) != 0) { return 4; }
- if(PAJ7620_CheckDataId(readData1, readData2)) {
- return 5;
- }
- for(uint16_t i = 0; i<(sizeof(initRegisterArray_test)/sizeof(initRegisterArray_test[0])); i++)
- {
- if(PAJ7620_WriteRegister((initRegisterArray_test[i] & 0xFF00 ) >> 8, (initRegisterArray_test[i] & 0x00FF)) != 0) {
- return 5;
- }
- }
- if(PAJ7620_SelectBank(0) != 0) { return 6; }
- if(opResoult != 0) { return opResoult; }
- for(uint16_t i = 0; i<(sizeof(setGestureModeRegisterArray_test)/sizeof(setGestureModeRegisterArray_test[0])); i++)
- {
- if(PAJ7620_WriteRegister((setGestureModeRegisterArray_test[i] & 0xFF00 ) >> 8, (setGestureModeRegisterArray_test[i] & 0x00FF)) != 0) {
- return 5;
- }
- }
- if(PAJ7620_WriteRegister(PAJ7620_REGISTER_BANK_SEL, PAJ7620_BANK0) != 0) { return 6; }
- if(opResoult != 0) { return opResoult; }
- return 0;
- }
Odczytanie danych:
- uint16_t PAJ7620_CheckGesture(void)
- {
- uint8_t readedGestureVal = 0;
- uint16_t retVal = 0;
- if(PAJ7620_GetGesturesReg0(&readedGestureVal) != HAL_OK) {
- return 0xFF;
- }
- #ifdef ENABLE_PAJ7620_DEBUG
- int len = 0;
- char array[30] = {0x00};
- len = sprintf(array, "xxx %u\r\n", readedGestureVal);
- HAL_UART_Transmit(&huart2, (uint8_t *)&array[0], len, 100);
- #endif
- if(readedGestureVal != 0)
- {
- retVal = readedGestureVal;
- if((readedGestureVal & GES_UP_FLAG) == GES_UP_FLAG) {
- #ifdef ENABLE_PAJ7620_DEBUG
- int len = sprintf(array, "GES_UP_FLAG\r\n");
- HAL_UART_Transmit(&huart2, (uint8_t *)&array[0], len, 100);
- #endif
- }
- if((readedGestureVal & GES_DOWN_FLAG) == GES_DOWN_FLAG) {
- #ifdef ENABLE_PAJ7620_DEBUG
- int len = sprintf(array, "GES_DOWN_FLAG\r\n");
- HAL_UART_Transmit(&huart2, (uint8_t *)&array[0], len, 100);
- #endif
- }
- if((readedGestureVal & GES_LEFT_FLAG) == GES_LEFT_FLAG) {
- #ifdef ENABLE_PAJ7620_DEBUG
- int len = sprintf(array, "GES_LEFT_FLAG\r\n");
- HAL_UART_Transmit(&huart2, (uint8_t *)&array[0], len, 100);
- #endif
- }
- if((readedGestureVal & GES_RIGHT_FLAG) == GES_RIGHT_FLAG) {
- #ifdef ENABLE_PAJ7620_DEBUG
- int len = sprintf(array, "GES_RIGHT_FLAG\r\n");
- HAL_UART_Transmit(&huart2, (uint8_t *)&array[0], len, 100);
- #endif
- }
- if((readedGestureVal & GES_FORWARD_FLAG) == GES_FORWARD_FLAG) {
- #ifdef ENABLE_PAJ7620_DEBUG
- int len = sprintf(array, "GES_FORWARD_FLAG\r\n");
- HAL_UART_Transmit(&huart2, (uint8_t *)&array[0], len, 100);
- #endif
- }
- if((readedGestureVal & GES_BACKWARD_FLAG) == GES_BACKWARD_FLAG) {
- #ifdef ENABLE_PAJ7620_DEBUG
- int len = sprintf(array, "GES_BACKWARD_FLAG\r\n");
- HAL_UART_Transmit(&huart2, (uint8_t *)&array[0], len, 100);
- #endif
- }
- if((readedGestureVal & GES_CLOCKWISE_FLAG) == GES_CLOCKWISE_FLAG) {
- #ifdef ENABLE_PAJ7620_DEBUG
- int len = sprintf(array, "GES_CLOCKWISE_FLAG\r\n");
- HAL_UART_Transmit(&huart2, (uint8_t *)&array[0], len, 100);
- #endif
- }
- if((readedGestureVal & GES_ANTI_CLOCKWISE_FLAG) == GES_ANTI_CLOCKWISE_FLAG) {
- #ifdef ENABLE_PAJ7620_DEBUG
- int len = sprintf(array, "GES_ANTI_CLOCKWISE_FLAG\r\n");
- HAL_UART_Transmit(&huart2, (uint8_t *)&array[0], len, 100);
- #endif
- }
- }
- if(PAJ7620_GetGesturesReg1(&readedGestureVal) != HAL_OK) {
- return 0xFF;
- }
- if(readedGestureVal != 0)
- {
- retVal |= readedGestureVal << 8;
- if((readedGestureVal & GES_WAVE_FLAG) == GES_WAVE_FLAG) {
- #ifdef ENABLE_PAJ7620_DEBUG
- int len = sprintf(array, "GES_WAVE_FLAG\r\n");
- HAL_UART_Transmit(&huart2, (uint8_t *)&array[0], len, 100);
- #endif
- }
- }
- return retVal;
- }
Powyższa funkcja wywoływana jest w pętli. Następuje odczyt z dwóch rejestrów przechowujących odczytane dane o zarejestrowanym geście. Powyższe instrukcję warunkowe sprawdzają każdy z możliwych gestów, ponieważ w zależności od częstotliwości sprawdzania może nastąpić rejestracja większej ilości gestów.
Drugi sposób polega na sprawdzaniu czy zostało wywołane przerwanie, informujące o odczytanym geście.
- volatile uint8_t PAJ7620_InterruptEnable = 0;
- void GPIO_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct = {0};
- __HAL_RCC_GPIOB_CLK_ENABLE();
- /*Configure GPIO pin : PB3 */
- GPIO_InitStruct.Pin = GPIO_PIN_3;
- GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
- /* EXTI interrupt init*/
- HAL_NVIC_SetPriority(EXTI3_IRQn, 0, 0);
- HAL_NVIC_EnableIRQ(EXTI3_IRQn);
- }
- uint16_t PAJ7620_CheckGesture_Interrupt(void)
- {
- if(PAJ7620_InterruptEnable == 1) {
- PAJ7620_InterruptEnable = 0;
- return PAJ7620_CheckGesture();
- }
- return 0;
- }
Sprawdzanie gestu następuje w pętli głównej programu.
Istnieją jeszcze dwa tryby pracy Corners oraz Cursor. Pierwszy z nich zwraca informację o położeniu obiektu w czterech rogach czujnika oraz na środku. Drugi natomiast zwraca pozycję X,Y obiektu w zasięgu czujnika.
Wywołanie funkcji wygląda następująco:
- //...
- PAJ7620_Init();
- PAJ7620_setCursorMode();
- while (1)
- {
- if(!PAJ7620_CheckIfCursorInView())
- {
- uint16_t cursor_x = PAJ7620_getCursorX();
- uint16_t cursor_y = PAJ7620_getCursorY();
- int len = 0;
- char array[30] = {0x00};
- len = sprintf(array, "X:%u Y:%u\r\n", cursor_x, cursor_y);
- HAL_UART_Transmit(&huart2, (uint8_t *)&array[0], len, 100);
- }
- }