├── LICENSE ├── README.MD ├── bin ├── Config.jpg ├── ESP32_SoftAP_Cam.ino.bin ├── ESP32_SoftAP_Cam.ino.partitions.bin ├── boot_app0.bin └── bootloader_dio_80m.bin ├── examples ├── BIGIOT_UpdateImage │ └── BIGIOT_UpdateImage.ino ├── ESP32_SoftAP_Cam │ └── ESP32_SoftAP_Cam.ino └── ESP32_Station_Cam │ └── ESP32_Station_Cam.ino ├── gitgnore ├── images └── TTGOcam.jpg ├── keywords.txt ├── library.json ├── library.properties ├── ov2640_v1.3.pdf └── src ├── OV2640.cpp ├── OV2640.h ├── camera.c ├── camera.h ├── camera_common.h ├── ov2640_regs.h ├── ov264_drive.c ├── ov264_drive.h ├── ov7725.c ├── ov7725.h ├── ov7725_regs.h ├── sccb.c ├── sccb.h ├── sensor.h ├── twi.c └── twi.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 lewis he 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | ESP32 Camera Ardunio Library 2 | ==================== 3 | Each example is tested using the TTGO T-Journal. 4 | 5 | ![image](./images/TTGOcam.jpg) 6 | 7 | The example contains three programs: 8 | - Upload a photo to the [BIGIOT](https://www.bigiot.net/) platform every 30 seconds 9 | - WiFi Soft-AP mode video stream 10 | - WiFi Station mode video stream 11 | 12 | In order to use the Station mode for video transmission quality, it should be as close as possible to the router, and the Soft-AP mode should be as close as possible to the device. 13 | 14 | This library is based on [esp32-cam-demo](https://github.com/igrr/esp32-cam-demo) changes and is used in Arduino. 15 | 16 | # Installing 17 | To install this library: 18 | - Install [oled-ssd1306](https://github.com/ThingPulse/esp8266-oled-ssd1306),If you want to use oled. 19 | - Install it using the Arduino Library manager ("Sketch" -> "Include Library" -> "Manage Libraries..."), or 20 | - Download a zipfile from github using the "Download ZIP" button and install it using the IDE ("Sketch" -> "Include Library" -> "Add .ZIP Library..." 21 | - Clone this git repository into your sketchbook/libraries folder. 22 | 23 | For more info, see https://www.arduino.cc/en/Guide/Libraries 24 | 25 | # How to use 26 | - Change the WiFi ssid and password in the program. 27 | - In Sota-AP you should connect `TTGO-CAMERA` and password is "`12345678`" , enter `192.168.1.1` in the browser to access the video stream. 28 | - In the Station mode, If you use OLED ,you should enter the IP address displayed in the OLED for access. You can also enter the IP address in the log to access 29 | - If you run the [BIGIOT](https://www.bigiot.net/) sample program, you need to add the device ID, interface ID, and password to add your wifi account. 30 | -------------------------------------------------------------------------------- /bin/Config.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilyGO/ESP32-Camera/ca43a7a387c3214332e29c91dd9e12ba3fef0002/bin/Config.jpg -------------------------------------------------------------------------------- /bin/ESP32_SoftAP_Cam.ino.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilyGO/ESP32-Camera/ca43a7a387c3214332e29c91dd9e12ba3fef0002/bin/ESP32_SoftAP_Cam.ino.bin -------------------------------------------------------------------------------- /bin/ESP32_SoftAP_Cam.ino.partitions.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilyGO/ESP32-Camera/ca43a7a387c3214332e29c91dd9e12ba3fef0002/bin/ESP32_SoftAP_Cam.ino.partitions.bin -------------------------------------------------------------------------------- /bin/boot_app0.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilyGO/ESP32-Camera/ca43a7a387c3214332e29c91dd9e12ba3fef0002/bin/boot_app0.bin -------------------------------------------------------------------------------- /bin/bootloader_dio_80m.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilyGO/ESP32-Camera/ca43a7a387c3214332e29c91dd9e12ba3fef0002/bin/bootloader_dio_80m.bin -------------------------------------------------------------------------------- /examples/BIGIOT_UpdateImage/BIGIOT_UpdateImage.ino: -------------------------------------------------------------------------------- 1 | #include "OV2640.h" 2 | #include 3 | #include 4 | #include 5 | #include "time.h" 6 | 7 | #define HTTPS_HOST "www.bigiot.net" 8 | #define HTTPS_PORT 443 9 | #define BIGIOT_API_KEY "" // Put your API Key here 10 | #define BIGIOT_DEVICE_ID "" 11 | #define BIGIOT_INTERFACE_ID "" 12 | const char *ssid = ""; // Put your SSID here 13 | const char *password = ""; // Put your PASSWORD here 14 | const char *ntpServer = "pool.ntp.org"; 15 | const long gmtOffset_sec = 3600; 16 | const int daylightOffset_sec = 3600; 17 | 18 | char *request_content = "--------------------------ef73a32d43e7f04d\r\n" 19 | "Content-Disposition: form-data; name=\"data\"; filename=\"%s.jpg\"\r\n" 20 | "Content-Type: image/jpeg\r\n\r\n"; 21 | 22 | char *request_end = "\r\n--------------------------ef73a32d43e7f04d--\r\n"; 23 | 24 | 25 | OV2640 cam; 26 | WiFiClientSecure client; 27 | StaticJsonBuffer<512> jsonBuffer; 28 | 29 | void update_image(void) 30 | { 31 | char status[64] = {0}; 32 | char buf[1024]; 33 | struct tm timeinfo; 34 | 35 | cam.run(); 36 | 37 | if (!client.connect(HTTPS_HOST, HTTPS_PORT)) 38 | { 39 | Serial.println("Connection failed"); 40 | return; 41 | } 42 | 43 | if (!getLocalTime(&timeinfo)) 44 | { 45 | Serial.println("Failed to obtain time"); 46 | snprintf(buf, sizeof(buf), request_content, String(millis()).c_str()); 47 | } 48 | else 49 | { 50 | strftime(status, sizeof(status), "%Y%m%d%H%M%S", &timeinfo); 51 | snprintf(buf, sizeof(buf), request_content, status); 52 | } 53 | 54 | uint32_t content_len = cam.getSize() + strlen(buf) + strlen(request_end); 55 | 56 | 57 | String request = "POST /pubapi/uploadImg/did/"BIGIOT_DEVICE_ID"/inputid/"BIGIOT_INTERFACE_ID" HTTP/1.1\r\n"; 58 | request += "Host: www.bigiot.net\r\n"; 59 | request += "User-Agent: TTGO-Camera-Demo\r\n"; 60 | request += "Accept: */*\r\n"; 61 | request += "API-KEY: " + String(BIGIOT_API_KEY) + "\r\n"; 62 | request += "Content-Length: " + String(content_len) + "\r\n"; 63 | request += "Content-Type: multipart/form-data; boundary=------------------------ef73a32d43e7f04d\r\n"; 64 | request += "Expect: 100-continue\r\n"; 65 | request += "\r\n"; 66 | 67 | Serial.print(request); 68 | client.print(request); 69 | 70 | client.readBytesUntil('\r', status, sizeof(status)); 71 | Serial.println(status); 72 | if (strcmp(status, "HTTP/1.1 100 Continue") != 0) 73 | { 74 | Serial.print("Unexpected response: "); 75 | client.stop(); 76 | return; 77 | } 78 | 79 | client.print(buf); 80 | 81 | uint8_t *image = cam.getfb(); 82 | size_t size = cam.getSize(); 83 | size_t offset = 0; 84 | size_t ret = 0; 85 | while (1) 86 | { 87 | ret = client.write(image + offset, size); 88 | offset += ret; 89 | size -= ret; 90 | if (cam.getSize() == offset) 91 | { 92 | break; 93 | } 94 | } 95 | client.print(request_end); 96 | 97 | client.find("\r\n"); 98 | 99 | bzero(status, sizeof(status)); 100 | client.readBytesUntil('\r', status, sizeof(status)); 101 | if (strncmp(status, "HTTP/1.1 200 OK", strlen("HTTP/1.1 200 OK"))) 102 | { 103 | Serial.print("Unexpected response: "); 104 | Serial.println(status); 105 | client.stop(); 106 | return; 107 | } 108 | 109 | if (!client.find("\r\n\r\n")) 110 | { 111 | Serial.println("Invalid response"); 112 | } 113 | 114 | request = client.readStringUntil('\n'); 115 | 116 | char *str = strdup(request.c_str()); 117 | if (!str) 118 | { 119 | client.stop(); 120 | return; 121 | } 122 | 123 | char *start = strchr(str, '{'); 124 | 125 | JsonObject &root = jsonBuffer.parseObject(start); 126 | if (!root.success()) 127 | { 128 | Serial.println("parse data fail"); 129 | client.stop(); 130 | free(str); 131 | return; 132 | } 133 | if (!strcmp((const char *)root["R"], "1")) 134 | { 135 | Serial.println("Update Success"); 136 | } 137 | free(str); 138 | client.stop(); 139 | } 140 | 141 | void setup() 142 | { 143 | Serial.begin(115200); 144 | while (!Serial) 145 | { 146 | ; 147 | } 148 | camera_config_t camera_config; 149 | camera_config.ledc_channel = LEDC_CHANNEL_0; 150 | camera_config.ledc_timer = LEDC_TIMER_0; 151 | camera_config.pin_d0 = 17; 152 | camera_config.pin_d1 = 35; 153 | camera_config.pin_d2 = 34; 154 | camera_config.pin_d3 = 5; 155 | camera_config.pin_d4 = 39; 156 | camera_config.pin_d5 = 18; 157 | camera_config.pin_d6 = 36; 158 | camera_config.pin_d7 = 19; 159 | camera_config.pin_xclk = 27; 160 | camera_config.pin_pclk = 21; 161 | camera_config.pin_vsync = 22; 162 | camera_config.pin_href = 26; 163 | camera_config.pin_sscb_sda = 25; 164 | camera_config.pin_sscb_scl = 23; 165 | camera_config.pin_reset = 15; 166 | camera_config.xclk_freq_hz = 20000000; 167 | camera_config.pixel_format = CAMERA_PF_JPEG; 168 | camera_config.frame_size = CAMERA_FS_SVGA; 169 | 170 | cam.init(camera_config); 171 | 172 | WiFi.mode(WIFI_STA); 173 | WiFi.begin(ssid, password); 174 | while (WiFi.status() != WL_CONNECTED) 175 | { 176 | delay(500); 177 | Serial.print(F(".")); 178 | } 179 | Serial.println(F("WiFi connected")); 180 | Serial.println(""); 181 | Serial.println(WiFi.localIP()); 182 | 183 | configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); 184 | } 185 | 186 | void loop() 187 | { 188 | update_image(); 189 | delay(30000); 190 | } 191 | -------------------------------------------------------------------------------- /examples/ESP32_SoftAP_Cam/ESP32_SoftAP_Cam.ino: -------------------------------------------------------------------------------- 1 | #include "OV2640.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #define ENABLE_OLED //if want use oled ,turn on thi macro 7 | 8 | #ifdef ENABLE_OLED 9 | #include "SSD1306.h" 10 | #define OLED_ADDRESS 0x3c 11 | #define I2C_SDA 14 12 | #define I2C_SCL 13 13 | SSD1306Wire display(OLED_ADDRESS, I2C_SDA, I2C_SCL, GEOMETRY_128_32); 14 | #endif 15 | 16 | OV2640 cam; 17 | WebServer server(80); 18 | 19 | IPAddress apIP = IPAddress(192, 168, 1, 1); 20 | 21 | void handle_jpg_stream(void) 22 | { 23 | WiFiClient client = server.client(); 24 | String response = "HTTP/1.1 200 OK\r\n"; 25 | response += "Content-Type: multipart/x-mixed-replace; boundary=frame\r\n\r\n"; 26 | server.sendContent(response); 27 | 28 | while (1) 29 | { 30 | cam.run(); 31 | if (!client.connected()) 32 | break; 33 | response = "--frame\r\n"; 34 | response += "Content-Type: image/jpeg\r\n\r\n"; 35 | server.sendContent(response); 36 | 37 | client.write((char *)cam.getfb(), cam.getSize()); 38 | server.sendContent("\r\n"); 39 | if (!client.connected()) 40 | break; 41 | } 42 | } 43 | 44 | void handle_jpg(void) 45 | { 46 | WiFiClient client = server.client(); 47 | 48 | cam.run(); 49 | if (!client.connected()) 50 | { 51 | return; 52 | } 53 | String response = "HTTP/1.1 200 OK\r\n"; 54 | response += "Content-disposition: inline; filename=capture.jpg\r\n"; 55 | response += "Content-type: image/jpeg\r\n\r\n"; 56 | server.sendContent(response); 57 | client.write((char *)cam.getfb(), cam.getSize()); 58 | } 59 | 60 | void handleNotFound() 61 | { 62 | String message = "Server is running!\n\n"; 63 | message += "URI: "; 64 | message += server.uri(); 65 | message += "\nMethod: "; 66 | message += (server.method() == HTTP_GET) ? "GET" : "POST"; 67 | message += "\nArguments: "; 68 | message += server.args(); 69 | message += "\n"; 70 | server.send(200, "text/plain", message); 71 | } 72 | 73 | void setup() 74 | { 75 | Serial.begin(115200); 76 | while (!Serial) 77 | { 78 | ; 79 | } 80 | camera_config_t camera_config; 81 | camera_config.ledc_channel = LEDC_CHANNEL_0; 82 | camera_config.ledc_timer = LEDC_TIMER_0; 83 | camera_config.pin_d0 = 17; 84 | camera_config.pin_d1 = 35; 85 | camera_config.pin_d2 = 34; 86 | camera_config.pin_d3 = 5; 87 | camera_config.pin_d4 = 39; 88 | camera_config.pin_d5 = 18; 89 | camera_config.pin_d6 = 36; 90 | camera_config.pin_d7 = 19; 91 | camera_config.pin_xclk = 27; 92 | camera_config.pin_pclk = 21; 93 | camera_config.pin_vsync = 22; 94 | camera_config.pin_href = 26; 95 | camera_config.pin_sscb_sda = 25; 96 | camera_config.pin_sscb_scl = 23; 97 | camera_config.pin_reset = 15; 98 | camera_config.xclk_freq_hz = 20000000; 99 | camera_config.pixel_format = CAMERA_PF_JPEG; 100 | camera_config.frame_size = CAMERA_FS_SVGA; 101 | 102 | cam.init(camera_config); 103 | 104 | WiFi.mode(WIFI_AP); 105 | WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); 106 | bool result = WiFi.softAP("TTGO-CAMERA", "12345678", 1, 0); 107 | if (!result) 108 | { 109 | Serial.println("AP Config failed."); 110 | return; 111 | } 112 | else 113 | { 114 | Serial.println("AP Config Success."); 115 | Serial.print("AP MAC: "); 116 | Serial.println(WiFi.softAPmacAddress()); 117 | } 118 | 119 | #ifdef ENABLE_OLED 120 | display.init(); 121 | display.flipScreenVertically(); 122 | display.setFont(ArialMT_Plain_16); 123 | display.setTextAlignment(TEXT_ALIGN_CENTER); 124 | display.drawString(128 / 2, 32 / 2, WiFi.softAPIP().toString()); 125 | display.display(); 126 | #endif 127 | 128 | server.on("/", HTTP_GET, handle_jpg_stream); 129 | server.on("/jpg", HTTP_GET, handle_jpg); 130 | server.onNotFound(handleNotFound); 131 | server.begin(); 132 | } 133 | 134 | void loop() 135 | { 136 | server.handleClient(); 137 | } 138 | -------------------------------------------------------------------------------- /examples/ESP32_Station_Cam/ESP32_Station_Cam.ino: -------------------------------------------------------------------------------- 1 | #include "OV2640.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #define ENABLE_OLED //if want use oled ,turn on thi macro 7 | 8 | #ifdef ENABLE_OLED 9 | #include "SSD1306.h" 10 | #define OLED_ADDRESS 0x3c 11 | #define I2C_SDA 14 12 | #define I2C_SCL 13 13 | SSD1306Wire display(OLED_ADDRESS, I2C_SDA, I2C_SCL, GEOMETRY_128_32); 14 | #endif 15 | 16 | OV2640 cam; 17 | WebServer server(80); 18 | 19 | const char *ssid = ""; // Put your SSID here 20 | const char *password = ""; // Put your PASSWORD here 21 | 22 | void handle_jpg_stream(void) 23 | { 24 | WiFiClient client = server.client(); 25 | String response = "HTTP/1.1 200 OK\r\n"; 26 | response += "Content-Type: multipart/x-mixed-replace; boundary=frame\r\n\r\n"; 27 | server.sendContent(response); 28 | 29 | while (1) 30 | { 31 | cam.run(); 32 | if (!client.connected()) 33 | break; 34 | response = "--frame\r\n"; 35 | response += "Content-Type: image/jpeg\r\n\r\n"; 36 | server.sendContent(response); 37 | 38 | client.write((char *)cam.getfb(), cam.getSize()); 39 | server.sendContent("\r\n"); 40 | if (!client.connected()) 41 | break; 42 | } 43 | } 44 | 45 | void handle_jpg(void) 46 | { 47 | WiFiClient client = server.client(); 48 | 49 | cam.run(); 50 | if (!client.connected()) 51 | { 52 | Serial.println("fail ... \n"); 53 | return; 54 | } 55 | String response = "HTTP/1.1 200 OK\r\n"; 56 | response += "Content-disposition: inline; filename=capture.jpg\r\n"; 57 | response += "Content-type: image/jpeg\r\n\r\n"; 58 | server.sendContent(response); 59 | client.write((char *)cam.getfb(), cam.getSize()); 60 | } 61 | 62 | void handleNotFound() 63 | { 64 | String message = "Server is running!\n\n"; 65 | message += "URI: "; 66 | message += server.uri(); 67 | message += "\nMethod: "; 68 | message += (server.method() == HTTP_GET) ? "GET" : "POST"; 69 | message += "\nArguments: "; 70 | message += server.args(); 71 | message += "\n"; 72 | server.send(200, "text/plain", message); 73 | } 74 | 75 | void setup() 76 | { 77 | Serial.begin(115200); 78 | while (!Serial) 79 | { 80 | ; 81 | } 82 | 83 | camera_config_t camera_config; 84 | camera_config.ledc_channel = LEDC_CHANNEL_0; 85 | camera_config.ledc_timer = LEDC_TIMER_0; 86 | camera_config.pin_d0 = 17; 87 | camera_config.pin_d1 = 35; 88 | camera_config.pin_d2 = 34; 89 | camera_config.pin_d3 = 5; 90 | camera_config.pin_d4 = 39; 91 | camera_config.pin_d5 = 18; 92 | camera_config.pin_d6 = 36; 93 | camera_config.pin_d7 = 19; 94 | camera_config.pin_xclk = 27; 95 | camera_config.pin_pclk = 21; 96 | camera_config.pin_vsync = 22; 97 | camera_config.pin_href = 26; 98 | camera_config.pin_sscb_sda = 25; 99 | camera_config.pin_sscb_scl = 23; 100 | camera_config.pin_reset = 15; 101 | camera_config.xclk_freq_hz = 20000000; 102 | camera_config.pixel_format = CAMERA_PF_JPEG; 103 | camera_config.frame_size = CAMERA_FS_SVGA; 104 | 105 | cam.init(camera_config); 106 | 107 | WiFi.mode(WIFI_STA); 108 | WiFi.begin(ssid, password); 109 | while (WiFi.status() != WL_CONNECTED) 110 | { 111 | delay(500); 112 | Serial.print(F(".")); 113 | } 114 | Serial.println(F("WiFi connected")); 115 | Serial.println(""); 116 | Serial.println(WiFi.localIP()); 117 | 118 | #ifdef ENABLE_OLED 119 | display.init(); 120 | display.flipScreenVertically(); 121 | display.setFont(ArialMT_Plain_16); 122 | display.setTextAlignment(TEXT_ALIGN_CENTER); 123 | display.drawString(128 / 2, 32 / 2, WiFi.localIP().toString()); 124 | display.display(); 125 | #endif 126 | 127 | server.on("/", HTTP_GET, handle_jpg_stream); 128 | server.on("/jpg", HTTP_GET, handle_jpg); 129 | server.onNotFound(handleNotFound); 130 | server.begin(); 131 | } 132 | 133 | void loop() 134 | { 135 | server.handleClient(); 136 | } 137 | -------------------------------------------------------------------------------- /gitgnore: -------------------------------------------------------------------------------- 1 | .vscode 2 | build -------------------------------------------------------------------------------- /images/TTGOcam.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilyGO/ESP32-Camera/ca43a7a387c3214332e29c91dd9e12ba3fef0002/images/TTGOcam.jpg -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map ESP32_Camera 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | OV2640 KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | run KEYWORD2 15 | detach KEYWORD2 16 | getSize KEYWORD2 17 | getfb KEYWORD2 18 | getWidth KEYWORD2 19 | getHeight KEYWORD2 20 | getFrameSize KEYWORD2 21 | getPixelFormat KEYWORD2 22 | setFrameSize KEYWORD2 23 | setPixelFormat KEYWORD2 24 | setVflip KEYWORD2 25 | ####################################### 26 | # Constants (LITERAL1) 27 | ####################################### -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ESP32 Camear", 3 | "keywords": "Camera", 4 | "description": "This Arduino library supports ESP32 OV2640 Camera", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/lewisxhe/ESP32-Camear-Arduino.git" 9 | }, 10 | "version": "0.0.1", 11 | "exclude": "doc", 12 | "frameworks": "arduino", 13 | "platforms": ["esp32"] 14 | } 15 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP32 Camear 2 | version=0.1.0 3 | author=lewisHe 4 | maintainer=lewisHe 5 | sentence=Communicate with ESP32 using a FIFO-free camera 6 | paragraph=This Arduino library supports ESP32 OV2640 Camera 7 | category=Sensors 8 | url=https://github.com/lewisxhe/ESP32-Camear-Arduino 9 | architectures=esp32 10 | -------------------------------------------------------------------------------- /ov2640_v1.3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LilyGO/ESP32-Camera/ca43a7a387c3214332e29c91dd9e12ba3fef0002/ov2640_v1.3.pdf -------------------------------------------------------------------------------- /src/OV2640.cpp: -------------------------------------------------------------------------------- 1 | #include "OV2640.h" 2 | 3 | #define TAG "OV2640" 4 | 5 | void OV2640::run(void) 6 | { 7 | camera_run(); 8 | } 9 | 10 | int OV2640::getWidth(void) 11 | { 12 | return camera_get_fb_width(); 13 | } 14 | 15 | int OV2640::getHeight(void) 16 | { 17 | return camera_get_fb_height(); 18 | } 19 | 20 | size_t OV2640::getSize(void) 21 | { 22 | return camera_get_data_size(); 23 | } 24 | 25 | uint8_t *OV2640::getfb(void) 26 | { 27 | return camera_get_fb(); 28 | } 29 | 30 | camera_framesize_t OV2640::getFrameSize(void) 31 | { 32 | return _cam_config.frame_size; 33 | } 34 | 35 | void OV2640::setFrameSize(camera_framesize_t size) 36 | { 37 | switch (size) 38 | { 39 | case CAMERA_FS_QQVGA: 40 | case CAMERA_FS_QVGA: 41 | case CAMERA_FS_VGA: 42 | case CAMERA_FS_SVGA: 43 | _cam_config.frame_size = size; 44 | break; 45 | default: 46 | _cam_config.frame_size = CAMERA_FS_SVGA; 47 | break; 48 | } 49 | } 50 | 51 | camera_pixelformat_t OV2640::getPixelFormat(void) 52 | { 53 | return _cam_config.pixel_format; 54 | } 55 | 56 | void OV2640::setPixelFormat(camera_pixelformat_t format) 57 | { 58 | switch (format) 59 | { 60 | case CAMERA_PF_RGB565: 61 | case CAMERA_PF_YUV422: 62 | case CAMERA_PF_GRAYSCALE: 63 | case CAMERA_PF_JPEG: 64 | _cam_config.pixel_format = format; 65 | break; 66 | default: 67 | _cam_config.pixel_format = CAMERA_PF_GRAYSCALE; 68 | break; 69 | } 70 | } 71 | 72 | void OV2640::setVflip(bool enable) 73 | { 74 | camera_set_vflip(enable); 75 | } 76 | 77 | 78 | esp_err_t OV2640::init(camera_config_t config) 79 | { 80 | camera_model_t camera_model; 81 | memset(&_cam_config, 0, sizeof(_cam_config)); 82 | memcpy(&_cam_config, &config, sizeof(config)); 83 | 84 | esp_err_t err = camera_probe(&_cam_config, &camera_model); 85 | if (err != ESP_OK) 86 | { 87 | ESP_LOGD(TAG, "Camera probe failed with error 0x%x", err); 88 | return err; 89 | } 90 | if (camera_model == CAMERA_OV7725) 91 | { 92 | ESP_LOGD(TAG, "Detected OV7725 camera"); 93 | } 94 | else if (camera_model == CAMERA_OV2640) 95 | { 96 | ESP_LOGD(TAG,"Detected OV2640 camera"); 97 | _cam_config.jpeg_quality = 15; 98 | } 99 | else 100 | { 101 | ESP_LOGD(TAG,"Camera not supported"); 102 | return ESP_ERR_CAMERA_NOT_SUPPORTED; 103 | } 104 | ESP_ERROR_CHECK(gpio_install_isr_service(0)); 105 | 106 | err = camera_init(&_cam_config); 107 | if (err != ESP_OK) 108 | { 109 | ESP_LOGE(TAG, "Camera init failed with error 0x%x", err); 110 | return err; 111 | } 112 | 113 | return ESP_OK; 114 | } 115 | -------------------------------------------------------------------------------- /src/OV2640.h: -------------------------------------------------------------------------------- 1 | #ifndef OV2640_H_ 2 | #define OV2640_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "esp_log.h" 8 | #include "esp_attr.h" 9 | #include "camera.h" 10 | 11 | class OV2640 12 | { 13 | public: 14 | OV2640(){}; 15 | ~OV2640(){}; 16 | esp_err_t init(camera_config_t config); 17 | void run(void); 18 | size_t getSize(void); 19 | uint8_t *getfb(void); 20 | int getWidth(void); 21 | int getHeight(void); 22 | camera_framesize_t getFrameSize(void); 23 | camera_pixelformat_t getPixelFormat(void); 24 | 25 | void setFrameSize(camera_framesize_t size); 26 | void setPixelFormat(camera_pixelformat_t format); 27 | void setVflip(bool enable); 28 | 29 | private: 30 | // camera_framesize_t _frame_size; 31 | // camera_pixelformat_t _pixel_format; 32 | camera_config_t _cam_config; 33 | }; 34 | 35 | #endif //OV2640_H_ -------------------------------------------------------------------------------- /src/camera.c: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | #include 15 | #include 16 | #include 17 | #include "time.h" 18 | #include "sys/time.h" 19 | #include "freertos/FreeRTOS.h" 20 | #include "freertos/task.h" 21 | #include "freertos/semphr.h" 22 | #include "rom/lldesc.h" 23 | #include "soc/soc.h" 24 | #include "soc/gpio_sig_map.h" 25 | #include "soc/i2s_reg.h" 26 | #include "soc/i2s_struct.h" 27 | #include "soc/io_mux_reg.h" 28 | #include "driver/gpio.h" 29 | #include "driver/rtc_io.h" 30 | #include "driver/periph_ctrl.h" 31 | #include "esp_intr_alloc.h" 32 | #include "esp_log.h" 33 | #include "sccb.h" 34 | #include "camera.h" 35 | #include "camera_common.h" 36 | #if CONFIG_OV2640_SUPPORT 37 | #include "ov264_drive.h" 38 | #endif 39 | #if CONFIG_OV7725_SUPPORT 40 | #include "ov7725.h" 41 | #endif 42 | 43 | #define ENABLE_TEST_PATTERN CONFIG_ENABLE_TEST_PATTERN 44 | 45 | #define REG_PID 0x0A 46 | #define REG_VER 0x0B 47 | #define REG_MIDH 0x1C 48 | #define REG_MIDL 0x1D 49 | 50 | static esp_err_t camera_enable_out_clock(camera_config_t *config); 51 | static void camera_disable_out_clock(); 52 | 53 | static const char *TAG = "camera"; 54 | 55 | camera_state_t *s_state = NULL; 56 | 57 | const int resolution[][2] = { 58 | {40, 30}, /* 40x30 */ 59 | {64, 32}, /* 64x32 */ 60 | {64, 64}, /* 64x64 */ 61 | {88, 72}, /* QQCIF */ 62 | {160, 120}, /* QQVGA */ 63 | {128, 160}, /* QQVGA2*/ 64 | {176, 144}, /* QCIF */ 65 | {240, 160}, /* HQVGA */ 66 | {320, 240}, /* QVGA */ 67 | {352, 288}, /* CIF */ 68 | {640, 480}, /* VGA */ 69 | {800, 600}, /* SVGA */ 70 | {1280, 1024}, /* SXGA */ 71 | {1600, 1200}, /* UXGA */ 72 | }; 73 | 74 | static void i2s_init(); 75 | static void i2s_run(); 76 | static void IRAM_ATTR gpio_isr(void *arg); 77 | static void IRAM_ATTR i2s_isr(void *arg); 78 | static esp_err_t dma_desc_init(); 79 | static void dma_desc_deinit(); 80 | static void dma_filter_task(void *pvParameters); 81 | static void dma_filter_grayscale(const dma_elem_t *src, lldesc_t *dma_desc, uint8_t *dst); 82 | static void dma_filter_grayscale_highspeed(const dma_elem_t *src, lldesc_t *dma_desc, uint8_t *dst); 83 | static void dma_filter_jpeg(const dma_elem_t *src, lldesc_t *dma_desc, uint8_t *dst); 84 | static void dma_filter_rgb565(const dma_elem_t *src, lldesc_t *dma_desc, uint8_t *dst); 85 | static void i2s_stop(); 86 | 87 | static bool is_hs_mode() 88 | { 89 | return s_state->config.xclk_freq_hz > 10000000; 90 | } 91 | 92 | static size_t i2s_bytes_per_sample(i2s_sampling_mode_t mode) 93 | { 94 | switch (mode) 95 | { 96 | case SM_0A00_0B00: 97 | return 4; 98 | case SM_0A0B_0B0C: 99 | return 4; 100 | case SM_0A0B_0C0D: 101 | return 2; 102 | default: 103 | assert(0 && "invalid sampling mode"); 104 | return 0; 105 | } 106 | } 107 | 108 | static void vsync_intr_disable() 109 | { 110 | gpio_set_intr_type(s_state->config.pin_vsync, GPIO_INTR_DISABLE); 111 | } 112 | 113 | static void vsync_intr_enable() 114 | { 115 | gpio_set_intr_type(s_state->config.pin_vsync, GPIO_INTR_NEGEDGE); 116 | } 117 | 118 | esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model) 119 | { 120 | if (s_state != NULL) 121 | { 122 | return ESP_ERR_INVALID_STATE; 123 | } 124 | 125 | s_state = (camera_state_t *)calloc(sizeof(*s_state), 1); 126 | if (!s_state) 127 | { 128 | return ESP_ERR_NO_MEM; 129 | } 130 | 131 | ESP_LOGI(TAG, "Enabling XCLK output"); 132 | camera_enable_out_clock(config); 133 | 134 | ESP_LOGD(TAG, "Initializing SSCB"); 135 | SCCB_Init(config->pin_sscb_sda, config->pin_sscb_scl); 136 | 137 | ESP_LOGD(TAG, "Resetting camera"); 138 | gpio_config_t conf = {0}; 139 | conf.pin_bit_mask = 1LL << config->pin_reset; 140 | conf.mode = GPIO_MODE_OUTPUT; 141 | gpio_config(&conf); 142 | 143 | gpio_set_level(config->pin_reset, 0); 144 | delay(10); 145 | gpio_set_level(config->pin_reset, 1); 146 | delay(10); 147 | 148 | ESP_LOGD(TAG, "Searching for camera address"); 149 | /* Probe the sensor */ 150 | delay(10); 151 | uint8_t slv_addr = SCCB_Probe(); 152 | if (slv_addr == 0) 153 | { 154 | *out_camera_model = CAMERA_NONE; 155 | return ESP_ERR_CAMERA_NOT_DETECTED; 156 | } 157 | s_state->sensor.slv_addr = slv_addr; 158 | ESP_LOGD(TAG, "Detected camera at address=0x%02x", slv_addr); 159 | sensor_id_t *id = &s_state->sensor.id; 160 | id->PID = SCCB_Read(slv_addr, REG_PID); 161 | id->VER = SCCB_Read(slv_addr, REG_VER); 162 | id->MIDL = SCCB_Read(slv_addr, REG_MIDL); 163 | id->MIDH = SCCB_Read(slv_addr, REG_MIDH); 164 | delay(10); 165 | ESP_LOGI(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x", 166 | id->PID, id->VER, id->MIDH, id->MIDL); 167 | 168 | switch (id->PID) 169 | { 170 | #if CONFIG_OV2640_SUPPORT 171 | case OV2640_PID: 172 | *out_camera_model = CAMERA_OV2640; 173 | ov2640_init(&s_state->sensor); 174 | break; 175 | #endif 176 | #if CONFIG_OV7725_SUPPORT 177 | case OV7725_PID: 178 | *out_camera_model = CAMERA_OV7725; 179 | ov7725_init(&s_state->sensor); 180 | break; 181 | #endif 182 | default: 183 | id->PID = 0; 184 | *out_camera_model = CAMERA_UNKNOWN; 185 | ESP_LOGD(TAG, "Detected camera not supported."); 186 | return ESP_ERR_CAMERA_NOT_SUPPORTED; 187 | } 188 | 189 | ESP_LOGD(TAG, "Doing SW reset of sensor"); 190 | s_state->sensor.reset(&s_state->sensor); 191 | 192 | return ESP_OK; 193 | } 194 | 195 | esp_err_t camera_init(const camera_config_t *config) 196 | { 197 | if (!s_state) 198 | { 199 | return ESP_ERR_INVALID_STATE; 200 | } 201 | if (s_state->sensor.id.PID == 0) 202 | { 203 | return ESP_ERR_CAMERA_NOT_SUPPORTED; 204 | } 205 | memcpy(&s_state->config, config, sizeof(*config)); 206 | esp_err_t err = ESP_OK; 207 | framesize_t frame_size = (framesize_t)config->frame_size; 208 | pixformat_t pix_format = (pixformat_t)config->pixel_format; 209 | s_state->width = resolution[frame_size][0]; 210 | s_state->height = resolution[frame_size][1]; 211 | s_state->sensor.set_pixformat(&s_state->sensor, pix_format); 212 | 213 | ESP_LOGD(TAG, "Setting frame size to %dx%d", s_state->width, s_state->height); 214 | if (s_state->sensor.set_framesize(&s_state->sensor, FRAMESIZE_SVGA) != 0) 215 | { 216 | ESP_LOGE(TAG, "Failed to set frame size"); 217 | err = ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE; 218 | goto fail; 219 | } 220 | s_state->sensor.set_pixformat(&s_state->sensor, pix_format); 221 | 222 | #if ENABLE_TEST_PATTERN 223 | /* Test pattern may get handy 224 | if you are unable to get the live image right. 225 | Once test pattern is enable, sensor will output 226 | vertical shaded bars instead of live image. 227 | */ 228 | s_state->sensor.set_colorbar(&s_state->sensor, 1); 229 | ESP_LOGD(TAG, "Test pattern enabled"); 230 | #endif 231 | 232 | if (pix_format == PIXFORMAT_GRAYSCALE) 233 | { 234 | if (s_state->sensor.id.PID != OV7725_PID) 235 | { 236 | ESP_LOGE(TAG, "Grayscale format is only supported for ov7225"); 237 | err = ESP_ERR_NOT_SUPPORTED; 238 | goto fail; 239 | } 240 | s_state->fb_size = s_state->width * s_state->height; 241 | if (is_hs_mode()) 242 | { 243 | s_state->sampling_mode = SM_0A0B_0B0C; 244 | s_state->dma_filter = &dma_filter_grayscale_highspeed; 245 | } 246 | else 247 | { 248 | s_state->sampling_mode = SM_0A0B_0C0D; 249 | s_state->dma_filter = &dma_filter_grayscale; 250 | } 251 | s_state->in_bytes_per_pixel = 2; // camera sends YUYV 252 | s_state->fb_bytes_per_pixel = 1; // frame buffer stores Y8 253 | } 254 | else if (pix_format == PIXFORMAT_RGB565) 255 | { 256 | if (s_state->sensor.id.PID != OV7725_PID) 257 | { 258 | ESP_LOGE(TAG, "RGB565 format is only supported for ov7225"); 259 | err = ESP_ERR_NOT_SUPPORTED; 260 | goto fail; 261 | } 262 | s_state->fb_size = s_state->width * s_state->height * 3; 263 | if (is_hs_mode()) 264 | { 265 | s_state->sampling_mode = SM_0A0B_0B0C; 266 | } 267 | else 268 | { 269 | s_state->sampling_mode = SM_0A00_0B00; 270 | } 271 | s_state->in_bytes_per_pixel = 2; // camera sends RGB565 (2 bytes) 272 | s_state->fb_bytes_per_pixel = 3; // frame buffer stores RGB888 273 | s_state->dma_filter = &dma_filter_rgb565; 274 | } 275 | else if (pix_format == PIXFORMAT_JPEG) 276 | { 277 | if (s_state->sensor.id.PID != OV2640_PID) 278 | { 279 | ESP_LOGE(TAG, "JPEG format is only supported for ov2640"); 280 | err = ESP_ERR_NOT_SUPPORTED; 281 | goto fail; 282 | } 283 | int qp = config->jpeg_quality; 284 | int compression_ratio_bound; 285 | if (qp >= 30) 286 | { 287 | compression_ratio_bound = 5; 288 | } 289 | else if (qp >= 10) 290 | { 291 | compression_ratio_bound = 10; 292 | } 293 | else 294 | { 295 | compression_ratio_bound = 20; 296 | } 297 | (*s_state->sensor.set_quality)(&s_state->sensor, qp); 298 | size_t equiv_line_count = s_state->height / compression_ratio_bound; 299 | s_state->fb_size = s_state->width * equiv_line_count * 2 /* bpp */; 300 | s_state->dma_filter = &dma_filter_jpeg; 301 | if (is_hs_mode()) 302 | { 303 | s_state->sampling_mode = SM_0A0B_0B0C; 304 | } 305 | else 306 | { 307 | s_state->sampling_mode = SM_0A00_0B00; 308 | } 309 | s_state->in_bytes_per_pixel = 2; 310 | s_state->fb_bytes_per_pixel = 2; 311 | } 312 | else 313 | { 314 | ESP_LOGE(TAG, "Requested format is not supported"); 315 | err = ESP_ERR_NOT_SUPPORTED; 316 | goto fail; 317 | } 318 | 319 | ESP_LOGD(TAG, "in_bpp: %d, fb_bpp: %d, fb_size: %d, mode: %d, width: %d height: %d", 320 | s_state->in_bytes_per_pixel, s_state->fb_bytes_per_pixel, 321 | s_state->fb_size, s_state->sampling_mode, 322 | s_state->width, s_state->height); 323 | 324 | ESP_LOGD(TAG, "Allocating frame buffer (%d bytes)", s_state->fb_size); 325 | s_state->fb = (uint8_t *)calloc(s_state->fb_size, 1); 326 | if (s_state->fb == NULL) 327 | { 328 | ESP_LOGE(TAG, "Failed to allocate frame buffer"); 329 | err = ESP_ERR_NO_MEM; 330 | goto fail; 331 | } 332 | 333 | ESP_LOGD(TAG, "Initializing I2S and DMA"); 334 | i2s_init(); 335 | err = dma_desc_init(); 336 | if (err != ESP_OK) 337 | { 338 | ESP_LOGE(TAG, "Failed to initialize I2S and DMA"); 339 | goto fail; 340 | } 341 | 342 | s_state->data_ready = xQueueCreate(16, sizeof(size_t)); 343 | s_state->frame_ready = xSemaphoreCreateBinary(); 344 | if (s_state->data_ready == NULL || s_state->frame_ready == NULL) 345 | { 346 | ESP_LOGE(TAG, "Failed to create semaphores"); 347 | err = ESP_ERR_NO_MEM; 348 | goto fail; 349 | } 350 | if (!xTaskCreatePinnedToCore(&dma_filter_task, "dma_filter", 4096, NULL, 10, &s_state->dma_filter_task, 1)) 351 | { 352 | ESP_LOGE(TAG, "Failed to create DMA filter task"); 353 | err = ESP_ERR_NO_MEM; 354 | goto fail; 355 | } 356 | 357 | ESP_LOGD(TAG, "Initializing GPIO interrupts"); 358 | vsync_intr_disable(); 359 | err = gpio_isr_handler_add(s_state->config.pin_vsync, &gpio_isr, NULL); 360 | if (err != ESP_OK) 361 | { 362 | ESP_LOGE(TAG, "gpio_isr_handler_add failed (%x)", err); 363 | goto fail; 364 | } 365 | 366 | // skip at least one frame after changing camera settings 367 | while (gpio_get_level(s_state->config.pin_vsync) == 0) 368 | { 369 | ; 370 | } 371 | while (gpio_get_level(s_state->config.pin_vsync) != 0) 372 | { 373 | ; 374 | } 375 | while (gpio_get_level(s_state->config.pin_vsync) == 0) 376 | { 377 | ; 378 | } 379 | s_state->frame_count = 0; 380 | ESP_LOGD(TAG, "Init done"); 381 | return ESP_OK; 382 | 383 | fail: 384 | camera_deinit(); 385 | return err; 386 | } 387 | 388 | esp_err_t camera_deinit() 389 | { 390 | if (s_state == NULL) 391 | { 392 | return ESP_ERR_INVALID_STATE; 393 | } 394 | if (s_state->dma_filter_task) 395 | { 396 | vTaskDelete(s_state->dma_filter_task); 397 | } 398 | if (s_state->data_ready) 399 | { 400 | vQueueDelete(s_state->data_ready); 401 | } 402 | if (s_state->frame_ready) 403 | { 404 | vSemaphoreDelete(s_state->frame_ready); 405 | } 406 | gpio_isr_handler_remove(s_state->config.pin_vsync); 407 | if (s_state->i2s_intr_handle) 408 | { 409 | esp_intr_disable(s_state->i2s_intr_handle); 410 | esp_intr_free(s_state->i2s_intr_handle); 411 | } 412 | dma_desc_deinit(); 413 | free(s_state->fb); 414 | free(s_state); 415 | s_state = NULL; 416 | camera_disable_out_clock(); 417 | periph_module_disable(PERIPH_I2S0_MODULE); 418 | return ESP_OK; 419 | } 420 | 421 | uint8_t *camera_get_fb() 422 | { 423 | if (s_state == NULL) 424 | { 425 | return NULL; 426 | } 427 | return s_state->fb; 428 | } 429 | 430 | int camera_get_fb_width() 431 | { 432 | if (s_state == NULL) 433 | { 434 | return 0; 435 | } 436 | return s_state->width; 437 | } 438 | 439 | int camera_get_fb_height() 440 | { 441 | if (s_state == NULL) 442 | { 443 | return 0; 444 | } 445 | return s_state->height; 446 | } 447 | 448 | size_t camera_get_data_size() 449 | { 450 | if (s_state == NULL) 451 | { 452 | return 0; 453 | } 454 | return s_state->data_size; 455 | } 456 | 457 | esp_err_t camera_run() 458 | { 459 | if (s_state == NULL) 460 | { 461 | return ESP_ERR_INVALID_STATE; 462 | } 463 | struct timeval tv_start; 464 | gettimeofday(&tv_start, NULL); 465 | #ifndef _NDEBUG 466 | memset(s_state->fb, 0, s_state->fb_size); 467 | #endif // _NDEBUG 468 | i2s_run(); 469 | ESP_LOGD(TAG, "Waiting for frame"); 470 | xSemaphoreTake(s_state->frame_ready, portMAX_DELAY); 471 | struct timeval tv_end; 472 | gettimeofday(&tv_end, NULL); 473 | int time_ms = (tv_end.tv_sec - tv_start.tv_sec) * 1000 + (tv_end.tv_usec - tv_start.tv_usec) / 1000; 474 | ESP_LOGI(TAG, "Frame %d done in %d ms", s_state->frame_count, time_ms); 475 | s_state->frame_count++; 476 | return ESP_OK; 477 | } 478 | 479 | static esp_err_t dma_desc_init() 480 | { 481 | assert(s_state->width % 4 == 0); 482 | size_t line_size = s_state->width * s_state->in_bytes_per_pixel * 483 | i2s_bytes_per_sample(s_state->sampling_mode); 484 | ESP_LOGD(TAG, "Line width (for DMA): %d bytes", line_size); 485 | size_t dma_per_line = 1; 486 | size_t buf_size = line_size; 487 | while (buf_size >= 4096) 488 | { 489 | buf_size /= 2; 490 | dma_per_line *= 2; 491 | } 492 | size_t dma_desc_count = dma_per_line * 4; 493 | s_state->dma_buf_width = line_size; 494 | s_state->dma_per_line = dma_per_line; 495 | s_state->dma_desc_count = dma_desc_count; 496 | ESP_LOGD(TAG, "DMA buffer size: %d, DMA buffers per line: %d", buf_size, dma_per_line); 497 | ESP_LOGD(TAG, "DMA buffer count: %d", dma_desc_count); 498 | 499 | s_state->dma_buf = (dma_elem_t **)malloc(sizeof(dma_elem_t *) * dma_desc_count); 500 | if (s_state->dma_buf == NULL) 501 | { 502 | return ESP_ERR_NO_MEM; 503 | } 504 | s_state->dma_desc = (lldesc_t *)malloc(sizeof(lldesc_t) * dma_desc_count); 505 | if (s_state->dma_desc == NULL) 506 | { 507 | return ESP_ERR_NO_MEM; 508 | } 509 | size_t dma_sample_count = 0; 510 | for (int i = 0; i < dma_desc_count; ++i) 511 | { 512 | ESP_LOGD(TAG, "Allocating DMA buffer #%d, size=%d", i, buf_size); 513 | dma_elem_t *buf = (dma_elem_t *)malloc(buf_size); 514 | if (buf == NULL) 515 | { 516 | return ESP_ERR_NO_MEM; 517 | } 518 | s_state->dma_buf[i] = buf; 519 | ESP_LOGV(TAG, "dma_buf[%d]=%p", i, buf); 520 | 521 | lldesc_t *pd = &s_state->dma_desc[i]; 522 | pd->length = buf_size; 523 | if (s_state->sampling_mode == SM_0A0B_0B0C && 524 | (i + 1) % dma_per_line == 0) 525 | { 526 | pd->length -= 4; 527 | } 528 | dma_sample_count += pd->length / 4; 529 | pd->size = pd->length; 530 | pd->owner = 1; 531 | pd->sosf = 1; 532 | pd->buf = (uint8_t *)buf; 533 | pd->offset = 0; 534 | pd->empty = 0; 535 | pd->eof = 1; 536 | pd->qe.stqe_next = &s_state->dma_desc[(i + 1) % dma_desc_count]; 537 | } 538 | s_state->dma_done = false; 539 | s_state->dma_sample_count = dma_sample_count; 540 | return ESP_OK; 541 | } 542 | 543 | static void dma_desc_deinit() 544 | { 545 | if (s_state->dma_buf) 546 | { 547 | for (int i = 0; i < s_state->dma_desc_count; ++i) 548 | { 549 | free(s_state->dma_buf[i]); 550 | } 551 | } 552 | free(s_state->dma_buf); 553 | free(s_state->dma_desc); 554 | } 555 | 556 | static inline void i2s_conf_reset() 557 | { 558 | const uint32_t lc_conf_reset_flags = I2S_IN_RST_M | I2S_AHBM_RST_M | I2S_AHBM_FIFO_RST_M; 559 | I2S0.lc_conf.val |= lc_conf_reset_flags; 560 | I2S0.lc_conf.val &= ~lc_conf_reset_flags; 561 | 562 | const uint32_t conf_reset_flags = I2S_RX_RESET_M | I2S_RX_FIFO_RESET_M | I2S_TX_RESET_M | I2S_TX_FIFO_RESET_M; 563 | I2S0.conf.val |= conf_reset_flags; 564 | I2S0.conf.val &= ~conf_reset_flags; 565 | while (I2S0.state.rx_fifo_reset_back) 566 | { 567 | ; 568 | } 569 | } 570 | 571 | static void i2s_init() 572 | { 573 | camera_config_t *config = &s_state->config; 574 | 575 | // Configure input GPIOs 576 | gpio_num_t pins[] = { 577 | config->pin_d7, 578 | config->pin_d6, 579 | config->pin_d5, 580 | config->pin_d4, 581 | config->pin_d3, 582 | config->pin_d2, 583 | config->pin_d1, 584 | config->pin_d0, 585 | config->pin_vsync, 586 | config->pin_href, 587 | config->pin_pclk}; 588 | gpio_config_t conf = { 589 | .mode = GPIO_MODE_INPUT, 590 | .pull_up_en = GPIO_PULLUP_ENABLE, 591 | .pull_down_en = GPIO_PULLDOWN_DISABLE, 592 | .intr_type = GPIO_INTR_DISABLE}; 593 | for (int i = 0; i < sizeof(pins) / sizeof(gpio_num_t); ++i) 594 | { 595 | if (rtc_gpio_is_valid_gpio(pins[i])) 596 | { 597 | rtc_gpio_deinit(pins[i]); 598 | } 599 | conf.pin_bit_mask = 1LL << pins[i]; 600 | gpio_config(&conf); 601 | } 602 | 603 | // Route input GPIOs to I2S peripheral using GPIO matrix 604 | gpio_matrix_in(config->pin_d0, I2S0I_DATA_IN0_IDX, false); 605 | gpio_matrix_in(config->pin_d1, I2S0I_DATA_IN1_IDX, false); 606 | gpio_matrix_in(config->pin_d2, I2S0I_DATA_IN2_IDX, false); 607 | gpio_matrix_in(config->pin_d3, I2S0I_DATA_IN3_IDX, false); 608 | gpio_matrix_in(config->pin_d4, I2S0I_DATA_IN4_IDX, false); 609 | gpio_matrix_in(config->pin_d5, I2S0I_DATA_IN5_IDX, false); 610 | gpio_matrix_in(config->pin_d6, I2S0I_DATA_IN6_IDX, false); 611 | gpio_matrix_in(config->pin_d7, I2S0I_DATA_IN7_IDX, false); 612 | gpio_matrix_in(config->pin_vsync, I2S0I_V_SYNC_IDX, false); 613 | gpio_matrix_in(0x38, I2S0I_H_SYNC_IDX, false); 614 | gpio_matrix_in(config->pin_href, I2S0I_H_ENABLE_IDX, false); 615 | gpio_matrix_in(config->pin_pclk, I2S0I_WS_IN_IDX, false); 616 | 617 | // Enable and configure I2S peripheral 618 | periph_module_enable(PERIPH_I2S0_MODULE); 619 | // Toggle some reset bits in LC_CONF register 620 | // Toggle some reset bits in CONF register 621 | i2s_conf_reset(); 622 | // Enable slave mode (sampling clock is external) 623 | I2S0.conf.rx_slave_mod = 1; 624 | // Enable parallel mode 625 | I2S0.conf2.lcd_en = 1; 626 | // Use HSYNC/VSYNC/HREF to control sampling 627 | I2S0.conf2.camera_en = 1; 628 | // Configure clock divider 629 | I2S0.clkm_conf.clkm_div_a = 1; 630 | I2S0.clkm_conf.clkm_div_b = 0; 631 | I2S0.clkm_conf.clkm_div_num = 2; 632 | // FIFO will sink data to DMA 633 | I2S0.fifo_conf.dscr_en = 1; 634 | // FIFO configuration 635 | I2S0.fifo_conf.rx_fifo_mod = s_state->sampling_mode; 636 | I2S0.fifo_conf.rx_fifo_mod_force_en = 1; 637 | I2S0.conf_chan.rx_chan_mod = 1; 638 | // Clear flags which are used in I2S serial mode 639 | I2S0.sample_rate_conf.rx_bits_mod = 0; 640 | I2S0.conf.rx_right_first = 0; 641 | I2S0.conf.rx_msb_right = 0; 642 | I2S0.conf.rx_msb_shift = 0; 643 | I2S0.conf.rx_mono = 0; 644 | I2S0.conf.rx_short_sync = 0; 645 | I2S0.timing.val = 0; 646 | 647 | // Allocate I2S interrupt, keep it disabled 648 | esp_intr_alloc(ETS_I2S0_INTR_SOURCE, 649 | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM, 650 | &i2s_isr, NULL, &s_state->i2s_intr_handle); 651 | } 652 | 653 | static void i2s_stop() 654 | { 655 | esp_intr_disable(s_state->i2s_intr_handle); 656 | vsync_intr_disable(); 657 | i2s_conf_reset(); 658 | I2S0.conf.rx_start = 0; 659 | size_t val = SIZE_MAX; 660 | BaseType_t higher_priority_task_woken; 661 | xQueueSendFromISR(s_state->data_ready, &val, &higher_priority_task_woken); 662 | } 663 | 664 | static void i2s_run() 665 | { 666 | #ifndef _NDEBUG 667 | for (int i = 0; i < s_state->dma_desc_count; ++i) 668 | { 669 | lldesc_t *d = &s_state->dma_desc[i]; 670 | ESP_LOGV(TAG, "DMA desc %2d: %u %u %u %u %u %u %p %p", 671 | i, d->length, d->size, d->offset, d->eof, d->sosf, d->owner, d->buf, d->qe.stqe_next); 672 | memset(s_state->dma_buf[i], 0, d->length); 673 | } 674 | #endif 675 | 676 | // wait for vsync 677 | ESP_LOGD(TAG, "Waiting for positive edge on VSYNC"); 678 | while (gpio_get_level(s_state->config.pin_vsync) == 0) 679 | { 680 | ; 681 | } 682 | while (gpio_get_level(s_state->config.pin_vsync) != 0) 683 | { 684 | ; 685 | } 686 | ESP_LOGD(TAG, "Got VSYNC"); 687 | 688 | s_state->dma_done = false; 689 | s_state->dma_desc_cur = 0; 690 | s_state->dma_received_count = 0; 691 | s_state->dma_filtered_count = 0; 692 | esp_intr_disable(s_state->i2s_intr_handle); 693 | i2s_conf_reset(); 694 | 695 | I2S0.rx_eof_num = s_state->dma_sample_count; 696 | I2S0.in_link.addr = (uint32_t)&s_state->dma_desc[0]; 697 | I2S0.in_link.start = 1; 698 | I2S0.int_clr.val = I2S0.int_raw.val; 699 | I2S0.int_ena.val = 0; 700 | I2S0.int_ena.in_done = 1; 701 | esp_intr_enable(s_state->i2s_intr_handle); 702 | if (s_state->config.pixel_format == CAMERA_PF_JPEG) 703 | { 704 | vsync_intr_enable(); 705 | } 706 | I2S0.conf.rx_start = 1; 707 | } 708 | 709 | static void IRAM_ATTR signal_dma_buf_received(bool *need_yield) 710 | { 711 | size_t dma_desc_filled = s_state->dma_desc_cur; 712 | s_state->dma_desc_cur = (dma_desc_filled + 1) % s_state->dma_desc_count; 713 | s_state->dma_received_count++; 714 | BaseType_t higher_priority_task_woken; 715 | BaseType_t ret = xQueueSendFromISR(s_state->data_ready, &dma_desc_filled, &higher_priority_task_woken); 716 | if (ret != pdTRUE) 717 | { 718 | ESP_EARLY_LOGW(TAG, "queue send failed (%d), dma_received_count=%d", ret, s_state->dma_received_count); 719 | } 720 | *need_yield = (ret == pdTRUE && higher_priority_task_woken == pdTRUE); 721 | } 722 | 723 | static void IRAM_ATTR i2s_isr(void *arg) 724 | { 725 | I2S0.int_clr.val = I2S0.int_raw.val; 726 | bool need_yield; 727 | signal_dma_buf_received(&need_yield); 728 | ESP_EARLY_LOGV(TAG, "isr, cnt=%d", s_state->dma_received_count); 729 | if (s_state->dma_received_count == s_state->height * s_state->dma_per_line) 730 | { 731 | i2s_stop(); 732 | } 733 | if (need_yield) 734 | { 735 | portYIELD_FROM_ISR(); 736 | } 737 | } 738 | 739 | static void IRAM_ATTR gpio_isr(void *arg) 740 | { 741 | bool need_yield = false; 742 | ESP_EARLY_LOGV(TAG, "gpio isr, cnt=%d", s_state->dma_received_count); 743 | if (gpio_get_level(s_state->config.pin_vsync) == 0 && 744 | s_state->dma_received_count > 0 && 745 | !s_state->dma_done) 746 | { 747 | signal_dma_buf_received(&need_yield); 748 | i2s_stop(); 749 | } 750 | if (need_yield) 751 | { 752 | portYIELD_FROM_ISR(); 753 | } 754 | } 755 | 756 | static size_t get_fb_pos() 757 | { 758 | return s_state->dma_filtered_count * s_state->width * 759 | s_state->fb_bytes_per_pixel / s_state->dma_per_line; 760 | } 761 | 762 | static void IRAM_ATTR dma_filter_task(void *pvParameters) 763 | { 764 | while (true) 765 | { 766 | size_t buf_idx; 767 | xQueueReceive(s_state->data_ready, &buf_idx, portMAX_DELAY); 768 | if (buf_idx == SIZE_MAX) 769 | { 770 | s_state->data_size = get_fb_pos(); 771 | xSemaphoreGive(s_state->frame_ready); 772 | continue; 773 | } 774 | 775 | size_t fb_pos = get_fb_pos(); 776 | assert(fb_pos <= s_state->fb_size + s_state->width * 777 | s_state->fb_bytes_per_pixel / s_state->dma_per_line); 778 | 779 | uint8_t *pfb = s_state->fb + fb_pos; 780 | const dma_elem_t *buf = s_state->dma_buf[buf_idx]; 781 | lldesc_t *desc = &s_state->dma_desc[buf_idx]; 782 | (*s_state->dma_filter)(buf, desc, pfb); 783 | s_state->dma_filtered_count++; 784 | ESP_LOGV(TAG, "dma_flt: flt_count=%d ", s_state->dma_filtered_count); 785 | } 786 | } 787 | 788 | static void IRAM_ATTR dma_filter_grayscale(const dma_elem_t *src, lldesc_t *dma_desc, uint8_t *dst) 789 | { 790 | assert(s_state->sampling_mode == SM_0A0B_0C0D); 791 | size_t end = dma_desc->length / sizeof(dma_elem_t) / 4; 792 | for (size_t i = 0; i < end; ++i) 793 | { 794 | // manually unrolling 4 iterations of the loop here 795 | dst[0] = src[0].sample1; 796 | dst[1] = src[1].sample1; 797 | dst[2] = src[2].sample1; 798 | dst[3] = src[3].sample1; 799 | src += 4; 800 | dst += 4; 801 | } 802 | } 803 | 804 | static void IRAM_ATTR dma_filter_grayscale_highspeed(const dma_elem_t *src, lldesc_t *dma_desc, uint8_t *dst) 805 | { 806 | assert(s_state->sampling_mode == SM_0A0B_0B0C); 807 | size_t end = dma_desc->length / sizeof(dma_elem_t) / 8; 808 | for (size_t i = 0; i < end; ++i) 809 | { 810 | // manually unrolling 4 iterations of the loop here 811 | dst[0] = src[0].sample1; 812 | dst[1] = src[2].sample1; 813 | dst[2] = src[4].sample1; 814 | dst[3] = src[6].sample1; 815 | src += 8; 816 | dst += 4; 817 | } 818 | // the final sample of a line in SM_0A0B_0B0C sampling mode needs special handling 819 | if ((dma_desc->length & 0x7) != 0) 820 | { 821 | dst[0] = src[0].sample1; 822 | dst[1] = src[2].sample1; 823 | } 824 | } 825 | 826 | static void IRAM_ATTR dma_filter_jpeg(const dma_elem_t *src, lldesc_t *dma_desc, uint8_t *dst) 827 | { 828 | assert(s_state->sampling_mode == SM_0A0B_0B0C || 829 | s_state->sampling_mode == SM_0A00_0B00); 830 | size_t end = dma_desc->length / sizeof(dma_elem_t) / 4; 831 | // manually unrolling 4 iterations of the loop here 832 | for (size_t i = 0; i < end; ++i) 833 | { 834 | dst[0] = src[0].sample1; 835 | dst[1] = src[1].sample1; 836 | dst[2] = src[2].sample1; 837 | dst[3] = src[3].sample1; 838 | src += 4; 839 | dst += 4; 840 | } 841 | // the final sample of a line in SM_0A0B_0B0C sampling mode needs special handling 842 | if ((dma_desc->length & 0x7) != 0) 843 | { 844 | dst[0] = src[0].sample1; 845 | dst[1] = src[1].sample1; 846 | dst[2] = src[2].sample1; 847 | dst[3] = src[2].sample2; 848 | } 849 | } 850 | 851 | static inline void rgb565_to_888(uint8_t in1, uint8_t in2, uint8_t *dst) 852 | { 853 | dst[0] = (in2 & 0b00011111) << 3; // blue 854 | dst[1] = ((in1 & 0b111) << 5) | ((in2 & 0b11100000 >> 5)); // green 855 | dst[2] = in1 & 0b11111000; // red 856 | } 857 | 858 | static void IRAM_ATTR dma_filter_rgb565(const dma_elem_t *src, lldesc_t *dma_desc, uint8_t *dst) 859 | { 860 | assert(s_state->sampling_mode == SM_0A0B_0B0C || 861 | s_state->sampling_mode == SM_0A00_0B00); 862 | 863 | const int unroll = 2; // manually unrolling 2 iterations of the loop 864 | const int samples_per_pixel = 2; 865 | const int bytes_per_pixel = 3; 866 | size_t end = dma_desc->length / sizeof(dma_elem_t) / unroll / samples_per_pixel; 867 | for (size_t i = 0; i < end; ++i) 868 | { 869 | rgb565_to_888(src[0].sample1, src[1].sample1, &dst[0]); 870 | rgb565_to_888(src[2].sample1, src[3].sample1, &dst[3]); 871 | dst += bytes_per_pixel * unroll; 872 | src += samples_per_pixel * unroll; 873 | } 874 | if ((dma_desc->length & 0x7) != 0) 875 | { 876 | rgb565_to_888(src[0].sample1, src[1].sample1, &dst[0]); 877 | rgb565_to_888(src[2].sample1, src[2].sample2, &dst[3]); 878 | } 879 | } 880 | 881 | void camera_print_fb() 882 | { 883 | /* Number of pixels to skip 884 | in order to fit into terminal screen. 885 | Assumed picture to be 80 columns wide. 886 | Skip twice as more rows as they look higher. 887 | */ 888 | int pixels_to_skip = s_state->width / 80; 889 | 890 | for (int ih = 0; ih < s_state->height; ih += pixels_to_skip * 2) 891 | { 892 | for (int iw = 0; iw < s_state->width * 2; iw += pixels_to_skip) 893 | { 894 | uint8_t px = (s_state->fb[iw + (ih * s_state->width * 2)]); 895 | if (px < 26) 896 | printf(" "); 897 | else if (px < 51) 898 | printf("."); 899 | else if (px < 77) 900 | printf(":"); 901 | else if (px < 102) 902 | printf("-"); 903 | else if (px < 128) 904 | printf("="); 905 | else if (px < 154) 906 | printf("+"); 907 | else if (px < 179) 908 | printf("*"); 909 | else if (px < 205) 910 | printf("#"); 911 | else if (px < 230) 912 | printf("%%"); 913 | else 914 | printf("@"); 915 | } 916 | printf("\n"); 917 | } 918 | } 919 | 920 | static esp_err_t camera_enable_out_clock(camera_config_t *config) 921 | { 922 | periph_module_enable(PERIPH_LEDC_MODULE); 923 | 924 | ledc_timer_config_t timer_conf; 925 | timer_conf.duty_resolution = 1; 926 | timer_conf.freq_hz = config->xclk_freq_hz; 927 | timer_conf.speed_mode = LEDC_HIGH_SPEED_MODE; 928 | timer_conf.timer_num = config->ledc_timer; 929 | esp_err_t err = ledc_timer_config(&timer_conf); 930 | if (err != ESP_OK) 931 | { 932 | ESP_LOGE(TAG, "ledc_timer_config failed, rc=%x", err); 933 | return err; 934 | } 935 | 936 | ledc_channel_config_t ch_conf; 937 | ch_conf.channel = config->ledc_channel; 938 | ch_conf.timer_sel = config->ledc_timer; 939 | ch_conf.intr_type = LEDC_INTR_DISABLE; 940 | ch_conf.duty = 1; 941 | ch_conf.speed_mode = LEDC_HIGH_SPEED_MODE; 942 | ch_conf.gpio_num = config->pin_xclk; 943 | ch_conf.hpoint = 0; 944 | err = ledc_channel_config(&ch_conf); 945 | if (err != ESP_OK) 946 | { 947 | ESP_LOGE(TAG, "ledc_channel_config failed, rc=%x", err); 948 | return err; 949 | } 950 | return ESP_OK; 951 | } 952 | 953 | static void camera_disable_out_clock() 954 | { 955 | periph_module_disable(PERIPH_LEDC_MODULE); 956 | } 957 | 958 | int camera_set_hmirror(int en) 959 | { 960 | return s_state->sensor.set_hmirror(&s_state->sensor, en); 961 | } 962 | 963 | int camera_set_gainceiling(gainceiling_t gain) 964 | { 965 | return s_state->sensor.set_gainceiling(&s_state->sensor, gain); 966 | } 967 | 968 | int camera_set_vflip(int en) 969 | { 970 | return s_state->sensor.set_vflip(&s_state->sensor, en); 971 | } -------------------------------------------------------------------------------- /src/camera.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma once 16 | 17 | #include "esp_err.h" 18 | #include "driver/ledc.h" 19 | #include "sensor.h" 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | #define _NDEBUG 26 | #define CONFIG_OV2640_SUPPORT 1 27 | #define CONFIG_OV7725_SUPPORT 0 28 | 29 | typedef enum { 30 | CAMERA_PF_RGB565 = 0, //!< RGB, 2 bytes per pixel (not implemented) 31 | CAMERA_PF_YUV422 = 1, //!< YUYV, 2 bytes per pixel (not implemented) 32 | CAMERA_PF_GRAYSCALE = 2, //!< 1 byte per pixel 33 | CAMERA_PF_JPEG = 3, //!< JPEG compressed 34 | } camera_pixelformat_t; 35 | 36 | typedef enum { 37 | CAMERA_FS_QQVGA = 4, //!< 160x120 38 | CAMERA_FS_QVGA = 8, //!< 320x240 39 | CAMERA_FS_VGA = 10, //!< 640x480 40 | CAMERA_FS_SVGA = 11, //!< 800x600 41 | } camera_framesize_t; 42 | 43 | typedef enum { 44 | CAMERA_NONE = 0, 45 | CAMERA_UNKNOWN = 1, 46 | CAMERA_OV7725 = 7725, 47 | CAMERA_OV2640 = 2640, 48 | } camera_model_t; 49 | 50 | typedef struct { 51 | int pin_reset; /*!< GPIO pin for camera reset line */ 52 | int pin_xclk; /*!< GPIO pin for camera XCLK line */ 53 | int pin_sscb_sda; /*!< GPIO pin for camera SDA line */ 54 | int pin_sscb_scl; /*!< GPIO pin for camera SCL line */ 55 | int pin_d7; /*!< GPIO pin for camera D7 line */ 56 | int pin_d6; /*!< GPIO pin for camera D6 line */ 57 | int pin_d5; /*!< GPIO pin for camera D5 line */ 58 | int pin_d4; /*!< GPIO pin for camera D4 line */ 59 | int pin_d3; /*!< GPIO pin for camera D3 line */ 60 | int pin_d2; /*!< GPIO pin for camera D2 line */ 61 | int pin_d1; /*!< GPIO pin for camera D1 line */ 62 | int pin_d0; /*!< GPIO pin for camera D0 line */ 63 | int pin_vsync; /*!< GPIO pin for camera VSYNC line */ 64 | int pin_href; /*!< GPIO pin for camera HREF line */ 65 | int pin_pclk; /*!< GPIO pin for camera PCLK line */ 66 | 67 | int xclk_freq_hz; /*!< Frequency of XCLK signal, in Hz */ 68 | 69 | ledc_timer_t ledc_timer; /*!< LEDC timer to be used for generating XCLK */ 70 | ledc_channel_t ledc_channel; /*!< LEDC channel to be used for generating XCLK */ 71 | 72 | camera_pixelformat_t pixel_format; 73 | camera_framesize_t frame_size; 74 | 75 | int jpeg_quality; 76 | } camera_config_t; 77 | 78 | #define ESP_ERR_CAMERA_BASE 0x20000 79 | #define ESP_ERR_CAMERA_NOT_DETECTED (ESP_ERR_CAMERA_BASE + 1) 80 | #define ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE (ESP_ERR_CAMERA_BASE + 2) 81 | #define ESP_ERR_CAMERA_NOT_SUPPORTED (ESP_ERR_CAMERA_BASE + 3) 82 | 83 | /** 84 | * @brief Probe the camera 85 | * This function enables LEDC peripheral to generate XCLK signal, 86 | * detects the camera I2C address and detects camera model. 87 | * 88 | * @param config camera configuration parameters 89 | * @param[out] out_camera_model output, detected camera model 90 | * @return ESP_OK if camera was detected 91 | */ 92 | esp_err_t camera_probe(const camera_config_t* config, camera_model_t* out_camera_model); 93 | 94 | /** 95 | * @brief Initialize the camera driver 96 | * 97 | * @note call camera_probe before calling this function 98 | * 99 | * This function configures camera over I2C interface, 100 | * allocates framebuffer and DMA buffers, 101 | * initializes parallel I2S input, and sets up DMA descriptors. 102 | * 103 | * Currently this function can only be called once and there is 104 | * no way to de-initialize this module. 105 | * 106 | * @param config Camera configuration parameters 107 | * @return ESP_OK on success 108 | */ 109 | esp_err_t camera_init(const camera_config_t* config); 110 | 111 | /** 112 | * Deinitialize the camera driver 113 | * 114 | * @return 115 | * - ESP_OK on success 116 | * - ESP_ERR_INVALID_STATE if the driver hasn't been initialized yet 117 | */ 118 | esp_err_t camera_deinit(); 119 | 120 | /** 121 | * @brief Obtain the pointer to framebuffer allocated by camera_init function. 122 | * 123 | * @return pointer to framebuffer 124 | */ 125 | uint8_t* camera_get_fb(); 126 | 127 | /** 128 | * @brief Return the size of valid data in the framebuffer 129 | * 130 | * For grayscale mode, this function returns width * height of the framebuffer. 131 | * For JPEG mode, this function returns the actual size of encoded JPEG image. 132 | * @return size of valid data in framebuffer, in bytes 133 | */ 134 | size_t camera_get_data_size(); 135 | 136 | /** 137 | * @brief Get the width of framebuffer, in pixels. 138 | * @return width of framebuffer, in pixels 139 | */ 140 | int camera_get_fb_width(); 141 | 142 | /** 143 | * @brief Get the height of framebuffer, in pixels. 144 | * @return height of framebuffer, in pixels 145 | */ 146 | int camera_get_fb_height(); 147 | 148 | /** 149 | * @brief Acquire one frame and store it into framebuffer 150 | * 151 | * This function waits for the next VSYNC, starts DMA to get data from camera, 152 | * and blocks until all lines of the image are stored into the framebuffer. 153 | * Once all lines are stored, the function returns. 154 | * 155 | * @return ESP_OK on success 156 | */ 157 | esp_err_t camera_run(); 158 | 159 | /** 160 | * @brief Print contents of framebuffer on terminal 161 | * 162 | */ 163 | void camera_print_fb(); 164 | 165 | int camera_set_hmirror(int en); 166 | int camera_set_gainceiling(gainceiling_t gain); 167 | int camera_set_vflip(int en); 168 | 169 | #ifdef __cplusplus 170 | } 171 | #endif 172 | -------------------------------------------------------------------------------- /src/camera_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "rom/lldesc.h" 7 | #include "esp_err.h" 8 | #include "esp_intr_alloc.h" 9 | #include "freertos/FreeRTOS.h" 10 | #include "freertos/semphr.h" 11 | #include "freertos/task.h" 12 | #include "camera.h" 13 | #include "sensor.h" 14 | 15 | typedef union { 16 | struct { 17 | uint8_t sample2; 18 | uint8_t unused2; 19 | uint8_t sample1; 20 | uint8_t unused1; 21 | }; 22 | uint32_t val; 23 | } dma_elem_t; 24 | 25 | typedef enum { 26 | /* camera sends byte sequence: s1, s2, s3, s4, ... 27 | * fifo receives: 00 s1 00 s2, 00 s2 00 s3, 00 s3 00 s4, ... 28 | */ 29 | SM_0A0B_0B0C = 0, 30 | /* camera sends byte sequence: s1, s2, s3, s4, ... 31 | * fifo receives: 00 s1 00 s2, 00 s3 00 s4, ... 32 | */ 33 | SM_0A0B_0C0D = 1, 34 | /* camera sends byte sequence: s1, s2, s3, s4, ... 35 | * fifo receives: 00 s1 00 00, 00 s2 00 00, 00 s3 00 00, ... 36 | */ 37 | SM_0A00_0B00 = 3, 38 | } i2s_sampling_mode_t; 39 | 40 | typedef void (*dma_filter_t)(const dma_elem_t* src, lldesc_t* dma_desc, uint8_t* dst); 41 | 42 | typedef struct { 43 | camera_config_t config; 44 | sensor_t sensor; 45 | uint8_t *fb; 46 | size_t fb_size; 47 | size_t data_size; 48 | size_t width; 49 | size_t height; 50 | size_t in_bytes_per_pixel; 51 | size_t fb_bytes_per_pixel; 52 | size_t stride; 53 | size_t frame_count; 54 | 55 | lldesc_t *dma_desc; 56 | dma_elem_t **dma_buf; 57 | bool dma_done; 58 | size_t dma_desc_count; 59 | size_t dma_desc_cur; 60 | size_t dma_received_count; 61 | size_t dma_filtered_count; 62 | size_t dma_per_line; 63 | size_t dma_buf_width; 64 | size_t dma_sample_count; 65 | i2s_sampling_mode_t sampling_mode; 66 | dma_filter_t dma_filter; 67 | intr_handle_t i2s_intr_handle; 68 | QueueHandle_t data_ready; 69 | SemaphoreHandle_t frame_ready; 70 | TaskHandle_t dma_filter_task; 71 | } camera_state_t; 72 | 73 | -------------------------------------------------------------------------------- /src/ov2640_regs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the OpenMV project. 3 | * Copyright (c) 2013/2014 Ibrahim Abdelkader 4 | * This work is licensed under the MIT license, see the file LICENSE for details. 5 | * 6 | * OV2640 register definitions. 7 | */ 8 | #ifndef __REG_REGS_H__ 9 | #define __REG_REGS_H__ 10 | /* DSP register bank FF=0x00*/ 11 | #define QS 0x44 12 | #define HSIZE 0x51 13 | #define VSIZE 0x52 14 | #define XOFFL 0x53 15 | #define YOFFL 0x54 16 | #define VHYX 0x55 17 | #define DPRP 0x56 18 | #define TEST 0x57 19 | #define ZMOW 0x5A 20 | #define ZMOH 0x5B 21 | #define ZMHH 0x5C 22 | #define BPADDR 0x7C 23 | #define BPDATA 0x7D 24 | #define SIZEL 0x8C 25 | #define HSIZE8 0xC0 26 | #define VSIZE8 0xC1 27 | #define CTRL1 0xC3 28 | #define MS_SP 0xF0 29 | #define SS_ID 0xF7 30 | #define SS_CTRL 0xF7 31 | #define MC_AL 0xFA 32 | #define MC_AH 0xFB 33 | #define MC_D 0xFC 34 | #define P_CMD 0xFD 35 | #define P_STATUS 0xFE 36 | 37 | #define CTRLI 0x50 38 | #define CTRLI_LP_DP 0x80 39 | #define CTRLI_ROUND 0x40 40 | 41 | #define CTRL0 0xC2 42 | #define CTRL0_AEC_EN 0x80 43 | #define CTRL0_AEC_SEL 0x40 44 | #define CTRL0_STAT_SEL 0x20 45 | #define CTRL0_VFIRST 0x10 46 | #define CTRL0_YUV422 0x08 47 | #define CTRL0_YUV_EN 0x04 48 | #define CTRL0_RGB_EN 0x02 49 | #define CTRL0_RAW_EN 0x01 50 | 51 | #define CTRL2 0x86 52 | #define CTRL2_DCW_EN 0x20 53 | #define CTRL2_SDE_EN 0x10 54 | #define CTRL2_UV_ADJ_EN 0x08 55 | #define CTRL2_UV_AVG_EN 0x04 56 | #define CTRL2_CMX_EN 0x01 57 | 58 | #define CTRL3 0x87 59 | #define CTRL3_BPC_EN 0x80 60 | #define CTRL3_WPC_EN 0x40 61 | #define R_DVP_SP 0xD3 62 | #define R_DVP_SP_AUTO_MODE 0x80 63 | 64 | #define R_BYPASS 0x05 65 | #define R_BYPASS_DSP_EN 0x00 66 | #define R_BYPASS_DSP_BYPAS 0x01 67 | 68 | #define IMAGE_MODE 0xDA 69 | #define IMAGE_MODE_Y8_DVP_EN 0x40 70 | #define IMAGE_MODE_JPEG_EN 0x10 71 | #define IMAGE_MODE_YUV422 0x00 72 | #define IMAGE_MODE_RAW10 0x04 73 | #define IMAGE_MODE_RGB565 0x08 74 | #define IMAGE_MODE_HREF_VSYNC 0x02 75 | #define IMAGE_MODE_LBYTE_FIRST 0x01 76 | 77 | #define RESET 0xE0 78 | #define RESET_MICROC 0x40 79 | #define RESET_SCCB 0x20 80 | #define RESET_JPEG 0x10 81 | #define RESET_DVP 0x04 82 | #define RESET_IPU 0x02 83 | #define RESET_CIF 0x01 84 | 85 | #define MC_BIST 0xF9 86 | #define MC_BIST_RESET 0x80 87 | #define MC_BIST_BOOT_ROM_SEL 0x40 88 | #define MC_BIST_12KB_SEL 0x20 89 | #define MC_BIST_12KB_MASK 0x30 90 | #define MC_BIST_512KB_SEL 0x08 91 | #define MC_BIST_512KB_MASK 0x0C 92 | #define MC_BIST_BUSY_BIT_R 0x02 93 | #define MC_BIST_MC_RES_ONE_SH_W 0x02 94 | #define MC_BIST_LAUNCH 0x01 95 | 96 | #define BANK_SEL 0xFF 97 | #define BANK_SEL_DSP 0x00 98 | #define BANK_SEL_SENSOR 0x01 99 | 100 | /* Sensor register bank FF=0x01*/ 101 | #define GAIN 0x00 102 | #define COM1 0x03 103 | #define REG_PID 0x0A 104 | #define REG_VER 0x0B 105 | #define COM4 0x0D 106 | #define AEC 0x10 107 | #define CLKRC 0x11 108 | #define COM10 0x15 109 | #define HSTART 0x17 110 | #define HSTOP 0x18 111 | #define VSTART 0x19 112 | #define VSTOP 0x1A 113 | #define MIDH 0x1C 114 | #define MIDL 0x1D 115 | #define AEW 0x24 116 | #define AEB 0x25 117 | #define REG2A 0x2A 118 | #define FRARL 0x2B 119 | #define ADDVSL 0x2D 120 | #define ADDVSH 0x2E 121 | #define YAVG 0x2F 122 | #define HSDY 0x30 123 | #define HEDY 0x31 124 | #define ARCOM2 0x34 125 | #define REG45 0x45 126 | #define FLL 0x46 127 | #define FLH 0x47 128 | #define COM19 0x48 129 | #define ZOOMS 0x49 130 | #define COM22 0x4B 131 | #define COM25 0x4E 132 | #define BD50 0x4F 133 | #define BD60 0x50 134 | #define REG5D 0x5D 135 | #define REG5E 0x5E 136 | #define REG5F 0x5F 137 | #define REG60 0x60 138 | #define HISTO_LOW 0x61 139 | #define HISTO_HIGH 0x62 140 | 141 | #define REG04 0x04 142 | #define REG04_DEFAULT 0x28 143 | #define REG04_VFLIP_IMG 0x40 144 | #define REG04_HFLIP_IMG 0x80 145 | #define REG04_VREF_EN 0x10 146 | #define REG04_HREF_EN 0x08 147 | #define REG04_SET(x) (REG04_DEFAULT|x) 148 | 149 | #define REG08 0x08 150 | #define COM2 0x09 151 | #define COM2_STDBY 0x10 152 | #define COM2_OUT_DRIVE_1x 0x00 153 | #define COM2_OUT_DRIVE_2x 0x01 154 | #define COM2_OUT_DRIVE_3x 0x02 155 | #define COM2_OUT_DRIVE_4x 0x03 156 | 157 | #define COM3 0x0C 158 | #define COM3_DEFAULT 0x38 159 | #define COM3_BAND_50Hz 0x04 160 | #define COM3_BAND_60Hz 0x00 161 | #define COM3_BAND_AUTO 0x02 162 | #define COM3_BAND_SET(x) (COM3_DEFAULT|x) 163 | 164 | #define COM7 0x12 165 | #define COM7_SRST 0x80 166 | #define COM7_RES_UXGA 0x00 /* UXGA */ 167 | #define COM7_RES_SVGA 0x40 /* SVGA */ 168 | #define COM7_RES_CIF 0x20 /* CIF */ 169 | #define COM7_ZOOM_EN 0x04 /* Enable Zoom */ 170 | #define COM7_COLOR_BAR 0x02 /* Enable Color Bar Test */ 171 | 172 | #define COM8 0x13 173 | #define COM8_DEFAULT 0xC0 174 | #define COM8_BNDF_EN 0x20 /* Enable Banding filter */ 175 | #define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */ 176 | #define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */ 177 | #define COM8_SET(x) (COM8_DEFAULT|x) 178 | 179 | #define COM9 0x14 /* AGC gain ceiling */ 180 | #define COM9_DEFAULT 0x08 181 | #define COM9_AGC_GAIN_2x 0x00 /* AGC: 2x */ 182 | #define COM9_AGC_GAIN_4x 0x01 /* AGC: 4x */ 183 | #define COM9_AGC_GAIN_8x 0x02 /* AGC: 8x */ 184 | #define COM9_AGC_GAIN_16x 0x03 /* AGC: 16x */ 185 | #define COM9_AGC_GAIN_32x 0x04 /* AGC: 32x */ 186 | #define COM9_AGC_GAIN_64x 0x05 /* AGC: 64x */ 187 | #define COM9_AGC_GAIN_128x 0x06 /* AGC: 128x */ 188 | #define COM9_AGC_SET(x) (COM9_DEFAULT|(x<<5)) 189 | 190 | #define COM10 0x15 191 | #define COM10_HREF_EN 0x80 /* HSYNC changes to HREF */ 192 | #define COM10_HSYNC_EN 0x40 /* HREF changes to HSYNC */ 193 | #define COM10_PCLK_FREE 0x20 /* PCLK output option: free running PCLK */ 194 | #define COM10_PCLK_EDGE 0x10 /* Data is updated at the rising edge of PCLK */ 195 | #define COM10_HREF_NEG 0x08 /* HREF negative */ 196 | #define COM10_VSYNC_NEG 0x02 /* VSYNC negative */ 197 | #define COM10_HSYNC_NEG 0x01 /* HSYNC negative */ 198 | 199 | #define CTRL1_AWB 0x08 /* Enable AWB */ 200 | 201 | #define VV 0x26 202 | #define VV_AGC_TH_SET(h,l) ((h<<4)|(l&0x0F)) 203 | 204 | #define REG32 0x32 205 | #define REG32_UXGA 0x36 206 | #define REG32_SVGA 0x09 207 | #define REG32_CIF 0x00 208 | #endif //__REG_REGS_H__ 209 | -------------------------------------------------------------------------------- /src/ov264_drive.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the OpenMV project. 3 | * Copyright (c) 2013/2014 Ibrahim Abdelkader 4 | * This work is licensed under the MIT license, see the file LICENSE for details. 5 | * 6 | * OV2640 driver. 7 | * 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include "sccb.h" 13 | #include "ov264_drive.h" 14 | #include "ov2640_regs.h" 15 | 16 | #define SVGA_HSIZE (800) 17 | #define SVGA_VSIZE (600) 18 | 19 | #define UXGA_HSIZE (1600) 20 | #define UXGA_VSIZE (1200) 21 | 22 | static const uint8_t default_regs[][2] = { 23 | { BANK_SEL, BANK_SEL_DSP }, 24 | { 0x2c, 0xff }, 25 | { 0x2e, 0xdf }, 26 | { BANK_SEL, BANK_SEL_SENSOR }, 27 | { 0x3c, 0x32 }, 28 | { CLKRC, 0x80 }, /* Set PCLK divider */ 29 | { COM2, COM2_OUT_DRIVE_3x }, /* Output drive x2 */ 30 | #ifdef OPENMV2 31 | { REG04, 0xF8}, /* Mirror/VFLIP/AEC[1:0] */ 32 | #else 33 | { REG04_SET(REG04_HREF_EN)}, 34 | #endif 35 | { COM8, COM8_SET(COM8_BNDF_EN | COM8_AGC_EN | COM8_AEC_EN) }, 36 | { COM9, COM9_AGC_SET(COM9_AGC_GAIN_8x)}, 37 | {COM10, 0}, //Invert VSYNC 38 | { 0x2c, 0x0c }, 39 | { 0x33, 0x78 }, 40 | { 0x3a, 0x33 }, 41 | { 0x3b, 0xfb }, 42 | { 0x3e, 0x00 }, 43 | { 0x43, 0x11 }, 44 | { 0x16, 0x10 }, 45 | { 0x39, 0x02 }, 46 | { 0x35, 0x88 }, 47 | { 0x22, 0x0a }, 48 | { 0x37, 0x40 }, 49 | { 0x23, 0x00 }, 50 | { ARCOM2, 0xa0 }, 51 | { 0x06, 0x02 }, 52 | { 0x06, 0x88 }, 53 | { 0x07, 0xc0 }, 54 | { 0x0d, 0xb7 }, 55 | { 0x0e, 0x01 }, 56 | { 0x4c, 0x00 }, 57 | { 0x4a, 0x81 }, 58 | { 0x21, 0x99 }, 59 | { AEW, 0x40 }, 60 | { AEB, 0x38 }, 61 | /* AGC/AEC fast mode operating region */ 62 | { VV, VV_AGC_TH_SET(0x08, 0x02) }, 63 | { COM19, 0x00 }, /* Zoom control 2 MSBs */ 64 | { ZOOMS, 0x00 }, /* Zoom control 8 MSBs */ 65 | { 0x5c, 0x00 }, 66 | { 0x63, 0x00 }, 67 | { FLL, 0x00 }, 68 | { FLH, 0x00 }, 69 | 70 | /* Set banding filter */ 71 | { COM3, COM3_BAND_SET(COM3_BAND_AUTO) }, 72 | { REG5D, 0x55 }, 73 | { REG5E, 0x7d }, 74 | { REG5F, 0x7d }, 75 | { REG60, 0x55 }, 76 | { HISTO_LOW, 0x70 }, 77 | { HISTO_HIGH, 0x80 }, 78 | { 0x7c, 0x05 }, 79 | { 0x20, 0x80 }, 80 | { 0x28, 0x30 }, 81 | { 0x6c, 0x00 }, 82 | { 0x6d, 0x80 }, 83 | { 0x6e, 0x00 }, 84 | { 0x70, 0x02 }, 85 | { 0x71, 0x94 }, 86 | { 0x73, 0xc1 }, 87 | { 0x3d, 0x34 }, 88 | //{ COM7, COM7_RES_UXGA | COM7_ZOOM_EN }, 89 | { 0x5a, 0x57 }, 90 | { BD50, 0xbb }, 91 | { BD60, 0x9c }, 92 | 93 | { BANK_SEL, BANK_SEL_DSP }, 94 | { 0xe5, 0x7f }, 95 | { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL }, 96 | { 0x41, 0x24 }, 97 | { RESET, RESET_JPEG | RESET_DVP }, 98 | { 0x76, 0xff }, 99 | { 0x33, 0xa0 }, 100 | { 0x42, 0x20 }, 101 | { 0x43, 0x18 }, 102 | { 0x4c, 0x00 }, 103 | { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 }, 104 | { 0x88, 0x3f }, 105 | { 0xd7, 0x03 }, 106 | { 0xd9, 0x10 }, 107 | { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 }, 108 | { 0xc8, 0x08 }, 109 | { 0xc9, 0x80 }, 110 | { BPADDR, 0x00 }, 111 | { BPDATA, 0x00 }, 112 | { BPADDR, 0x03 }, 113 | { BPDATA, 0x48 }, 114 | { BPDATA, 0x48 }, 115 | { BPADDR, 0x08 }, 116 | { BPDATA, 0x20 }, 117 | { BPDATA, 0x10 }, 118 | { BPDATA, 0x0e }, 119 | { 0x90, 0x00 }, 120 | { 0x91, 0x0e }, 121 | { 0x91, 0x1a }, 122 | { 0x91, 0x31 }, 123 | { 0x91, 0x5a }, 124 | { 0x91, 0x69 }, 125 | { 0x91, 0x75 }, 126 | { 0x91, 0x7e }, 127 | { 0x91, 0x88 }, 128 | { 0x91, 0x8f }, 129 | { 0x91, 0x96 }, 130 | { 0x91, 0xa3 }, 131 | { 0x91, 0xaf }, 132 | { 0x91, 0xc4 }, 133 | { 0x91, 0xd7 }, 134 | { 0x91, 0xe8 }, 135 | { 0x91, 0x20 }, 136 | { 0x92, 0x00 }, 137 | { 0x93, 0x06 }, 138 | { 0x93, 0xe3 }, 139 | { 0x93, 0x03 }, 140 | { 0x93, 0x03 }, 141 | { 0x93, 0x00 }, 142 | { 0x93, 0x02 }, 143 | { 0x93, 0x00 }, 144 | { 0x93, 0x00 }, 145 | { 0x93, 0x00 }, 146 | { 0x93, 0x00 }, 147 | { 0x93, 0x00 }, 148 | { 0x93, 0x00 }, 149 | { 0x93, 0x00 }, 150 | { 0x96, 0x00 }, 151 | { 0x97, 0x08 }, 152 | { 0x97, 0x19 }, 153 | { 0x97, 0x02 }, 154 | { 0x97, 0x0c }, 155 | { 0x97, 0x24 }, 156 | { 0x97, 0x30 }, 157 | { 0x97, 0x28 }, 158 | { 0x97, 0x26 }, 159 | { 0x97, 0x02 }, 160 | { 0x97, 0x98 }, 161 | { 0x97, 0x80 }, 162 | { 0x97, 0x00 }, 163 | { 0x97, 0x00 }, 164 | { 0xa4, 0x00 }, 165 | { 0xa8, 0x00 }, 166 | { 0xc5, 0x11 }, 167 | { 0xc6, 0x51 }, 168 | { 0xbf, 0x80 }, 169 | { 0xc7, 0x10 }, 170 | { 0xb6, 0x66 }, 171 | { 0xb8, 0xA5 }, 172 | { 0xb7, 0x64 }, 173 | { 0xb9, 0x7C }, 174 | { 0xb3, 0xaf }, 175 | { 0xb4, 0x97 }, 176 | { 0xb5, 0xFF }, 177 | { 0xb0, 0xC5 }, 178 | { 0xb1, 0x94 }, 179 | { 0xb2, 0x0f }, 180 | { 0xc4, 0x5c }, 181 | { 0xa6, 0x00 }, 182 | { 0xa7, 0x20 }, 183 | { 0xa7, 0xd8 }, 184 | { 0xa7, 0x1b }, 185 | { 0xa7, 0x31 }, 186 | { 0xa7, 0x00 }, 187 | { 0xa7, 0x18 }, 188 | { 0xa7, 0x20 }, 189 | { 0xa7, 0xd8 }, 190 | { 0xa7, 0x19 }, 191 | { 0xa7, 0x31 }, 192 | { 0xa7, 0x00 }, 193 | { 0xa7, 0x18 }, 194 | { 0xa7, 0x20 }, 195 | { 0xa7, 0xd8 }, 196 | { 0xa7, 0x19 }, 197 | { 0xa7, 0x31 }, 198 | { 0xa7, 0x00 }, 199 | { 0xa7, 0x18 }, 200 | { 0x7f, 0x00 }, 201 | { 0xe5, 0x1f }, 202 | { 0xe1, 0x77 }, 203 | { 0xdd, 0x7f }, 204 | { CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN | CTRL0_RGB_EN }, 205 | { 0x00, 0x00 } 206 | }; 207 | 208 | static const uint8_t cif_regs[][2] = { 209 | }; 210 | 211 | static const uint8_t svga_regs[][2] = { 212 | { BANK_SEL, BANK_SEL_SENSOR }, 213 | /* DSP input image resoultion and window size control */ 214 | { COM7, COM7_RES_SVGA}, 215 | { COM1, 0x0F }, /* UXGA=0x0F, SVGA=0x0A, CIF=0x06 */ 216 | { REG32, 0x09 }, /* UXGA=0x36, SVGA/CIF=0x09 */ 217 | 218 | { HSTART, 0x11 }, /* UXGA=0x11, SVGA/CIF=0x11 */ 219 | { HSTOP, 0x43 }, /* UXGA=0x75, SVGA/CIF=0x43 */ 220 | 221 | { VSTART, 0x00 }, /* UXGA=0x01, SVGA/CIF=0x00 */ 222 | { VSTOP, 0x4b }, /* UXGA=0x97, SVGA/CIF=0x4b */ 223 | { 0x3d, 0x38 }, /* UXGA=0x34, SVGA/CIF=0x38 */ 224 | 225 | { 0x35, 0xda }, 226 | { 0x22, 0x1a }, 227 | { 0x37, 0xc3 }, 228 | { 0x34, 0xc0 }, 229 | { 0x06, 0x88 }, 230 | { 0x0d, 0x87 }, 231 | { 0x0e, 0x41 }, 232 | { 0x42, 0x03 }, 233 | 234 | /* Set DSP input image size and offset. 235 | The sensor output image can be scaled with OUTW/OUTH */ 236 | { BANK_SEL, BANK_SEL_DSP }, 237 | { R_BYPASS, R_BYPASS_DSP_BYPAS }, 238 | 239 | { RESET, RESET_DVP }, 240 | { HSIZE8, (SVGA_HSIZE>>3)}, /* Image Horizontal Size HSIZE[10:3] */ 241 | { VSIZE8, (SVGA_VSIZE>>3)}, /* Image Vertiacl Size VSIZE[10:3] */ 242 | 243 | /* {HSIZE[11], HSIZE[2:0], VSIZE[2:0]} */ 244 | { SIZEL, ((SVGA_HSIZE>>6)&0x40) | ((SVGA_HSIZE&0x7)<<3) | (SVGA_VSIZE&0x7)}, 245 | 246 | { XOFFL, 0x00 }, /* OFFSET_X[7:0] */ 247 | { YOFFL, 0x00 }, /* OFFSET_Y[7:0] */ 248 | { HSIZE, ((SVGA_HSIZE>>2)&0xFF) }, /* H_SIZE[7:0]= HSIZE/4 */ 249 | { VSIZE, ((SVGA_VSIZE>>2)&0xFF) }, /* V_SIZE[7:0]= VSIZE/4 */ 250 | 251 | /* V_SIZE[8]/OFFSET_Y[10:8]/H_SIZE[8]/OFFSET_X[10:8] */ 252 | { VHYX, ((SVGA_VSIZE>>3)&0x80) | ((SVGA_HSIZE>>7)&0x08) }, 253 | { TEST, (SVGA_HSIZE>>4)&0x80}, /* H_SIZE[9] */ 254 | 255 | { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | 256 | CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN }, 257 | 258 | /* H_DIVIDER/V_DIVIDER */ 259 | { CTRLI, CTRLI_LP_DP | 0x00}, 260 | /* DVP prescalar */ 261 | { R_DVP_SP, R_DVP_SP_AUTO_MODE}, 262 | 263 | { R_BYPASS, R_BYPASS_DSP_EN }, 264 | { RESET, 0x00 }, 265 | {0, 0}, 266 | }; 267 | 268 | static const uint8_t uxga_regs[][2] = { 269 | { BANK_SEL, BANK_SEL_SENSOR }, 270 | /* DSP input image resoultion and window size control */ 271 | { COM7, COM7_RES_UXGA}, 272 | { COM1, 0x0F }, /* UXGA=0x0F, SVGA=0x0A, CIF=0x06 */ 273 | { REG32, 0x36 }, /* UXGA=0x36, SVGA/CIF=0x09 */ 274 | 275 | { HSTART, 0x11 }, /* UXGA=0x11, SVGA/CIF=0x11 */ 276 | { HSTOP, 0x75 }, /* UXGA=0x75, SVGA/CIF=0x43 */ 277 | 278 | { VSTART, 0x01 }, /* UXGA=0x01, SVGA/CIF=0x00 */ 279 | { VSTOP, 0x97 }, /* UXGA=0x97, SVGA/CIF=0x4b */ 280 | { 0x3d, 0x34 }, /* UXGA=0x34, SVGA/CIF=0x38 */ 281 | 282 | { 0x35, 0x88 }, 283 | { 0x22, 0x0a }, 284 | { 0x37, 0x40 }, 285 | { 0x34, 0xa0 }, 286 | { 0x06, 0x02 }, 287 | { 0x0d, 0xb7 }, 288 | { 0x0e, 0x01 }, 289 | { 0x42, 0x83 }, 290 | 291 | /* Set DSP input image size and offset. 292 | The sensor output image can be scaled with OUTW/OUTH */ 293 | { BANK_SEL, BANK_SEL_DSP }, 294 | { R_BYPASS, R_BYPASS_DSP_BYPAS }, 295 | 296 | { RESET, RESET_DVP }, 297 | { HSIZE8, (UXGA_HSIZE>>3)}, /* Image Horizontal Size HSIZE[10:3] */ 298 | { VSIZE8, (UXGA_VSIZE>>3)}, /* Image Vertiacl Size VSIZE[10:3] */ 299 | 300 | /* {HSIZE[11], HSIZE[2:0], VSIZE[2:0]} */ 301 | { SIZEL, ((UXGA_HSIZE>>6)&0x40) | ((UXGA_HSIZE&0x7)<<3) | (UXGA_VSIZE&0x7)}, 302 | 303 | { XOFFL, 0x00 }, /* OFFSET_X[7:0] */ 304 | { YOFFL, 0x00 }, /* OFFSET_Y[7:0] */ 305 | { HSIZE, ((UXGA_HSIZE>>2)&0xFF) }, /* H_SIZE[7:0] real/4 */ 306 | { VSIZE, ((UXGA_VSIZE>>2)&0xFF) }, /* V_SIZE[7:0] real/4 */ 307 | 308 | /* V_SIZE[8]/OFFSET_Y[10:8]/H_SIZE[8]/OFFSET_X[10:8] */ 309 | { VHYX, ((UXGA_VSIZE>>3)&0x80) | ((UXGA_HSIZE>>7)&0x08) }, 310 | { TEST, (UXGA_HSIZE>>4)&0x80}, /* H_SIZE[9] */ 311 | 312 | { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | 313 | CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN }, 314 | 315 | /* H_DIVIDER/V_DIVIDER */ 316 | { CTRLI, CTRLI_LP_DP | 0x00}, 317 | /* DVP prescalar */ 318 | { R_DVP_SP, R_DVP_SP_AUTO_MODE | 0x04}, 319 | 320 | { R_BYPASS, R_BYPASS_DSP_EN }, 321 | { RESET, 0x00 }, 322 | {0, 0}, 323 | }; 324 | 325 | static const uint8_t yuv422_regs[][2] = { 326 | { BANK_SEL, BANK_SEL_DSP }, 327 | { RESET, RESET_DVP}, 328 | { IMAGE_MODE, IMAGE_MODE_YUV422 }, 329 | { 0xD7, 0x01 }, 330 | { 0xE1, 0x67 }, 331 | { RESET, 0x00 }, 332 | {0, 0}, 333 | }; 334 | 335 | static const uint8_t rgb565_regs[][2] = { 336 | { BANK_SEL, BANK_SEL_DSP }, 337 | { RESET, RESET_DVP}, 338 | { IMAGE_MODE, IMAGE_MODE_RGB565 }, 339 | { 0xD7, 0x03 }, 340 | { 0xE1, 0x77 }, 341 | { RESET, 0x00 }, 342 | {0, 0}, 343 | }; 344 | 345 | static const uint8_t jpeg_regs[][2] = { 346 | { BANK_SEL, BANK_SEL_DSP }, 347 | { RESET, RESET_DVP}, 348 | { IMAGE_MODE, IMAGE_MODE_JPEG_EN|IMAGE_MODE_RGB565 }, 349 | { 0xD7, 0x03 }, 350 | { 0xE1, 0x77 }, 351 | { QS, 0x0C }, 352 | { RESET, 0x00 }, 353 | {0, 0}, 354 | }; 355 | 356 | //|IMAGE_MODE_HREF_VSYNC 357 | 358 | #define NUM_BRIGHTNESS_LEVELS (5) 359 | static const uint8_t brightness_regs[NUM_BRIGHTNESS_LEVELS + 1][5] = { 360 | { BPADDR, BPDATA, BPADDR, BPDATA, BPDATA }, 361 | { 0x00, 0x04, 0x09, 0x00, 0x00 }, /* -2 */ 362 | { 0x00, 0x04, 0x09, 0x10, 0x00 }, /* -1 */ 363 | { 0x00, 0x04, 0x09, 0x20, 0x00 }, /* 0 */ 364 | { 0x00, 0x04, 0x09, 0x30, 0x00 }, /* +1 */ 365 | { 0x00, 0x04, 0x09, 0x40, 0x00 }, /* +2 */ 366 | }; 367 | 368 | #define NUM_CONTRAST_LEVELS (5) 369 | static const uint8_t contrast_regs[NUM_CONTRAST_LEVELS + 1][7] = { 370 | { BPADDR, BPDATA, BPADDR, BPDATA, BPDATA, BPDATA, BPDATA }, 371 | { 0x00, 0x04, 0x07, 0x20, 0x18, 0x34, 0x06 }, /* -2 */ 372 | { 0x00, 0x04, 0x07, 0x20, 0x1c, 0x2a, 0x06 }, /* -1 */ 373 | { 0x00, 0x04, 0x07, 0x20, 0x20, 0x20, 0x06 }, /* 0 */ 374 | { 0x00, 0x04, 0x07, 0x20, 0x24, 0x16, 0x06 }, /* +1 */ 375 | { 0x00, 0x04, 0x07, 0x20, 0x28, 0x0c, 0x06 }, /* +2 */ 376 | }; 377 | 378 | #define NUM_SATURATION_LEVELS (5) 379 | static const uint8_t saturation_regs[NUM_SATURATION_LEVELS + 1][5] = { 380 | { BPADDR, BPDATA, BPADDR, BPDATA, BPDATA }, 381 | { 0x00, 0x02, 0x03, 0x28, 0x28 }, /* -2 */ 382 | { 0x00, 0x02, 0x03, 0x38, 0x38 }, /* -1 */ 383 | { 0x00, 0x02, 0x03, 0x48, 0x48 }, /* 0 */ 384 | { 0x00, 0x02, 0x03, 0x58, 0x58 }, /* +1 */ 385 | { 0x00, 0x02, 0x03, 0x58, 0x58 }, /* +2 */ 386 | }; 387 | 388 | static int reset(sensor_t *sensor) 389 | { 390 | int i=0; 391 | const uint8_t (*regs)[2]; 392 | 393 | /* Reset all registers */ 394 | SCCB_Write(sensor->slv_addr, BANK_SEL, BANK_SEL_SENSOR); 395 | SCCB_Write(sensor->slv_addr, COM7, COM7_SRST); 396 | 397 | /* delay n ms */ 398 | delay(10); 399 | 400 | i = 0; 401 | regs = default_regs; 402 | /* Write initial regsiters */ 403 | while (regs[i][0]) { 404 | SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]); 405 | i++; 406 | } 407 | 408 | i = 0; 409 | regs = svga_regs; 410 | /* Write DSP input regsiters */ 411 | while (regs[i][0]) { 412 | SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]); 413 | i++; 414 | } 415 | 416 | return 0; 417 | } 418 | 419 | static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) 420 | { 421 | int i=0; 422 | const uint8_t (*regs)[2]=NULL; 423 | 424 | /* read pixel format reg */ 425 | switch (pixformat) { 426 | case PIXFORMAT_RGB565: 427 | regs = rgb565_regs; 428 | break; 429 | case PIXFORMAT_YUV422: 430 | case PIXFORMAT_GRAYSCALE: 431 | regs = yuv422_regs; 432 | break; 433 | case PIXFORMAT_JPEG: 434 | regs = jpeg_regs; 435 | break; 436 | default: 437 | return -1; 438 | } 439 | 440 | /* Write initial regsiters */ 441 | while (regs[i][0]) { 442 | SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]); 443 | i++; 444 | } 445 | 446 | /* delay n ms */ 447 | delay(30); 448 | 449 | return 0; 450 | } 451 | 452 | static int set_framesize(sensor_t *sensor, framesize_t framesize) 453 | { 454 | int ret=0; 455 | uint8_t clkrc; 456 | uint16_t w = resolution[framesize][0]; 457 | uint16_t h = resolution[framesize][1]; 458 | 459 | int i=0; 460 | const uint8_t (*regs)[2]; 461 | 462 | if (framesize <= FRAMESIZE_SVGA) { 463 | clkrc =0x83; 464 | regs = svga_regs; 465 | } else { 466 | clkrc =0x87; 467 | regs = uxga_regs; 468 | } 469 | 470 | /* Disable DSP */ 471 | 472 | ret |= SCCB_Write(sensor->slv_addr, BANK_SEL, BANK_SEL_DSP); 473 | ret |= SCCB_Write(sensor->slv_addr, R_BYPASS, R_BYPASS_DSP_BYPAS); 474 | 475 | /* Write output width */ 476 | ret |= SCCB_Write(sensor->slv_addr, ZMOW, (w>>2)&0xFF); // OUTW[7:0] (real/4) 477 | ret |= SCCB_Write(sensor->slv_addr, ZMOH, (h>>2)&0xFF); // OUTH[7:0] (real/4) 478 | ret |= SCCB_Write(sensor->slv_addr, ZMHH, ((h>>8)&0x04)|((w>>10)&0x03)); // OUTH[8]/OUTW[9:8] 479 | 480 | /* Set CLKRC */ 481 | ret |= SCCB_Write(sensor->slv_addr, BANK_SEL, BANK_SEL_SENSOR); 482 | ret |= SCCB_Write(sensor->slv_addr, CLKRC, clkrc); 483 | 484 | /* Write DSP input regsiters */ 485 | while (regs[i][0]) { 486 | SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]); 487 | i++; 488 | } 489 | 490 | /* Enable DSP */ 491 | ret |= SCCB_Write(sensor->slv_addr, BANK_SEL, BANK_SEL_DSP); 492 | ret |= SCCB_Write(sensor->slv_addr, R_BYPASS, R_BYPASS_DSP_EN); 493 | /* delay n ms */ 494 | delay(30); 495 | 496 | return ret; 497 | } 498 | 499 | static int set_framerate(sensor_t *sensor, framerate_t framerate) 500 | { 501 | return 0; 502 | } 503 | 504 | static int set_contrast(sensor_t *sensor, int level) 505 | { 506 | int ret=0; 507 | 508 | level += (NUM_CONTRAST_LEVELS / 2 + 1); 509 | if (level < 0 || level > NUM_CONTRAST_LEVELS) { 510 | return -1; 511 | } 512 | 513 | /* Switch to DSP register bank */ 514 | ret |= SCCB_Write(sensor->slv_addr, BANK_SEL, BANK_SEL_DSP); 515 | 516 | /* Write contrast registers */ 517 | for (int i=0; islv_addr, contrast_regs[0][i], contrast_regs[level][i]); 519 | } 520 | 521 | return ret; 522 | } 523 | 524 | static int set_brightness(sensor_t *sensor, int level) 525 | { 526 | int ret=0; 527 | 528 | level += (NUM_BRIGHTNESS_LEVELS / 2 + 1); 529 | if (level < 0 || level > NUM_BRIGHTNESS_LEVELS) { 530 | return -1; 531 | } 532 | 533 | /* Switch to DSP register bank */ 534 | ret |= SCCB_Write(sensor->slv_addr, BANK_SEL, BANK_SEL_DSP); 535 | 536 | /* Write brightness registers */ 537 | for (int i=0; islv_addr, brightness_regs[0][i], brightness_regs[level][i]); 539 | } 540 | 541 | return ret; 542 | } 543 | 544 | static int set_saturation(sensor_t *sensor, int level) 545 | { 546 | int ret=0; 547 | 548 | level += (NUM_SATURATION_LEVELS / 2 + 1); 549 | if (level < 0 || level > NUM_SATURATION_LEVELS) { 550 | return -1; 551 | } 552 | 553 | /* Switch to DSP register bank */ 554 | ret |= SCCB_Write(sensor->slv_addr, BANK_SEL, BANK_SEL_DSP); 555 | 556 | /* Write contrast registers */ 557 | for (int i=0; islv_addr, saturation_regs[0][i], saturation_regs[level][i]); 559 | } 560 | 561 | return ret; 562 | } 563 | 564 | static int set_gainceiling(sensor_t *sensor, gainceiling_t gainceiling) 565 | { 566 | int ret =0; 567 | 568 | /* Switch to SENSOR register bank */ 569 | ret |= SCCB_Write(sensor->slv_addr, BANK_SEL, BANK_SEL_SENSOR); 570 | 571 | /* Write gain ceiling register */ 572 | ret |= SCCB_Write(sensor->slv_addr, COM9, COM9_AGC_SET(gainceiling)); 573 | 574 | return ret; 575 | } 576 | 577 | static int set_quality(sensor_t *sensor, int qs) 578 | { 579 | int ret=0; 580 | 581 | /* Switch to DSP register bank */ 582 | ret |= SCCB_Write(sensor->slv_addr, BANK_SEL, BANK_SEL_DSP); 583 | 584 | /* Write QS register */ 585 | ret |= SCCB_Write(sensor->slv_addr, QS, qs); 586 | 587 | return ret; 588 | } 589 | 590 | static int set_colorbar(sensor_t *sensor, int enable) 591 | { 592 | int ret=0; 593 | uint8_t reg; 594 | 595 | /* Switch to SENSOR register bank */ 596 | ret |= SCCB_Write(sensor->slv_addr, BANK_SEL, BANK_SEL_SENSOR); 597 | 598 | /* Update COM7 */ 599 | reg = SCCB_Read(sensor->slv_addr, COM7); 600 | 601 | if (enable) { 602 | reg |= COM7_COLOR_BAR; 603 | } else { 604 | reg &= ~COM7_COLOR_BAR; 605 | } 606 | 607 | ret |= SCCB_Write(sensor->slv_addr, COM7, reg); 608 | return ret; 609 | } 610 | 611 | static int set_whitebal(sensor_t *sensor, int enable) 612 | { 613 | int ret=0; 614 | uint8_t reg; 615 | 616 | /* Switch to SENSOR register bank */ 617 | ret |= SCCB_Write(sensor->slv_addr, BANK_SEL, BANK_SEL_DSP); 618 | 619 | /* Update CTRL1 */ 620 | reg = SCCB_Read(sensor->slv_addr, CTRL1); 621 | 622 | if (enable) { 623 | reg |= CTRL1_AWB; 624 | } else { 625 | reg &= ~CTRL1_AWB; 626 | } 627 | 628 | ret |= SCCB_Write(sensor->slv_addr, CTRL1, reg); 629 | return ret; 630 | } 631 | 632 | static int set_gain_ctrl(sensor_t *sensor, int enable) 633 | { 634 | int ret=0; 635 | uint8_t reg; 636 | 637 | /* Switch to SENSOR register bank */ 638 | ret |= SCCB_Write(sensor->slv_addr, BANK_SEL, BANK_SEL_SENSOR); 639 | 640 | /* Update COM8 */ 641 | reg = SCCB_Read(sensor->slv_addr, COM8); 642 | 643 | if (enable) { 644 | reg |= COM8_AGC_EN; 645 | } else { 646 | reg &= ~COM8_AGC_EN; 647 | } 648 | 649 | ret |= SCCB_Write(sensor->slv_addr, COM8, reg); 650 | return ret; 651 | } 652 | 653 | static int set_exposure_ctrl(sensor_t *sensor, int enable) 654 | { 655 | int ret=0; 656 | uint8_t reg; 657 | 658 | /* Switch to SENSOR register bank */ 659 | ret |= SCCB_Write(sensor->slv_addr, BANK_SEL, BANK_SEL_SENSOR); 660 | 661 | /* Update COM8 */ 662 | reg = SCCB_Read(sensor->slv_addr, COM8); 663 | 664 | if (enable) { 665 | reg |= COM8_AEC_EN; 666 | } else { 667 | reg &= ~COM8_AEC_EN; 668 | } 669 | 670 | ret |= SCCB_Write(sensor->slv_addr, COM8, reg); 671 | return ret; 672 | } 673 | 674 | static int set_hmirror(sensor_t *sensor, int enable) 675 | { 676 | int ret=0; 677 | uint8_t reg; 678 | 679 | /* Switch to SENSOR register bank */ 680 | ret |= SCCB_Write(sensor->slv_addr, BANK_SEL, BANK_SEL_SENSOR); 681 | 682 | /* Update REG04 */ 683 | reg = SCCB_Read(sensor->slv_addr, REG04); 684 | 685 | if (enable) { 686 | reg |= REG04_HFLIP_IMG; 687 | } else { 688 | reg &= ~REG04_HFLIP_IMG; 689 | } 690 | 691 | ret |= SCCB_Write(sensor->slv_addr, REG04, reg); 692 | return ret; 693 | } 694 | 695 | static int set_vflip(sensor_t *sensor, int enable) 696 | { 697 | int ret=0; 698 | uint8_t reg; 699 | 700 | /* Switch to SENSOR register bank */ 701 | ret |= SCCB_Write(sensor->slv_addr, BANK_SEL, BANK_SEL_SENSOR); 702 | 703 | /* Update REG04 */ 704 | reg = SCCB_Read(sensor->slv_addr, REG04); 705 | 706 | if (enable) { 707 | reg |= REG04_VFLIP_IMG; 708 | } else { 709 | reg &= ~REG04_VFLIP_IMG; 710 | } 711 | 712 | ret |= SCCB_Write(sensor->slv_addr, REG04, reg); 713 | return ret; 714 | } 715 | 716 | int ov2640_init(sensor_t *sensor) 717 | { 718 | /* set function pointers */ 719 | sensor->reset = reset; 720 | sensor->set_pixformat = set_pixformat; 721 | sensor->set_framesize = set_framesize; 722 | sensor->set_framerate = set_framerate; 723 | sensor->set_contrast = set_contrast; 724 | sensor->set_brightness= set_brightness; 725 | sensor->set_saturation= set_saturation; 726 | sensor->set_gainceiling = set_gainceiling; 727 | sensor->set_quality = set_quality; 728 | sensor->set_colorbar = set_colorbar; 729 | sensor->set_gain_ctrl = set_gain_ctrl; 730 | sensor->set_exposure_ctrl = set_exposure_ctrl; 731 | sensor->set_whitebal = set_whitebal; 732 | sensor->set_hmirror = set_hmirror; 733 | sensor->set_vflip = set_vflip; 734 | 735 | // Set sensor flags 736 | SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_VSYNC, 1); 737 | SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_HSYNC, 0); 738 | SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_PIXCK, 1); 739 | SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_FSYNC, 1); 740 | SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_JPEGE, 0); 741 | 742 | return 0; 743 | } 744 | -------------------------------------------------------------------------------- /src/ov264_drive.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the OpenMV project. 3 | * Copyright (c) 2013/2014 Ibrahim Abdelkader 4 | * This work is licensed under the MIT license, see the file LICENSE for details. 5 | * 6 | * OV2640 driver. 7 | * 8 | */ 9 | #ifndef __OV2640_DRIVE_H__ 10 | #define __OV2640_DRIVE_H__ 11 | #include "sensor.h" 12 | int ov2640_init(sensor_t *sensor); 13 | #endif // __OV2640_DRIVE_H__ 14 | -------------------------------------------------------------------------------- /src/ov7725.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the OpenMV project. 3 | * Copyright (c) 2013/2014 Ibrahim Abdelkader 4 | * This work is licensed under the MIT license, see the file LICENSE for details. 5 | * 6 | * OV7725 driver. 7 | * 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include "sccb.h" 13 | #include "ov7725.h" 14 | #include "ov7725_regs.h" 15 | #include 16 | 17 | static const uint8_t default_regs[][2] = { 18 | {COM3, COM3_SWAP_YUV}, 19 | {COM7, COM7_RES_QVGA | COM7_FMT_YUV}, 20 | 21 | {COM4, 0x01}, /* bypass PLL */ 22 | {CLKRC, 0xC0}, /* Res/Bypass pre-scalar */ 23 | 24 | // QVGA Window Size 25 | {HSTART, 0x3F}, 26 | {HSIZE, 0x50}, 27 | {VSTART, 0x03}, 28 | {VSIZE, 0x78}, 29 | {HREF, 0x00}, 30 | 31 | // Scale down to QVGA Resolution 32 | {HOUTSIZE, 0x50}, 33 | {VOUTSIZE, 0x78}, 34 | 35 | {COM12, 0x03}, 36 | {EXHCH, 0x00}, 37 | {TGT_B, 0x7F}, 38 | {FIXGAIN, 0x09}, 39 | {AWB_CTRL0, 0xE0}, 40 | {DSP_CTRL1, 0xFF}, 41 | 42 | {DSP_CTRL2, DSP_CTRL2_VDCW_EN | DSP_CTRL2_HDCW_EN | DSP_CTRL2_HZOOM_EN | DSP_CTRL2_VZOOM_EN}, 43 | 44 | {DSP_CTRL3, 0x00}, 45 | {DSP_CTRL4, 0x00}, 46 | {DSPAUTO, 0xFF}, 47 | 48 | {COM8, 0xF0}, 49 | {COM6, 0xC5}, 50 | {COM9, 0x11}, 51 | {COM10, COM10_VSYNC_NEG | COM10_PCLK_MASK}, //Invert VSYNC and MASK PCLK 52 | {BDBASE, 0x7F}, 53 | {DBSTEP, 0x03}, 54 | {AEW, 0x96}, 55 | {AEB, 0x64}, 56 | {VPT, 0xA1}, 57 | {EXHCL, 0x00}, 58 | {AWB_CTRL3, 0xAA}, 59 | {COM8, 0xFF}, 60 | 61 | //Gamma 62 | {GAM1, 0x0C}, 63 | {GAM2, 0x16}, 64 | {GAM3, 0x2A}, 65 | {GAM4, 0x4E}, 66 | {GAM5, 0x61}, 67 | {GAM6, 0x6F}, 68 | {GAM7, 0x7B}, 69 | {GAM8, 0x86}, 70 | {GAM9, 0x8E}, 71 | {GAM10, 0x97}, 72 | {GAM11, 0xA4}, 73 | {GAM12, 0xAF}, 74 | {GAM13, 0xC5}, 75 | {GAM14, 0xD7}, 76 | {GAM15, 0xE8}, 77 | 78 | {SLOP, 0x20}, 79 | {EDGE1, 0x05}, 80 | {EDGE2, 0x03}, 81 | {EDGE3, 0x00}, 82 | {DNSOFF, 0x01}, 83 | 84 | {MTX1, 0xB0}, 85 | {MTX2, 0x9D}, 86 | {MTX3, 0x13}, 87 | {MTX4, 0x16}, 88 | {MTX5, 0x7B}, 89 | {MTX6, 0x91}, 90 | {MTX_CTRL, 0x1E}, 91 | 92 | {BRIGHTNESS, 0x08}, 93 | {CONTRAST, 0x30}, 94 | {UVADJ0, 0x81}, 95 | {SDE, (SDE_CONT_BRIGHT_EN | SDE_SATURATION_EN)}, 96 | 97 | // For 30 fps/60Hz 98 | {DM_LNL, 0x00}, 99 | {DM_LNH, 0x00}, 100 | {BDBASE, 0x7F}, 101 | {DBSTEP, 0x03}, 102 | 103 | // Lens Correction, should be tuned with real camera module 104 | {LC_RADI, 0x10}, 105 | {LC_COEF, 0x10}, 106 | {LC_COEFB, 0x14}, 107 | {LC_COEFR, 0x17}, 108 | {LC_CTR, 0x05}, 109 | {COM5, 0xF5}, //0x65 110 | 111 | {0x00, 0x00}, 112 | }; 113 | 114 | 115 | static int reset(sensor_t *sensor) 116 | { 117 | int i=0; 118 | const uint8_t (*regs)[2]; 119 | 120 | // Reset all registers 121 | SCCB_Write(sensor->slv_addr, COM7, COM7_RESET); 122 | 123 | // Delay 10 ms 124 | systick_sleep(10); 125 | 126 | // Write default regsiters 127 | for (i=0, regs = default_regs; regs[i][0]; i++) { 128 | SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]); 129 | } 130 | 131 | // Delay 132 | systick_sleep(30); 133 | 134 | return 0; 135 | } 136 | 137 | 138 | static int set_pixformat(sensor_t *sensor, pixformat_t pixformat) 139 | { 140 | int ret=0; 141 | // Read register COM7 142 | uint8_t reg = SCCB_Read(sensor->slv_addr, COM7); 143 | 144 | switch (pixformat) { 145 | case PIXFORMAT_RGB565: 146 | reg = COM7_SET_FMT(reg, COM7_FMT_RGB) | COM7_FMT_RGB565; 147 | break; 148 | case PIXFORMAT_YUV422: 149 | case PIXFORMAT_GRAYSCALE: 150 | reg = COM7_SET_FMT(reg, COM7_FMT_YUV); 151 | break; 152 | default: 153 | return -1; 154 | } 155 | 156 | // Write back register COM7 157 | ret = SCCB_Write(sensor->slv_addr, COM7, reg); 158 | 159 | // Delay 160 | systick_sleep(30); 161 | 162 | return ret; 163 | } 164 | 165 | static int set_framesize(sensor_t *sensor, framesize_t framesize) 166 | { 167 | int ret=0; 168 | uint16_t w = resolution[framesize][0]; 169 | uint16_t h = resolution[framesize][1]; 170 | 171 | // Write MSBs 172 | ret |= SCCB_Write(sensor->slv_addr, HOUTSIZE, w>>2); 173 | ret |= SCCB_Write(sensor->slv_addr, VOUTSIZE, h>>1); 174 | 175 | // Write LSBs 176 | ret |= SCCB_Write(sensor->slv_addr, EXHCH, ((w&0x3) | ((h&0x1) << 2))); 177 | 178 | if (framesize < FRAMESIZE_VGA) { 179 | // Enable auto-scaling/zooming factors 180 | ret |= SCCB_Write(sensor->slv_addr, DSPAUTO, 0xFF); 181 | } else { 182 | // Disable auto-scaling/zooming factors 183 | ret |= SCCB_Write(sensor->slv_addr, DSPAUTO, 0xF3); 184 | 185 | // Clear auto-scaling/zooming factors 186 | ret |= SCCB_Write(sensor->slv_addr, SCAL0, 0x00); 187 | ret |= SCCB_Write(sensor->slv_addr, SCAL1, 0x00); 188 | ret |= SCCB_Write(sensor->slv_addr, SCAL2, 0x00); 189 | } 190 | 191 | // Delay 192 | systick_sleep(30); 193 | 194 | if (ret == 0) { 195 | sensor->framesize = framesize; 196 | } 197 | 198 | return ret; 199 | } 200 | 201 | static int set_colorbar(sensor_t *sensor, int enable) 202 | { 203 | int ret=0; 204 | uint8_t reg; 205 | 206 | // Read reg COM3 207 | reg = SCCB_Read(sensor->slv_addr, COM3); 208 | // Enable colorbar test pattern output 209 | reg = COM3_SET_CBAR(reg, enable); 210 | // Write back COM3 211 | ret |= SCCB_Write(sensor->slv_addr, COM3, reg); 212 | 213 | // Read reg DSP_CTRL3 214 | reg = SCCB_Read(sensor->slv_addr, DSP_CTRL3); 215 | // Enable DSP colorbar output 216 | reg = DSP_CTRL3_SET_CBAR(reg, enable); 217 | // Write back DSP_CTRL3 218 | ret |= SCCB_Write(sensor->slv_addr, DSP_CTRL3, reg); 219 | 220 | return ret; 221 | } 222 | 223 | static int set_whitebal(sensor_t *sensor, int enable) 224 | { 225 | // Read register COM8 226 | uint8_t reg = SCCB_Read(sensor->slv_addr, COM8); 227 | 228 | // Set white bal on/off 229 | reg = COM8_SET_AWB(reg, enable); 230 | 231 | // Write back register COM8 232 | return SCCB_Write(sensor->slv_addr, COM8, reg); 233 | } 234 | 235 | static int set_gain_ctrl(sensor_t *sensor, int enable) 236 | { 237 | // Read register COM8 238 | uint8_t reg = SCCB_Read(sensor->slv_addr, COM8); 239 | 240 | // Set white bal on/off 241 | reg = COM8_SET_AGC(reg, enable); 242 | 243 | // Write back register COM8 244 | return SCCB_Write(sensor->slv_addr, COM8, reg); 245 | } 246 | 247 | static int set_exposure_ctrl(sensor_t *sensor, int enable) 248 | { 249 | // Read register COM8 250 | uint8_t reg = SCCB_Read(sensor->slv_addr, COM8); 251 | 252 | // Set white bal on/off 253 | reg = COM8_SET_AEC(reg, enable); 254 | 255 | // Write back register COM8 256 | return SCCB_Write(sensor->slv_addr, COM8, reg); 257 | } 258 | 259 | static int set_hmirror(sensor_t *sensor, int enable) 260 | { 261 | // Read register COM3 262 | uint8_t reg = SCCB_Read(sensor->slv_addr, COM3); 263 | 264 | // Set mirror on/off 265 | reg = COM3_SET_MIRROR(reg, enable); 266 | 267 | // Write back register COM3 268 | return SCCB_Write(sensor->slv_addr, COM3, reg); 269 | } 270 | 271 | static int set_vflip(sensor_t *sensor, int enable) 272 | { 273 | // Read register COM3 274 | uint8_t reg = SCCB_Read(sensor->slv_addr, COM3); 275 | 276 | // Set mirror on/off 277 | reg = COM3_SET_FLIP(reg, enable); 278 | 279 | // Write back register COM3 280 | return SCCB_Write(sensor->slv_addr, COM3, reg); 281 | } 282 | 283 | int ov7725_init(sensor_t *sensor) 284 | { 285 | // Set function pointers 286 | sensor->reset = reset; 287 | sensor->set_pixformat = set_pixformat; 288 | sensor->set_framesize = set_framesize; 289 | sensor->set_colorbar = set_colorbar; 290 | sensor->set_whitebal = set_whitebal; 291 | sensor->set_gain_ctrl = set_gain_ctrl; 292 | sensor->set_exposure_ctrl = set_exposure_ctrl; 293 | sensor->set_hmirror = set_hmirror; 294 | sensor->set_vflip = set_vflip; 295 | 296 | // Retrieve sensor's signature 297 | sensor->id.MIDH = SCCB_Read(sensor->slv_addr, REG_MIDH); 298 | sensor->id.MIDL = SCCB_Read(sensor->slv_addr, REG_MIDL); 299 | sensor->id.PID = SCCB_Read(sensor->slv_addr, REG_PID); 300 | sensor->id.VER = SCCB_Read(sensor->slv_addr, REG_VER); 301 | 302 | // Set sensor flags 303 | SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_VSYNC, 1); 304 | SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_HSYNC, 0); 305 | SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_PIXCK, 1); 306 | SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_FSYNC, 1); 307 | SENSOR_HW_FLAGS_SET(sensor, SENSOR_HW_FLAGS_JPEGE, 0); 308 | 309 | return 0; 310 | } 311 | -------------------------------------------------------------------------------- /src/ov7725.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the OpenMV project. 3 | * Copyright (c) 2013/2014 Ibrahim Abdelkader 4 | * This work is licensed under the MIT license, see the file LICENSE for details. 5 | * 6 | * OV7725 driver. 7 | * 8 | */ 9 | #ifndef __OV7725_H__ 10 | #define __OV7725_H__ 11 | #include "sensor.h" 12 | 13 | int ov7725_init(sensor_t *sensor); 14 | #endif // __OV7725_H__ 15 | -------------------------------------------------------------------------------- /src/ov7725_regs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the OpenMV project. 3 | * Copyright (c) 2013/2014 Ibrahim Abdelkader 4 | * This work is licensed under the MIT license, see the file LICENSE for details. 5 | * 6 | * OV2640 register definitions. 7 | */ 8 | #ifndef __REG_REGS_H__ 9 | #define __REG_REGS_H__ 10 | #define GAIN 0x00 /* AGC – Gain control gain setting */ 11 | #define BLUE 0x01 /* AWB – Blue channel gain setting */ 12 | #define RED 0x02 /* AWB – Red channel gain setting */ 13 | #define GREEN 0x03 /* AWB – Green channel gain setting */ 14 | #define BAVG 0x05 /* U/B Average Level */ 15 | #define GAVG 0x06 /* Y/Gb Average Level */ 16 | #define RAVG 0x07 /* V/R Average Level */ 17 | #define AECH 0x08 /* Exposure Value – AEC MSBs */ 18 | 19 | #define COM2 0x09 /* Common Control 2 */ 20 | #define COM2_SOFT_SLEEP 0x10 /* Soft sleep mode */ 21 | #define COM2_OUT_DRIVE_1x 0x00 /* Output drive capability 1x */ 22 | #define COM2_OUT_DRIVE_2x 0x01 /* Output drive capability 2x */ 23 | #define COM2_OUT_DRIVE_3x 0x02 /* Output drive capability 3x */ 24 | #define COM2_OUT_DRIVE_4x 0x03 /* Output drive capability 4x */ 25 | 26 | #define REG_PID 0x0A /* Product ID Number MSB */ 27 | #define REG_VER 0x0B /* Product ID Number LSB */ 28 | 29 | #define COM3 0x0C /* Common Control 3 */ 30 | #define COM3_VFLIP 0x80 /* Vertical flip image ON/OFF selection */ 31 | #define COM3_MIRROR 0x40 /* Horizontal mirror image ON/OFF selection */ 32 | #define COM3_SWAP_BR 0x20 /* Swap B/R output sequence in RGB output mode */ 33 | #define COM3_SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV output mode */ 34 | #define COM3_SWAP_MSB 0x08 /* Swap output MSB/LSB */ 35 | #define COM3_TRI_CLOCK 0x04 /* Tri-state option for output clock at power-down period */ 36 | #define COM3_TRI_DATA 0x02 /* Tri-state option for output data at power-down period */ 37 | #define COM3_COLOR_BAR 0x01 /* Sensor color bar test pattern output enable */ 38 | #define COM3_SET_CBAR(r, x) ((r&0xFE)|((x&1)<<0)) 39 | #define COM3_SET_MIRROR(r, x) ((r&0xBF)|((x&1)<<6)) 40 | #define COM3_SET_FLIP(r, x) ((r&0x7F)|((x&1)<<7)) 41 | 42 | #define COM4 0x0D /* Common Control 4 */ 43 | #define COM4_PLL_BYPASS 0x00 /* Bypass PLL */ 44 | #define COM4_PLL_4x 0x40 /* PLL frequency 4x */ 45 | #define COM4_PLL_6x 0x80 /* PLL frequency 6x */ 46 | #define COM4_PLL_8x 0xc0 /* PLL frequency 8x */ 47 | #define COM4_AEC_FULL 0x00 /* AEC evaluate full window */ 48 | #define COM4_AEC_1_2 0x10 /* AEC evaluate 1/2 window */ 49 | #define COM4_AEC_1_4 0x20 /* AEC evaluate 1/4 window */ 50 | #define COM4_AEC_2_3 0x30 /* AEC evaluate 2/3 window */ 51 | 52 | #define COM5 0x0E /* Common Control 5 */ 53 | #define COM5_AFR 0x80 /* Auto frame rate control ON/OFF selection (night mode) */ 54 | #define COM5_AFR_SPEED 0x40 /* Auto frame rate control speed selection */ 55 | #define COM5_AFR_0 0x00 /* No reduction of frame rate */ 56 | #define COM5_AFR_1_2 0x10 /* Max reduction to 1/2 frame rate */ 57 | #define COM5_AFR_1_4 0x20 /* Max reduction to 1/4 frame rate */ 58 | #define COM5_AFR_1_8 0x30 /* Max reduction to 1/8 frame rate */ 59 | #define COM5_AFR_4x 0x04 /* Add frame when AGC reaches 4x gain */ 60 | #define COM5_AFR_8x 0x08 /* Add frame when AGC reaches 8x gain */ 61 | #define COM5_AFR_16x 0x0c /* Add frame when AGC reaches 16x gain */ 62 | #define COM5_AEC_NO_LIMIT 0x01 /* No limit to AEC increase step */ 63 | 64 | #define COM6 0x0F /* Common Control 6 */ 65 | #define COM6_AUTO_WINDOW 0x01 /* Auto window setting ON/OFF selection when format changes */ 66 | 67 | #define AEC 0x10 /* AEC[7:0] (see register AECH for AEC[15:8]) */ 68 | #define CLKRC 0x11 /* Internal Clock */ 69 | 70 | #define COM7 0x12 /* Common Control 7 */ 71 | #define COM7_RESET 0x80 /* SCCB Register Reset */ 72 | #define COM7_RES_VGA 0x00 /* Resolution VGA */ 73 | #define COM7_RES_QVGA 0x40 /* Resolution QVGA */ 74 | #define COM7_BT656 0x20 /* BT.656 protocol ON/OFF */ 75 | #define COM7_SENSOR_RAW 0x10 /* Sensor RAW */ 76 | #define COM7_FMT_GBR422 0x00 /* RGB output format GBR422 */ 77 | #define COM7_FMT_RGB565 0x04 /* RGB output format RGB565 */ 78 | #define COM7_FMT_RGB555 0x08 /* RGB output format RGB555 */ 79 | #define COM7_FMT_RGB444 0x0C /* RGB output format RGB444 */ 80 | #define COM7_FMT_YUV 0x00 /* Output format YUV */ 81 | #define COM7_FMT_P_BAYER 0x01 /* Output format Processed Bayer RAW */ 82 | #define COM7_FMT_RGB 0x02 /* Output format RGB */ 83 | #define COM7_FMT_R_BAYER 0x03 /* Output format Bayer RAW */ 84 | #define COM7_SET_FMT(r, x) ((r&0xFC)|((x&0x3)<<0)) 85 | 86 | #define COM8 0x13 /* Common Control 8 */ 87 | #define COM8_FAST_AUTO 0x80 /* Enable fast AGC/AEC algorithm */ 88 | #define COM8_STEP_VSYNC 0x00 /* AEC - Step size limited to vertical blank */ 89 | #define COM8_STEP_UNLIMIT 0x40 /* AEC - Step size unlimited step size */ 90 | #define COM8_BANDF_EN 0x20 /* Banding filter ON/OFF */ 91 | #define COM8_AEC_BANDF 0x10 /* Enable AEC below banding value */ 92 | #define COM8_AEC_FINE_EN 0x08 /* Fine AEC ON/OFF control */ 93 | #define COM8_AGC_EN 0x04 /* AGC Enable */ 94 | #define COM8_AWB_EN 0x02 /* AWB Enable */ 95 | #define COM8_AEC_EN 0x01 /* AEC Enable */ 96 | #define COM8_SET_AGC(r, x) ((r&0xFB)|((x&0x1)<<2)) 97 | #define COM8_SET_AWB(r, x) ((r&0xFD)|((x&0x1)<<1)) 98 | #define COM8_SET_AEC(r, x) ((r&0xFE)|((x&0x1)<<0)) 99 | 100 | #define COM9 0x14 /* Common Control 9 */ 101 | #define COM9_HISTO_AVG 0x80 /* Histogram or average based AEC/AGC selection */ 102 | #define COM9_AGC_GAIN_2x 0x00 /* Automatic Gain Ceiling 2x */ 103 | #define COM9_AGC_GAIN_4x 0x10 /* Automatic Gain Ceiling 4x */ 104 | #define COM9_AGC_GAIN_8x 0x20 /* Automatic Gain Ceiling 8x */ 105 | #define COM9_AGC_GAIN_16x 0x30 /* Automatic Gain Ceiling 16x */ 106 | #define COM9_AGC_GAIN_32x 0x40 /* Automatic Gain Ceiling 32x */ 107 | #define COM9_DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */ 108 | #define COM9_DROP_HREF 0x02 /* Drop HREF output of corrupt frame */ 109 | #define COM9_SET_AGC(r, x) ((r&0x8F)|((x&0x07)<<4)) 110 | 111 | #define COM10 0x15 /* Common Control 10 */ 112 | #define COM10_NEGATIVE 0x80 /* Output negative data */ 113 | #define COM10_HSYNC_EN 0x40 /* HREF changes to HSYNC */ 114 | #define COM10_PCLK_FREE 0x00 /* PCLK output option: free running PCLK */ 115 | #define COM10_PCLK_MASK 0x20 /* PCLK output option: masked during horizontal blank */ 116 | #define COM10_PCLK_REV 0x10 /* PCLK reverse */ 117 | #define COM10_HREF_REV 0x08 /* HREF reverse */ 118 | #define COM10_VSYNC_FALLING 0x00 /* VSYNC changes on falling edge of PCLK */ 119 | #define COM10_VSYNC_RISING 0x04 /* VSYNC changes on rising edge of PCLK */ 120 | #define COM10_VSYNC_NEG 0x02 /* VSYNC negative */ 121 | #define COM10_OUT_RANGE_8 0x01 /* Output data range: Full range */ 122 | #define COM10_OUT_RANGE_10 0x00 /* Output data range: Data from [10] to [F0] (8 MSBs) */ 123 | 124 | #define REG16 0x16 /* Register 16 */ 125 | #define REG16_BIT_SHIFT 0x80 /* Bit shift test pattern options */ 126 | #define HSTART 0x17 /* Horizontal Frame (HREF column) Start 8 MSBs (2 LSBs are at HREF[5:4]) */ 127 | #define HSIZE 0x18 /* Horizontal Sensor Size (2 LSBs are at HREF[1:0]) */ 128 | #define VSTART 0x19 /* Vertical Frame (row) Start 8 MSBs (1 LSB is at HREF[6]) */ 129 | #define VSIZE 0x1A /* Vertical Sensor Size (1 LSB is at HREF[2]) */ 130 | #define PSHFT 0x1B /* Data Format - Pixel Delay Select */ 131 | #define REG_MIDH 0x1C /* Manufacturer ID Byte – High */ 132 | #define REG_MIDL 0x1D /* Manufacturer ID Byte – Low */ 133 | #define LAEC 0x1F /* Fine AEC Value - defines exposure value less than one row period */ 134 | 135 | #define COM11 0x20 /* Common Control 11 */ 136 | #define COM11_SNGL_FRAME_EN 0x02 /* Single frame ON/OFF selection */ 137 | #define COM11_SNGL_XFR_TRIG 0x01 /* Single frame transfer trigger */ 138 | 139 | #define BDBASE 0x22 /* Banding Filter Minimum AEC Value */ 140 | #define DBSTEP 0x23 /* Banding Filter Maximum Step */ 141 | #define AEW 0x24 /* AGC/AEC - Stable Operating Region (Upper Limit) */ 142 | #define AEB 0x25 /* AGC/AEC - Stable Operating Region (Lower Limit) */ 143 | #define VPT 0x26 /* AGC/AEC Fast Mode Operating Region */ 144 | #define REG28 0x28 /* Selection on the number of dummy rows, N */ 145 | #define HOUTSIZE 0x29 /* Horizontal Data Output Size MSBs (2 LSBs at register EXHCH[1:0]) */ 146 | #define EXHCH 0x2A /* Dummy Pixel Insert MSB */ 147 | #define EXHCL 0x2B /* Dummy Pixel Insert LSB */ 148 | #define VOUTSIZE 0x2C /* Vertical Data Output Size MSBs (LSB at register EXHCH[2]) */ 149 | #define ADVFL 0x2D /* LSB of Insert Dummy Rows in Vertical Sync (1 bit equals 1 row) */ 150 | #define ADVFH 0x2E /* MSB of Insert Dummy Rows in Vertical Sync */ 151 | #define YAVE 0x2F /* Y/G Channel Average Value */ 152 | #define LUMHTH 0x30 /* Histogram AEC/AGC Luminance High Level Threshold */ 153 | #define LUMLTH 0x31 /* Histogram AEC/AGC Luminance Low Level Threshold */ 154 | #define HREF 0x32 /* Image Start and Size Control */ 155 | #define DM_LNL 0x33 /* Dummy Row Low 8 Bits */ 156 | #define DM_LNH 0x34 /* Dummy Row High 8 Bits */ 157 | #define ADOFF_B 0x35 /* AD Offset Compensation Value for B Channel */ 158 | #define ADOFF_R 0x36 /* AD Offset Compensation Value for R Channel */ 159 | #define ADOFF_GB 0x37 /* AD Offset Compensation Value for GB Channel */ 160 | #define ADOFF_GR 0x38 /* AD Offset Compensation Value for GR Channel */ 161 | #define OFF_B 0x39 /* AD Offset Compensation Value for B Channel */ 162 | #define OFF_R 0x3A /* AD Offset Compensation Value for R Channel */ 163 | #define OFF_GB 0x3B /* AD Offset Compensation Value for GB Channel */ 164 | #define OFF_GR 0x3C /* AD Offset Compensation Value for GR Channel */ 165 | #define COM12 0x3D /* DC offset compensation for analog process */ 166 | 167 | #define COM13 0x3E /* Common Control 13 */ 168 | #define COM13_BLC_EN 0x80 /* BLC enable */ 169 | #define COM13_ADC_EN 0x40 /* ADC channel BLC ON/OFF control */ 170 | #define COM13_ANALOG_BLC 0x20 /* Analog processing channel BLC ON/OFF control */ 171 | #define COM13_ABLC_GAIN_EN 0x04 /* ABLC gain trigger enable */ 172 | 173 | #define COM14 0x3F /* Common Control 14 */ 174 | #define COM15 0x40 /* Common Control 15 */ 175 | #define COM16 0x41 /* Common Control 16 */ 176 | #define TGT_B 0x42 /* BLC Blue Channel Target Value */ 177 | #define TGT_R 0x43 /* BLC Red Channel Target Value */ 178 | #define TGT_GB 0x44 /* BLC Gb Channel Target Value */ 179 | #define TGT_GR 0x45 /* BLC Gr Channel Target Value */ 180 | 181 | #define LC_CTR 0x46 /* Lens Correction Control */ 182 | #define LC_CTR_RGB_COMP_1 0x00 /* R, G, and B channel compensation coefficient is set by LC_COEF (0x49) */ 183 | #define LC_CTR_RGB_COMP_3 0x04 /* R, G, and B channel compensation coefficient is set by registers 184 | LC_COEFB (0x4B), LC_COEF (0x49), and LC_COEFR (0x4C), respectively */ 185 | #define LC_CTR_EN 0x01 /* Lens correction enable */ 186 | #define LC_XC 0x47 /* X Coordinate of Lens Correction Center Relative to Array Center */ 187 | #define LC_YC 0x48 /* Y Coordinate of Lens Correction Center Relative to Array Center */ 188 | #define LC_COEF 0x49 /* Lens Correction Coefficient */ 189 | #define LC_RADI 0x4A /* Lens Correction Radius */ 190 | #define LC_COEFB 0x4B /* Lens Correction B Channel Compensation Coefficient */ 191 | #define LC_COEFR 0x4C /* Lens Correction R Channel Compensation Coefficient */ 192 | 193 | #define FIXGAIN 0x4D /* Analog Fix Gain Amplifier */ 194 | #define AREF0 0x4E /* Sensor Reference Control */ 195 | #define AREF1 0x4F /* Sensor Reference Current Control */ 196 | #define AREF2 0x50 /* Analog Reference Control */ 197 | #define AREF3 0x51 /* ADC Reference Control */ 198 | #define AREF4 0x52 /* ADC Reference Control */ 199 | #define AREF5 0x53 /* ADC Reference Control */ 200 | #define AREF6 0x54 /* Analog Reference Control */ 201 | #define AREF7 0x55 /* Analog Reference Control */ 202 | #define UFIX 0x60 /* U Channel Fixed Value Output */ 203 | #define VFIX 0x61 /* V Channel Fixed Value Output */ 204 | #define AWBB_BLK 0x62 /* AWB Option for Advanced AWB */ 205 | 206 | #define AWB_CTRL0 0x63 /* AWB Control Byte 0 */ 207 | #define AWB_CTRL0_GAIN_EN 0x80 /* AWB gain enable */ 208 | #define AWB_CTRL0_CALC_EN 0x40 /* AWB calculate enable */ 209 | #define AWB_CTRL0_WBC_MASK 0x0F /* WBC threshold 2 */ 210 | 211 | #define DSP_CTRL1 0x64 /* DSP Control Byte 1 */ 212 | #define DSP_CTRL1_FIFO_EN 0x80 /* FIFO enable/disable selection */ 213 | #define DSP_CTRL1_UV_EN 0x40 /* UV adjust function ON/OFF selection */ 214 | #define DSP_CTRL1_SDE_EN 0x20 /* SDE enable */ 215 | #define DSP_CTRL1_MTRX_EN 0x10 /* Color matrix ON/OFF selection */ 216 | #define DSP_CTRL1_INTRP_EN 0x08 /* Interpolation ON/OFF selection */ 217 | #define DSP_CTRL1_GAMMA_EN 0x04 /* Gamma function ON/OFF selection */ 218 | #define DSP_CTRL1_BLACK_EN 0x02 /* Black defect auto correction ON/OFF */ 219 | #define DSP_CTRL1_WHITE_EN 0x01 /* White defect auto correction ON/OFF */ 220 | 221 | #define DSP_CTRL2 0x65 /* DSP Control Byte 2 */ 222 | #define DSP_CTRL2_VDCW_EN 0x08 /* Vertical DCW enable */ 223 | #define DSP_CTRL2_HDCW_EN 0x04 /* Horizontal DCW enable */ 224 | #define DSP_CTRL2_VZOOM_EN 0x02 /* Vertical zoom out enable */ 225 | #define DSP_CTRL2_HZOOM_EN 0x01 /* Horizontal zoom out enable */ 226 | 227 | #define DSP_CTRL3 0x66 /* DSP Control Byte 3 */ 228 | #define DSP_CTRL3_UV_EN 0x80 /* UV output sequence option */ 229 | #define DSP_CTRL3_CBAR_EN 0x20 /* DSP color bar ON/OFF selection */ 230 | #define DSP_CTRL3_FIFO_EN 0x08 /* FIFO power down ON/OFF selection */ 231 | #define DSP_CTRL3_SCAL1_PWDN 0x04 /* Scaling module power down control 1 */ 232 | #define DSP_CTRL3_SCAL2_PWDN 0x02 /* Scaling module power down control 2 */ 233 | #define DSP_CTRL3_INTRP_PWDN 0x01 /* Interpolation module power down control */ 234 | #define DSP_CTRL3_SET_CBAR(r, x) ((r&0xDF)|((x&1)<<5)) 235 | 236 | 237 | #define DSP_CTRL4 0x67 /* DSP Control Byte 4 */ 238 | #define DSP_CTRL4_YUV_RGB 0x00 /* Output selection YUV or RGB */ 239 | #define DSP_CTRL4_RAW8 0x02 /* Output selection RAW8 */ 240 | #define DSP_CTRL4_RAW10 0x03 /* Output selection RAW10 */ 241 | 242 | 243 | #define AWB_BIAS 0x68 /* AWB BLC Level Clip */ 244 | #define AWB_CTRL1 0x69 /* AWB Control 1 */ 245 | #define AWB_CTRL2 0x6A /* AWB Control 2 */ 246 | 247 | #define AWB_CTRL3 0x6B /* AWB Control 3 */ 248 | #define AWB_CTRL3_ADVANCED 0x80 /* AWB mode select - Advanced AWB */ 249 | #define AWB_CTRL3_SIMPLE 0x00 /* AWB mode select - Simple AWB */ 250 | 251 | #define AWB_CTRL4 0x6C /* AWB Control 4 */ 252 | #define AWB_CTRL5 0x6D /* AWB Control 5 */ 253 | #define AWB_CTRL6 0x6E /* AWB Control 6 */ 254 | #define AWB_CTRL7 0x6F /* AWB Control 7 */ 255 | #define AWB_CTRL8 0x70 /* AWB Control 8 */ 256 | #define AWB_CTRL9 0x71 /* AWB Control 9 */ 257 | #define AWB_CTRL10 0x72 /* AWB Control 10 */ 258 | #define AWB_CTRL11 0x73 /* AWB Control 11 */ 259 | #define AWB_CTRL12 0x74 /* AWB Control 12 */ 260 | #define AWB_CTRL13 0x75 /* AWB Control 13 */ 261 | #define AWB_CTRL14 0x76 /* AWB Control 14 */ 262 | #define AWB_CTRL15 0x77 /* AWB Control 15 */ 263 | #define AWB_CTRL16 0x78 /* AWB Control 16 */ 264 | #define AWB_CTRL17 0x79 /* AWB Control 17 */ 265 | #define AWB_CTRL18 0x7A /* AWB Control 18 */ 266 | #define AWB_CTRL19 0x7B /* AWB Control 19 */ 267 | #define AWB_CTRL20 0x7C /* AWB Control 20 */ 268 | #define AWB_CTRL21 0x7D /* AWB Control 21 */ 269 | #define GAM1 0x7E /* Gamma Curve 1st Segment Input End Point 0x04 Output Value */ 270 | #define GAM2 0x7F /* Gamma Curve 2nd Segment Input End Point 0x08 Output Value */ 271 | #define GAM3 0x80 /* Gamma Curve 3rd Segment Input End Point 0x10 Output Value */ 272 | #define GAM4 0x81 /* Gamma Curve 4th Segment Input End Point 0x20 Output Value */ 273 | #define GAM5 0x82 /* Gamma Curve 5th Segment Input End Point 0x28 Output Value */ 274 | #define GAM6 0x83 /* Gamma Curve 6th Segment Input End Point 0x30 Output Value */ 275 | #define GAM7 0x84 /* Gamma Curve 7th Segment Input End Point 0x38 Output Value */ 276 | #define GAM8 0x85 /* Gamma Curve 8th Segment Input End Point 0x40 Output Value */ 277 | #define GAM9 0x86 /* Gamma Curve 9th Segment Input End Point 0x48 Output Value */ 278 | #define GAM10 0x87 /* Gamma Curve 10th Segment Input End Point 0x50 Output Value */ 279 | #define GAM11 0x88 /* Gamma Curve 11th Segment Input End Point 0x60 Output Value */ 280 | #define GAM12 0x89 /* Gamma Curve 12th Segment Input End Point 0x70 Output Value */ 281 | #define GAM13 0x8A /* Gamma Curve 13th Segment Input End Point 0x90 Output Value */ 282 | #define GAM14 0x8B /* Gamma Curve 14th Segment Input End Point 0xB0 Output Value */ 283 | #define GAM15 0x8C /* Gamma Curve 15th Segment Input End Point 0xD0 Output Value */ 284 | #define SLOP 0x8D /* Gamma Curve Highest Segment Slope */ 285 | #define DNSTH 0x8E /* De-noise Threshold */ 286 | #define EDGE0 0x8F /* Edge Enhancement Strength Control */ 287 | #define EDGE1 0x90 /* Edge Enhancement Threshold Control */ 288 | #define DNSOFF 0x91 /* Auto De-noise Threshold Control */ 289 | #define EDGE2 0x92 /* Edge Enhancement Strength Upper Limit */ 290 | #define EDGE3 0x93 /* Edge Enhancement Strength Upper Limit */ 291 | #define MTX1 0x94 /* Matrix Coefficient 1 */ 292 | #define MTX2 0x95 /* Matrix Coefficient 2 */ 293 | #define MTX3 0x96 /* Matrix Coefficient 3 */ 294 | #define MTX4 0x97 /* Matrix Coefficient 4 */ 295 | #define MTX5 0x98 /* Matrix Coefficient 5 */ 296 | #define MTX6 0x99 /* Matrix Coefficient 6 */ 297 | 298 | #define MTX_CTRL 0x9A /* Matrix Control */ 299 | #define MTX_CTRL_DBL_EN 0x80 /* Matrix double ON/OFF selection */ 300 | 301 | #define BRIGHTNESS 0x9B /* Brightness Control */ 302 | #define CONTRAST 0x9C /* Contrast Gain */ 303 | #define UVADJ0 0x9E /* Auto UV Adjust Control 0 */ 304 | #define UVADJ1 0x9F /* Auto UV Adjust Control 1 */ 305 | #define SCAL0 0xA0 /* DCW Ratio Control */ 306 | #define SCAL1 0xA1 /* Horizontal Zoom Out Control */ 307 | #define SCAL2 0xA2 /* Vertical Zoom Out Control */ 308 | #define FIFODLYM 0xA3 /* FIFO Manual Mode Delay Control */ 309 | #define FIFODLYA 0xA4 /* FIFO Auto Mode Delay Control */ 310 | 311 | #define SDE 0xA6 /* Special Digital Effect Control */ 312 | #define SDE_NEGATIVE_EN 0x40 /* Negative image enable */ 313 | #define SDE_GRAYSCALE_EN 0x20 /* Gray scale image enable */ 314 | #define SDE_V_FIXED_EN 0x10 /* V fixed value enable */ 315 | #define SDE_U_FIXED_EN 0x08 /* U fixed value enable */ 316 | #define SDE_CONT_BRIGHT_EN 0x04 /* Contrast/Brightness enable */ 317 | #define SDE_SATURATION_EN 0x02 /* Saturation enable */ 318 | #define SDE_HUE_EN 0x01 /* Hue enable */ 319 | 320 | #define USAT 0xA7 /* U Component Saturation Gain */ 321 | #define VSAT 0xA8 /* V Component Saturation Gain */ 322 | #define HUECOS 0xA9 /* Cosine value × 0x80 */ 323 | #define HUESIN 0xAA /* Sine value × 0x80 */ 324 | #define SIGN_BIT 0xAB /* Sign Bit for Hue and Brightness */ 325 | 326 | #define DSPAUTO 0xAC /* DSP Auto Function ON/OFF Control */ 327 | #define DSPAUTO_AWB_EN 0x80 /* AWB auto threshold control */ 328 | #define DSPAUTO_DENOISE_EN 0x40 /* De-noise auto threshold control */ 329 | #define DSPAUTO_EDGE_EN 0x20 /* Sharpness (edge enhancement) auto strength control */ 330 | #define DSPAUTO_UV_EN 0x10 /* UV adjust auto slope control */ 331 | #define DSPAUTO_SCAL0_EN 0x08 /* Auto scaling factor control (register SCAL0 (0xA0)) */ 332 | #define DSPAUTO_SCAL1_EN 0x04 /* Auto scaling factor control (registers SCAL1 (0xA1 and SCAL2 (0xA2))*/ 333 | #define SET_REG(reg, x) (##reg_DEFAULT|x) 334 | #endif //__REG_REGS_H__ 335 | -------------------------------------------------------------------------------- /src/sccb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the OpenMV project. 3 | * Copyright (c) 2013/2014 Ibrahim Abdelkader 4 | * This work is licensed under the MIT license, see the file LICENSE for details. 5 | * 6 | * SCCB (I2C like) driver. 7 | * 8 | */ 9 | #include 10 | #include "sccb.h" 11 | #include "twi.h" 12 | #include 13 | 14 | #define SCCB_FREQ (100000) // We don't need fast I2C. 100KHz is fine here. 15 | #define TIMEOUT (1000) /* Can't be sure when I2C routines return. Interrupts 16 | while polling hardware may result in unknown delays. */ 17 | 18 | 19 | int SCCB_Init(int pin_sda, int pin_scl) 20 | { 21 | twi_init(pin_sda, pin_scl); 22 | return 0; 23 | } 24 | 25 | uint8_t SCCB_Probe() 26 | { 27 | 28 | uint8_t reg = 0x00; 29 | uint8_t slv_addr = 0x00; 30 | 31 | for (uint8_t i=0; i<127; i++) { 32 | if (twi_writeTo(i, ®, 1, true) == 0) { 33 | slv_addr = i; 34 | break; 35 | } 36 | 37 | if (i!=126) { 38 | systick_sleep(1); // Necessary for OV7725 camera (not for OV2640). 39 | } 40 | } 41 | return slv_addr; 42 | } 43 | 44 | uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg) 45 | { 46 | uint8_t data=0; 47 | 48 | __disable_irq(); 49 | int rc = twi_writeTo(slv_addr, ®, 1, true); 50 | if (rc != 0) { 51 | data = 0xff; 52 | } 53 | else { 54 | rc = twi_readFrom(slv_addr, &data, 1, true); 55 | if (rc != 0) { 56 | data=0xFF; 57 | } 58 | } 59 | __enable_irq(); 60 | if (rc != 0) { 61 | printf("SCCB_Read [%02x] failed rc=%d\n", reg, rc); 62 | } 63 | return data; 64 | } 65 | 66 | uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data) 67 | { 68 | uint8_t ret=0; 69 | uint8_t buf[] = {reg, data}; 70 | 71 | __disable_irq(); 72 | if(twi_writeTo(slv_addr, buf, 2, true) != 0) { 73 | ret=0xFF; 74 | } 75 | __enable_irq(); 76 | if (ret != 0) { 77 | printf("SCCB_Write [%02x]=%02x failed\n", reg, data); 78 | } 79 | return ret; 80 | } 81 | -------------------------------------------------------------------------------- /src/sccb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the OpenMV project. 3 | * Copyright (c) 2013/2014 Ibrahim Abdelkader 4 | * This work is licensed under the MIT license, see the file LICENSE for details. 5 | * 6 | * SCCB (I2C like) driver. 7 | * 8 | */ 9 | #ifndef __SCCB_H__ 10 | #define __SCCB_H__ 11 | #include 12 | int SCCB_Init(int pin_sda, int pin_scl); 13 | uint8_t SCCB_Probe(); 14 | uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg); 15 | uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data); 16 | #endif // __SCCB_H__ 17 | -------------------------------------------------------------------------------- /src/sensor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the OpenMV project. 3 | * Copyright (c) 2013/2014 Ibrahim Abdelkader 4 | * This work is licensed under the MIT license, see the file LICENSE for details. 5 | * 6 | * Sensor abstraction layer. 7 | * 8 | */ 9 | #ifndef __SENSOR_H__ 10 | #define __SENSOR_H__ 11 | #include 12 | 13 | #define OV9650_PID (0x96) 14 | #define OV2640_PID (0x26) 15 | #define OV7725_PID (0x77) 16 | 17 | 18 | typedef struct { 19 | uint8_t MIDH; 20 | uint8_t MIDL; 21 | uint8_t PID; 22 | uint8_t VER; 23 | } sensor_id_t; 24 | 25 | typedef enum { 26 | PIXFORMAT_RGB565, // 2BPP/RGB565 27 | PIXFORMAT_YUV422, // 2BPP/YUV422 28 | PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE 29 | PIXFORMAT_JPEG, // JPEG/COMPRESSED 30 | } pixformat_t; 31 | 32 | typedef enum { 33 | FRAMESIZE_40x30, // 40x30 34 | FRAMESIZE_64x32, // 64x32 35 | FRAMESIZE_64x64, // 64x64 36 | FRAMESIZE_QQCIF, // 88x72 37 | FRAMESIZE_QQVGA, // 160x120 38 | FRAMESIZE_QQVGA2, // 128x160 39 | FRAMESIZE_QCIF, // 176x144 40 | FRAMESIZE_HQVGA, // 220x160 41 | FRAMESIZE_QVGA, // 320x240 42 | FRAMESIZE_CIF, // 352x288 43 | FRAMESIZE_VGA, // 640x480 44 | FRAMESIZE_SVGA, // 800x600 45 | FRAMESIZE_SXGA, // 1280x1024 46 | FRAMESIZE_UXGA, // 1600x1200 47 | } framesize_t; 48 | 49 | typedef enum { 50 | FRAMERATE_2FPS =0x9F, 51 | FRAMERATE_8FPS =0x87, 52 | FRAMERATE_15FPS=0x83, 53 | FRAMERATE_30FPS=0x81, 54 | FRAMERATE_60FPS=0x80, 55 | } framerate_t; 56 | 57 | typedef enum { 58 | GAINCEILING_2X, 59 | GAINCEILING_4X, 60 | GAINCEILING_8X, 61 | GAINCEILING_16X, 62 | GAINCEILING_32X, 63 | GAINCEILING_64X, 64 | GAINCEILING_128X, 65 | } gainceiling_t; 66 | 67 | typedef enum { 68 | SDE_NORMAL, 69 | SDE_NEGATIVE, 70 | } sde_t; 71 | 72 | typedef enum { 73 | ATTR_CONTRAST=0, 74 | ATTR_BRIGHTNESS, 75 | ATTR_SATURATION, 76 | ATTR_GAINCEILING, 77 | } sensor_attr_t; 78 | 79 | typedef enum { 80 | ACTIVE_LOW, 81 | ACTIVE_HIGH 82 | } reset_polarity_t; 83 | 84 | typedef void (*line_filter_t) (uint8_t *src, int src_stride, uint8_t *dst, int dst_stride, void *args); 85 | 86 | #define SENSOR_HW_FLAGS_VSYNC (0) // vertical sync polarity. 87 | #define SENSOR_HW_FLAGS_HSYNC (1) // horizontal sync polarity. 88 | #define SENSOR_HW_FLAGS_PIXCK (2) // pixel clock edge. 89 | #define SENSOR_HW_FLAGS_FSYNC (3) // hardware frame sync. 90 | #define SENSOR_HW_FLAGS_JPEGE (4) // hardware JPEG encoder. 91 | #define SENSOR_HW_FLAGS_GET(s, x) ((s)->hw_flags & (1<hw_flags |= (v<hw_flags &= ~(1< 22 | #include 23 | #include "twi.h" 24 | #include "soc/gpio_reg.h" 25 | #include 26 | #include "esp32-hal-gpio.h" 27 | 28 | unsigned char twi_dcount = 18; 29 | static unsigned char twi_sda, twi_scl; 30 | 31 | static inline void SDA_LOW() 32 | { 33 | //Enable SDA (becomes output and since GPO is 0 for the pin, 34 | // it will pull the line low) 35 | if (twi_sda < 32) 36 | { 37 | REG_WRITE(GPIO_ENABLE_W1TS_REG, BIT(twi_sda)); 38 | } 39 | else 40 | { 41 | REG_WRITE(GPIO_ENABLE1_W1TS_REG, BIT(twi_sda - 32)); 42 | } 43 | } 44 | 45 | static inline void SDA_HIGH() 46 | { 47 | //Disable SDA (becomes input and since it has pullup it will go high) 48 | if (twi_sda < 32) 49 | { 50 | REG_WRITE(GPIO_ENABLE_W1TC_REG, BIT(twi_sda)); 51 | } 52 | else 53 | { 54 | REG_WRITE(GPIO_ENABLE1_W1TC_REG, BIT(twi_sda - 32)); 55 | } 56 | } 57 | 58 | static inline uint32_t SDA_READ() 59 | { 60 | if (twi_sda < 32) 61 | { 62 | return (REG_READ(GPIO_IN_REG) & BIT(twi_sda)) != 0; 63 | } 64 | else 65 | { 66 | return (REG_READ(GPIO_IN1_REG) & BIT(twi_sda - 32)) != 0; 67 | } 68 | } 69 | 70 | static void SCL_LOW() 71 | { 72 | if (twi_scl < 32) 73 | { 74 | REG_WRITE(GPIO_ENABLE_W1TS_REG, BIT(twi_scl)); 75 | } 76 | else 77 | { 78 | REG_WRITE(GPIO_ENABLE1_W1TS_REG, BIT(twi_scl - 32)); 79 | } 80 | } 81 | 82 | static void SCL_HIGH() 83 | { 84 | if (twi_scl < 32) 85 | { 86 | REG_WRITE(GPIO_ENABLE_W1TC_REG, BIT(twi_scl)); 87 | } 88 | else 89 | { 90 | REG_WRITE(GPIO_ENABLE1_W1TC_REG, BIT(twi_scl - 32)); 91 | } 92 | } 93 | 94 | static uint32_t SCL_READ() 95 | { 96 | if (twi_scl < 32) 97 | { 98 | return (REG_READ(GPIO_IN_REG) & BIT(twi_scl)) != 0; 99 | } 100 | else 101 | { 102 | return (REG_READ(GPIO_IN1_REG) & BIT(twi_scl - 32)) != 0; 103 | } 104 | } 105 | 106 | #ifndef FCPU80 107 | #define FCPU80 80000000L 108 | #endif 109 | 110 | #if F_CPU == FCPU80 111 | #define TWI_CLOCK_STRETCH 800 112 | #else 113 | #define TWI_CLOCK_STRETCH 1600 114 | #endif 115 | 116 | void twi_setClock(unsigned int freq) 117 | { 118 | #if F_CPU == FCPU80 119 | if (freq <= 100000) 120 | twi_dcount = 19; //about 100KHz 121 | else if (freq <= 200000) 122 | twi_dcount = 8; //about 200KHz 123 | else if (freq <= 300000) 124 | twi_dcount = 3; //about 300KHz 125 | else if (freq <= 400000) 126 | twi_dcount = 1; //about 400KHz 127 | else 128 | twi_dcount = 1; //about 400KHz 129 | #else 130 | if (freq <= 100000) 131 | twi_dcount = 32; //about 100KHz 132 | else if (freq <= 200000) 133 | twi_dcount = 14; //about 200KHz 134 | else if (freq <= 300000) 135 | twi_dcount = 8; //about 300KHz 136 | else if (freq <= 400000) 137 | twi_dcount = 5; //about 400KHz 138 | else if (freq <= 500000) 139 | twi_dcount = 3; //about 500KHz 140 | else if (freq <= 600000) 141 | twi_dcount = 2; //about 600KHz 142 | else 143 | twi_dcount = 1; //about 700KHz 144 | #endif 145 | } 146 | 147 | void i2c_pinMode(int pin, int mode) 148 | { 149 | gpio_config_t conf = {0}; 150 | conf.pin_bit_mask = 1LL << pin; 151 | if (mode == OUTPUT) 152 | { 153 | conf.mode = GPIO_MODE_OUTPUT; 154 | } 155 | if (mode == INPUT || mode == INPUT_PULLUP) 156 | { 157 | conf.mode = GPIO_MODE_INPUT; 158 | } 159 | if (mode == INPUT) 160 | { 161 | conf.pull_down_en = GPIO_PULLDOWN_DISABLE; 162 | conf.pull_up_en = GPIO_PULLUP_DISABLE; 163 | } 164 | else if (mode == INPUT_PULLUP) 165 | { 166 | conf.pull_down_en = GPIO_PULLDOWN_DISABLE; 167 | conf.pull_up_en = GPIO_PULLUP_ENABLE; 168 | } 169 | gpio_config(&conf); 170 | } 171 | 172 | void twi_init(unsigned char sda, unsigned char scl) 173 | { 174 | twi_sda = sda; 175 | twi_scl = scl; 176 | i2c_pinMode(twi_sda, OUTPUT); 177 | i2c_pinMode(twi_scl, OUTPUT); 178 | 179 | gpio_set_level(twi_sda, 0); 180 | gpio_set_level(twi_scl, 0); 181 | 182 | i2c_pinMode(twi_sda, INPUT_PULLUP); 183 | i2c_pinMode(twi_scl, INPUT_PULLUP); 184 | twi_setClock(100000); 185 | } 186 | 187 | void twi_stop(void) 188 | { 189 | i2c_pinMode(twi_sda, INPUT); 190 | i2c_pinMode(twi_scl, INPUT); 191 | } 192 | 193 | static void twi_delay(unsigned char v) 194 | { 195 | unsigned int i; 196 | #pragma GCC diagnostic push 197 | #pragma GCC diagnostic ignored "-Wunused-but-set-variable" 198 | unsigned int reg; 199 | for (i = 0; i < v; i++) 200 | reg = REG_READ(GPIO_IN_REG); 201 | #pragma GCC diagnostic pop 202 | } 203 | 204 | static bool twi_write_start(void) 205 | { 206 | SCL_HIGH(); 207 | SDA_HIGH(); 208 | if (SDA_READ() == 0) 209 | return false; 210 | twi_delay(twi_dcount); 211 | SDA_LOW(); 212 | twi_delay(twi_dcount); 213 | return true; 214 | } 215 | 216 | static bool twi_write_stop(void) 217 | { 218 | unsigned int i = 0; 219 | SCL_LOW(); 220 | SDA_LOW(); 221 | twi_delay(twi_dcount); 222 | SCL_HIGH(); 223 | while (SCL_READ() == 0 && (i++) < TWI_CLOCK_STRETCH) 224 | ; // Clock stretching (up to 100us) 225 | twi_delay(twi_dcount); 226 | SDA_HIGH(); 227 | twi_delay(twi_dcount); 228 | 229 | return true; 230 | } 231 | 232 | bool do_log = false; 233 | static bool twi_write_bit(bool bit) 234 | { 235 | unsigned int i = 0; 236 | SCL_LOW(); 237 | if (bit) 238 | { 239 | SDA_HIGH(); 240 | if (do_log) 241 | { 242 | twi_delay(twi_dcount + 1); 243 | } 244 | } 245 | else 246 | { 247 | SDA_LOW(); 248 | if (do_log) 249 | { 250 | } 251 | } 252 | twi_delay(twi_dcount + 1); 253 | SCL_HIGH(); 254 | while (SCL_READ() == 0 && (i++) < TWI_CLOCK_STRETCH) 255 | ; // Clock stretching (up to 100us) 256 | twi_delay(twi_dcount); 257 | return true; 258 | } 259 | 260 | static bool twi_read_bit(void) 261 | { 262 | unsigned int i = 0; 263 | SCL_LOW(); 264 | SDA_HIGH(); 265 | twi_delay(twi_dcount + 2); 266 | SCL_HIGH(); 267 | while (SCL_READ() == 0 && (i++) < TWI_CLOCK_STRETCH) 268 | ; // Clock stretching (up to 100us) 269 | bool bit = SDA_READ(); 270 | twi_delay(twi_dcount); 271 | return bit; 272 | } 273 | 274 | static bool twi_write_byte(unsigned char byte) 275 | { 276 | 277 | if (byte == 0x43) 278 | { 279 | // printf("TWB %02x ", (uint32_t) byte); 280 | // do_log = true; 281 | } 282 | unsigned char bit; 283 | for (bit = 0; bit < 8; bit++) 284 | { 285 | twi_write_bit((byte & 0x80) != 0); 286 | byte <<= 1; 287 | } 288 | if (do_log) 289 | { 290 | printf("\n"); 291 | do_log = false; 292 | } 293 | return !twi_read_bit(); //NACK/ACK 294 | } 295 | 296 | static unsigned char twi_read_byte(bool nack) 297 | { 298 | unsigned char byte = 0; 299 | unsigned char bit; 300 | for (bit = 0; bit < 8; bit++) 301 | byte = (byte << 1) | twi_read_bit(); 302 | twi_write_bit(nack); 303 | return byte; 304 | } 305 | 306 | unsigned char twi_writeTo(unsigned char address, unsigned char *buf, unsigned int len, unsigned char sendStop) 307 | { 308 | unsigned int i; 309 | if (!twi_write_start()) 310 | return 4; //line busy 311 | if (!twi_write_byte(((address << 1) | 0) & 0xFF)) 312 | { 313 | if (sendStop) 314 | twi_write_stop(); 315 | return 2; //received NACK on transmit of address 316 | } 317 | for (i = 0; i < len; i++) 318 | { 319 | if (!twi_write_byte(buf[i])) 320 | { 321 | if (sendStop) 322 | twi_write_stop(); 323 | return 3; //received NACK on transmit of data 324 | } 325 | } 326 | if (sendStop) 327 | twi_write_stop(); 328 | i = 0; 329 | while (SDA_READ() == 0 && (i++) < 10) 330 | { 331 | SCL_LOW(); 332 | twi_delay(twi_dcount); 333 | SCL_HIGH(); 334 | twi_delay(twi_dcount); 335 | } 336 | return 0; 337 | } 338 | 339 | unsigned char twi_readFrom(unsigned char address, unsigned char *buf, unsigned int len, unsigned char sendStop) 340 | { 341 | unsigned int i; 342 | if (!twi_write_start()) 343 | return 4; //line busy 344 | if (!twi_write_byte(((address << 1) | 1) & 0xFF)) 345 | { 346 | if (sendStop) 347 | twi_write_stop(); 348 | return 2; //received NACK on transmit of address 349 | } 350 | for (i = 0; i < (len - 1); i++) 351 | buf[i] = twi_read_byte(false); 352 | buf[len - 1] = twi_read_byte(true); 353 | if (sendStop) 354 | twi_write_stop(); 355 | i = 0; 356 | while (SDA_READ() == 0 && (i++) < 10) 357 | { 358 | SCL_LOW(); 359 | twi_delay(twi_dcount); 360 | SCL_HIGH(); 361 | twi_delay(twi_dcount); 362 | } 363 | return 0; 364 | } 365 | -------------------------------------------------------------------------------- /src/twi.h: -------------------------------------------------------------------------------- 1 | /* 2 | twi.h - Software I2C library for ESP31B 3 | 4 | Copyright (c) 2015 Hristo Gochkov. All rights reserved. 5 | This file is part of the ESP31B core for Arduino environment. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | #ifndef SI2C_h 22 | #define SI2C_h 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | #define systick_sleep(t) delay(t) 29 | #define __disable_irq() 30 | #define __enable_irq() 31 | 32 | void twi_init(unsigned char sda, unsigned char scl); 33 | void twi_stop(void); 34 | void twi_setClock(unsigned int freq); 35 | uint8_t twi_writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop); 36 | uint8_t twi_readFrom(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop); 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | 42 | #endif --------------------------------------------------------------------------------