├── OV7670-ESP32 ├── I2Scamera.c ├── I2Scamera.h ├── OV7670.cpp ├── OV7670.h ├── examples │ ├── OV7670_ILI9341 │ │ └── OV7670_ILI9341.ino │ ├── OV7670_Servo │ │ └── OV7670_Servo.ino │ ├── OV7670_Web │ │ └── OV7670_Web.ino │ └── OV7670_WebAdjust │ │ └── OV7670_WebAdjust.ino └── keywords.txt └── README.md /OV7670-ESP32/I2Scamera.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "freertos/FreeRTOS.h" 3 | #include "freertos/task.h" 4 | #include "freertos/semphr.h" 5 | #include "soc/soc.h" 6 | #include "driver/gpio.h" 7 | #include "soc/gpio_sig_map.h" 8 | #include "soc/i2s_reg.h" 9 | #include "soc/i2s_struct.h" 10 | #include "soc/io_mux_reg.h" 11 | #include 12 | #include 13 | #include "rom/lldesc.h" 14 | #include "esp_intr_alloc.h" 15 | #include "I2Scamera.h" 16 | #include "esp_log.h" 17 | #include "driver/periph_ctrl.h" 18 | 19 | static const char* TAG = "camera"; 20 | 21 | static camera_config_t s_config; 22 | static lldesc_t s_dma_desc[2]; 23 | static uint32_t* s_dma_buf[2]; 24 | static uint8_t* s_fb[2]; 25 | static volatile int s_fb_idx = 0; 26 | static bool s_initialized = false; 27 | static int s_buf_line_width; 28 | static int s_buf_height; 29 | static volatile int s_line_count = 0; 30 | static volatile int s_cur_buffer = 0; 31 | static volatile bool s_i2s_running = false; 32 | static SemaphoreHandle_t s_data_ready; 33 | static SemaphoreHandle_t s_line_ready; 34 | static SemaphoreHandle_t s_vsync_catch; 35 | static volatile bool vsync_check = false; 36 | 37 | static intr_handle_t s_i2s_intr_handle = NULL; 38 | 39 | static void i2s_init(); 40 | static void i2s_frameReadStart(void); 41 | static void i2s_readStart(int index); 42 | static void IRAM_ATTR i2s_isr(void* arg); 43 | static esp_err_t dma_desc_init(void); 44 | static void line_filter_task(void *pvParameters); 45 | static void IRAM_ATTR VSYNC_isr( void *arg ); 46 | 47 | //---------- VSYNC Interrupt -------------------------- 48 | static void IRAM_ATTR VSYNC_isr( void *arg ) 49 | { 50 | // uint32_t gpio_num = (uint32_t)arg; 51 | if( vsync_check ){ 52 | BaseType_t xHigherPriorityTaskWoken;// = pdFALSE; 53 | xSemaphoreGiveFromISR( s_vsync_catch, &xHigherPriorityTaskWoken); 54 | } 55 | } 56 | 57 | //--------------------------------------------------- 58 | esp_err_t I2S_camera_init(camera_config_t* config) 59 | { 60 | memcpy(&s_config, config, sizeof(s_config)); 61 | 62 | s_buf_line_width = s_config.frame_width * s_config.pixel_byte_num; 63 | s_buf_height = s_config.frame_height; 64 | 65 | for(int i = 0; i < 2; i++){ 66 | if( s_fb[i] != NULL) free( s_fb[i] ); 67 | s_fb[i] = (uint8_t*)malloc(s_buf_line_width); 68 | if (s_fb[i] == NULL) { 69 | ESP_LOGE(TAG, "Failed to allocate frame buffer"); 70 | return ESP_ERR_NO_MEM; 71 | } 72 | } 73 | s_fb_idx = 0; 74 | s_data_ready = xSemaphoreCreateBinary(); 75 | s_line_ready = xSemaphoreCreateBinary(); 76 | s_vsync_catch = xSemaphoreCreateBinary(); 77 | 78 | i2s_init(); 79 | esp_err_t err = dma_desc_init(); 80 | if (err != ESP_OK) { 81 | free(s_fb[0]); 82 | free(s_fb[1]); 83 | ESP_LOGE(TAG, "Faild to allocate dma buffer"); 84 | return err; 85 | } 86 | xTaskCreatePinnedToCore(&line_filter_task, "line_filter", 2048, NULL, 10, NULL, 0); 87 | 88 | // skip at least one frame after changing camera settings 89 | while (gpio_get_level(s_config.VSYNC) == 1) {} 90 | while (gpio_get_level(s_config.VSYNC) == 0) {} 91 | while (gpio_get_level(s_config.VSYNC) == 1) {} 92 | 93 | s_initialized = true; 94 | return ESP_OK; 95 | } 96 | 97 | static void i2s_frameReadStart(void) 98 | { 99 | xSemaphoreTake( s_vsync_catch, portMAX_DELAY); // VSYNC wait 100 | vsync_check = false; 101 | s_cur_buffer = 0; 102 | s_line_count = 0; 103 | s_i2s_running = true; 104 | i2s_readStart(s_cur_buffer); // start RX 105 | } 106 | 107 | uint16_t* camera_getLine(uint16_t lineno) 108 | { 109 | if (!s_initialized) { 110 | return NULL; 111 | } 112 | unsigned long time = millis();; 113 | do{ 114 | if(!s_i2s_running){ 115 | vsync_check = true; 116 | i2s_frameReadStart(); 117 | } 118 | xSemaphoreTake(s_line_ready, portMAX_DELAY); 119 | if( millis() - time > 1000 ) return NULL; 120 | }while(lineno != s_line_count); 121 | 122 | return (uint16_t*)s_fb[s_fb_idx]; 123 | } 124 | 125 | static inline void i2s_conf_reset() 126 | { 127 | const uint32_t conf_reset_flags = I2S_RX_RESET_M | I2S_RX_FIFO_RESET_M | I2S_TX_RESET_M | I2S_TX_FIFO_RESET_M; 128 | I2S0.conf.val |= conf_reset_flags; 129 | I2S0.conf.val &= ~conf_reset_flags; 130 | 131 | while (I2S0.state.rx_fifo_reset_back) { 132 | ; 133 | } 134 | } 135 | 136 | static void i2s_init() 137 | { 138 | // Configure input GPIOs 139 | gpio_num_t pins[] = { 140 | s_config.D0, 141 | s_config.D1, 142 | s_config.D2, 143 | s_config.D3, 144 | s_config.D4, 145 | s_config.D5, 146 | s_config.D6, 147 | s_config.D7, 148 | s_config.PCLK, 149 | s_config.VSYNC, 150 | }; 151 | 152 | gpio_config_t conf = { 153 | .pin_bit_mask = 0, 154 | .mode = GPIO_MODE_INPUT, 155 | .pull_up_en = GPIO_PULLUP_DISABLE, 156 | .pull_down_en = GPIO_PULLDOWN_DISABLE, 157 | .intr_type = GPIO_INTR_DISABLE 158 | }; 159 | 160 | for (int i = 0; i < 10; ++i) { 161 | conf.pin_bit_mask = 1LL << pins[i]; 162 | gpio_config(&conf); 163 | } 164 | 165 | // VSYNC Interrupt Enable 166 | gpio_set_intr_type( s_config.VSYNC, GPIO_INTR_NEGEDGE ); 167 | gpio_install_isr_service( 0 ); 168 | gpio_isr_handler_add( s_config.VSYNC, VSYNC_isr, (void*)s_config.VSYNC ); 169 | 170 | // Route input GPIOs to I2S peripheral using GPIO matrix 171 | gpio_matrix_in(s_config.D0, I2S0I_DATA_IN0_IDX, false); 172 | gpio_matrix_in(s_config.D1, I2S0I_DATA_IN1_IDX, false); 173 | gpio_matrix_in(s_config.D2, I2S0I_DATA_IN2_IDX, false); 174 | gpio_matrix_in(s_config.D3, I2S0I_DATA_IN3_IDX, false); 175 | gpio_matrix_in(s_config.D4, I2S0I_DATA_IN4_IDX, false); 176 | gpio_matrix_in(s_config.D5, I2S0I_DATA_IN5_IDX, false); 177 | gpio_matrix_in(s_config.D6, I2S0I_DATA_IN6_IDX, false); 178 | gpio_matrix_in(s_config.D7, I2S0I_DATA_IN7_IDX, false); 179 | gpio_matrix_in(s_config.VSYNC, I2S0I_V_SYNC_IDX, false); // VSYNC は ネガティブ にしておくこと 180 | gpio_matrix_in(0x38, I2S0I_H_SYNC_IDX, false); // 0x38 is Allways hight (0x30 is Allways low) 181 | gpio_matrix_in(0x38, I2S0I_H_ENABLE_IDX, false); // HREF は 見ない 182 | gpio_matrix_in(s_config.PCLK, I2S0I_WS_IN_IDX, false); // PCLK は HREF が ON の時のみ有効にすること 183 | 184 | // Enable and configure I2S peripheral 185 | periph_module_enable(PERIPH_I2S0_MODULE); // I2S0 enable 186 | 187 | // Toggle some reset bits in LC_CONF register 188 | const uint32_t lc_conf_reset_flags = I2S_IN_RST_S | I2S_AHBM_RST_S | I2S_AHBM_FIFO_RST_S; 189 | 190 | I2S0.lc_conf.val |= lc_conf_reset_flags; 191 | I2S0.lc_conf.val &= ~lc_conf_reset_flags; 192 | 193 | // Toggle some reset bits in CONF register 194 | i2s_conf_reset(); 195 | // Enable slave mode (sampling clock is external) 196 | I2S0.conf.rx_slave_mod = 1; 197 | // Enable parallel mode 198 | I2S0.conf2.lcd_en = 1; 199 | // Use HSYNC/VSYNC/HREF to control sampling 200 | I2S0.conf2.camera_en = 1; 201 | // Configure clock divider 202 | I2S0.clkm_conf.clkm_div_a = 1; 203 | I2S0.clkm_conf.clkm_div_b = 0; 204 | I2S0.clkm_conf.clkm_div_num = 2; 205 | // FIFO will sink data to DMA 206 | I2S0.fifo_conf.dscr_en = 1; 207 | // FIFO configuration, TBD if needed 208 | I2S0.fifo_conf.rx_fifo_mod_force_en = 1; // The bit should always be set to 1.receive FIFO on 209 | I2S0.fifo_conf.rx_fifo_mod = 1; // receive 16-bit single channel data 210 | I2S0.conf_chan.rx_chan_mod = 1; // left channel + left channel [31:0] 211 | // Grab 16 samples 212 | I2S0.sample_rate_conf.rx_bits_mod = 16; // Set the bits to configure the bit length of I2S receiver channel. 213 | // Clear flags which are used in I2S serial mode 214 | I2S0.conf.rx_right_first = 0; // left fast 215 | I2S0.conf.rx_msb_right = 0; // msb left 216 | I2S0.conf.rx_msb_shift = 0; // Set this bit to enable receiver in Philips standard mode. 217 | I2S0.conf.rx_mono = 0; // Set this bit to enable receiver’s mono mode. 218 | I2S0.conf.rx_short_sync = 0; // Set this bit to enable receiver in PCM standard mode. 219 | 220 | // Allocate I2S interrupt, keep it disabled 221 | esp_intr_alloc( ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM, 222 | &i2s_isr, NULL, &s_i2s_intr_handle); 223 | } 224 | 225 | static void i2s_readStart(int index) 226 | { 227 | esp_intr_disable(s_i2s_intr_handle); 228 | 229 | i2s_conf_reset(); 230 | I2S0.rx_eof_num = s_buf_line_width; 231 | I2S0.in_link.addr = (uint32_t) &s_dma_desc[index]; 232 | I2S0.in_link.start = 1; 233 | I2S0.int_clr.val = I2S0.int_raw.val; 234 | I2S0.int_ena.in_done = 1; // DMA interrupt Enable when the current txlink descriptor is handled 235 | 236 | esp_intr_enable(s_i2s_intr_handle); 237 | I2S0.conf.rx_start = 1; // receive Start 238 | } 239 | 240 | static void i2s_stop() 241 | { 242 | I2S0.conf.rx_start = 0; 243 | esp_intr_disable(s_i2s_intr_handle); 244 | I2S0.int_ena.in_done = 0; // DMA interrupt Disable 245 | s_i2s_running = false; 246 | i2s_conf_reset(); 247 | } 248 | 249 | //------------------------------------------------- 250 | esp_err_t dma_desc_init(void) 251 | { 252 | size_t buf_size = s_buf_line_width * 2; // 2byte --> 32bit(4bytes) 253 | 254 | for (int i = 0; i < 2; ++i) { 255 | s_dma_buf[i] = (uint32_t*) malloc(buf_size); 256 | if (s_dma_buf[i] == NULL){ 257 | return ESP_ERR_NO_MEM; 258 | } 259 | s_dma_desc[i].length = buf_size; // size of a single DMA buf 260 | s_dma_desc[i].size = buf_size; // total size of the chain 261 | s_dma_desc[i].owner = 1; 262 | s_dma_desc[i].sosf = 1; 263 | s_dma_desc[i].buf = (uint8_t*) s_dma_buf[i]; 264 | s_dma_desc[i].offset = i; 265 | s_dma_desc[i].empty = 0; 266 | s_dma_desc[i].eof = 1; 267 | s_dma_desc[i].qe.stqe_next = NULL; 268 | } 269 | return ESP_OK; 270 | } 271 | 272 | //============= task =================================== 273 | 274 | static void line_filter_task(void *pvParameters) 275 | { 276 | while (true) { 277 | gpio_set_level( 2, 1); // 2 = LED pin 278 | 279 | xSemaphoreTake(s_data_ready, portMAX_DELAY); 280 | int buf_idx = !s_cur_buffer; 281 | 282 | gpio_set_level( 2, 0); // 2 = LED pin 283 | 284 | s_fb_idx = (s_fb_idx + 1) % 1; 285 | uint8_t* pfb = s_fb[s_fb_idx]; 286 | const uint32_t* buf = s_dma_buf[buf_idx]; 287 | 288 | for (int i = 0; i < s_buf_line_width/2; ++i) { 289 | uint32_t v = *buf++; // Get 32 bit from DMA buffer 290 | // 1 Pixel = (2Byte i2s overhead + 2Byte pixeldata) 291 | *pfb++ = (uint8_t)(v & 0x000000ff); 292 | *pfb++ = (uint8_t)((v & 0x00ff0000)>>16); 293 | } 294 | xSemaphoreGive(s_line_ready); 295 | } 296 | } 297 | 298 | //---------- Interrupt ------------------------------ 299 | static void IRAM_ATTR i2s_isr(void* arg) // 1 Line read done 300 | { 301 | I2S0.int_clr.val = I2S0.int_raw.val; 302 | 303 | s_cur_buffer = !s_cur_buffer; 304 | ++s_line_count; 305 | if(s_line_count == s_buf_height) { // 1 Frame read done 306 | i2s_stop(); 307 | } else { 308 | i2s_readStart(s_cur_buffer); 309 | } 310 | BaseType_t xHigherPriorityTaskWoken = pdFALSE; 311 | xSemaphoreGiveFromISR(s_data_ready, &xHigherPriorityTaskWoken); 312 | /* if (xHigherPriorityTaskWoken != pdFALSE) { 313 | portYIELD_FROM_ISR(); 314 | } 315 | */ 316 | } 317 | 318 | 319 | -------------------------------------------------------------------------------- /OV7670-ESP32/I2Scamera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esp_err.h" 4 | #include "driver/ledc.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | typedef struct { 11 | int D0; /*!< GPIO pin for camera D0 line */ 12 | int D1; /*!< GPIO pin for camera D1 line */ 13 | int D2; /*!< GPIO pin for camera D2 line */ 14 | int D3; /*!< GPIO pin for camera D3 line */ 15 | int D4; /*!< GPIO pin for camera D4 line */ 16 | int D5; /*!< GPIO pin for camera D5 line */ 17 | int D6; /*!< GPIO pin for camera D6 line */ 18 | int D7; /*!< GPIO pin for camera D7 line */ 19 | int XCLK; /*!< GPIO pin for camera XCLK line */ 20 | int PCLK; /*!< GPIO pin for camera PCLK line */ 21 | int VSYNC; /*!< GPIO pin for camera VSYNC line */ 22 | int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz */ 23 | ledc_timer_t ledc_timer; /*!< LEDC timer to be used for generating XCLK */ 24 | ledc_channel_t ledc_channel;/*!< LEDC channel to be used for generating XCLK */ 25 | int frame_width; 26 | int frame_height; 27 | uint8_t pixel_byte_num; 28 | 29 | }camera_config_t; 30 | 31 | esp_err_t I2S_camera_init(camera_config_t* config); 32 | uint16_t* camera_getLine(uint16_t lineno); 33 | //uint16_t* camera_getFrame(void); 34 | 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif -------------------------------------------------------------------------------- /OV7670-ESP32/OV7670.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "esp32-hal-ledc.h" 6 | #include "I2Scamera.h" 7 | #include "driver/periph_ctrl.h" 8 | 9 | //--------- Screen mode ----------------------------------------------- 10 | 11 | static const struct regval_list vga_OV7670[] PROGMEM = { // 640 x 480 12 | {REG_COM3, 0}, // No scaling 13 | {REG_COM14, 0}, 14 | {REG_SCALING_XSC, 0x3a}, // Horizontal scale factor 15 | {REG_SCALING_YSC, 0x35}, // Vertical scale factor 16 | {REG_SCALING_DCWCTR, 0}, // Down sampling nothing 17 | {REG_SCALING_PCLK_DIV, SCALING_PCLK_DIV_RSVD | SCALING_PCLK_DIV_1}, // DSP scale control Clock divide by 1 18 | {REG_SCALING_PCLK_DELAY,0x02}, 19 | {0xff, 0xff} // END MARKER 20 | }; 21 | 22 | static const struct regval_list cif_OV7670[] PROGMEM = { // 352 x 288 23 | {REG_COM3, COM3_DCWEN}, // Enable format scaling 24 | {REG_COM14, COM14_DCWEN | COM14_PCLKDIV_2}, // divide by 2 25 | {REG_SCALING_XSC, 0x3a}, // Horizontal scale factor 26 | {REG_SCALING_YSC, 0x35}, // Vertical scale factor 27 | {REG_SCALING_DCWCTR, SCALING_DCWCTR_VDS_by_2 | SCALING_DCWCTR_HDS_by_2}, // down sampling by 2 28 | {REG_SCALING_PCLK_DIV, SCALING_PCLK_DIV_RSVD | SCALING_PCLK_DIV_2}, // DSP scale control Clock divide by 2 29 | {REG_SCALING_PCLK_DELAY,0x02}, 30 | {0xff, 0xff} // END MARKER 31 | }; 32 | 33 | static const struct regval_list qvga_OV7670[] PROGMEM = { // 320 x 240 34 | {REG_COM3, COM3_DCWEN}, // Enable format scaling 35 | {REG_COM14, COM14_DCWEN | COM14_PCLKDIV_2}, // divide by 2 36 | {REG_SCALING_XSC, 0x3a}, // Horizontal scale factor 37 | {REG_SCALING_YSC, 0x35}, // Vertical scale factor 38 | {REG_SCALING_DCWCTR, SCALING_DCWCTR_VDS_by_2 | SCALING_DCWCTR_HDS_by_2}, // down sampling by 2 39 | {REG_SCALING_PCLK_DIV, SCALING_PCLK_DIV_RSVD | SCALING_PCLK_DIV_2}, // DSP scale control Clock divide by 2 40 | {REG_SCALING_PCLK_DELAY,0x02}, 41 | {0xff, 0xff} // END MARKER 42 | }; 43 | 44 | static const struct regval_list qqvga_OV7670[] PROGMEM = { // 160 x 120 45 | {REG_COM3, COM3_DCWEN}, // Enable format scaling 46 | {REG_COM14, COM14_DCWEN | COM14_MANUAL | COM14_PCLKDIV_4}, // divide by 4 47 | {REG_SCALING_XSC, 0x3a}, // Horizontal scale factor 48 | {REG_SCALING_YSC, 0x35}, // Vertical scale factor 49 | {REG_SCALING_DCWCTR, SCALING_DCWCTR_VDS_by_4 | SCALING_DCWCTR_HDS_by_4}, // down sampling by 4 50 | {REG_SCALING_PCLK_DIV, SCALING_PCLK_DIV_RSVD | SCALING_PCLK_DIV_4}, // DSP scale control Clock divide by 4 51 | {REG_SCALING_PCLK_DELAY,0x02}, 52 | {0xff, 0xff} // END MARKER 53 | }; 54 | 55 | static const struct regval_list qcif_OV7670[] PROGMEM = { 56 | {REG_COM3, COM3_SCALEEN | COM3_DCWEN}, // Enable format scaling 57 | {REG_COM3, COM3_DCWEN}, // Enable Downsampling/Dropping/Windowing 58 | {REG_COM14, COM14_DCWEN | COM14_MANUAL | COM14_PCLKDIV_2}, // divide by 2 59 | {REG_SCALING_XSC, 0x3a}, // Horizontal scale factor 60 | {REG_SCALING_YSC, 0x35}, // Vertical scale factor 61 | {REG_SCALING_DCWCTR, SCALING_DCWCTR_VDS_by_2 | SCALING_DCWCTR_HDS_by_2}, // downsample by 2 62 | {REG_SCALING_PCLK_DIV, SCALING_PCLK_DIV_RSVD | SCALING_PCLK_DIV_2}, // divide by 2 63 | {REG_SCALING_PCLK_DELAY,0x52}, 64 | {0xff, 0xff} /* END MARKER */ 65 | }; 66 | 67 | static const struct regval_list qqcif_OV7670[] PROGMEM = { 68 | {REG_COM3,COM3_SCALEEN | COM3_DCWEN}, 69 | {REG_COM14, COM14_DCWEN | COM14_PCLKDIV_4}, // divide by 4 70 | {REG_SCALING_XSC, 0x3a}, // Horizontal scale factor 71 | {REG_SCALING_YSC, 0x35}, // Vertical scale factor 72 | {REG_SCALING_DCWCTR, SCALING_DCWCTR_VDS_by_4 | SCALING_DCWCTR_HDS_by_4}, // down sampling by 4 73 | {REG_SCALING_PCLK_DIV, SCALING_PCLK_DIV_RSVD | SCALING_PCLK_DIV_4}, // DSP scale control Clock divide by 4 74 | {REG_SCALING_PCLK_DELAY,0x2A}, 75 | {0xff, 0xff} /* END MARKER */ 76 | }; 77 | 78 | //------- Color mode -------------------------------------- 79 | static const struct regval_list yuv422_OV7670[] PROGMEM = { 80 | {REG_RGB444, 0}, /* No RGB444 please */ 81 | {REG_COM1, 0}, 82 | {REG_COM15, COM15_R00FF}, 83 | {REG_COM9, COM9_AGC_GAIN_16x | 0x08}, /* 16x gain ceiling; 0x08 is reserved bit */ 84 | {REG_MTX1, 0x80}, /* "matrix coefficient 1" */ 85 | {REG_MTX2, 0x80}, /* "matrix coefficient 2" */ 86 | {REG_MTX3, 0}, /* vb */ 87 | {REG_MTX4, 0x22}, /* "matrix coefficient 4" */ 88 | {REG_MTX5, 0x5e}, /* "matrix coefficient 5" */ 89 | {REG_MTX6, 0x80}, /* "matrix coefficient 6" */ 90 | {REG_COM13,COM13_GAMMA|COM13_UVSAT|COM13_UVSWAP}, 91 | {0xff, 0xff} /* END MARKER */ 92 | }; 93 | 94 | static const struct regval_list rgb565_OV7670[] PROGMEM = { 95 | // {REG_COM7, COM7_RGB}, // select RGB mode 96 | {REG_RGB444, 0}, /* No RGB444 please */ 97 | {REG_COM1, 0x0}, 98 | {REG_COM15, COM15_R00FF | COM15_RGB565}, // RGB565 99 | {REG_TSLB, 0x04}, 100 | {REG_COM9, COM9_AGC_GAIN_16x | 0x08}, /* 16x gain ceiling; 0x08 is reserved bit */ 101 | {REG_MTX1, 0xb3}, /* "matrix coefficient 1" */ 102 | {REG_MTX2, 0xb3}, /* "matrix coefficient 2" */ 103 | {REG_MTX3, 0}, /* vb */ 104 | {REG_MTX4, 0x3d}, /* "matrix coefficient 4" */ 105 | {REG_MTX5, 0xa7}, /* "matrix coefficient 5" */ 106 | {REG_MTX6, 0xe4}, /* "matrix coefficient 6" */ 107 | {REG_COM13, COM13_GAMMA | COM13_UVSAT}, 108 | {0xff, 0xff} /* END MARKER */ 109 | }; 110 | 111 | static const struct regval_list bayerRGB_OV7670[] PROGMEM = { 112 | // {REG_COM7, COM7_PBAYER}, 113 | {REG_RGB444, R444_DISABLE}, 114 | {REG_COM15, COM15_R00FF}, 115 | {REG_TSLB, 0x04}, 116 | {REG_COM13, 0x08}, /* No gamma, magic rsvd bit */ 117 | {REG_COM16, 0x3d}, /* Edge enhancement, denoise */ 118 | {REG_REG76, 0xe1}, /* Pix correction, magic rsvd */ 119 | {0xff, 0xff} /* END MARKER */ 120 | }; 121 | //------------------------------------------------------------- 122 | /* 123 | const struct regval_list OV7670_default_regs[] PROGMEM = { 124 | {0x3a,0x04},{0x40,0xd0},{0x12,0x14},{0x32,0x80},{0x17,0x16},{0x18,0x04},{0x19,0x02},{0x1a,0x7b}, 125 | {0x03,0x06},{0x0c,0x04},{0x3e,0x19},{0x70,0x3a},{0x71,0x35},{0x72,0x11},{0x73,0xf1},{0xa2,0x02}, 126 | {0x11,0x81},{0x7a,0x20},{0x7b,0x1c},{0x7c,0x28},{0x7d,0x3c},{0x7e,0x55},{0x7f,0x68},{0x80,0x76}, 127 | {0x81,0x80},{0x82,0x88},{0x83,0x8f},{0x84,0x96},{0x85,0xa3},{0x86,0xaf},{0x87,0xc4},{0x88,0xd7}, 128 | {0x89,0xe8},{0x13,0xe0},{0x00,0x00},{0x10,0x00},{0x0d,0x00},{0x14,0x28},{0xa5,0x05},{0xab,0x07}, 129 | {0x24,0x75},{0x25,0x63},{0x26,0xA5},{0x9f,0x78},{0xa0,0x68},{0xa1,0x03},{0xa6,0xdf},{0xa7,0xdf}, 130 | {0xa8,0xf0},{0xa9,0x90},{0xaa,0x94},{0x13,0xe5},{0x0e,0x61},{0x0f,0x4b},{0x16,0x02},{0x1e,0x37}, //{0x1e,0x17} 131 | {0x21,0x02},{0x22,0x91},{0x29,0x07},{0x33,0x0b},{0x35,0x0b},{0x37,0x1d},{0x38,0x71},{0x39,0x2a}, 132 | {0x3c,0x78},{0x4d,0x40},{0x4e,0x20},{0x69,0x00},{0x6b,0x00},{0x74,0x19},{0x8d,0x4f},{0x8e,0x00}, 133 | {0x8f,0x00},{0x90,0x00},{0x91,0x00},{0x92,0x00},{0x96,0x00},{0x9a,0x80},{0xb0,0x84},{0xb1,0x0c}, 134 | {0xb2,0x0e},{0xb3,0x82},{0xb8,0x0a},{0x43,0x14},{0x44,0xf0},{0x45,0x34},{0x46,0x58},{0x47,0x28}, 135 | {0x48,0x3a},{0x59,0x88},{0x5a,0x88},{0x5b,0x44},{0x5c,0x67},{0x5d,0x49},{0x5e,0x0e},{0x64,0x04}, 136 | {0x65,0x20},{0x66,0x05},{0x94,0x04},{0x95,0x08},{0x6c,0x0a},{0x6d,0x55},{0x6e,0x11},{0x6f,0x9f}, 137 | {0x6a,0x40},{0x01,0x40},{0x02,0x40},{0x13,0x8f},{0x15,0x22},{0x4f,0x80},{0x50,0x80},{0x51,0x00}, 138 | {0x52,0x22},{0x53,0x5e},{0x54,0x80},{0x58,0x9e},{0x41,0x08},{0x3f,0x00},{0x75,0x05},{0x76,0xe1}, 139 | {0x4c,0x00},{0x77,0x01},{0x3d,0xc2},{0x4b,0x09},{0xc9,0x60},{0x41,0x38},{0x56,0x40},{0x34,0x11}, 140 | {0x3b,0x02},{0xa4,0x89},{0x96,0x00},{0x97,0x30},{0x98,0x20},{0x99,0x30},{0x9a,0x84},{0x9b,0x29}, 141 | {0x9c,0x03},{0x9d,0x4c},{0x9e,0x3f},{0x78,0x04},{0x79,0x01},{0xc8,0xf0},{0x79,0x0f},{0xc8,0x00}, 142 | {0x79,0x10},{0xc8,0x7e},{0x79,0x0a},{0xc8,0x80},{0x79,0x0b},{0xc8,0x01},{0x79,0x0c},{0xc8,0x0f}, 143 | {0x79,0x0d},{0xc8,0x20},{0x79,0x09},{0xc8,0x80},{0x79,0x02},{0xc8,0xc0},{0x79,0x03},{0xc8,0x40}, 144 | {0x79,0x05},{0xc8,0x30},{0x79,0x26},{0x09,0x03},{0x3b,0x42},{0xff,0xff}, 145 | }; 146 | */ 147 | const struct regval_list OV7670_default2_regs[] PROGMEM = { 148 | {REG_TSLB, 0x04}, 149 | {REG_COM15, COM15_R00FF | COM15_RGB565}, 150 | {REG_COM7, COM7_FMT_QVGA | COM7_RGB}, 151 | {REG_HREF, 0x80}, 152 | {REG_HSTART, 0x16}, 153 | {REG_HSTOP, 0x04}, 154 | {REG_VSTART, 0x02}, 155 | {REG_VSTOP, 0x7b}, 156 | {REG_VREF, 0x06}, 157 | {REG_COM3, COM3_DCWEN}, 158 | {REG_COM14, COM14_DCWEN | COM14_MANUAL | COM14_PCLKDIV_2}, 159 | {REG_SCALING_XSC, 0x3a}, 160 | {REG_SCALING_YSC, 0x35}, 161 | {REG_SCALING_DCWCTR, SCALING_DCWCTR_VDS_by_2 | SCALING_DCWCTR_HDS_by_2}, 162 | {REG_SCALING_PCLK_DIV, SCALING_PCLK_DIV_RSVD | SCALING_PCLK_DIV_2}, 163 | {REG_SCALING_PCLK_DELAY, 0x02}, 164 | {REG_CLKRC, CLK_RSVD | 0x01}, // clock divid 2 165 | // Gamma curve values 166 | {REG_SLOP, 0x20}, // SLOP = (256-GAM15)*40/30 167 | {REG_GAM1, 0x1c}, 168 | {REG_GAM2, 0x28}, 169 | {REG_GAM3, 0x3c}, 170 | {REG_GAM4, 0x55}, 171 | {REG_GAM5, 0x68}, 172 | {REG_GAM6, 0x76}, 173 | {REG_GAM7, 0x80}, 174 | {REG_GAM8, 0x88}, 175 | {REG_GAM9, 0x8f}, 176 | {REG_GAM10,0x96}, 177 | {REG_GAM11,0xa3}, 178 | {REG_GAM12,0xaf}, 179 | {REG_GAM13,0xc4}, 180 | {REG_GAM14,0xd7}, 181 | {REG_GAM15,0xe8}, 182 | 183 | // AGC(Auto Gain Celling) and AEC(Auto Exposure(露出) Control) parameters. 184 | // AGC/AEC parameters. Note we start by disabling those features,then turn them only after tweaking the values. 185 | {REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT}, 186 | 187 | {REG_GAIN, 0x00}, 188 | {REG_AECH, 0x00}, 189 | {REG_COM4, COM4_AEC_FULL}, 190 | {REG_COM9, COM9_AGC_GAIN_8x | 0x08}, 191 | {REG_BD50MAX, 0x05}, 192 | {REG_BD60MAX, 0x07}, 193 | {REG_AEW, 0x75}, // AGC upper limit 194 | {REG_AEB, 0x63}, // AGC lower limit 195 | {REG_VPT, 0xA5}, // AGC/AEC fast mode op region 196 | {REG_HAECC1, 0x78}, // Hist AEC/AGC control 1 197 | {REG_HAECC2, 0x68}, // Hist AEC/AGC control 2 198 | {0xa1, 0x03}, // Reserved 199 | {REG_HAECC3, 0xdf}, // Hist AEC/AGC control 3 200 | {REG_HAECC4, 0xdf}, // Hist AEC/AGC control 4 201 | {REG_HAECC5, 0xf0}, // Hist AEC/AGC control 5 202 | {REG_HAECC6, 0x90}, // Hist AEC/AGC control 6 203 | {REG_HAECC7, 0x94}, // Hist AEC/AGC control 7 204 | 205 | {REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT | COM8_AGC | COM8_AEC}, 206 | 207 | {REG_COM5, 0x61}, // Reserved 208 | {REG_COM6, 0x4b}, // Reset all timing when format changes 209 | {0x16,0x02}, // Reserved 210 | {REG_MVFP, 0x37},//0x07}, 211 | {REG_ADCCTR1, 0x02},// Reserved 212 | {REG_ADCCTR2, 0x91},// Reserved 213 | {0x29,0x07},{0x33,0x0b},{0x35,0x0b},{0x37,0x1d},{0x38,0x71},{0x39,0x2a}, // All Reserved 214 | {REG_COM12, 0x78}, 215 | {0x4d,0x40},{0x4e,0x20}, // Reserved 216 | 217 | {REG_GFIX, 0x00}, // AWB Pre gain control 218 | {REG_DBLV, DBLV_BYPASS}, // PLL control,Regulator control 219 | {REG_REG74, 0x19}, // Digital gain manual control 220 | {0x8d,0x4f},{0x8e,0x00},{0x8f,0x00},{0x90,0x00},{0x91,0x00}, // Reserved 221 | {REG_DM_LNL, 0x00}, 222 | {0x96,0x00},{0x9a,0x80},{0xb0,0x84}, 223 | {REG_ABLC1, 0x0c}, 224 | {0xb2,0x0e}, 225 | {REG_THL_ST,0x82}, 226 | {0xb8,0x0a}, 227 | 228 | {REG_AWBC1, 0x14}, //AWB Control 1 229 | {REG_AWBC2, 0xf0}, //AWB Control 2 230 | {REG_AWBC3, 0x34}, //AWB Control 3 231 | {REG_AWBC4, 0x58}, //AWB Control 4 232 | {REG_AWBC5, 0x28}, //AWB Control 5 233 | {REG_AWBC6, 0x3a}, //AWB Control 6 234 | {REG_AWBC7, 0x88}, //AWB Control 7 235 | {REG_AWBC8, 0x88}, //AWB Control 8 236 | {REG_AWBC9, 0x44}, //AWB Control 9 237 | {REG_AWBC10,0x67}, //AWB Control 10 238 | {REG_AWBC11,0x49}, //AWB Control 11 239 | {REG_AWBC12,0x0e}, //AWB Control 12 240 | 241 | // {REG_LCC1, 0x00}, // Lens Correction Option 1 242 | // {REG_LCC2, 0x00}, // Lens Correction Option 2 243 | {REG_LCC3, 0x04}, // Lens Correction Option 3 244 | {REG_LCC4, 0x20}, // Lens Correction Option 4 245 | {REG_LCC5, 0x05}, // Lens Correction Option 5 246 | {REG_LCC6, 0x04}, // Lens Correction Option 6 247 | {REG_LCC7, 0x08}, // Lens Correction Option 7 248 | {REG_AWBCTR3, 0x0a}, 249 | {REG_AWBCTR2, 0x55}, 250 | {REG_AWBCTR1, 0x11}, 251 | {REG_AWBCTR0, 0x9f}, 252 | {REG_GGAIN, 0x40}, // AWB Green gain 253 | {REG_BLUE, 0x40}, // AWB Blue gain (00-ff) 254 | {REG_RED, 0x40}, // AWB Red gain (00-ff) 255 | 256 | {REG_COM8, COM8_FASTAEC | COM8_RSVD | COM8_AGC | COM8_AWB | COM8_AEC}, 257 | 258 | {REG_COM10, COM10_PCLK_HB | COM10_VS_NEG}, // PCLK does not toggle during horizontal blank & VSYNC negative 259 | 260 | {REG_MTX1, 0x80}, // Matrix Coefficient 1 261 | {REG_MTX2, 0x80}, // Matrix Coefficient 2 262 | {REG_MTX3, 0x00}, // Matrix Coefficient 3 263 | {REG_MTX4, 0x22}, // Matrix Coefficient 4 264 | {REG_MTX5, 0x5e}, // Matrix Coefficient 5 265 | {REG_MTX6, 0x80}, // Matrix Coefficient 6 266 | {REG_MTXS, 0x9e}, // Matrix Coefficient Sign 267 | 268 | {REG_COM16, COM16_AWBGAIN}, // AWB gain enable 269 | {REG_EDGE, 0x00}, // Edge enhancement factor 270 | {REG_REG75, 0x05}, // Edge enhanced lower limit 271 | {REG_REG76, 0xe1}, // Edge enhanced higher limit ,Black/white pixcel correction enable 272 | {REG_DNSTH, 0x00}, // De-noise Threshold 273 | {REG_REG77, 0x01}, // Offset, de-noise range control 274 | {REG_COM13, 0xc2}, // Gamma enable, UV saturation auto adjustment 275 | {0x4b, 0x09}, 276 | {REG_SATCTR, 0x60}, // UV saturatin control min 277 | {REG_COM16, COM16_YUV_ENHANC | COM16_DE_NOISE | COM16_AWBGAIN}, 278 | {REG_CONTRAS, 0x40}, // Contrast Control 279 | {0x34, 0x11}, 280 | 281 | {REG_COM11, COM11_EXP}, // Exposure timing can be less than limit of banding filter when light is too strong 282 | {REG_NT_CTRL, 0x89}, // Auto frame rate adjustment dummy row selection 283 | 284 | // Magic setting 285 | {0x96,0x00},{0x97,0x30},{0x98,0x20}, 286 | {0x99,0x30},{0x9a,0x84},{0x9b,0x29}, 287 | {0x9c,0x03},{0x9d,0x4c},{0x9e,0x3f}, 288 | {0x78,0x04}, 289 | {0x79,0x01},{0xc8,0xf0}, 290 | {0x79,0x0f},{0xc8,0x00}, 291 | {0x79,0x10},{0xc8,0x7e}, 292 | {0x79,0x0a},{0xc8,0x80}, 293 | {0x79,0x0b},{0xc8,0x01}, 294 | {0x79,0x0c},{0xc8,0x0f}, 295 | {0x79,0x0d},{0xc8,0x20}, 296 | {0x79,0x09},{0xc8,0x80}, 297 | {0x79,0x02},{0xc8,0xc0}, 298 | {0x79,0x03},{0xc8,0x40}, 299 | {0x79,0x05},{0xc8,0x30}, 300 | {0x79,0x26}, 301 | 302 | {REG_COM2, COM2_OUT_DRIVE_4x}, // Output Drive Capability 4x 303 | {REG_COM11, COM11_FR_BY_4 | COM11_EXP}, // 1/4 normal mode frame rate, Exposure timing 304 | 305 | {0xff,0xff}, 306 | }; 307 | 308 | //--------------------------------------------------------- 309 | 310 | OV7670::OV7670(void){ 311 | } 312 | 313 | OV7670::~OV7670(void){ 314 | } 315 | 316 | void OV7670::conf_setFrameSize(uint8_t res){ 317 | switch(res){ 318 | case VGA: 319 | cam_conf.frame_width = 640; 320 | cam_conf.frame_height = 480; 321 | break; 322 | case QVGA: 323 | cam_conf.frame_width = 320; 324 | cam_conf.frame_height = 240; 325 | break; 326 | case QQVGA: 327 | cam_conf.frame_width = 160; 328 | cam_conf.frame_height = 120; 329 | break; 330 | 331 | case CIF: 332 | cam_conf.frame_width = 352; 333 | cam_conf.frame_height = 288; 334 | break; 335 | case QCIF: 336 | cam_conf.frame_width = 176; 337 | cam_conf.frame_height = 144; 338 | break; 339 | case QQCIF: 340 | cam_conf.frame_width = 88; 341 | cam_conf.frame_height = 72; 342 | break; 343 | } 344 | } 345 | 346 | void OV7670::reset(void){ 347 | wrReg(REG_COM7, COM7_RESET); // All reg reset 348 | delay(100); 349 | 350 | Serial.println(F("--- Default setting -----")); 351 | wrRegs(OV7670_default2_regs); // Camera Default setting 352 | Serial.println(F("--- Resolution setting -----")); 353 | setResolution( _resolution ); // 解像度設定 354 | Serial.println(F("--- ColorMode setting -----")); 355 | setColor( _colormode ); // カラーモード設定 356 | setPCLK(1, DBLV_CLK_x4); // PCLK 設定 : 10MHz / (pre+1) * 4 --> 20MHz 357 | } 358 | 359 | esp_err_t OV7670::init(const camera_config_t *value, uint8_t res, uint8_t colmode){ 360 | memcpy(&cam_conf, value, sizeof(cam_conf)); 361 | _resolution = res; 362 | _colormode = colmode; 363 | 364 | // Wire.begin(); 365 | // Wire.setClock(400000); 366 | // delay(1000); 367 | 368 | // XCLOK 出力 369 | pinMode(cam_conf.XCLK, OUTPUT); 370 | pinMode(cam_conf.XCLK, LOW); 371 | ledcSetup(cam_conf.ledc_channel, cam_conf.xclk_freq_hz, 2 ); 372 | ledcAttachPin(cam_conf.XCLK, cam_conf.ledc_channel); 373 | ledcWrite( cam_conf.ledc_channel, 2 ); 374 | 375 | conf_setFrameSize(res); 376 | 377 | switch(colmode){ 378 | case YUV422: 379 | case RGB565: 380 | cam_conf.pixel_byte_num = 2; 381 | break; 382 | case BAYER_RAW: 383 | cam_conf.pixel_byte_num = 1; 384 | break; 385 | case PBAYER_RAW: 386 | cam_conf.pixel_byte_num = 1; // ??? 387 | break; 388 | } 389 | 390 | esp_err_t err = I2S_camera_init(&cam_conf); // I2S initialize 391 | if (err != ESP_OK) { 392 | Serial.println(F(" I2S Camera init ERROR")); 393 | return err; 394 | } 395 | 396 | reset(); 397 | 398 | Serial.println(F("---- Camera init ok! ----")); 399 | return err; 400 | } 401 | 402 | void OV7670::setResolution(uint8_t res){ 403 | uint8_t temp; 404 | uint16_t vstart,vstop,hstart,hstop; 405 | uint8_t pclkdiv; 406 | 407 | conf_setFrameSize(res); 408 | 409 | temp = rdReg(REG_COM7); 410 | temp &= 0b01000111; 411 | 412 | switch(res){ 413 | case VGA: 414 | wrReg(REG_COM7, temp | COM7_FMT_VGA); // set Resolution 415 | wrRegs(vga_OV7670); 416 | hstart = 158; 417 | vstart = 10; 418 | pclkdiv = 4; 419 | break; 420 | case QVGA: 421 | wrReg(REG_COM7, temp | COM7_FMT_QVGA); // set Resolution 422 | wrRegs(qvga_OV7670); 423 | hstart = 180; 424 | vstart = 12; 425 | pclkdiv = 2; 426 | break; 427 | case QQVGA: 428 | wrReg(REG_COM7, temp | COM7_FMT_QVGA); // set Resolution 429 | wrRegs(qqvga_OV7670); 430 | hstart = 190; 431 | vstart = 10; 432 | pclkdiv = 1; 433 | break; 434 | 435 | case CIF: 436 | wrReg(REG_COM7, temp | COM7_FMT_CIF); // set Resolution 437 | wrRegs(cif_OV7670); 438 | hstart = 178; 439 | vstart = 14; 440 | pclkdiv = 2; 441 | break; 442 | case QCIF: 443 | wrReg(REG_COM7, temp | COM7_FMT_QCIF); // set Resolution 444 | wrRegs(qcif_OV7670); 445 | hstart = 456; 446 | vstart = 14; 447 | pclkdiv = 1; 448 | break; 449 | case QQCIF: 450 | wrReg(REG_COM7, temp | COM7_FMT_QCIF); // set Resolution 451 | wrRegs(qcif_OV7670); 452 | hstart = 456; 453 | vstart = 14; 454 | pclkdiv = 1; 455 | break; 456 | } 457 | setPCLK( pclkdiv, DBLV_CLK_x4); 458 | setHStart(hstart); 459 | setVStart(vstart); 460 | } 461 | 462 | void OV7670::setHStart(uint16_t hstart){ 463 | uint16_t hstop; 464 | switch(_resolution){ 465 | case VGA: 466 | case QVGA: 467 | case QQVGA: 468 | hstop = 640; 469 | break; 470 | case CIF: 471 | hstop = 704; 472 | break; 473 | case QCIF: 474 | hstop = 352; 475 | break; 476 | case QQCIF: 477 | hstop = 176; 478 | break; 479 | } 480 | hstop = ( hstart + hstop ) % 784; 481 | wrReg(REG_HSTART, (uint8_t)(hstart / 8)); 482 | wrReg(REG_HSTOP, (uint8_t)(hstop / 8)); 483 | wrReg(REG_HREF, 0x80 | (uint8_t)((hstop % 8) << 3) | (uint8_t)(hstart % 8)); 484 | rewrCLKRC(); 485 | } 486 | 487 | void OV7670::setVStart(uint16_t vstart){ 488 | uint16_t vstop; 489 | vstop = vstart + 480; 490 | wrReg(REG_VSTART, (uint8_t)(vstart/4)); 491 | wrReg(REG_VSTOP, (uint8_t)(vstop/4)); 492 | wrReg(REG_VREF, (uint8_t)((vstop%4)<<2) | (uint8_t)(vstart%4)); 493 | rewrCLKRC(); 494 | } 495 | 496 | uint16_t OV7670::getHStart(void){ 497 | uint16_t hstart; 498 | hstart = (uint16_t)rdReg(REG_HSTART) * 8 + (uint16_t)( rdReg(REG_HREF) & 0x07); 499 | return hstart; 500 | } 501 | uint16_t OV7670::getVStart(void){ 502 | uint16_t vstart; 503 | vstart = (uint16_t)rdReg(REG_VSTART) * 4 + (uint16_t)( rdReg(REG_VREF) & 0x03); 504 | return vstart; 505 | } 506 | 507 | void OV7670::stop(void){ 508 | ledcDetachPin(cam_conf.XCLK); 509 | } 510 | 511 | uint16_t* OV7670::getLine(uint16_t lineno){ 512 | uint16_t *p_buf; 513 | p_buf = camera_getLine(lineno); 514 | return p_buf; 515 | } 516 | 517 | bool OV7670::getLines(uint16_t lineno, uint8_t *buf, uint16_t n){ 518 | uint16_t i,*p_buf; 519 | uint16_t wb = cam_conf.frame_width * cam_conf.pixel_byte_num; 520 | 521 | for(i=0; i1023) val = 1023; 599 | uint8_t temp; 600 | wrReg(REG_GAIN, val % 256); 601 | temp = rdReg(REG_VREF) & 0x3F; 602 | wrReg(REG_VREF, temp | ((val / 256) << 6)); 603 | } 604 | 605 | uint16_t OV7670::getGain(void){ 606 | uint16_t val; 607 | val = (uint16_t)rdReg(REG_GAIN) + (uint16_t)((rdReg(REG_VREF) & 0x3F)) * 256; 608 | return val; 609 | } 610 | 611 | void OV7670::setAGC(uint8_t val){ 612 | uint8_t temp; 613 | temp = rdReg(REG_COM8) & ~(COM8_FASTAEC | COM8_AGC); 614 | if(val == 1) temp |= COM8_FASTAEC | COM8_AGC; 615 | wrReg(REG_COM8, temp); 616 | } 617 | bool OV7670::getAGC(void){ 618 | uint8_t temp; 619 | temp = rdReg(REG_COM8); 620 | if( (temp & COM8_AGC) != 0) return true; 621 | return false; 622 | } 623 | void OV7670::setAWB(uint8_t val){ 624 | uint8_t temp; 625 | temp = rdReg(REG_COM8) & ~COM8_AWB; 626 | if(val == 1) temp |= COM8_AWB; 627 | wrReg(REG_COM8, temp); 628 | } 629 | bool OV7670::getAWB(void){ 630 | uint8_t temp; 631 | temp = rdReg(REG_COM8); 632 | if( (temp & COM8_AWB) != 0) return true; 633 | return false; 634 | } 635 | void OV7670::setAWBB(uint8_t val){ 636 | wrReg(REG_BLUE,val); 637 | } 638 | void OV7670::setAWBR(uint8_t val){ 639 | wrReg(REG_RED,val); 640 | } 641 | void OV7670::setAWBG(uint8_t val){ 642 | wrReg(REG_GGAIN,val); 643 | } 644 | void OV7670::setAEC(uint8_t val){ 645 | uint8_t temp; 646 | temp = rdReg(REG_COM8) & ~(COM8_FASTAEC | COM8_AEC); 647 | if(val == 1) temp |= COM8_FASTAEC | COM8_AEC; 648 | wrReg(REG_COM8, temp); 649 | } 650 | bool OV7670::getAEC(void){ 651 | uint8_t temp; 652 | temp = rdReg(REG_COM8); 653 | if( (temp & COM8_AEC) != 0) return true; 654 | return false; 655 | } 656 | void OV7670::setBright(int8_t val){ 657 | /* uint8_t temp; 658 | temp = rdReg(REG_COM8) & ~COM8_AEC; 659 | wrReg(REG_COM8, temp); 660 | */ 661 | wrReg(REG_BRIGHT, (uint8_t)val); 662 | } 663 | int8_t OV7670::getBright(void){ 664 | int8_t temp; 665 | temp = (int8_t)rdReg(REG_BRIGHT); 666 | return temp; 667 | } 668 | void OV7670::setContrast(uint8_t val){ 669 | wrReg(REG_CONTRAS, val); 670 | } 671 | uint8_t OV7670::getContrast(void){ 672 | uint8_t temp; 673 | temp = rdReg(REG_CONTRAS); 674 | return temp; 675 | } 676 | void OV7670::setExposure(uint16_t val){ 677 | uint8_t temp; 678 | temp = rdReg(REG_COM1) & 0x03; 679 | wrReg(REG_COM1, temp | (uint8_t)(val % 4)); 680 | wrReg(REG_AECH, (uint8_t)((val / 4) % 256)); 681 | temp = rdReg(REG_AECHH) & 0x3F; 682 | wrReg(REG_AECHH, temp | (uint8_t)(val / 1024)); 683 | } 684 | 685 | void OV7670::colorbar(bool on){ 686 | uint8_t temp; 687 | temp = rdReg(REG_COM17); 688 | if(on) 689 | wrReg(REG_COM17, temp | COM17_CBAR); 690 | else 691 | wrReg(REG_COM17, temp & ~COM17_CBAR); 692 | } 693 | 694 | void OV7670::colorbar_super(bool on){ 695 | uint8_t temp; 696 | temp = rdReg(REG_COM7); 697 | if(on) 698 | wrReg(REG_COM7, temp | COM7_CBAR); 699 | else 700 | wrReg(REG_COM7, temp & ~COM7_CBAR); 701 | } 702 | 703 | //---------------------------------------------- 704 | void OV7670::wrReg(uint8_t reg, uint8_t dat){ 705 | uint8_t rdat; 706 | 707 | Wire.beginTransmission(OV7670_ADDR); 708 | Wire.write(reg); 709 | // delay(20); 710 | Wire.write(dat); 711 | // delay(30); 712 | Wire.endTransmission(); 713 | Serial.printf("i2c write reg:%02X data:%02X\n\r",reg,dat); 714 | 715 | // rdat = rdReg(reg); 716 | } 717 | 718 | uint8_t OV7670::rdReg(uint8_t reg){ 719 | uint8_t dat; 720 | 721 | Wire.beginTransmission(OV7670_ADDR); 722 | Wire.write(reg); 723 | // delay(20); 724 | Wire.endTransmission(true); 725 | // delay(20); 726 | Wire.requestFrom(OV7670_ADDR, 1, true); 727 | // delay(20); 728 | dat = Wire.read(); 729 | // Wire.endTransmission(); 730 | 731 | Serial.printf("i2c read reg:%02X data:%02X\n\r",reg,dat); 732 | 733 | return dat; 734 | } 735 | 736 | void OV7670::wrRegs(const struct regval_list *reglist){ 737 | const struct regval_list *next = reglist; 738 | uint8_t val; 739 | 740 | for(;;){ 741 | uint8_t reg_addr = pgm_read_byte(&next->reg_num); 742 | uint8_t reg_val = pgm_read_byte(&next->value); 743 | if((reg_addr==0xff)&&(reg_val==0xff)) // end marker 744 | break; 745 | wrReg(reg_addr, reg_val); 746 | next++; 747 | } 748 | delay(30); 749 | } 750 | 751 | -------------------------------------------------------------------------------- /OV7670-ESP32/OV7670.h: -------------------------------------------------------------------------------- 1 | #ifndef OV7670_h_ 2 | #define OV7670_h_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "esp_log.h" 8 | #include "esp_attr.h" 9 | #include "I2Scamera.h" 10 | 11 | #define VGA 0 // 640 x 480 12 | #define QVGA 1 // 320 X 240 13 | #define QQVGA 2 // 160 x 120 14 | #define CIF 3 // 352 x 288 15 | #define QCIF 4 // 176 x 144 16 | #define QQCIF 5 // 88 x 72 17 | 18 | #define YUV422 0x00 // color mode 19 | #define RGB565 0x04 20 | #define BAYER_RAW 0x01 21 | #define PBAYER_RAW 0x05 22 | 23 | #define OV7670_ADDR 0x21 24 | 25 | struct regval_list{ 26 | uint8_t reg_num; 27 | uint8_t value; 28 | }; 29 | 30 | class OV7670{ 31 | public: 32 | OV7670(void); 33 | ~OV7670(void); 34 | 35 | void reset(void); 36 | esp_err_t init(const camera_config_t *value, uint8_t res, uint8_t colmode); 37 | 38 | void setResolution(uint8_t res); 39 | void setPCLK(uint8_t pre, uint8_t pll); 40 | void rewrCLKRC(void); 41 | void setHStart(uint16_t val); 42 | void setVStart(uint16_t val); 43 | uint16_t getHStart(void); 44 | uint16_t getVStart(void); 45 | 46 | uint16_t* getLine(uint16_t lineno); 47 | bool getLines(uint16_t lineno, uint8_t *buf, uint16_t n); 48 | void getFrame(uint8_t *buf); 49 | 50 | void stop(void); 51 | uint16_t getMID(void); 52 | uint16_t getPID(void); 53 | void vflip( bool enable ); 54 | void setColor(uint8_t color); 55 | void setGain(uint16_t val); 56 | uint16_t getGain(void); 57 | void setAGC(uint8_t val); 58 | bool getAGC(void); 59 | void setAWB(uint8_t val); 60 | bool getAWB(void); 61 | void setAEC(uint8_t val); 62 | bool getAEC(void); 63 | void setBright(int8_t val); 64 | int8_t getBright(void); 65 | void setContrast(uint8_t val); 66 | uint8_t getContrast(void); 67 | void setAWBB(uint8_t val); 68 | void setAWBR(uint8_t val); 69 | void setAWBG(uint8_t val); 70 | void setExposure(uint16_t val); 71 | void colorbar(bool on); 72 | void colorbar_super(bool on); 73 | 74 | void wrReg(uint8_t reg,uint8_t dat); 75 | uint8_t rdReg(uint8_t reg); 76 | 77 | private: 78 | void wrRegs(const struct regval_list *reglist); 79 | void conf_setFrameSize(uint8_t res); 80 | camera_config_t cam_conf; 81 | uint8_t _resolution; 82 | uint8_t _colormode; 83 | }; 84 | 85 | // OV7670 Registers --------------------------------------------- 86 | // 87 | // AEC : Automatic Exposure(露出) Control Mode 88 | // AGC : Automatic Gain Control 89 | // AWB : Automatic White Balance Control 90 | // BLC : Black Level Calibration 91 | // ABLC: Automatic Black Level Calibration 92 | // DSP : Digital Signal Processor 93 | // 94 | #define REG_GAIN 0x00 // AGC[9:0] lower 8bit (higher 2bit VREF[7:6]) 95 | #define REG_BLUE 0x01 // AWB Blue gain (00-ff) 96 | #define REG_RED 0x02 // AWB Red gain (00-ff) 97 | #define REG_VREF 0x03 // Pieces of AGC[9:8], VSTOP[1:0], VSTART[1:0] 98 | #define REG_COM1 0x04 /* Control 1 */ 99 | #define COM1_CCIR656 0x40 /* CCIR656 enable */ 100 | #define REG_BAVE 0x05 /* U/B Average level */ 101 | #define REG_GbAVE 0x06 /* Y/Gb Average level */ 102 | #define REG_AECHH 0x07 /* AEC MS 5 bits */ 103 | #define REG_RAVE 0x08 /* V/R Average level */ 104 | #define REG_COM2 0x09 /* Control 2 */ 105 | #define COM2_SSLEEP 0x10 /* Soft sleep mode */ 106 | #define COM2_OUT_DRIVE_1x 0x00 /* Output drive capability 1x */ 107 | #define COM2_OUT_DRIVE_2x 0x01 /* Output drive capability 2x */ 108 | #define COM2_OUT_DRIVE_3x 0x02 /* Output drive capability 3x */ 109 | #define COM2_OUT_DRIVE_4x 0x03 /* Output drive capability 4x */ 110 | #define REG_PID 0x0a /* Product ID MSB */ 111 | #define REG_VER 0x0b /* Product ID LSB */ 112 | #define REG_COM3 0x0c /* Control 3 */ 113 | #define COM3_SWAP 0x40 /* Byte swap */ 114 | #define COM3_SCALEEN 0x08 /* Enable Scaling */ 115 | #define COM3_DCWEN 0x04 /* Enable Downsampling/Dropping/Windowing */ 116 | #define REG_COM4 0x0d /* Control 4 */ 117 | #define COM4_AEC_FULL 0x00 /* AEC evaluate full window */ 118 | #define COM4_AEC_1_2 0x10 /* AEC evaluate 1/2 window */ 119 | #define COM4_AEC_1_4 0x20 /* AEC evaluate 1/4 window */ 120 | #define COM4_AEC_2_3 0x30 /* AEC evaluate 2/3 window */ 121 | #define REG_COM5 0x0e /* All "reserved" */ 122 | #define REG_COM6 0x0f /* Control 6 */ 123 | #define COM6_BLK_LINE 0x80 // Enable HREF at optional black 124 | #define COM6_RESET_TIM 0x02 // Reset all timing when format changes 125 | #define COM6_RESERVE 0x41 // Reserved bit 126 | #define REG_AECH 0x10 /* More bits of AEC value */ 127 | #define REG_CLKRC 0x11 /* Clocl control */ 128 | #define CLK_RSVD 0x80 // Reserved 129 | #define CLK_EXT 0x40 /* Use external clock directly */ 130 | #define CLK_SCALE 0x3f /* Mask for internal clock scale */ 131 | #define REG_COM7 0x12 /* Control 7 */ 132 | #define COM7_RESET 0x80 /* Register reset */ 133 | #define COM7_FMT_MASK 0x38 134 | #define COM7_FMT_VGA 0x00 135 | #define COM7_FMT_CIF 0x20 /* CIF format */ 136 | #define COM7_FMT_QVGA 0x10 /* QVGA format */ 137 | #define COM7_FMT_QCIF 0x08 /* QCIF format */ 138 | #define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ 139 | #define COM7_YUV 0x00 /* YUV */ 140 | #define COM7_BAYER 0x01 /* Bayer format */ 141 | #define COM7_CBAR 0x02 // Color bar 142 | #define COM7_PBAYER 0x05 /* "Processed bayer" */ 143 | #define REG_COM8 0x13 /* Control 8 */ 144 | #define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ 145 | #define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ 146 | #define COM8_BFILT 0x20 /* Band filter enable */ 147 | #define COM8_RSVD 0x08 // Reserved fefault 1 148 | #define COM8_AGC 0x04 /* Auto gain enable */ 149 | #define COM8_AWB 0x02 // Auto White Balance enable 150 | #define COM8_AEC 0x01 /* Auto exposure enable */ 151 | #define REG_COM9 0x14 /* Control 9- gain ceiling */ 152 | #define COM9_AGC_GAIN_2x 0x00 /* Automatic Gain Ceiling 2x */ 153 | #define COM9_AGC_GAIN_4x 0x10 /* Automatic Gain Ceiling 4x */ 154 | #define COM9_AGC_GAIN_8x 0x20 /* Automatic Gain Ceiling 8x */ 155 | #define COM9_AGC_GAIN_16x 0x30 /* Automatic Gain Ceiling 16x */ 156 | #define COM9_AGC_GAIN_32x 0x40 /* Automatic Gain Ceiling 32x */ 157 | #define COM9_AGC_GAIN_64x 0x50 /* Automatic Gain Ceiling 64x */ 158 | #define COM9_AGC_GAIN_128x 0x60 /* Automatic Gain Ceiling 128x */ 159 | #define COM9_AGC_GAIN_NOT 0x70 /* Automatic Gain Ceiling Not allowed */ 160 | #define COM9_FREEZE_AGC_AEC 0x01 161 | #define REG_COM10 0x15 /* Control 10 */ 162 | #define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ 163 | #define COM10_PCLK_HB 0x20 /* PCLK does not toggle during horizontal blank */ 164 | #define COM10_PCLK_REV 0x10 // Reverse PCLK 165 | #define COM10_HREF_REV 0x08 /* Reverse HREF */ 166 | #define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ 167 | #define COM10_VS_NEG 0x02 /* VSYNC negative */ 168 | #define COM10_HS_NEG 0x01 /* HSYNC negative */ 169 | #define REG_HSTART 0x17 /* Horiz start high bits */ 170 | #define REG_HSTOP 0x18 /* Horiz stop high bits */ 171 | #define REG_VSTART 0x19 /* Vert start high bits */ 172 | #define REG_VSTOP 0x1a /* Vert stop high bits */ 173 | #define REG_PSHFT 0x1b /* Pixel delay after HREF */ 174 | #define REG_MIDH 0x1c /* Manuf. ID high */ 175 | #define REG_MIDL 0x1d /* Manuf. ID low */ 176 | #define REG_MVFP 0x1e /* Mirror / vflip */ 177 | #define MVFP_MIRROR 0x20 /* Mirror image */ 178 | #define MVFP_FLIP 0x10 /* Vertical flip */ 179 | #define MVFP_BLACK_SUN 0x04 // black sun enable 180 | #define REG_LAEC 0x1f // Reserved - Fine AEC Value - defines exposure value less than one row period 181 | #define REG_ADCCTR0 0x20 // ADC range adjustment 182 | #define REG_ADCCTR1 0x21 // Reserved 183 | #define REG_ADCCTR2 0x22 // Reserved 184 | #define REG_ADCCTR3 0x23 // Reserved 185 | #define REG_AEW 0x24 /* AGC upper limit */ 186 | #define REG_AEB 0x25 /* AGC lower limit */ 187 | #define REG_VPT 0x26 /* AGC/AEC fast mode op region */ 188 | #define REG_BBIAS 0x27 // B Channel Signal Output Bias 189 | #define REG_GbBIAS 0x28 // Gb Channel Signal Output Bias 190 | 191 | #define REG_EXHCH 0x2a // Dummy Pixel Insert MSB 192 | #define REG_EXHCL 0x2b // Dummy Pixel Insert LSB 193 | #define REG_RBIAS 0x2c // R Channel Signal Output Bias 194 | #define REG_ADVFL 0x2d // LSB of insert dummy rows in vertical direction (1 bit equals 1 row) 195 | #define REG_ADVFH 0x2e // MSB of insert dummy rows in vertical direction 196 | #define REG_YAVE 0x2f // Y/G Channel Average Value 197 | #define REG_HSYST 0x30 /* HSYNC rising edge delay */ 198 | #define REG_HSYEN 0x31 /* HSYNC falling edge delay */ 199 | #define REG_HREF 0x32 /* HREF pieces */ 200 | #define REG_CHLF 0x33 // Array Current Control ( Reserved ) 201 | #define REG_ARBLM 0x34 // Array Referrence Control ( Reserved ) 202 | #define REG_ADC 0x37 // ADC Control ( Reserved ) 203 | #define REG_ACOM 0x38 // ADC and Analog Common MOde Control ( Reserved ) 204 | #define REG_OFON 0x39 // Reserved - ADC Offset Control ( Reserved ) 205 | #define REG_TSLB 0x3a // Line Buffer Test Option 206 | #define TSLB_NEGATE 0x20 // Negative image - see MANU & MANV 207 | #define TSLB_UVOUT 0x10 // Use fixed UV value 208 | #define TSLB_YLAST 0x08 /* UYVY or VYUY - see com13 */ 209 | #define TSLB_AUTO 0x01 // Auto output window 210 | #define REG_COM11 0x3b /* Control 11 */ 211 | #define COM11_NIGHT 0x80 /* NIght mode enable */ 212 | #define COM11_NMFR 0x60 /* Two bit NM frame rate */ 213 | #define COM11_FR_BY_2 0x20 // 1/2 of normal mode frame rate 214 | #define COM11_FR_BY_4 0x40 // 1/4 of normal mode frame rate 215 | #define COM11_FR_BY_8 0x60 // 1/8 of normal mode frame rate 216 | #define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ 217 | #define COM11_50HZ 0x08 /* Manual 50Hz select */ 218 | #define COM11_EXP 0x02 // Exposure timing can be less than limit of banding filter when light is too strong 219 | #define REG_COM12 0x3c /* Control 12 */ 220 | #define COM12_HREF 0x80 /* HREF always */ 221 | #define COM12_RSVD 0x68 // reserved bit 222 | #define REG_COM13 0x3d /* Control 13 */ 223 | #define COM13_GAMMA 0x80 /* Gamma enable */ 224 | #define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ 225 | #define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ 226 | #define COM13_RESV 0x08 // reserved bit 227 | #define REG_COM14 0x3e /* Control 14 */ 228 | #define COM14_DCWEN 0x10 // DCW/PCLK-scale enable */ 229 | #define COM14_MANUAL 0x08 // Manual scaling enable 230 | #define COM14_PCLKDIV_1 0x00 // PCLK Divided by 1 231 | #define COM14_PCLKDIV_2 0x01 // PCLK Divided by 2 232 | #define COM14_PCLKDIV_4 0x02 // PCLK Divided by 4 233 | #define COM14_PCLKDIV_8 0x03 // PCLK Divided by 8 234 | #define COM14_PCLKDIV_16 0x04 // PCLK Divided by 16 235 | #define REG_EDGE 0x3f /* Edge enhancement factor */ 236 | #define REG_COM15 0x40 /* Control 15 */ 237 | #define COM15_R10F0 0x00 /* Data range 10 to F0 */ 238 | #define COM15_R01FE 0x80 /* 01 to FE */ 239 | #define COM15_R00FF 0xc0 /* 00 to FF */ 240 | #define COM15_RGB565 0x10 /* RGB565 output */ 241 | #define COM15_RGB555 0x30 /* RGB555 output */ 242 | #define REG_COM16 0x41 /* Control 16 */ 243 | #define COM16_YUV_ENHANC 0x20 244 | #define COM16_DE_NOISE 0x10 // 245 | #define COM16_AWBGAIN 0x08 /* AWB gain enable */ 246 | #define REG_COM17 0x42 /* Control 17 */ 247 | #define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ 248 | #define COM17_AEC_FULL 0x00 /* AEC evaluate full window */ 249 | #define COM17_AEC_1_2 0x40 /* AEC evaluate 1/2 window */ 250 | #define COM17_AEC_1_4 0x80 /* AEC evaluate 1/4 window */ 251 | #define COM17_AEC_2_3 0xC0 /* AEC evaluate 2/3 window */ 252 | #define COM17_CBAR 0x08 /* DSP Color bar */ 253 | 254 | #define REG_AWBC1 0x43 // AWB Control 1 (Reserved ?) 255 | #define REG_AWBC2 0x44 // AWB Control 2 (Reserved ?) 256 | #define REG_AWBC3 0x45 // AWB Control 3 (Reserved ?) 257 | #define REG_AWBC4 0x46 // AWB Control 4 (Reserved ?) 258 | #define REG_AWBC5 0x47 // AWB Control 5 (Reserved ?) 259 | #define REG_AWBC6 0x48 // AWB Control 6 (Reserved ?) 260 | 261 | #define REG_4B 0x4b // UV average ebnable 262 | #define UV_AVR_EN 0x01 // UV average enable 263 | #define REG_DNSTH 0x4c // De-noise Threshold 264 | //#define REG_DM_POS 0x4d // Reserved - Dummy row position 265 | /* 266 | #define REG_CMATRIX_BASE 0x4f 267 | #define CMATRIX_LEN 6 268 | #define REG_CMATRIX_SIGN 0x58 269 | */ 270 | #define REG_MTX1 0x4f /* Matrix Coefficient 1 */ 271 | #define REG_MTX2 0x50 /* Matrix Coefficient 2 */ 272 | #define REG_MTX3 0x51 /* Matrix Coefficient 3 */ 273 | #define REG_MTX4 0x52 /* Matrix Coefficient 4 */ 274 | #define REG_MTX5 0x53 /* Matrix Coefficient 5 */ 275 | #define REG_MTX6 0x54 /* Matrix Coefficient 6 */ 276 | 277 | #define REG_BRIGHT 0x55 /* Brightness */ 278 | #define REG_CONTRAS 0x56 /* Contrast control */ 279 | #define REG_CONTRAS_CENTER 0x57 // Contrast Center 280 | #define REG_MTXS 0x58 /* Matrix Coefficient Sign */ 281 | #define REG_AWBC7 0x59 // AWB Control 7 282 | #define REG_AWBC8 0x5a // AWB Control 8 283 | #define REG_AWBC9 0x5b // AWB Control 9 284 | #define REG_AWBC10 0x5c // AWB Control 10 285 | #define REG_AWBC11 0x5d // AWB Control 11 286 | #define REG_AWBC12 0x5e // AWB Control 12 287 | #define REG_B_LMT 0x5f // AWB B Gain Range 288 | #define REG_R_LMT 0x60 // AWB R Gain Range 289 | #define REG_G_LMT 0x61 // AWB G Gain Range 290 | #define REG_LCC1 0x62 // Lens Correction Option 1 - X Coordinate 291 | #define REG_LCC2 0x63 // Lens Correction Option 2 - Y Coordinate 292 | #define REG_LCC3 0x64 // Lens Correction Option 3 293 | #define REG_LCC4 0x65 // Lens Correction Option 4 294 | #define REG_LCC5 0x66 // Lens Correction Control 295 | #define REG_MANU 0x67 // Manual U Value 296 | #define REG_MANV 0x68 // Manual V Value 297 | #define REG_GFIX 0x69 // AWB Pre gain control 298 | #define REG_GGAIN 0x6a /* G Channel AWB Gain */ 299 | #define REG_DBLV 0x6b // PLL control,Regulator control 300 | #define DBLV_BYPASS 0x00 // Bypass PLL 301 | #define DBLV_CLK_x4 0x40 // input clock x4 302 | #define DBLV_CLK_x6 0x80 // input clock x6 303 | #define DBLV_CLK_x8 0xC0 // input clock x8 304 | #define DBLV_RSVD 0x0A // reserved bit 305 | #define REG_AWBCTR3 0x6c /* AWB Control 3 */ 306 | #define REG_AWBCTR2 0x6d /* AWB Control 2 */ 307 | #define REG_AWBCTR1 0x6e /* AWB Control 1 */ 308 | #define REG_AWBCTR0 0x6f /* AWB Control 0 */ 309 | #define REG_SCALING_XSC 0x70 // test pattern, Horizontal scale factor 310 | #define REG_SCALING_YSC 0x71 // test pattern, Vertical scale factor 311 | #define REG_SCALING_DCWCTR 0x72 // DCW Control 312 | #define SCALING_DCWCTR_VDS_by_2 0x10 // Vertical Down Sampling rate by 2 313 | #define SCALING_DCWCTR_VDS_by_4 0x20 // Vertical Down Sampling rate by 4 314 | #define SCALING_DCWCTR_VDS_by_8 0x30 // Vertical Down Sampling rate by 8 315 | #define SCALING_DCWCTR_HDS_by_2 0x01 // Horizontal Down Sampling rate by 2 316 | #define SCALING_DCWCTR_HDS_by_4 0x02 // Horizontal Down Sampling rate by 2 317 | #define SCALING_DCWCTR_HDS_by_8 0x03 // Horizontal Down Sampling rate by 2 318 | 319 | #define REG_SCALING_PCLK_DIV 0x73 // Clock divider control for DSP scale 320 | #define SCALING_PCLK_DIV_RSVD 0xf0 // Reserved 321 | #define SCALING_PCLK_DIV_DIS 0x08 // Bypass clock divider 322 | #define SCALING_PCLK_DIV_1 0x00 // Divided by 1 323 | #define SCALING_PCLK_DIV_2 0x01 // Divided by 2 324 | #define SCALING_PCLK_DIV_4 0x02 // Divided by 4 325 | #define SCALING_PCLK_DIV_8 0x03 // Divided by 8 326 | #define SCALING_PCLK_DIV_16 0x04 // Divided by 16 327 | #define REG_REG74 0x74 // Digital gain manual control 328 | #define REG_REG75 0x75 // Edge enhanced lower limit 329 | #define REG_REG76 0x76 /* OV's name */ 330 | #define R76_BLKPCOR 0x80 /* Black pixel correction enable */ 331 | #define R76_WHTPCOR 0x40 /* White pixel correction enable */ 332 | #define REG_REG77 0x77 // Offset, de-noise range control 333 | // 0x7a - 0x89 Ganma Curve registor 334 | #define REG_SLOP 0x7a // SLOP = (256-GAM15)x40/30 335 | #define REG_GAM1 0x7b // XREF1 4 336 | #define REG_GAM2 0x7c // XREF2 8 337 | #define REG_GAM3 0x7d // XREF3 16 338 | #define REG_GAM4 0x7e // XREF4 32 339 | #define REG_GAM5 0x7f // XREF5 40 340 | #define REG_GAM6 0x80 // XREF6 48 341 | #define REG_GAM7 0x81 // XREF7 56 342 | #define REG_GAM8 0x82 // XREF8 64 343 | #define REG_GAM9 0x83 // XREF9 72 344 | #define REG_GAM10 0x84 // XREF10 80 345 | #define REG_GAM11 0x85 // XREF11 96 346 | #define REG_GAM12 0x86 // XREF12 112 347 | #define REG_GAM13 0x87 // XREF13 144 348 | #define REG_GAM14 0x88 // XREF14 176 349 | #define REG_GAM15 0x89 // XREF15 208 350 | 351 | #define REG_RGB444 0x8c /* RGB 444 control */ 352 | #define R444_DISABLE 0x00 353 | #define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ 354 | #define R444_RGBX 0x01 /* Empty nibble at end */ 355 | #define REG_DM_LNL 0x92 // Dummy Row low 8bit 356 | #define REG_DM_LNH 0x93 // Dummy Row high 8bit 357 | #define REG_LCC6 0x94 // Lens Correction Optin 6 358 | #define REG_LCC7 0x95 // Lens Correction Optin 7 359 | 360 | #define REG_BD50ST 0x9d // 50Hz Banding Filter Value 361 | #define REG_BD60ST 0x9e // 60Hz Banding Filter Value 362 | 363 | #define REG_HAECC1 0x9f // Hist AEC/AGC control 1 364 | #define REG_HAECC2 0xa0 // Hist AEC/AGC control 2 365 | /* 366 | #define REG_HRL 0x9f // High Reference Luminance 367 | #define REG_LRL 0xa0 // Low Reference Luminance 368 | #define REG_DSPC3 0xa1 // DSP Control 3 369 | */ 370 | #define REG_SCALING_PCLK_DELAY 0xa2 // Pixel Clock Delay 371 | 372 | #define REG_NT_CTRL 0xa4 // Auto frame rate adjustment 373 | #define NT_CTRL_ROWPF 0x08 // Auto frame rate adjust dummy row per frame 374 | #define NT_CTRL_DMR_2x 0x00 // insert dummy row at 2x gain 375 | #define NT_CTRL_DMR_4x 0x01 // insert dummy row at 4x gain 376 | #define NT_CTRL_DMR_8x 0x02 // insert dummy row at 28 gain 377 | 378 | #define REG_BD50MAX 0xa5 // 50hz banding step limit 379 | #define REG_HAECC3 0xa6 // Hist AEC/AGC control 3 380 | #define REG_HAECC4 0xa7 // Hist AEC/AGC control 4 381 | #define REG_HAECC5 0xa8 // Hist AEC/AGC control 5 382 | #define REG_HAECC6 0xa9 // Hist AEC/AGC control 6 383 | #define REG_HAECC7 0xaa // Hist AEC/AGC control 7 384 | #define REG_BD60MAX 0xab // 60hz banding step limit 385 | 386 | /* 387 | #define REG_AECGMAX 0xa5 // Maximum Banding Filter Step 388 | #define REG_LPH 0xa6 // Low Limit of Probability for HRL 389 | #define REG_UPL 0xa7 // Upper Limit of Probability for LRL 390 | #define REG_TPL 0xa8 // Probablility Threshold for LRL to control AEC/AGC speed 391 | #define REG_TPH 0xa9 // Probablility Threshold for HRL to control AEC/AGC speed 392 | #define REG_NALG 0xaa // AEC Algorithm selection 393 | */ 394 | 395 | #define REG_STR_OPT 0xac // R/G/B gain control 396 | #define REG_STR_R 0xad // R Gain for LED Output Frame 397 | #define REG_STR_G 0xae // G Gain for LED Output Frame 398 | #define REG_STR_B 0xaf // B Gain for LED Output Frame 399 | 400 | #define REG_ABLC1 0xb1 // ABLC enable 401 | #define ABLC1_EN 0x04 // ABLC enable 402 | #define REG_THL_ST 0xb3 // ABLC Target 403 | #define REG_THL_DLT 0xb5 // ABLC Stable Range 404 | #define REG_AD_CHB 0xbe // Blue Channel Black Level Compensation 405 | #define REG_AD_CHR 0xbf // Red Channel Black Level Compensation 406 | #define REG_AD_CHGb 0xc0 // Gb Channel Black Level Compensation 407 | #define REG_AD_CHGr 0xc1 // Gr Channel Black Level Compensation 408 | #define REG_SATCTR 0xc9 // Saturation Control 409 | 410 | 411 | 412 | #endif 413 | -------------------------------------------------------------------------------- /OV7670-ESP32/examples/OV7670_ILI9341/OV7670_ILI9341.ino: -------------------------------------------------------------------------------- 1 | //************************************************************************* 2 | // OV7670 (non FIFO) Sample program for ESP32 3 | // 4 | // OUTPUT: ILI9341 TFT(320x240) SPI 5 | // option XPT2046 Touchscreen 6 | // 7 | //************************************************************************* 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // For the Adafruit shield, these are the default. 16 | #define TFT_DC 16 17 | #define TFT_CS 5 18 | #define TFT_RST 17 19 | #define TFT_MISO 19 20 | #define TFT_MOSI 23 21 | #define TFT_CLK 18 22 | 23 | #define TS_CS 2 24 | #define TS_IRQ -1 25 | 26 | const camera_config_t cam_conf = { 27 | .D0 = 36, 28 | .D1 = 39, 29 | .D2 = 34, 30 | .D3 = 35, 31 | .D4 = 32, 32 | .D5 = 33, 33 | .D6 = 25, 34 | .D7 = 26, 35 | .XCLK = 27, 36 | .PCLK = 14, 37 | .VSYNC = 13, 38 | .xclk_freq_hz = 10000000, // XCLK 10MHz 39 | .ledc_timer = LEDC_TIMER_0, 40 | .ledc_channel = LEDC_CHANNEL_0 41 | }; 42 | // SSCB_SDA(SIOD) --> 21(ESP32) 43 | // SSCB_SCL(SIOC) --> 22(ESP32) 44 | // RESET --> 3.3V 45 | // PWDN --> GND 46 | // HREF --> NC 47 | 48 | Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST); 49 | XPT2046_Touchscreen ts(TS_CS); 50 | OV7670 cam; // camera 51 | 52 | void setup() { 53 | Serial.begin(115200); 54 | Wire.begin(); 55 | Wire.setClock(400000); 56 | 57 | tft.begin(); 58 | tft.setRotation(1); 59 | tft.fillScreen(0); 60 | ts.begin(); // タッチスクリーン 61 | 62 | Serial.println("OV7670 camera Init..."); 63 | esp_err_t err = cam.init(&cam_conf, QVGA, RGB565); // カメラを初期化 64 | if(err != ESP_OK) Serial.println("cam.init ERROR"); 65 | 66 | cam.setPCLK(2, DBLV_CLK_x4); // PCLK 設定 : 10MHz / (pre+1) * 4 --> 13.3MHz 67 | cam.vflip( false ); // 画面180度回転 68 | 69 | Serial.printf("cam MID = %X\n\r",cam.getMID()); 70 | Serial.printf("cam PID = %X\n\r",cam.getPID()); 71 | 72 | // cam.colorbar(true); // カラーバーを表示する場合 73 | 74 | } 75 | 76 | void loop(void) { 77 | uint16_t y, *buf; 78 | 79 | while(1){ 80 | for( y = 0; y < 240; y++){ 81 | buf = cam.getLine( y+1 ); // カメラから1ライン分読み込む LineNo(1~240) 82 | tft.drawRGBBitmap( 0, y, buf, 320, 1); // TFT に1ライン出力 83 | } 84 | if( ts.touched() ){ 85 | TS_Point p= ts.getPoint(); 86 | Serial.print("->("); 87 | Serial.print(p.x); Serial.print(", "); 88 | Serial.print(p.y); Serial.print(", "); 89 | Serial.print(p.z); 90 | Serial.println(")"); 91 | } 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /OV7670-ESP32/examples/OV7670_Servo/OV7670_Servo.ino: -------------------------------------------------------------------------------- 1 | //************************************************************************* 2 | // OV7670 (non FIFO) Web streamer for ESP32 3 | // and Servo x 2 control 4 | // 5 | // line 22,23,25,27,28,90,91,92 は環境に合わせて各自設定変更してください。 6 | // line 51~81 でカメラ解像度を指定(コメントを外す) 7 | // 8 | //************************************************************************* 9 | #include 10 | #include 11 | #include "freertos/FreeRTOS.h" 12 | #include "freertos/task.h" 13 | #include "freertos/semphr.h" 14 | #include "esp_log.h" 15 | #include 16 | #include 17 | #include "hwcrypto/sha.h" 18 | #include "base64.h" 19 | #include "libb64/cdecode.h" // base64.hにはencodeしかまだないのでdecode用にinclude 20 | #include 21 | #include "esp32-hal-ledc.h" 22 | #include 23 | 24 | IPAddress myIP = IPAddress(192,168,1,99); // 固定IPアドレス(各自に合わせて変更して下さい) 25 | IPAddress myGateway = IPAddress(192,168,1, 1); 26 | 27 | String webUserPass = "UserName:Password"; // Web閲覧用 ユーザー名:パスワード を「:」 を付けて設定してください 28 | 29 | const char *OTA_name = "myESP32"; // OTA名 30 | const char *OTA_pass = ""; // OTA用パスワード 31 | 32 | #define SERVO1_PIN 16 33 | #define SERVO2_PIN 17 34 | 35 | const camera_config_t cam_conf = { 36 | .D0 = 36, 37 | .D1 = 39, 38 | .D2 = 34, 39 | .D3 = 35, 40 | .D4 = 32, 41 | .D5 = 33, 42 | .D6 = 25, 43 | .D7 = 26, 44 | .XCLK = 15, // 27 にすると何故かWiFiが動かなくなる。 45 | .PCLK = 14, 46 | .VSYNC = 13, 47 | .xclk_freq_hz = 10000000, // XCLK 10MHz 48 | .ledc_timer = LEDC_TIMER_0, 49 | .ledc_channel = LEDC_CHANNEL_0 50 | }; 51 | // SSCB_SDA(SIOD) --> 21(ESP32) 52 | // SSCB_SCL(SIOC) --> 22(ESP32) 53 | // RESET --> 3.3V 54 | // PWDN --> GND 55 | // HREF --> NC 56 | 57 | //********* カメラ解像度指定 *************** 58 | //#define CAM_RES VGA // カメラ解像度 59 | //#define CAM_WIDTH 640 // カメラ幅 60 | //#define CAM_HEIGHT 480 // カメラ高さ 61 | //#define CAM_DIV 12 // 1画面分割数 62 | 63 | //#define CAM_RES CIF // カメラ解像度 64 | //#define CAM_WIDTH 352 // カメラ幅 65 | //#define CAM_HEIGHT 288 // カメラ高さ 66 | //#define CAM_DIV 4 // 1画面分割数 67 | 68 | //#define CAM_RES QVGA // カメラ解像度 69 | //#define CAM_WIDTH 320 // カメラ幅 70 | //#define CAM_HEIGHT 240 // カメラ高さ 71 | //#define CAM_DIV 3 // 1画面分割数 72 | 73 | #define CAM_RES QCIF // カメラ解像度 74 | #define CAM_WIDTH 176 // カメラ幅 75 | #define CAM_HEIGHT 144 // カメラ高さ 76 | #define CAM_DIV 1 // 1画面分割数 77 | 78 | //#define CAM_RES QQVGA // カメラ解像度 79 | //#define CAM_WIDTH 160 // カメラ幅 80 | //#define CAM_HEIGHT 120 // カメラ高さ 81 | //#define CAM_DIV 1 // 1画面分割数 82 | //****************************************** 83 | 84 | WiFiMulti wifiMulti; 85 | WiFiServer server(80); 86 | WiFiClient WSclient; 87 | OV7670 cam; // camera 88 | 89 | bool WS_on = false; // WS設定が済んだかどうかのフラグ 90 | 91 | void wifi_connect(){ 92 | wifiMulti.addAP("your-ssid_1", "password_1"); // 接続予定のWiFiアクセスポイントを登録 93 | wifiMulti.addAP("your-ssid_2", "password_2"); 94 | wifiMulti.addAP("your-ssid_3", "password_3"); 95 | 96 | Serial.println(F("Connecting Wifi...")); 97 | while(1){ 98 | if(wifiMulti.run() == WL_CONNECTED) { 99 | WiFi.config( myIP, myGateway, IPAddress(255,255,255,0)); //固定IPアドレスにする 100 | 101 | ArduinoOTA.setPort(8266); 102 | ArduinoOTA.setHostname( OTA_name ); 103 | ArduinoOTA.setPassword( OTA_pass ); 104 | ArduinoOTA.onStart([]() { 105 | String type; 106 | if (ArduinoOTA.getCommand() == U_FLASH) 107 | type = "sketch"; 108 | else // U_SPIFFS 109 | type = "filesystem"; 110 | 111 | // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() 112 | Serial.println("Start updating " + type); 113 | }); 114 | ArduinoOTA.onEnd([]() { 115 | Serial.println("\nEnd"); 116 | }); 117 | ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { 118 | Serial.printf("Progress: %u%%\r", (progress / (total / 100))); 119 | }); 120 | ArduinoOTA.onError([](ota_error_t error) { 121 | Serial.printf("Error[%u]: ", error); 122 | if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); 123 | else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); 124 | else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); 125 | else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); 126 | else if (error == OTA_END_ERROR) Serial.println("End Failed"); 127 | }); 128 | ArduinoOTA.begin(); 129 | Serial.println(F("--- WiFi connected ---")); 130 | Serial.print("SSID: "); 131 | Serial.println( WiFi.SSID() ); 132 | Serial.print(F("IP Address: ")); 133 | Serial.println( WiFi.localIP() ); 134 | Serial.print("signal strength (RSSI): "); 135 | Serial.print( WiFi.RSSI() ); // 信号レベル 136 | Serial.println("dBm"); 137 | break;; 138 | }else{ 139 | delay(5000); 140 | ESP.restart(); 141 | } 142 | } 143 | } 144 | 145 | const char *html_401 = "HTTP/1.1 401 Authorization Required\r\n" 146 | // "Server: Apache/2.0.52 (FreeBSD)\r\n" 147 | "WWW-Authenticate: Basic realm='Secret File'\r\n" 148 | "Content-type:text/html; charset=utf-8\r\n" 149 | "Connection:close\r\n" 150 | "\r\n" //1行空行が必要 151 | "\n" 152 | "\n" 153 | "\n" 154 | "401 パスワード認証\n" 155 | "\n" 156 | "\n" 157 | "

