OV528 Camera Module Library
Dependents: CameraOV528-Example
Revision 0:3f9d7ef7266c, committed 2017-07-07
- Comitter:
- jplunkett
- Date:
- Fri Jul 07 16:52:18 2017 +0000
- Child:
- 1:06afc809909b
- Commit message:
- Camera OV528 Library
Changed in this revision
| CameraOV528.cpp | Show annotated file Show diff for this revision Revisions of this file |
| CameraOV528.h | Show annotated file Show diff for this revision Revisions of this file |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/CameraOV528.cpp Fri Jul 07 16:52:18 2017 +0000
@@ -0,0 +1,530 @@
+/* Copyright (c) 2016 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Originally Based on the (Grove_Serial_Camera_Kit) - https://github.com/Seeed-Studio/Grove_Serial_Camera_Kit
+ */
+
+#include<stdint.h>
+#include<stdlib.h>
+#include<string.h>
+
+#include "CameraOV528.h"
+
+#define camera_printf(...)
+
+#define PIC_CLOCK_HZ 14745600
+#define BOOT_BAUD 9600
+
+#define COMMAND_LENGTH 6
+#define PIC_PAYLOAD_DATA_BEGIN 4
+#define PIC_PAYLOAD_OVERHEAD 6
+#define READ_WAKE_POS_INVALID 0xFFFFFFFF
+
+#define DEFAULT_BAUD 115200
+
+enum CameraCommand {
+ INITIAL = 0x01,
+ GET_PICTURE = 0x04,
+ SNAPSHOT = 0x05,
+ SET_PACKAGE_SIZE = 0x06,
+ SET_BAUD_RATE = 0x07,
+ RESET = 0x08,
+ POWER_DOWN = 0x09,
+ DATA = 0x0A,
+ SYNC = 0x0D,
+ ACK = 0x0E,
+ NAK = 0x0F
+};
+
+enum GetSetting {
+ GET_SNAPSHOT = 1,
+ GET_PREVIEW_PICTURE = 2,
+ GET_JPEG_PREVIEW_PICTURE = 3,
+};
+
+typedef struct {
+ uint8_t header;
+ uint8_t command;
+ uint8_t param[4];
+} camera_command_t;
+
+CameraOV528::Resolution supported_resolutions[] = {
+// CameraOV528::RES_80x60, //Does not work
+ CameraOV528::RES_160x120,
+ CameraOV528::RES_320x240,
+ CameraOV528::RES_640x480,
+};
+
+CameraOV528::Format supported_formats[] = {
+// CameraOV528::FMT_2_BIT_GRAY_SCALE, //Does not work
+// CameraOV528::FMT_4_BIT_GRAY_SCALE,
+// CameraOV528::FMT_8_BIT_GRAY_SCALE,
+// CameraOV528::FMT_2_BIT_COLOR,
+// CameraOV528::FMT_16_BIT,
+ CameraOV528::FMT_JPEG,
+};
+
+static uint32_t divide_round_up(uint32_t dividen, uint32_t divisor);
+static uint32_t min(uint32_t value1, uint32_t value2);
+static uint32_t queue_used(uint32_t head, uint32_t tail, uint32_t size);
+
+CameraOV528::CameraOV528(PinName rx, PinName tx) : _serial(rx, tx), _read_sem(0)
+{
+ _init_done = false;
+ _resolution = RES_640x480;
+ _format = FMT_JPEG;
+ _baud = DEFAULT_BAUD;
+
+ _dev_resolution = _resolution;
+ _dev_format = _format;
+ _dev_baud = _baud;
+
+ picture_length = 0;
+ picture_data_id = 0;
+ picture_data_id_count = 0;
+ memset(picture_buffer, 0, sizeof(picture_buffer));
+ picture_buffer_size_limit = sizeof(picture_buffer);
+ picture_buffer_pos = 0;
+ picture_buffer_size = 0;
+
+ memset(_read_buf, 0, sizeof(_read_buf));
+ _read_buf_head = 0;
+ _read_buf_tail = 0;
+ _read_wake_pos = READ_WAKE_POS_INVALID;
+}
+
+CameraOV528::~CameraOV528()
+{
+ powerdown();
+}
+
+void CameraOV528::powerup()
+{
+ if (_init_done) {
+ return;
+ }
+
+ _serial.attach(this, &CameraOV528::_rx_irq, SerialBase::RxIrq);
+
+ // Attempt to connect at the desired baud
+ _serial.baud(_baud);
+ bool success = _init_sequence();
+
+ // If unsuccessful try with boot baud
+ if (!success) {
+ _serial.baud(BOOT_BAUD);
+ success = _init_sequence();
+ }
+
+ if (!success) {
+ error("Unable to communicate with camera");
+ }
+
+ // Acknowledge the SYNC read in _init_sequence with an ACK
+ _send_ack(SYNC, 0, 0, 0);
+ camera_printf("\nCamera initialization done.\r\n");
+
+ _set_baud(_baud);
+
+ _set_fmt_and_res(_format, _resolution);
+ _set_package_size(picture_buffer_size_limit);
+
+ _init_done = false;
+}
+
+void CameraOV528::powerup(uint32_t baud)
+{
+ _baud = baud;
+ powerup();
+}
+
+void CameraOV528::powerdown()
+{
+ if (!_init_done) {
+ return;
+ }
+
+ if (!_send_cmd(POWER_DOWN, 0)) {
+ error("Powerdown failed");
+ }
+
+ // Reset picture transfer variables
+ picture_length = 0;
+ picture_data_id = 0;
+ picture_data_id_count = 0;
+ memset(picture_buffer, 0, sizeof(picture_buffer));
+ picture_buffer_size_limit = sizeof(picture_buffer);
+ picture_buffer_pos = 0;
+ picture_buffer_size = 0;
+
+ _serial.attach(NULL, SerialBase::RxIrq);
+ _flush_rx();
+
+ _init_done = false;
+}
+
+void CameraOV528::take_picture(void)
+{
+ // Ensure driver is powered up
+ powerup();
+
+ // Update settings if any have changed
+ if ((_dev_format != _format) || (_dev_resolution != _resolution)) {
+ _set_fmt_and_res(_format, _resolution);
+ _dev_format = _format;
+ _dev_resolution = _resolution;
+ }
+
+ // Take snapshot
+ camera_printf("Taking snapshot\r\n");
+ if (!_send_cmd(SNAPSHOT, 0)) {
+ error("Take snapshot failed");
+ }
+
+ // Start picture transfer
+ camera_printf("Starting transfer\r\n");
+ const GetSetting request = GET_JPEG_PREVIEW_PICTURE;
+ if (!_send_cmd(GET_PICTURE, request)) {
+ error("Get picture command failed");
+ }
+ camera_command_t resp = {0};
+ uint32_t size_read = _read((uint8_t*)&resp, COMMAND_LENGTH, 1000);
+ if (size_read != COMMAND_LENGTH) {
+ error("Get picture response invalid");
+ }
+ if (resp.header != 0xAA) error("Get picture response invalid sync");
+ if (resp.command != DATA) error("Get picture response invalid data");
+ if (resp.param[0] != request) error("Get picture response invalid content");
+ picture_length = (resp.param[1] << 0) |
+ (resp.param[2] << 8) |
+ (resp.param[3] << 16);
+ picture_data_id = 0;
+ uint32_t payload_length = picture_buffer_size_limit - PIC_PAYLOAD_OVERHEAD;
+ picture_data_id_count = divide_round_up(picture_length, payload_length);
+}
+
+uint32_t CameraOV528::get_picture_size()
+{
+ return picture_length;
+}
+
+uint32_t CameraOV528::read_picture_data(uint8_t * data, uint32_t size)
+{
+ uint32_t size_copied = 0;
+ while (size_copied < size) {
+
+ // Copy any data in the picture buffer
+ uint32_t size_left = size - size_copied;
+ uint32_t copy_size = min(picture_buffer_size, size_left);
+ memcpy(data + size_copied, picture_buffer + picture_buffer_pos, copy_size);
+ picture_buffer_pos += copy_size;
+ picture_buffer_size -= copy_size;
+ size_copied += copy_size;
+
+ // If picture buffer is empty read more data
+ if (0 == picture_buffer_size) {
+ _read_picture_block();
+ }
+
+ // If there is still no more data to read then break from loop
+ if (0 == picture_buffer_size) {
+ break;
+ }
+ }
+
+ return size_copied;
+}
+
+void CameraOV528::set_resolution(CameraOV528::Resolution resolution)
+{
+ const uint32_t count = sizeof(supported_resolutions) / sizeof(supported_resolutions[0]);
+ bool valid_resolution = false;
+ for (uint32_t i = 0; i < count; i++) {
+ if (resolution == supported_resolutions[i]) {
+ valid_resolution = true;
+ break;
+ }
+ }
+
+ if (!valid_resolution) {
+ error("Invalid resolution");
+ }
+
+ _resolution = resolution;
+
+}
+
+void CameraOV528::set_format(CameraOV528::Format format)
+{
+ const uint32_t count = sizeof(supported_formats) / sizeof(supported_formats[0]);
+ bool valid_format = false;
+ for (uint32_t i = 0; i < count; i++) {
+ if (format == supported_formats[i]) {
+ valid_format = true;
+ break;
+ }
+ }
+
+ if (!valid_format) {
+ error("Invalid format");
+ }
+
+ _format = format;
+}
+
+void CameraOV528::_set_baud(uint32_t baud)
+{
+ // Baud Rate = 14.7456MHz/(2*(2nd divider +1))/(2*(1st divider+1))
+ uint32_t div2 = 1;
+ uint32_t div1 = PIC_CLOCK_HZ / _baud / (2 * (div2 + 1)) / 2 - 1;
+ // Assert no rounding errors
+ MBED_ASSERT(PIC_CLOCK_HZ / ( 2 * (div2 + 1) ) / ( 2 * (div1 + 1)) == _baud);
+ if (!_send_cmd(SET_BAUD_RATE, div1, div2)) {
+ error("_set_baud failed");
+ }
+ _serial.baud(_baud);
+}
+
+void CameraOV528::_set_package_size(uint32_t size)
+{
+ camera_printf("_set_package_size(%lu)\r\n", size);
+ uint8_t size_low = (size >> 0) & 0xff;
+ uint8_t size_high = (size >> 8) & 0xff;
+ if (!_send_cmd(SET_PACKAGE_SIZE, 0x08, size_low, size_high, 0)) {
+ error("_set_package_size failed");
+ }
+}
+
+void CameraOV528::_set_fmt_and_res(Format fmt, Resolution res)
+{
+ if (!_send_cmd(INITIAL, 0x00, _format, 0x00, _resolution)) {
+ error("_set_fmt_and_res failed");
+ }
+}
+
+void CameraOV528::_read_picture_block()
+{
+ const uint32_t payload_length = picture_buffer_size_limit - PIC_PAYLOAD_OVERHEAD;
+ if (picture_data_id >= picture_data_id_count) {
+ // Transfer complete
+ return;
+ }
+
+ // Send an ACK to indicate that the next block id should be sent
+ uint8_t id_low = (picture_data_id >> 0) & 0xff;
+ uint8_t id_high = (picture_data_id >> 8) & 0xff;
+ _send_ack(0x00, 0x00, id_low, id_high);
+
+ // Read image data
+ memset(picture_buffer, 0, sizeof(picture_buffer));
+ uint32_t size_left = picture_length - payload_length * picture_data_id;
+ uint32_t size_to_read = min(size_left, payload_length) + PIC_PAYLOAD_OVERHEAD;
+ uint32_t size_read = _read(picture_buffer, size_to_read);
+ if (size_read != size_to_read) {
+ error("Image data protocol error");
+ }
+
+ // Validate checksum
+ uint8_t checksum = 0;
+ for (uint32_t i = 0; i < size_read - 2; i++) {
+ checksum += picture_buffer[i];
+ }
+ if (picture_buffer[size_read - 2] != checksum) {
+ error("Image data checksum failure");
+ }
+
+ // Update buffer information
+ picture_buffer_pos = PIC_PAYLOAD_DATA_BEGIN;
+ picture_buffer_size = size_read - PIC_PAYLOAD_OVERHEAD;
+
+ // If this is the last packet then send the completion ACK
+ if (picture_data_id + 1 == picture_data_id_count) {
+ _send_ack(0x00, 0x00, 0xF0, 0xF0);
+ }
+
+ // Increment position
+ camera_printf("%lu of %lu\r\n", picture_data_id + 1, picture_data_id_count);
+ camera_printf("size left %lu\r\n",
+ picture_length - payload_length * picture_data_id +
+ PIC_PAYLOAD_OVERHEAD - size_read);
+ picture_data_id++;
+}
+
+void CameraOV528::_send_ack(uint8_t p1, uint8_t p2, uint8_t p3, uint8_t p4)
+{
+ camera_command_t cmd = {0};
+ cmd.header = 0xAA;
+ cmd.command = ACK;
+ cmd.param[0] = p1;
+ cmd.param[1] = p2;
+ cmd.param[2] = p3;
+ cmd.param[3] = p4;
+ _send((uint8_t*)&cmd, COMMAND_LENGTH);
+}
+
+void CameraOV528::_rx_irq(void)
+{
+ if(_serial.readable()) {
+
+ // Check for overflow
+ if (_read_buf_head + 1 == _read_buf_tail) {
+ error("RX buffer overflow");
+ }
+
+ // Add data
+ _read_buf[_read_buf_head] = (uint8_t)_serial.getc();
+ _read_buf_head = (_read_buf_head + 1) % sizeof(_read_buf);
+
+ // Check if thread should be woken
+ if (_read_buf_head == _read_wake_pos) {
+ _read_sem.release();
+ _read_wake_pos = READ_WAKE_POS_INVALID;
+ }
+ }
+}
+
+bool CameraOV528::_send(const uint8_t *data, uint32_t size, uint32_t timeout_ms)
+{
+ for (uint32_t i = 0; i < size; i++) {
+ _serial.putc(data[i]);
+ }
+ return true;
+}
+
+uint32_t CameraOV528::_read(uint8_t *data, uint32_t size, uint32_t timeout_ms)
+{
+ MBED_ASSERT(size < sizeof(_read_buf));
+ MBED_ASSERT(0 == _read_sem.wait(0));
+
+ core_util_critical_section_enter();
+
+ // Atomically set wakeup condition
+ uint32_t size_available = queue_used(_read_buf_head, _read_buf_tail, sizeof(_read_buf));
+ if (size_available >= size) {
+ _read_wake_pos = READ_WAKE_POS_INVALID;
+ } else {
+ _read_wake_pos = (_read_buf_tail + size) % sizeof(_read_buf);
+ }
+
+ core_util_critical_section_exit();
+
+ // Wait until the requested number of bytes are available
+ if (_read_wake_pos != READ_WAKE_POS_INVALID) {
+ int32_t tokens = _read_sem.wait(timeout_ms);
+ if (tokens <= 0) {
+ // Timeout occurred so make sure semaphore is cleared
+ _read_wake_pos = READ_WAKE_POS_INVALID;
+ _read_sem.wait(0);
+ } else {
+ // If the semaphore was signaled then the requested number of
+ // bytes were read
+ MBED_ASSERT(queue_used(_read_buf_head, _read_buf_tail, sizeof(_read_buf)) >= size);
+ }
+ }
+
+ // Copy bytes
+ size_available = queue_used(_read_buf_head, _read_buf_tail, sizeof(_read_buf));
+ uint32_t read_size = min(size_available, size);
+ for (uint32_t i = 0; i < read_size; i++) {
+ data[i] = _read_buf[_read_buf_tail];
+ _read_buf_tail = (_read_buf_tail + 1) % sizeof(_read_buf);
+ }
+
+ return read_size;
+}
+
+
+void CameraOV528::_flush_rx(void)
+{
+
+ core_util_critical_section_enter();
+ _read_buf_head = 0;
+ _read_buf_tail = 0;
+ _read_wake_pos = 0;
+ core_util_critical_section_exit();
+}
+
+bool CameraOV528::_send_cmd(uint8_t cmd, uint8_t p1, uint8_t p2, uint8_t p3, uint8_t p4)
+{
+ _flush_rx();
+
+ camera_command_t command = {0};
+ command.header = 0xAA;
+ command.command = cmd;
+ command.param[0] = p1;
+ command.param[1] = p2;
+ command.param[2] = p3;
+ command.param[3] = p4;
+ _send((uint8_t*)&command, COMMAND_LENGTH);
+
+ camera_command_t resp = {0};
+ uint32_t len = _read((uint8_t*)&resp, COMMAND_LENGTH);
+ if (COMMAND_LENGTH != len) {
+ camera_printf("Wrong command response length %lu\r\n", len);
+ }
+ if ((resp.header != 0xAA) || (resp.command != ACK) || (resp.param[0] != command.command)) {
+ camera_printf("Invalid command: %i, %i, %i\r\n", resp.header, resp.command, resp.param[0]);
+ return false;
+ }
+ return true;
+}
+
+bool CameraOV528::_init_sequence()
+{
+ camera_printf("connecting to camera...");
+ bool success = false;
+ for (uint32_t i = 0; i < 4; i++) {
+ camera_printf(".");
+
+ // Send SYNC command repeatedly
+ if (!_send_cmd(SYNC, 0, 0, 0, 0)) {
+ continue;
+ }
+ // Device should send back SYNC command
+ camera_command_t resp = {0};
+ if (_read((uint8_t*)&resp, COMMAND_LENGTH, 500) != COMMAND_LENGTH) {
+ continue;
+ }
+ if (resp.header != 0xAA) continue;
+ if (resp.command != SYNC) continue;
+ if (resp.param[0] != 0) continue;
+ if (resp.param[1] != 0) continue;
+ if (resp.param[2] != 0) continue;
+ if (resp.param[3] != 0) continue;
+ success = true;
+ break;
+ }
+ return success;
+}
+
+static uint32_t divide_round_up(uint32_t dividen, uint32_t divisor)
+{
+ return (dividen + divisor - 1) / divisor;
+}
+
+static uint32_t min(uint32_t value1, uint32_t value2)
+{
+ return value1 < value2 ? value1 : value2;
+}
+
+static uint32_t queue_used(uint32_t head, uint32_t tail, uint32_t size)
+{
+ if (head < tail) {
+ return head + size - tail;
+ } else {
+ return head - tail;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/CameraOV528.h Fri Jul 07 16:52:18 2017 +0000
@@ -0,0 +1,107 @@
+/* Copyright (c) 2016 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef MBED_CAMERAOV528_H
+#define MBED_CAMERAOV528_H
+
+#include "mbed.h"
+#include "rtos.h"
+
+class Camera {
+public:
+ Camera(void) {};
+ virtual ~Camera(void) {};
+ virtual void powerup(void) = 0;
+ virtual void powerdown(void) = 0;
+ virtual void take_picture(void) = 0;
+ virtual uint32_t get_picture_size(void) = 0;
+ virtual uint32_t read_picture_data(uint8_t *data, uint32_t size) = 0;
+};
+
+class CameraOV528 : public Camera {
+
+public:
+ enum Resolution {
+// RES_80x60 = 0x01, // Does not work
+ RES_160x120 = 0x03,
+ RES_320x240 = 0x05,
+ RES_640x480 = 0x07,
+ };
+
+ enum Format {
+// FMT_2_BIT_GRAY_SCALE = 1, //Does not work
+// FMT_4_BIT_GRAY_SCALE = 2,
+// FMT_8_BIT_GRAY_SCALE = 3,
+// FMT_2_BIT_COLOR = 5,
+// FMT_16_BIT = 6,
+ FMT_JPEG = 7,
+ };
+
+ CameraOV528(PinName rx, PinName tx);
+ virtual ~CameraOV528(void);
+ virtual void powerup(void);
+ virtual void powerup(uint32_t baud);
+ virtual void powerdown(void);
+ virtual void take_picture(void);
+ virtual uint32_t get_picture_size(void);
+ virtual uint32_t read_picture_data(uint8_t *data, uint32_t size);
+
+ void set_resolution(Resolution resolution);
+ void set_format(Format format);
+ void set_baud(uint32_t baud);
+
+private:
+
+ bool _init_sequence();
+ void _set_baud(uint32_t baud);
+ void _set_package_size(uint32_t size);
+ void _set_fmt_and_res(Format fmt, Resolution res);
+ void _read_picture_block();
+
+ void _rx_irq(void);
+
+ bool _send(const uint8_t *data, uint32_t size, uint32_t timeout_ms=500);
+ uint32_t _read(uint8_t * data, uint32_t size, uint32_t timeout_ms=500);
+ void _flush_rx(void);
+
+ bool _send_cmd(uint8_t cmd, uint8_t p1=0, uint8_t p2=0, uint8_t p3=0, uint8_t p4=0);
+ void _send_ack(uint8_t p1, uint8_t p2, uint8_t p3, uint8_t p4);
+
+ RawSerial _serial;
+
+ bool _init_done;
+ Resolution _resolution;
+ Format _format;
+ uint32_t _baud;
+
+ Resolution _dev_resolution;
+ Format _dev_format;
+ uint32_t _dev_baud;
+
+ uint32_t picture_length;
+ uint32_t picture_data_id;
+ uint32_t picture_data_id_count;
+ uint8_t picture_buffer[128];
+ uint32_t picture_buffer_size_limit;
+ uint32_t picture_buffer_pos;
+ uint32_t picture_buffer_size;
+
+ uint8_t _read_buf[sizeof(picture_buffer) + 1];
+ uint32_t _read_buf_head;
+ uint32_t _read_buf_tail;
+ uint32_t _read_wake_pos;
+ Semaphore _read_sem;
+};
+
+#endif
Jenny Plunkett


Camera OV528