From 4331ff114c5b971b7ae197e158263187cc9b7737 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 19 Jul 2022 14:38:04 +0200 Subject: [PATCH] Add HM0360 driver. --- libraries/Camera/src/camera.h | 1 + libraries/Himax_HM0360/hm0360.cpp | 789 ++++++++++++++++++++++++++++++ libraries/Himax_HM0360/hm0360.h | 56 +++ 3 files changed, 846 insertions(+) create mode 100644 libraries/Himax_HM0360/hm0360.cpp create mode 100644 libraries/Himax_HM0360/hm0360.h diff --git a/libraries/Camera/src/camera.h b/libraries/Camera/src/camera.h index 41853c82f..5d84c9185 100644 --- a/libraries/Camera/src/camera.h +++ b/libraries/Camera/src/camera.h @@ -21,6 +21,7 @@ #include "Wire.h" #define HM01B0_I2C_ADDR (0x24) +#define HM0360_I2C_ADDR (0x24) #define GC2145_I2C_ADDR (0x3C) enum { diff --git a/libraries/Himax_HM0360/hm0360.cpp b/libraries/Himax_HM0360/hm0360.cpp new file mode 100644 index 000000000..8cd0b3703 --- /dev/null +++ b/libraries/Himax_HM0360/hm0360.cpp @@ -0,0 +1,789 @@ +/* + * Copyright 2021 Arduino SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * HM0360 driver. + */ +#include "Wire.h" +#include "hm0360.h" + +// Register set +// Read only registers +#define MODEL_ID_H 0x0000 +#define MODEL_ID_L 0x0001 +#define SILICON_REV 0x0002 +#define FRAME_COUNT_H 0x0005 +#define FRAME_COUNT_L 0x0006 +#define PIXEL_ORDER 0x0007 +// Sensor mode control +#define MODE_SELECT 0x0100 +#define IMG_ORIENTATION 0x0101 +#define EMBEDDED_LINE_EN 0x0102 +#define SW_RESET 0x0103 +#define COMMAND_UPDATE 0x0104 +// Sensor exposure gain control +#define INTEGRATION_H 0x0202 +#define INTEGRATION_L 0x0203 +#define ANALOG_GAIN 0x0205 +#define DIGITAL_GAIN_H 0x020E +#define DIGITAL_GAIN_L 0x020F +// Clock control +#define PLL1_CONFIG 0x0300 +#define PLL2_CONFIG 0x0301 +#define PLL3_CONFIG 0x0302 +// Frame timing control +#define FRAME_LEN_LINES_H 0x0340 +#define FRAME_LEN_LINES_L 0x0341 +#define LINE_LEN_PCK_H 0x0342 +#define LINE_LEN_PCK_L 0x0343 +// Monochrome programming +#define MONO_MODE 0x0370 +#define MONO_MODE_ISP 0x0371 +#define MONO_MODE_SEL 0x0372 +// Binning mode control +#define H_SUBSAMPLE 0x0380 +#define V_SUBSAMPLE 0x0381 +#define BINNING_MODE 0x0382 +// Test pattern control +#define TEST_PATTERN_MODE 0x0601 +// Black level control +#define BLC_TGT 0x1004 +#define BLC2_TGT 0x1009 +#define MONO_CTRL 0x100A +// VSYNC / HSYNC / pixel shift registers +#define OPFM_CTRL 0x1014 +// Tone mapping registers +#define CMPRS_CTRL 0x102F +#define CMPRS_01 0x1030 +#define CMPRS_02 0x1031 +#define CMPRS_03 0x1032 +#define CMPRS_04 0x1033 +#define CMPRS_05 0x1034 +#define CMPRS_06 0x1035 +#define CMPRS_07 0x1036 +#define CMPRS_08 0x1037 +#define CMPRS_09 0x1038 +#define CMPRS_10 0x1039 +#define CMPRS_11 0x103A +#define CMPRS_12 0x103B +#define CMPRS_13 0x103C +#define CMPRS_14 0x103D +#define CMPRS_15 0x103E +#define CMPRS_16 0x103F +// Automatic exposure control +#define AE_CTRL 0x2000 +#define AE_CTRL1 0x2001 +#define CNT_ORGH_H 0x2002 +#define CNT_ORGH_L 0x2003 +#define CNT_ORGV_H 0x2004 +#define CNT_ORGV_L 0x2005 +#define CNT_STH_H 0x2006 +#define CNT_STH_L 0x2007 +#define CNT_STV_H 0x2008 +#define CNT_STV_L 0x2009 +#define CTRL_PG_SKIPCNT 0x200A +#define BV_WIN_WEIGHT_EN 0x200D +#define MAX_INTG_H 0x2029 +#define MAX_INTG_L 0x202A +#define MAX_AGAIN 0x202B +#define MAX_DGAIN_H 0x202C +#define MAX_DGAIN_L 0x202D +#define MIN_INTG 0x202E +#define MIN_AGAIN 0x202F +#define MIN_DGAIN 0x2030 +#define T_DAMPING 0x2031 +#define N_DAMPING 0x2032 +#define ALC_TH 0x2033 +#define AE_TARGET_MEAN 0x2034 +#define AE_MIN_MEAN 0x2035 +#define AE_TARGET_ZONE 0x2036 +#define CONVERGE_IN_TH 0x2037 +#define CONVERGE_OUT_TH 0x2038 +#define FS_CTRL 0x203B +#define FS_60HZ_H 0x203C +#define FS_60HZ_L 0x203D +#define FS_50HZ_H 0x203E +#define FS_50HZ_L 0x203F +#define FRAME_CNT_TH 0x205B +#define AE_MEAN 0x205D +#define AE_CONVERGE 0x2060 +#define AE_BLI_TGT 0x2070 +// Interrupt control +#define PULSE_MODE 0x2061 +#define PULSE_TH_H 0x2062 +#define PULSE_TH_L 0x2063 +#define INT_INDIC 0x2064 +#define INT_CLEAR 0x2065 +// Motion detection control +#define MD_CTRL 0x2080 +#define ROI_START_END_V 0x2081 +#define ROI_START_END_H 0x2082 +#define MD_TH_MIN 0x2083 +#define MD_TH_STR_L 0x2084 +#define MD_TH_STR_H 0x2085 +#define MD_LIGHT_COEF 0x2099 +#define MD_BLOCK_NUM_TH 0x209B +#define MD_LATENCY 0x209C +#define MD_LATENCY_TH 0x209D +#define MD_CTRL1 0x209E +// Context switch control registers +#define PMU_CFG_3 0x3024 +#define PMU_CFG_4 0x3025 +// Operation mode control +#define WIN_MODE 0x3030 +// IO and clock control +#define PAD_REGISTER_07 0x3112 + +// Register bits/values +#define HIMAX_RESET 0x01 +#define HIMAX_MODE_STANDBY 0x00 +#define HIMAX_MODE_STREAMING 0x01 // I2C triggered streaming enable +#define HIMAX_MODE_STREAMING_NFRAMES 0x03 // Output N frames +#define HIMAX_MODE_STREAMING_TRIG 0x05 // Hardware Trigger +#define HIMAX_SET_HMIRROR(r, x) ((r&0xFE)|((x&1)<<0)) +#define HIMAX_SET_VMIRROR(r, x) ((r&0xFD)|((x&1)<<1)) + +#define PCLK_RISING_EDGE 0x00 +#define PCLK_FALLING_EDGE 0x01 +#define AE_CTRL_ENABLE 0x00 +#define AE_CTRL_DISABLE 0x01 + +/** + * @} + */ +/* Private variables ---------------------------------------------------------*/ + +/** @defgroup GAPUINO_HIMAX_Private_Variables I2C Private Variables + * @{ + */ +#define HIMAX_BOOT_RETRY (10) +#define HIMAX_LINE_LEN_PCK_VGA 0x300 +#define HIMAX_FRAME_LENGTH_VGA 0x214 + +#define HIMAX_LINE_LEN_PCK_QVGA 0x178 +#define HIMAX_FRAME_LENGTH_QVGA 0x109 + +#define HIMAX_LINE_LEN_PCK_QQVGA 0x178 +#define HIMAX_FRAME_LENGTH_QQVGA 0x084 + +#define HIMAX_MD_ROI_VGA_W 40 +#define HIMAX_MD_ROI_VGA_H 30 + +#define HIMAX_MD_ROI_QVGA_W 20 +#define HIMAX_MD_ROI_QVGA_H 15 + +#define HIMAX_MD_ROI_QQVGA_W 10 +#define HIMAX_MD_ROI_QQVGA_H 8 + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +enum { + HIMAX_Standby = 0x0, + HIMAX_Streaming = 0x1, // I2C triggered streaming enable + HIMAX_Streaming2 = 0x3, // Output N frames + HIMAX_Streaming3 = 0x5 // Hardware Trigger +}; + +typedef struct regval_list_ { + uint16_t reg_num; + uint8_t value; +} regval_list_t; + +static uint16_t himax_default_regs[][2] = { + {SW_RESET, 0x00}, + {MONO_MODE, 0x00}, + {MONO_MODE_ISP, 0x01}, + {MONO_MODE_SEL, 0x01}, + + // BLC control + {0x1000, 0x01}, + {0x1003, 0x04}, + {BLC_TGT, 0x04}, + {0x1007, 0x01}, + {0x1008, 0x04}, + {BLC2_TGT, 0x04}, + {MONO_CTRL, 0x01}, + + // Output format control + {OPFM_CTRL, 0x0C}, + + // Reserved regs + {0x101D, 0x00}, + {0x101E, 0x01}, + {0x101F, 0x00}, + {0x1020, 0x01}, + {0x1021, 0x00}, + + {CMPRS_CTRL, 0x00}, + {CMPRS_01, 0x09}, + {CMPRS_02, 0x12}, + {CMPRS_03, 0x23}, + {CMPRS_04, 0x31}, + {CMPRS_05, 0x3E}, + {CMPRS_06, 0x4B}, + {CMPRS_07, 0x56}, + {CMPRS_08, 0x5E}, + {CMPRS_09, 0x65}, + {CMPRS_10, 0x72}, + {CMPRS_11, 0x7F}, + {CMPRS_12, 0x8C}, + {CMPRS_13, 0x98}, + {CMPRS_14, 0xB2}, + {CMPRS_15, 0xCC}, + {CMPRS_16, 0xE6}, + + {0x3112, 0x00}, // PCLKO_polarity falling + + {PLL1_CONFIG, 0x08}, // Core = 24MHz PCLKO = 24MHz I2C = 12MHz + {PLL2_CONFIG, 0x0A}, // MIPI pre-dev (default) + {PLL3_CONFIG, 0x77}, // PMU/MIPI pre-dev (default) + + {PMU_CFG_3, 0x08}, // Disable context switching + {PAD_REGISTER_07, 0x00}, // PCLKO_polarity falling + + {AE_CTRL, 0x5F}, // Automatic Exposure (NOTE: Auto framerate enabled) + {AE_CTRL1, 0x00}, + {T_DAMPING, 0x20}, // AE T damping factor + {N_DAMPING, 0x00}, // AE N damping factor + {AE_TARGET_MEAN, 0x64}, // AE target + {AE_MIN_MEAN, 0x0A}, // AE min target mean + {AE_TARGET_ZONE, 0x23}, // AE target zone + {CONVERGE_IN_TH, 0x03}, // AE converge in threshold + {CONVERGE_OUT_TH, 0x05}, // AE converge out threshold + {MAX_INTG_H, (HIMAX_FRAME_LENGTH_QVGA-4)>>8}, + {MAX_INTG_L, (HIMAX_FRAME_LENGTH_QVGA-4)&0xFF}, + + {MAX_AGAIN, 0x04}, // Maximum analog gain + {MAX_DGAIN_H, 0x03}, + {MAX_DGAIN_L, 0x3F}, + {INTEGRATION_H, 0x01}, + {INTEGRATION_L, 0x08}, + + {MD_CTRL, 0x6A}, + {MD_TH_MIN, 0x01}, + {MD_BLOCK_NUM_TH, 0x01}, + {MD_CTRL1, 0x06}, + {PULSE_MODE, 0x00}, // Interrupt in level mode. + {ROI_START_END_V, 0xF0}, + {ROI_START_END_H, 0xF0}, + + {FRAME_LEN_LINES_H, HIMAX_FRAME_LENGTH_QVGA>>8}, + {FRAME_LEN_LINES_L, HIMAX_FRAME_LENGTH_QVGA&0xFF}, + {LINE_LEN_PCK_H, HIMAX_LINE_LEN_PCK_QVGA>>8}, + {LINE_LEN_PCK_L, HIMAX_LINE_LEN_PCK_QVGA&0xFF}, + {H_SUBSAMPLE, 0x01}, + {V_SUBSAMPLE, 0x01}, + {BINNING_MODE, 0x00}, + {WIN_MODE, 0x00}, + {IMG_ORIENTATION, 0x00}, + {COMMAND_UPDATE, 0x01}, + + /// SYNC function config. + {0x3010, 0x00}, + {0x3013, 0x01}, + {0x3019, 0x00}, + {0x301A, 0x00}, + {0x301B, 0x20}, + {0x301C, 0xFF}, + + // PREMETER config. + {0x3026, 0x03}, + {0x3027, 0x81}, + {0x3028, 0x01}, + {0x3029, 0x00}, + {0x302A, 0x30}, + {0x302E, 0x00}, + {0x302F, 0x00}, + + // Magic regs 🪄. + {0x302B, 0x2A}, + {0x302C, 0x00}, + {0x302D, 0x03}, + {0x3031, 0x01}, + {0x3051, 0x00}, + {0x305C, 0x03}, + {0x3060, 0x00}, + {0x3061, 0xFA}, + {0x3062, 0xFF}, + {0x3063, 0xFF}, + {0x3064, 0xFF}, + {0x3065, 0xFF}, + {0x3066, 0xFF}, + {0x3067, 0xFF}, + {0x3068, 0xFF}, + {0x3069, 0xFF}, + {0x306A, 0xFF}, + {0x306B, 0xFF}, + {0x306C, 0xFF}, + {0x306D, 0xFF}, + {0x306E, 0xFF}, + {0x306F, 0xFF}, + {0x3070, 0xFF}, + {0x3071, 0xFF}, + {0x3072, 0xFF}, + {0x3073, 0xFF}, + {0x3074, 0xFF}, + {0x3075, 0xFF}, + {0x3076, 0xFF}, + {0x3077, 0xFF}, + {0x3078, 0xFF}, + {0x3079, 0xFF}, + {0x307A, 0xFF}, + {0x307B, 0xFF}, + {0x307C, 0xFF}, + {0x307D, 0xFF}, + {0x307E, 0xFF}, + {0x307F, 0xFF}, + {0x3080, 0x01}, + {0x3081, 0x01}, + {0x3082, 0x03}, + {0x3083, 0x20}, + {0x3084, 0x00}, + {0x3085, 0x20}, + {0x3086, 0x00}, + {0x3087, 0x20}, + {0x3088, 0x00}, + {0x3089, 0x04}, + {0x3094, 0x02}, + {0x3095, 0x02}, + {0x3096, 0x00}, + {0x3097, 0x02}, + {0x3098, 0x00}, + {0x3099, 0x02}, + {0x309E, 0x05}, + {0x309F, 0x02}, + {0x30A0, 0x02}, + {0x30A1, 0x00}, + {0x30A2, 0x08}, + {0x30A3, 0x00}, + {0x30A4, 0x20}, + {0x30A5, 0x04}, + {0x30A6, 0x02}, + {0x30A7, 0x02}, + {0x30A8, 0x01}, + {0x30A9, 0x00}, + {0x30AA, 0x02}, + {0x30AB, 0x34}, + {0x30B0, 0x03}, + {0x30C4, 0x10}, + {0x30C5, 0x01}, + {0x30C6, 0xBF}, + {0x30C7, 0x00}, + {0x30C8, 0x00}, + {0x30CB, 0xFF}, + {0x30CC, 0xFF}, + {0x30CD, 0x7F}, + {0x30CE, 0x7F}, + {0x30D3, 0x01}, + {0x30D4, 0xFF}, + {0x30D5, 0x00}, + {0x30D6, 0x40}, + {0x30D7, 0x00}, + {0x30D8, 0xA7}, + {0x30D9, 0x05}, + {0x30DA, 0x01}, + {0x30DB, 0x40}, + {0x30DC, 0x00}, + {0x30DD, 0x27}, + {0x30DE, 0x05}, + {0x30DF, 0x07}, + {0x30E0, 0x40}, + {0x30E1, 0x00}, + {0x30E2, 0x27}, + {0x30E3, 0x05}, + {0x30E4, 0x47}, + {0x30E5, 0x30}, + {0x30E6, 0x00}, + {0x30E7, 0x27}, + {0x30E8, 0x05}, + {0x30E9, 0x87}, + {0x30EA, 0x30}, + {0x30EB, 0x00}, + {0x30EC, 0x27}, + {0x30ED, 0x05}, + {0x30EE, 0x00}, + {0x30EF, 0x40}, + {0x30F0, 0x00}, + {0x30F1, 0xA7}, + {0x30F2, 0x05}, + {0x30F3, 0x01}, + {0x30F4, 0x40}, + {0x30F5, 0x00}, + {0x30F6, 0x27}, + {0x30F7, 0x05}, + {0x30F8, 0x07}, + {0x30F9, 0x40}, + {0x30FA, 0x00}, + {0x30FB, 0x27}, + {0x30FC, 0x05}, + {0x30FD, 0x47}, + {0x30FE, 0x30}, + {0x30FF, 0x00}, + {0x3100, 0x27}, + {0x3101, 0x05}, + {0x3102, 0x87}, + {0x3103, 0x30}, + {0x3104, 0x00}, + {0x3105, 0x27}, + {0x3106, 0x05}, + {0x310B, 0x10}, + {0x3113, 0xA0}, + {0x3114, 0x67}, + {0x3115, 0x42}, + {0x3116, 0x10}, + {0x3117, 0x0A}, + {0x3118, 0x3F}, + {0x311C, 0x10}, + {0x311D, 0x06}, + {0x311E, 0x0F}, + {0x311F, 0x0E}, + {0x3120, 0x0D}, + {0x3121, 0x0F}, + {0x3122, 0x00}, + {0x3123, 0x1D}, + {0x3126, 0x03}, + {0x3128, 0x57}, + {0x312A, 0x11}, + {0x312B, 0x41}, + {0x312E, 0x00}, + {0x312F, 0x00}, + {0x3130, 0x0C}, + {0x3141, 0x2A}, + {0x3142, 0x9F}, + {0x3147, 0x18}, + {0x3149, 0x18}, + {0x314B, 0x01}, + {0x3150, 0x50}, + {0x3152, 0x00}, + {0x3156, 0x2C}, + {0x315A, 0x0A}, + {0x315B, 0x2F}, + {0x315C, 0xE0}, + {0x315F, 0x02}, + {0x3160, 0x1F}, + {0x3163, 0x1F}, + {0x3164, 0x7F}, + {0x3165, 0x7F}, + {0x317B, 0x94}, + {0x317C, 0x00}, + {0x317D, 0x02}, + {0x318C, 0x00}, + + {COMMAND_UPDATE, 0x01}, + {0x0000, 0x00}, +}; + +static const uint16_t himax_vga_regs[][2] = { + {PLL1_CONFIG, 0x08}, // Core = 24MHz PCLKO = 24MHz I2C = 12MHz + {H_SUBSAMPLE, 0x00}, + {V_SUBSAMPLE, 0x00}, + {BINNING_MODE, 0x00}, + {WIN_MODE, 0x00}, + {MAX_INTG_H, (HIMAX_FRAME_LENGTH_VGA-4)>>8}, + {MAX_INTG_L, (HIMAX_FRAME_LENGTH_VGA-4)&0xFF}, + {FRAME_LEN_LINES_H, (HIMAX_FRAME_LENGTH_VGA>>8)}, + {FRAME_LEN_LINES_L, (HIMAX_FRAME_LENGTH_VGA&0xFF)}, + {LINE_LEN_PCK_H, (HIMAX_LINE_LEN_PCK_VGA>>8)}, + {LINE_LEN_PCK_L, (HIMAX_LINE_LEN_PCK_VGA&0xFF)}, + {ROI_START_END_H, 0xF0}, + {ROI_START_END_V, 0xE0}, + {COMMAND_UPDATE, 0x01}, + {0x0000, 0x00}, +}; + +static const uint16_t himax_qvga_regs[][2] = { + {PLL1_CONFIG, 0x09}, // Core = 12MHz PCLKO = 24MHz I2C = 12MHz + {H_SUBSAMPLE, 0x01}, + {V_SUBSAMPLE, 0x01}, + {BINNING_MODE, 0x00}, + {WIN_MODE, 0x00}, + {MAX_INTG_H, (HIMAX_FRAME_LENGTH_QVGA-4)>>8}, + {MAX_INTG_L, (HIMAX_FRAME_LENGTH_QVGA-4)&0xFF}, + {FRAME_LEN_LINES_H, (HIMAX_FRAME_LENGTH_QVGA>>8)}, + {FRAME_LEN_LINES_L, (HIMAX_FRAME_LENGTH_QVGA&0xFF)}, + {LINE_LEN_PCK_H, (HIMAX_LINE_LEN_PCK_QVGA>>8)}, + {LINE_LEN_PCK_L, (HIMAX_LINE_LEN_PCK_QVGA&0xFF)}, + {ROI_START_END_H, 0xF0}, + {ROI_START_END_V, 0xE0}, + {COMMAND_UPDATE, 0x01}, + {0x0000, 0x00}, +}; + +static const uint16_t himax_qqvga_regs[][2] = { + {PLL1_CONFIG, 0x09}, // Core = 12MHz PCLKO = 24MHz I2C = 12MHz + {H_SUBSAMPLE, 0x02}, + {V_SUBSAMPLE, 0x02}, + {BINNING_MODE, 0x00}, + {WIN_MODE, 0x00}, + {MAX_INTG_H, (HIMAX_FRAME_LENGTH_QQVGA-4)>>8}, + {MAX_INTG_L, (HIMAX_FRAME_LENGTH_QQVGA-4)&0xFF}, + {FRAME_LEN_LINES_H, (HIMAX_FRAME_LENGTH_QQVGA>>8)}, + {FRAME_LEN_LINES_L, (HIMAX_FRAME_LENGTH_QQVGA&0xFF)}, + {LINE_LEN_PCK_H, (HIMAX_LINE_LEN_PCK_QQVGA>>8)}, + {LINE_LEN_PCK_L, (HIMAX_LINE_LEN_PCK_QQVGA&0xFF)}, + {ROI_START_END_H, 0xF0}, + {ROI_START_END_V, 0xD0}, + {COMMAND_UPDATE, 0x01}, + {0x0000, 0x00}, +}; + +HM0360::HM0360(arduino::MbedI2C &i2c) : + _i2c(&i2c), + md_irq(PC_15), + _md_callback(NULL) +{ +} + +void HM0360::irqHandler() +{ + if (_md_callback) { + _md_callback(); + } +} + +int HM0360::init() +{ + _i2c->begin(); + _i2c->setClock(100000); + + if (reset() != 0 ) { + return -1; + } + + for (uint32_t i=0; himax_default_regs[i][0]; i++) { + regWrite(HM0360_I2C_ADDR, himax_default_regs[i][0], himax_default_regs[i][1], true); + } + + regWrite(HM0360_I2C_ADDR, MODE_SELECT, HIMAX_Streaming, true); + return 0; +} + +int HM0360::reset() +{ + uint32_t max_timeout=100; + do { + regWrite(HM0360_I2C_ADDR, SW_RESET, HIMAX_RESET, true); + delay(1); + } while (regRead(HM0360_I2C_ADDR, MODE_SELECT, true) != HIMAX_Standby && ((--max_timeout) > 0) ); + + return (max_timeout > 0) ? 0 : -1; +} + +int HM0360::setResolution(int32_t resolution) +{ + int ret = 0; + + switch (resolution) { + case CAMERA_R160x120: + for(uint32_t i = 0; himax_qqvga_regs[i][0]; i++) { + ret |= regWrite(HM0360_I2C_ADDR, himax_qqvga_regs[i][0], himax_qqvga_regs[i][1], true); + } + break; + case CAMERA_R320x240: + case CAMERA_R320x320: + for(uint32_t i = 0; himax_qvga_regs[i][0]; i++) { + ret |= regWrite(HM0360_I2C_ADDR, himax_qvga_regs[i][0], himax_qvga_regs[i][1], true); + } + break; + case CAMERA_R640x480: + for(uint32_t i = 0; himax_vga_regs[i][0]; i++) { + ret |= regWrite(HM0360_I2C_ADDR, himax_vga_regs[i][0], himax_vga_regs[i][1], true); + } + break; + default: + return -1; + } + + return ret; +} + +int HM0360::setFrameRate(int32_t framerate) +{ + uint8_t pll_cfg = 0; + uint8_t osc_div = 0; + uint8_t highres = false; + + highres = ((regRead(HM0360_I2C_ADDR, H_SUBSAMPLE, true) & 0x03) + | (regRead(HM0360_I2C_ADDR, V_SUBSAMPLE, true) & 0x03)); + + if (framerate <= 10) { + osc_div = (highres == true) ? 0x03 : 0x03; + } else if (framerate <= 15) { + osc_div = (highres == true) ? 0x02 : 0x03; + } else if (framerate <= 30) { + osc_div = (highres == true) ? 0x01 : 0x02; + } else { + // Set to the max possible FPS at this resolution. + osc_div = (highres == true) ? 0x00 : 0x01; + } + + pll_cfg = regRead(HM0360_I2C_ADDR, PLL1_CONFIG, true); + return regWrite(HM0360_I2C_ADDR, PLL1_CONFIG, (pll_cfg & 0xFC) | osc_div, true); +} + +int HM0360::setPixelFormat(int32_t pixformat) +{ + return (pixformat == CAMERA_GRAYSCALE) ? 0 : -1; +} + +int HM0360::setTestPattern(bool enable, bool walking) +{ + return regWrite(HM0360_I2C_ADDR, 0x0601, (walking ? (2 << 4) : 0) | 1, true); +} + +int HM0360::setMotionDetectionThreshold(uint32_t threshold) +{ + // Set motion detection threshold/sensitivity. + int ret = 0; + ret |= regWrite(HM0360_I2C_ADDR, MD_TH_STR_L, threshold, true); + ret |= regWrite(HM0360_I2C_ADDR, MD_TH_STR_H, threshold, true); + ret |= regWrite(HM0360_I2C_ADDR, MD_LIGHT_COEF, threshold, true); + return ret; +} + +int HM0360::setMotionDetectionWindow(uint32_t x, uint32_t y, uint32_t w, uint32_t h) +{ + int ret = 0; + int32_t roi_w = 0; + int32_t roi_h = 0; + int32_t roi_max_h = 14; + + int32_t x1 = x; + int32_t y1 = y; + int32_t x2 = x + w; + int32_t y2 = y + h; + + uint8_t hsub = regRead(HM0360_I2C_ADDR, H_SUBSAMPLE, true) & 0x03; + uint8_t vsub = regRead(HM0360_I2C_ADDR, V_SUBSAMPLE, true) & 0x03; + + if (hsub == 0 && vsub == 0) { // VGA + roi_w = HIMAX_MD_ROI_VGA_W; + roi_h = HIMAX_MD_ROI_VGA_H; + roi_max_h = 14; + } else if (hsub == 1 && vsub == 1) { // QVGA + roi_w = HIMAX_MD_ROI_QVGA_W; + roi_h = HIMAX_MD_ROI_QVGA_H; + roi_max_h = 14; + } else if (hsub == 2 && vsub == 2) { // QQVGA + roi_w = HIMAX_MD_ROI_QQVGA_W; + roi_h = HIMAX_MD_ROI_QQVGA_H; + roi_max_h = 13; + } else { + return -1; + } + + x1 = MAX((x1 / roi_w - 1), 0); + y1 = MAX((y1 / roi_h - 1), 0); + x2 = MIN((x2 / roi_w) + !!(x2 % roi_w), 0xF); + y2 = MIN((y2 / roi_h) + !!(y2 % roi_h), roi_max_h); + ret |= regWrite(HM0360_I2C_ADDR, ROI_START_END_H, ((x2 & 0xF) << 4) | (x1 & 0x0F), true); + ret |= regWrite(HM0360_I2C_ADDR, ROI_START_END_V, ((y2 & 0xF) << 4) | (y1 & 0x0F), true); + return ret; +} + +int HM0360::enableMotionDetection(md_callback_t callback) +{ + md_irq.rise(0); + _md_callback = callback; + md_irq.rise(mbed::Callback(this, &HM0360::irqHandler)); + md_irq.enable_irq(); + + int ret = clearMotionDetection(); + uint8_t md_ctrl = regRead(HM0360_I2C_ADDR, MD_CTRL, true); + ret |= regWrite(HM0360_I2C_ADDR, MD_CTRL, (md_ctrl & 0xFE) | 1, true); + return ret; +} + +int HM0360::disableMotionDetection() +{ + _md_callback = NULL; + int ret = clearMotionDetection(); + uint8_t md_ctrl = regRead(HM0360_I2C_ADDR, MD_CTRL, true); + ret |= regWrite(HM0360_I2C_ADDR, MD_CTRL, (md_ctrl & 0xFE) | 0, true); + return ret; +} + +int HM0360::motionDetected() +{ + uint8_t ret = pollMotionDetection(); + if (ret) { + clearMotionDetection(); + } + return ret; +} + +int HM0360::pollMotionDetection() +{ + return regRead(HM0360_I2C_ADDR, INT_INDIC, true) & 0x08; +} + +int HM0360::clearMotionDetection() +{ + return regWrite(HM0360_I2C_ADDR, INT_CLEAR, (1 << 3), true); +} + +uint8_t HM0360::printRegs() +{ + for (uint32_t i=0; himax_default_regs[i][0]; i++) { + printf("0x%04X: 0x%02X 0x%02X \n", + himax_default_regs[i][0], + himax_default_regs[i][1], + regRead(HM0360_I2C_ADDR, himax_default_regs[i][0], true)); + } + return 0; +} + +int HM0360::regWrite(uint8_t dev_addr, uint16_t reg_addr, uint8_t reg_data, bool wide_addr) +{ + _i2c->beginTransmission(dev_addr); + uint8_t buf[3] = {(uint8_t) (reg_addr >> 8), (uint8_t) (reg_addr & 0xFF), reg_data}; + if (wide_addr == true) { + _i2c->write(buf, 1); + } + _i2c->write(&buf[1], 2); + return _i2c->endTransmission(); +} + +uint8_t HM0360::regRead(uint8_t dev_addr, uint16_t reg_addr, bool wide_addr) +{ + uint8_t reg_data = 0; + uint8_t buf[2] = {(uint8_t) (reg_addr >> 8), (uint8_t) (reg_addr & 0xFF)}; + _i2c->beginTransmission(dev_addr); + if (wide_addr) { + _i2c->write(buf, 2); + } else { + _i2c->write(&buf[1], 1); + } + _i2c->endTransmission(false); + _i2c->requestFrom(dev_addr, 1); + if (_i2c->available()) { + reg_data = _i2c->read(); + } + while (_i2c->available()) { + _i2c->read(); + } + return reg_data; +} + +void HM0360::debug(Stream &stream) +{ + _debug = &stream; +} + diff --git a/libraries/Himax_HM0360/hm0360.h b/libraries/Himax_HM0360/hm0360.h new file mode 100644 index 000000000..eb2d06c41 --- /dev/null +++ b/libraries/Himax_HM0360/hm0360.h @@ -0,0 +1,56 @@ +/* + * Copyright 2021 Arduino SA + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * HM0360 driver. + */ +#ifndef __HIMAX_H +#define __HIMAX_H + +#include "camera.h" +#include "drivers/InterruptIn.h" + +class HM0360: public ImageSensor { + private: + Stream *_debug; + arduino::MbedI2C *_i2c; + mbed::InterruptIn md_irq; + md_callback_t _md_callback; + void irqHandler(); + int regWrite(uint8_t dev_addr, uint16_t reg_addr, uint8_t reg_data, bool wide_addr = false); + uint8_t regRead(uint8_t dev_addr, uint16_t reg_addr, bool wide_addr = false); + + public: + HM0360(arduino::MbedI2C &i2c = CameraWire); + int init(); + int reset(); + int getID() { return HM0360_I2C_ADDR; }; + uint32_t getClockFrequency() { return 24000000; }; + int setFrameRate(int32_t framerate); + int setResolution(int32_t resolution); + int setPixelFormat(int32_t pixformat); + int setTestPattern(bool enable, bool walking); + int enableMotionDetection(md_callback_t callback=NULL); + int disableMotionDetection(); + int setMotionDetectionWindow(uint32_t x, uint32_t y, uint32_t w, uint32_t h); + int setMotionDetectionThreshold(uint32_t threshold); + int motionDetected(); + int pollMotionDetection(); + int clearMotionDetection(); + + uint8_t printRegs(); + void debug(Stream &stream); +}; +#endif /* __HIMAX_H */