Authorization Required

\n" 158 | "

This server could not verify that you are authorized to access the document requested.

\n" 159 | "\n" 160 | "\n"; 161 | 162 | const char *html_head = "HTTP/1.1 200 OK\r\n" 163 | "Content-type:text/html\r\n" 164 | "Connection:close\r\n" 165 | "\r\n" //1行空行が必要 166 | "\n" 167 | "\n" 168 | "\n" 169 | "\n" 170 | "\n" 171 | "OV7670 Live\n" 172 | "\n" 173 | "\n" 174 | "\n"; 178 | 179 | const char *html_script = 180 | "var socket = null;\n" 181 | "var tms;\n" 182 | "var msgIn;\n" 183 | "var msg;\n" 184 | "var ctx;\n" 185 | "var width;\n" 186 | "var height;\n" 187 | "var imageData;\n" 188 | "var pixels;\n" 189 | "var fps = 0;\n" 190 | 191 | "window.onload = function(){\n" 192 | " msgIn = document.getElementById('msgIn');\n" 193 | " msg = document.getElementById('msg');\n" 194 | " var c = document.getElementById('cam_canvas');\n" 195 | " ctx = c.getContext('2d');\n" 196 | " width = c.width;\n" 197 | " height = c.height;\n" 198 | " imageData = ctx.createImageData( width, 1 );\n" 199 | " pixels = imageData.data;\n" 200 | " setTimeout('ws_connect()', 1000);\n" 201 | "}\n" 202 | 203 | "function Msg(message){ msg.innerHTML = message;}\n" 204 | 205 | "function ws_connect(){\n" 206 | " tms = new Date();\n" 207 | " if(socket == null){\n" 208 | " socket = new WebSocket(wsUri);\n" 209 | " socket.binaryType = 'arraybuffer';\n" 210 | //" socket.binaryType = 'Blob';\n" 211 | " socket.onopen = function(evt){ onOpen(evt) };\n" 212 | " socket.onclose = function(evt){ onClose(evt) };\n" 213 | " socket.onmessage = function(evt){ onMessage(evt) };\n" 214 | " socket.onerror = function(evt){ onError(evt) };\n" 215 | " }\n" 216 | " setTimeout('fpsShow()', 1000);\n" 217 | "}\n" 218 | 219 | "function onOpen(evt){ Msg('CONNECTED');}\n" 220 | "function onClose(evt){ Msg('WS.Close.DisConnected ' + evt.code +':'+ evt.reason); WS_close();}\n" 221 | "function onError(evt){ Msg(evt.data);}\n" 222 | 223 | "function onMessage(evt){\n" 224 | " var data = evt.data;\n" 225 | " if( typeof data == 'string'){\n" 226 | " msgIn.innerHTML = data;\n" 227 | " }else if( data instanceof ArrayBuffer){\n" 228 | " drawLine(evt.data);\n" 229 | " }else if( data instanceof Blob){\n" 230 | " Msg('Blob data received');\n" 231 | " }\n" 232 | "}\n" 233 | 234 | "function doSend(ch,data){\n" 235 | " var str = String(\"\");\n" 236 | " str += String(ch) + String(data);\n" 237 | " var mms = new Date();\n" 238 | " if(mms-tms > 100){\n" 239 | " WS_send(str);\n" 240 | " tms = new Date();\n" 241 | " }\n" 242 | "}\n" 243 | 244 | "function doSendNow(ch,data){\n" 245 | " var str = String(\"\");\n" 246 | " str += String(ch) + String(data);\n" 247 | " WS_send(str);\n" 248 | "}\n" 249 | 250 | "function WS_send(str){\n" 251 | " if( socket.readyState == 1){\n" 252 | " if( socket.bufferedAmount == 0){\n" 253 | " socket.send(str);}}}\n" 254 | 255 | "function WS_close(){\n" 256 | " socket.close();\n" 257 | " socket = null;\n" 258 | " setTimeout('ws_connect()', 1);\n" // 1m秒後に再接続を試みる 259 | "}\n" 260 | 261 | "function fpsShow(){\n" // 1秒間に何フレーム表示出来たかを表示 262 | " msgIn.innerHTML = String(fps)+'fps';\n" 263 | " fps = 0;\n" 264 | " setTimeout('fpsShow()', 1000);\n" 265 | "}\n" 266 | 267 | "function drawLine(data){\n" 268 | " var buf = new Uint16Array(data);\n" 269 | " var lineNo = buf[0];\n" 270 | // " Msg(String(lineNo));\n" 271 | " for(var y = 0; y < (buf.length-1)/width; y += 1){\n" 272 | " var base = 0;\n" 273 | " for(var x = 0; x < width; x += 1){\n" 274 | " var c = 1 + x + y * width;\n" 275 | " pixels[base+0] = (buf[c] & 0xf800) >> 8 | (buf[c] & 0xe000) >> 13;\n" // Red 276 | " pixels[base+1] = (buf[c] & 0x07e0) >> 3 | (buf[c] & 0x0600) >> 9;\n" // Green 277 | " pixels[base+2] = (buf[c] & 0x001f) << 3 | (buf[c] & 0x001c) >> 2;\n" // Blue 278 | " pixels[base+3] = 255;\n" // Alpha 279 | " base += 4;\n" 280 | " }\n" 281 | " ctx.putImageData( imageData, 0, lineNo + y);\n" 282 | " }\n" 283 | " if(lineNo + y == height) fps+=1;\n" 284 | "}\n" 285 | "\n" 286 | "\n" 287 | "\n"; 288 | 289 | void printHTML(WiFiClient &client){ 290 | client.println( html_head ); 291 | 292 | client.print(F("")); 293 | client.print(F("")); 298 | client.println(F("")); 299 | client.println(F("
")); 300 | client.println(F("
Websocket connecting...
")); 301 | client.println(F(" ")); 302 | 303 | client.println(F("\n" 207 | "\n" 208 | "\n"; 209 | 210 | void printHTML(WiFiClient &client){ 211 | Serial.println("sendHTML ..."); 212 | client.print(html_head); 213 | Serial.println("head done"); 214 | 215 | client.print(F("\n")); 220 | 221 | client.print( html_body ); 222 | client.print( WiFi.localIP() ); 223 | client.println(F("/';")); 224 | Serial.println("body done"); 225 | client.println(html_script); 226 | Serial.println("sendHTML Done"); 227 | } 228 | 229 | //************ Hash sha1 base64 encord **************************** 230 | String Hash_Key( String h_req_key ){ 231 | unsigned char hash[20]; 232 | 233 | String str = h_req_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 234 | esp_sha( SHA1, (unsigned char*)str.c_str(), str.length(), hash ); 235 | str = base64::encode( hash, 20 ); 236 | return str; 237 | } 238 | //***************************************************************** 239 | 240 | void WS_handshake( WiFiClient &client ){ 241 | String req; 242 | String hash_req_key; 243 | 244 | Serial.println(F("-----from Browser HTTP WebSocket Request---------")); 245 | //ブラウザからのリクエストで空行(\r\nが先頭になる)まで読み込む 246 | do{ 247 | req = client.readStringUntil('\n'); //\nまで読み込むが\n自身は文字列に含まれず、捨てられる 248 | Serial.println(req); 249 | if(req.indexOf("Sec-WebSocket-Key") >= 0){ 250 | hash_req_key = req.substring(req.indexOf(':')+2,req.indexOf('\r')); 251 | Serial.println(); 252 | Serial.print(F("hash_req_key =")); 253 | Serial.println( hash_req_key ); 254 | } 255 | }while(req.indexOf("\r") != 0); 256 | 257 | req =""; 258 | delay(10); 259 | 260 | //-------ここからHTTPレスポンスのHTMLとJavaScriptコード 261 | Serial.println(F("---send WS HTML...")); 262 | String str = "HTTP/1.1 101 Switching Protocols\r\n"; 263 | str += "Upgrade: websocket\r\n"; 264 | str += "Connection: Upgrade\r\n"; 265 | str += "Sec-WebSocket-Accept: "; 266 | str += Hash_Key( hash_req_key ); // hash -> BASE64エンコードキー 267 | str += "\r\n\r\n"; // 空行は必須 268 | Serial.println(str); 269 | client.print(str); // client に送信 270 | str = ""; 271 | WSclient = client; 272 | } 273 | 274 | void Ini_HTTP_Response(void){ 275 | int pos; 276 | bool valueRead = false; 277 | uint8_t gain; 278 | String req; 279 | 280 | WiFiClient client = server.available(); // サーバーに対して外部から接続があるかどうかを監視 281 | if(!client) return; // クライアントからのアクセス要求があり、接続が確立し、読み取りが出来る状態になるとtrue 282 | 283 | while(client.connected()){ // クライアントが接続状態の間 284 | if(!client.available()) break; // 読み取り可能バイトが無いなら終了 285 | Serial.println(F("----Client Receive----")); 286 | req = client.readStringUntil('\n'); // 1行読み込み 287 | 288 | if(req.indexOf("GET / HTTP") != -1){ // ブラウザからリクエストを受信したらこの文字列を検知する 289 | while(req.indexOf("\r") != 0){ // ブラウザからのリクエストで空行(\r\nが先頭になる)まで読み込む 290 | req = client.readStringUntil('\n'); // \nまで読み込むが\n自身は文字列に含まれず、捨てられる 291 | Serial.println(req); 292 | if(req.indexOf("websocket") != -1){ 293 | Serial.println(F("\nPrint WS HandShake---")); 294 | WS_handshake(client); // WS 続きの読込 & 送信 295 | WS_on = true; // ws 設定終了フラグ 296 | return; 297 | } 298 | } 299 | delay(10); // 10ms待ってレスポンスをブラウザに送信 300 | Serial.println(F("\nPrint HTML-----------")); 301 | printHTML(client); // レスポンス(HTML)を返す 302 | Serial.println(F("\nPrint HTML end-------")); 303 | } 304 | else{ // その他のリクエスト(faviconなど)全部読み飛ばす 305 | Serial.println(F("*** Anather Request ***")); 306 | Serial.print(req); 307 | while(client.available()){ 308 | Serial.write(client.read()); // ブラウザからデータが送られている間読み込む 309 | } 310 | } 311 | if(!WS_on){ 312 | delay(1); //これが重要!これが無いと切断できないかもしれない。 313 | client.stop(); //一旦ブラウザとコネクション切断する。 314 | delay(1); 315 | Serial.println(F("===== Client stop =====")); 316 | req = ""; 317 | } 318 | } 319 | } 320 | 321 | #define WS_FIN 0x80 322 | #define OP_TEXT 0x81 323 | #define OP_BIN 0x82 324 | #define OP_CLOSE 0x88 325 | #define OP_PING 0x89 326 | #define OP_PONG 0x8A 327 | #define WS_MASK 0x80; 328 | 329 | uint8_t *WScamData; 330 | uint16_t data_size; 331 | uint16_t line_size; 332 | uint16_t line_h; 333 | 334 | bool setImgHeader(uint16_t w, uint16_t h){ 335 | line_h = h; 336 | line_size = w * 2; 337 | data_size = 2 + line_size * h; // (LineNo + img) バイト数 338 | WScamData = (uint8_t*)malloc(data_size + 4); // + head size 339 | if(WScamData == NULL){ 340 | Serial.println(F("******** Memory allocate Error! ***********")); 341 | return false; 342 | } 343 | Serial.println("WS Buffer Keep OK"); 344 | WScamData[0] = OP_BIN; // バイナリデータ送信ヘッダ 345 | WScamData[1] = 126; // 126:この後に続く2バイトがデータ長。127なら8バイトがデータ長 346 | WScamData[2] = (uint8_t)(data_size / 256); // 送信バイト数(Hi) 347 | WScamData[3] = (uint8_t)(data_size % 256); // 送信バイト数(Lo) 348 | return true; 349 | } 350 | 351 | #define UNIT_SIZE 2048 352 | 353 | void WS_sendImg(uint16_t lineNo) 354 | { 355 | uint16_t len, send_size; 356 | uint8_t *pData; 357 | 358 | WScamData[4] = (uint8_t)(lineNo % 256); 359 | WScamData[5] = (uint8_t)(lineNo / 256); 360 | 361 | len = data_size + 4; 362 | pData = WScamData; 363 | while(len){ 364 | send_size = (len > UNIT_SIZE) ? UNIT_SIZE : len; 365 | WSclient.write(pData, send_size ); // websocketデータ送信 ( UNITサイズ以下に区切って送る ) 366 | len -= send_size; 367 | pData += send_size; 368 | } 369 | } 370 | 371 | void setup() { 372 | Serial.begin(115200); 373 | Serial.println(F("OV7670 Web")); 374 | Wire.begin(); 375 | Wire.setClock(400000); 376 | 377 | WS_on = false; 378 | if(wifi_connect()){ 379 | server.begin(); // クライアントの接続待ち状態にする 380 | } 381 | Serial.println(F("---- cam init ----")); 382 | esp_err_t err = cam.init(&cam_conf, CAM_RES, RGB565); // カメラを初期化 383 | if(err != ESP_OK){ 384 | Serial.println(F("cam.init ERROR")); 385 | while(1); 386 | } 387 | // cam.setPCLK(2, DBLV_CLK_x4); // PCLK = 10MHz / (pre+1) * 4 Hz 13.3MHz 388 | cam.vflip( false ); // 画面180度回転 389 | 390 | Serial.printf("cam MID = %X\n\r",cam.getMID()); 391 | Serial.printf("cam PID = %X\n\r",cam.getPID()); 392 | 393 | // cam.colorbar(true); 394 | Serial.println(F("---- cam init done ----")); 395 | } 396 | 397 | void loop(void) { 398 | uint16_t y,dy; 399 | 400 | dy = CAM_HEIGHT / CAM_DIV; // 1度に送るライン数 401 | setImgHeader( CAM_WIDTH, dy ); // Websocket用ヘッダを用意 402 | 403 | while(1){ 404 | for( y = 0; y < CAM_HEIGHT; y += dy){ 405 | cam.getLines( y+1 , &WScamData[6] , dy); // カメラから dyライン分得る。LineNo(top:1) 406 | if(WS_on){ 407 | if(WSclient){ 408 | WS_sendImg(y); // Websocket 画像送信 409 | } 410 | else{ 411 | WSclient.stop(); // 接続が切れたら、ブラウザとコネクション切断する。 412 | WS_on = false; 413 | Serial.println(F("====< Client Stop >====")); 414 | } 415 | } 416 | } 417 | if(!WS_on){ 418 | Ini_HTTP_Response(); 419 | } 420 | } 421 | free(WScamData); 422 | } 423 | 424 | -------------------------------------------------------------------------------- /OV7670-ESP32/examples/OV7670_WebAdjust/OV7670_WebAdjust.ino: -------------------------------------------------------------------------------- 1 | //************************************************************************* 2 | // OV7670 (non FIFO) Web Adjustment for ESP32 3 | // 4 | // line 22,23,82,83,84 は環境に合わせて各自設定変更してください。 5 | // line 48~71 でカメラ解像度を指定(コメントを外す) 6 | // 7 | //************************************************************************* 8 | #include 9 | #include 10 | #include "freertos/FreeRTOS.h" 11 | #include "freertos/task.h" 12 | #include "freertos/semphr.h" 13 | #include "esp_log.h" 14 | #include 15 | #include 16 | #include "hwcrypto/sha.h" 17 | #include "base64.h" 18 | #include 19 | #include "esp32-hal-ledc.h" 20 | 21 | IPAddress myIP = IPAddress(192,168,1,99); // 固定IPアドレス(各自に合わせて変更して下さい) 22 | IPAddress myGateway = IPAddress(192,168,1, 1); 23 | 24 | const camera_config_t cam_conf = { 25 | .D0 = 36, 26 | .D1 = 39, 27 | .D2 = 34, 28 | .D3 = 35, 29 | .D4 = 32, 30 | .D5 = 33, 31 | .D6 = 25, 32 | .D7 = 26, 33 | .XCLK = 15, // 27 にすると何故かwebsocket通信時に動かなくなる。 34 | .PCLK = 14, 35 | .VSYNC = 13, 36 | .xclk_freq_hz = 10000000, // XCLK 10MHz 37 | .ledc_timer = LEDC_TIMER_0, 38 | .ledc_channel = LEDC_CHANNEL_0 39 | }; 40 | // SSCB_SDA(SIOD) --> 21(ESP32) 41 | // SSCB_SCL(SIOC) --> 22(ESP32) 42 | // RESET --> 3.3V 43 | // PWDN --> GND 44 | // HREF --> NC 45 | 46 | //********* カメラ解像度指定 *************** 47 | //#define CAM_RES VGA // カメラ解像度 48 | //#define CAM_WIDTH 640 // カメラ幅 49 | //#define CAM_HEIGHT 480 // カメラ高さ 50 | //#define CAM_DIV 12 // 1画面分割数 51 | 52 | //#define CAM_RES CIF // カメラ解像度 53 | //#define CAM_WIDTH 352 // カメラ幅 54 | //#define CAM_HEIGHT 288 // カメラ高さ 55 | //#define CAM_DIV 4 // 1画面分割数 56 | 57 | //#define CAM_RES QVGA // カメラ解像度 58 | //#define CAM_WIDTH 320 // カメラ幅 59 | //#define CAM_HEIGHT 240 // カメラ高さ 60 | //#define CAM_DIV 3 // 1画面分割数 61 | 62 | #define CAM_RES QCIF // カメラ解像度 63 | #define CAM_WIDTH 176 // カメラ幅 64 | #define CAM_HEIGHT 144 // カメラ高さ 65 | #define CAM_DIV 1 // 1画面分割数 66 | 67 | //#define CAM_RES QQVGA // カメラ解像度 68 | //#define CAM_WIDTH 160 // カメラ幅 69 | //#define CAM_HEIGHT 120 // カメラ高さ 70 | //#define CAM_DIV 1 // 1画面分割数 71 | 72 | //****************************************** 73 | 74 | OV7670 cam; // camera 75 | WiFiServer server(80); 76 | WiFiClient WSclient; 77 | bool WS_on = false; // WS設定が済んだかどうかのフラグ 78 | WiFiMulti wifiMulti; 79 | 80 | bool wifi_connect(){ 81 | wifiMulti.addAP("your-ssid_1", "password_1"); // 接続予定のWiFiアクセスポイントを登録 82 | wifiMulti.addAP("your-ssid_2", "password_2"); 83 | wifiMulti.addAP("your-ssid_3", "password_3"); 84 | 85 | Serial.println("Connecting Wifi..."); 86 | if(wifiMulti.run() == WL_CONNECTED) { 87 | WiFi.config( myIP, myGateway, IPAddress(255,255,255,0)); //固定IPアドレスにする 88 | 89 | Serial.println(F("--- WiFi connected ---")); 90 | Serial.print("SSID: "); 91 | Serial.println( WiFi.SSID() ); 92 | Serial.print(F("IP Address: ")); 93 | Serial.println( WiFi.localIP() ); 94 | Serial.print(F("signal strength (RSSI): ")); 95 | Serial.print( WiFi.RSSI() ); // 信号レベル 96 | Serial.println(F("dBm")); 97 | return true; 98 | } 99 | else return false; 100 | } 101 | 102 | uint8_t v_gain = 0; 103 | uint8_t v_awbb = 0; 104 | uint8_t v_awbr = 0; 105 | uint8_t v_awbg = 0; 106 | uint16_t v_aec = 0; 107 | int8_t v_bright = 0; 108 | uint8_t v_cnt = 0; 109 | uint16_t v_hstart = 0; 110 | uint16_t v_vstart = 0; 111 | bool b_agc = false; 112 | bool b_awb = false; 113 | bool b_aec = false; 114 | 115 | const char *html_head = "HTTP/1.1 200 OK\r\n" 116 | "Content-type:text/html\r\n" 117 | "Connection:close\r\n" 118 | "\r\n" //1行空行が必要 119 | "\n" 120 | "\n" 121 | "\n" 122 | "\n" 123 | "\n" 124 | "OV7670 Live\n" 125 | "\n" 126 | "\n" 127 | "\n"; 130 | 131 | const char *html_script = "" 132 | "var socket = null;\n" 133 | "var tms;\n" 134 | "var msgIn;\n" 135 | "var msg;\n" 136 | "var ctx;\n" 137 | "var width;\n" 138 | "var imageData;\n" 139 | "var pixels;\n" 140 | 141 | "window.onload = function(){\n" 142 | " msgIn = document.getElementById('msgIn');\n" 143 | " msg = document.getElementById('msg');\n" 144 | " var c = document.getElementById('cam_canvas');\n" 145 | " ctx = c.getContext('2d');\n" 146 | " width = c.width;\n" 147 | " imageData = ctx.createImageData( width, 1 );\n" 148 | " pixels = imageData.data;\n" 149 | " setTimeout('ws_connect()', 1000);\n" 150 | "}\n" 151 | 152 | "function Msg(message){ msg.innerHTML = message;}\n" 153 | 154 | "function ws_connect(){\n" 155 | " tms = new Date();\n" 156 | " if(socket == null){\n" 157 | " socket = new WebSocket(wsUri);\n" 158 | " socket.binaryType = 'arraybuffer';\n" 159 | //" socket.binaryType = 'Blob';\n" 160 | " socket.onopen = function(evt){ onOpen(evt) };\n" 161 | " socket.onclose = function(evt){ onClose(evt) };\n" 162 | " socket.onmessage = function(evt){ onMessage(evt) };\n" 163 | " socket.onerror = function(evt){ onError(evt) };\n" 164 | " }\n" 165 | "}\n" 166 | 167 | "function onOpen(evt){ Msg('CONNECTED');doSend('m','WebSocket Open');}\n" 168 | "function onClose(evt){ Msg('WS.Close.DisConnected ' + evt.code +':'+ evt.reason); WS_close();}\n" 169 | "function onError(evt){ Msg(evt.data);}\n" 170 | 171 | "function onMessage(evt){\n" 172 | " var data = evt.data;\n" 173 | " if( typeof data == 'string'){\n" 174 | " var vreg = document.getElementById('v_regdata');\n" 175 | " var suji = parseInt(evt.data);\n" // 文字を数値に変換 176 | " vreg.value = suji.toString(16);\n " // 数値を16進文字に変換 177 | " }else if( data instanceof ArrayBuffer){\n" 178 | " drawLine(evt.data);\n" 179 | " }else if( data instanceof Blob){\n" 180 | " Msg('Blob data received');\n" 181 | " }\n" 182 | "}\n" 183 | 184 | "function doSend(ch,data){\n" 185 | " var str = String(\"\");\n" 186 | " str += String(ch) + String(data);\n" 187 | " var mms = new Date();\n" 188 | " if(mms-tms > 100){\n" 189 | " WS_send(str);\n" 190 | " tms = new Date();\n" 191 | " }\n" 192 | "}\n" 193 | 194 | "function WS_send(str){\n" 195 | " if( socket.readyState == 1){\n" 196 | " if( socket.bufferedAmount == 0){\n" 197 | " socket.send(str);}}}\n" 198 | 199 | "function WS_close(){\n" 200 | " socket.close();\n" 201 | " socket = null;\n" 202 | " setTimeout('ws_connect()', 1);\n" // 1m秒後に再接続を試みる 203 | "}\n" 204 | 205 | "function checkSend(id, boolEnable){if(boolEnable){ doSend(id,'1');}else{doSend(id,'0');}}\n" 206 | 207 | "function toggleDisable(checkbox, field_id ) {\n" 208 | " var toggle = document.getElementById(field_id);\n" 209 | " checkbox.checked ? toggle.disabled = true : toggle.disabled = false;\n" 210 | "}\n" 211 | 212 | "function regRead(){\n" 213 | " var hex = document.getElementById('v_regadr').value;\n" 214 | " var adr = parseInt(hex,16);\n" 215 | " doSend('D',adr);\n" 216 | "}\n" 217 | 218 | "function regWrite(){\n" 219 | " var hex = document.getElementById('v_regadr').value;\n" 220 | " var adr = parseInt(hex,16);\n" 221 | " var hex = document.getElementById('v_regdata').value;\n" 222 | " var d = parseInt(hex,16);\n" 223 | " var wd = (adr * 256) | d;\n" 224 | " doSend('d',wd);\n" 225 | "}\n" 226 | 227 | "function drawLine(data){\n" 228 | " var buf = new Uint16Array(data);\n" 229 | " var lineNo = buf[0];\n" 230 | " Msg(String(lineNo));\n" 231 | " for(var y = 0; y < (buf.length-1)/width; y+=1){\n" 232 | " var base = 0;\n" 233 | " for(var x = 0; x < width; x += 1){\n" 234 | " var c = 1 + x + y * width;\n" 235 | " pixels[base+0] = (buf[c] & 0xf800) >> 8 | (buf[c] & 0xe000) >> 13;\n" // Red 236 | " pixels[base+1] = (buf[c] & 0x07e0) >> 3 | (buf[c] & 0x0600) >> 9;\n" // Green 237 | " pixels[base+2] = (buf[c] & 0x001f) << 3 | (buf[c] & 0x001c) >> 2;\n" // Blue 238 | " pixels[base+3] = 255;\n" // Alpha 239 | " base += 4;\n" 240 | " }\n" 241 | " ctx.putImageData(imageData, 0, lineNo + y);\n" 242 | " }\n" 243 | "}\n" 244 | // "-->\n" 245 | "\n" 246 | "\n" 247 | "\n"; 248 | 249 | void printHTML(WiFiClient &client){ 250 | Serial.println(F("sendHTML ...")); 251 | 252 | client.println(html_head); 253 | Serial.println(F("HTML head done")); 254 | 255 | client.print(F("\n")); 260 | 261 | client.print(F("
Websocket connecting...
\n")); 262 | client.print(F("
Camera Setting
\n")); 263 | 264 | client.print(F("Bright:")); 267 | client.print(F("
")); 270 | 271 | client.print(F("Contrast:")); 274 | client.print(F("
")); 277 | 278 | client.print(F("ColorBar:
")); 279 | 280 | client.print(F("AGC:
")); 283 | 284 | client.print(F("
")); 285 | client.print(F("Gain:")); 288 | client.print(F("
")); 291 | client.print(F("

")); 292 | 293 | client.print(F("AWB:
")); 296 | 297 | client.print(F("
")); 298 | client.print(F(" R:")); 301 | client.print(F("
")); 304 | 305 | client.print(F(" G:")); 308 | client.print(F("
")); 311 | 312 | client.print(F(" B:")); 315 | client.print(F("
")); 318 | client.print(F("

")); 319 | 320 | client.print(F("AEC:
")); 323 | 324 | client.print(F("
")); 325 | client.print(F("Exp:")); 328 | client.print(F("
")); 331 | client.print(F("

")); 332 | 333 | client.print(F("HSTRT: ")); 336 | client.print(F("
")); 339 | 340 | client.print(F("VSTRT: ")); 343 | client.print(F("

")); 346 | 347 | client.println(F("REG:")); 348 | client.println(F("")); 349 | client.println(F("")); 350 | client.println(F("")); 351 | 352 | client.println(F("

")); 353 | client.println(F(" ")); 354 | 355 | client.println(F("