OV528 Camera Module Library

Dependents:   CameraOV528-Example

Committer:
jplunkett
Date:
Fri Jul 07 16:52:18 2017 +0000
Revision:
0:3f9d7ef7266c
Child:
1:06afc809909b
Camera OV528 Library

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jplunkett 0:3f9d7ef7266c 1 /* Copyright (c) 2016 ARM Limited
jplunkett 0:3f9d7ef7266c 2 *
jplunkett 0:3f9d7ef7266c 3 * Licensed under the Apache License, Version 2.0 (the "License");
jplunkett 0:3f9d7ef7266c 4 * you may not use this file except in compliance with the License.
jplunkett 0:3f9d7ef7266c 5 * You may obtain a copy of the License at
jplunkett 0:3f9d7ef7266c 6 *
jplunkett 0:3f9d7ef7266c 7 * http://www.apache.org/licenses/LICENSE-2.0
jplunkett 0:3f9d7ef7266c 8 *
jplunkett 0:3f9d7ef7266c 9 * Unless required by applicable law or agreed to in writing, software
jplunkett 0:3f9d7ef7266c 10 * distributed under the License is distributed on an "AS IS" BASIS,
jplunkett 0:3f9d7ef7266c 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
jplunkett 0:3f9d7ef7266c 12 * See the License for the specific language governing permissions and
jplunkett 0:3f9d7ef7266c 13 * limitations under the License.
jplunkett 0:3f9d7ef7266c 14 */
jplunkett 0:3f9d7ef7266c 15
jplunkett 0:3f9d7ef7266c 16 /*
jplunkett 0:3f9d7ef7266c 17 * Originally Based on the (Grove_Serial_Camera_Kit) - https://github.com/Seeed-Studio/Grove_Serial_Camera_Kit
jplunkett 0:3f9d7ef7266c 18 */
jplunkett 0:3f9d7ef7266c 19
jplunkett 0:3f9d7ef7266c 20 #include<stdint.h>
jplunkett 0:3f9d7ef7266c 21 #include<stdlib.h>
jplunkett 0:3f9d7ef7266c 22 #include<string.h>
jplunkett 0:3f9d7ef7266c 23
jplunkett 0:3f9d7ef7266c 24 #include "CameraOV528.h"
jplunkett 0:3f9d7ef7266c 25
jplunkett 0:3f9d7ef7266c 26 #define camera_printf(...)
jplunkett 0:3f9d7ef7266c 27
jplunkett 0:3f9d7ef7266c 28 #define PIC_CLOCK_HZ 14745600
jplunkett 0:3f9d7ef7266c 29 #define BOOT_BAUD 9600
jplunkett 0:3f9d7ef7266c 30
jplunkett 0:3f9d7ef7266c 31 #define COMMAND_LENGTH 6
jplunkett 0:3f9d7ef7266c 32 #define PIC_PAYLOAD_DATA_BEGIN 4
jplunkett 0:3f9d7ef7266c 33 #define PIC_PAYLOAD_OVERHEAD 6
jplunkett 0:3f9d7ef7266c 34 #define READ_WAKE_POS_INVALID 0xFFFFFFFF
jplunkett 0:3f9d7ef7266c 35
jplunkett 0:3f9d7ef7266c 36 #define DEFAULT_BAUD 115200
jplunkett 0:3f9d7ef7266c 37
jplunkett 0:3f9d7ef7266c 38 enum CameraCommand {
jplunkett 0:3f9d7ef7266c 39 INITIAL = 0x01,
jplunkett 0:3f9d7ef7266c 40 GET_PICTURE = 0x04,
jplunkett 0:3f9d7ef7266c 41 SNAPSHOT = 0x05,
jplunkett 0:3f9d7ef7266c 42 SET_PACKAGE_SIZE = 0x06,
jplunkett 0:3f9d7ef7266c 43 SET_BAUD_RATE = 0x07,
jplunkett 0:3f9d7ef7266c 44 RESET = 0x08,
jplunkett 0:3f9d7ef7266c 45 POWER_DOWN = 0x09,
jplunkett 0:3f9d7ef7266c 46 DATA = 0x0A,
jplunkett 0:3f9d7ef7266c 47 SYNC = 0x0D,
jplunkett 0:3f9d7ef7266c 48 ACK = 0x0E,
jplunkett 0:3f9d7ef7266c 49 NAK = 0x0F
jplunkett 0:3f9d7ef7266c 50 };
jplunkett 0:3f9d7ef7266c 51
jplunkett 0:3f9d7ef7266c 52 enum GetSetting {
jplunkett 0:3f9d7ef7266c 53 GET_SNAPSHOT = 1,
jplunkett 0:3f9d7ef7266c 54 GET_PREVIEW_PICTURE = 2,
jplunkett 0:3f9d7ef7266c 55 GET_JPEG_PREVIEW_PICTURE = 3,
jplunkett 0:3f9d7ef7266c 56 };
jplunkett 0:3f9d7ef7266c 57
jplunkett 0:3f9d7ef7266c 58 typedef struct {
jplunkett 0:3f9d7ef7266c 59 uint8_t header;
jplunkett 0:3f9d7ef7266c 60 uint8_t command;
jplunkett 0:3f9d7ef7266c 61 uint8_t param[4];
jplunkett 0:3f9d7ef7266c 62 } camera_command_t;
jplunkett 0:3f9d7ef7266c 63
jplunkett 0:3f9d7ef7266c 64 CameraOV528::Resolution supported_resolutions[] = {
jplunkett 0:3f9d7ef7266c 65 // CameraOV528::RES_80x60, //Does not work
jplunkett 0:3f9d7ef7266c 66 CameraOV528::RES_160x120,
jplunkett 0:3f9d7ef7266c 67 CameraOV528::RES_320x240,
jplunkett 0:3f9d7ef7266c 68 CameraOV528::RES_640x480,
jplunkett 0:3f9d7ef7266c 69 };
jplunkett 0:3f9d7ef7266c 70
jplunkett 0:3f9d7ef7266c 71 CameraOV528::Format supported_formats[] = {
jplunkett 0:3f9d7ef7266c 72 // CameraOV528::FMT_2_BIT_GRAY_SCALE, //Does not work
jplunkett 0:3f9d7ef7266c 73 // CameraOV528::FMT_4_BIT_GRAY_SCALE,
jplunkett 0:3f9d7ef7266c 74 // CameraOV528::FMT_8_BIT_GRAY_SCALE,
jplunkett 0:3f9d7ef7266c 75 // CameraOV528::FMT_2_BIT_COLOR,
jplunkett 0:3f9d7ef7266c 76 // CameraOV528::FMT_16_BIT,
jplunkett 0:3f9d7ef7266c 77 CameraOV528::FMT_JPEG,
jplunkett 0:3f9d7ef7266c 78 };
jplunkett 0:3f9d7ef7266c 79
jplunkett 0:3f9d7ef7266c 80 static uint32_t divide_round_up(uint32_t dividen, uint32_t divisor);
jplunkett 0:3f9d7ef7266c 81 static uint32_t min(uint32_t value1, uint32_t value2);
jplunkett 0:3f9d7ef7266c 82 static uint32_t queue_used(uint32_t head, uint32_t tail, uint32_t size);
jplunkett 0:3f9d7ef7266c 83
jplunkett 0:3f9d7ef7266c 84 CameraOV528::CameraOV528(PinName rx, PinName tx) : _serial(rx, tx), _read_sem(0)
jplunkett 0:3f9d7ef7266c 85 {
jplunkett 0:3f9d7ef7266c 86 _init_done = false;
jplunkett 0:3f9d7ef7266c 87 _resolution = RES_640x480;
jplunkett 0:3f9d7ef7266c 88 _format = FMT_JPEG;
jplunkett 0:3f9d7ef7266c 89 _baud = DEFAULT_BAUD;
jplunkett 0:3f9d7ef7266c 90
jplunkett 0:3f9d7ef7266c 91 _dev_resolution = _resolution;
jplunkett 0:3f9d7ef7266c 92 _dev_format = _format;
jplunkett 0:3f9d7ef7266c 93 _dev_baud = _baud;
jplunkett 0:3f9d7ef7266c 94
jplunkett 0:3f9d7ef7266c 95 picture_length = 0;
jplunkett 0:3f9d7ef7266c 96 picture_data_id = 0;
jplunkett 0:3f9d7ef7266c 97 picture_data_id_count = 0;
jplunkett 0:3f9d7ef7266c 98 memset(picture_buffer, 0, sizeof(picture_buffer));
jplunkett 0:3f9d7ef7266c 99 picture_buffer_size_limit = sizeof(picture_buffer);
jplunkett 0:3f9d7ef7266c 100 picture_buffer_pos = 0;
jplunkett 0:3f9d7ef7266c 101 picture_buffer_size = 0;
jplunkett 0:3f9d7ef7266c 102
jplunkett 0:3f9d7ef7266c 103 memset(_read_buf, 0, sizeof(_read_buf));
jplunkett 0:3f9d7ef7266c 104 _read_buf_head = 0;
jplunkett 0:3f9d7ef7266c 105 _read_buf_tail = 0;
jplunkett 0:3f9d7ef7266c 106 _read_wake_pos = READ_WAKE_POS_INVALID;
jplunkett 0:3f9d7ef7266c 107 }
jplunkett 0:3f9d7ef7266c 108
jplunkett 0:3f9d7ef7266c 109 CameraOV528::~CameraOV528()
jplunkett 0:3f9d7ef7266c 110 {
jplunkett 0:3f9d7ef7266c 111 powerdown();
jplunkett 0:3f9d7ef7266c 112 }
jplunkett 0:3f9d7ef7266c 113
jplunkett 0:3f9d7ef7266c 114 void CameraOV528::powerup()
jplunkett 0:3f9d7ef7266c 115 {
jplunkett 0:3f9d7ef7266c 116 if (_init_done) {
jplunkett 0:3f9d7ef7266c 117 return;
jplunkett 0:3f9d7ef7266c 118 }
jplunkett 0:3f9d7ef7266c 119
jplunkett 0:3f9d7ef7266c 120 _serial.attach(this, &CameraOV528::_rx_irq, SerialBase::RxIrq);
jplunkett 0:3f9d7ef7266c 121
jplunkett 0:3f9d7ef7266c 122 // Attempt to connect at the desired baud
jplunkett 0:3f9d7ef7266c 123 _serial.baud(_baud);
jplunkett 0:3f9d7ef7266c 124 bool success = _init_sequence();
jplunkett 0:3f9d7ef7266c 125
jplunkett 0:3f9d7ef7266c 126 // If unsuccessful try with boot baud
jplunkett 0:3f9d7ef7266c 127 if (!success) {
jplunkett 0:3f9d7ef7266c 128 _serial.baud(BOOT_BAUD);
jplunkett 0:3f9d7ef7266c 129 success = _init_sequence();
jplunkett 0:3f9d7ef7266c 130 }
jplunkett 0:3f9d7ef7266c 131
jplunkett 0:3f9d7ef7266c 132 if (!success) {
jplunkett 0:3f9d7ef7266c 133 error("Unable to communicate with camera");
jplunkett 0:3f9d7ef7266c 134 }
jplunkett 0:3f9d7ef7266c 135
jplunkett 0:3f9d7ef7266c 136 // Acknowledge the SYNC read in _init_sequence with an ACK
jplunkett 0:3f9d7ef7266c 137 _send_ack(SYNC, 0, 0, 0);
jplunkett 0:3f9d7ef7266c 138 camera_printf("\nCamera initialization done.\r\n");
jplunkett 0:3f9d7ef7266c 139
jplunkett 0:3f9d7ef7266c 140 _set_baud(_baud);
jplunkett 0:3f9d7ef7266c 141
jplunkett 0:3f9d7ef7266c 142 _set_fmt_and_res(_format, _resolution);
jplunkett 0:3f9d7ef7266c 143 _set_package_size(picture_buffer_size_limit);
jplunkett 0:3f9d7ef7266c 144
jplunkett 0:3f9d7ef7266c 145 _init_done = false;
jplunkett 0:3f9d7ef7266c 146 }
jplunkett 0:3f9d7ef7266c 147
jplunkett 0:3f9d7ef7266c 148 void CameraOV528::powerup(uint32_t baud)
jplunkett 0:3f9d7ef7266c 149 {
jplunkett 0:3f9d7ef7266c 150 _baud = baud;
jplunkett 0:3f9d7ef7266c 151 powerup();
jplunkett 0:3f9d7ef7266c 152 }
jplunkett 0:3f9d7ef7266c 153
jplunkett 0:3f9d7ef7266c 154 void CameraOV528::powerdown()
jplunkett 0:3f9d7ef7266c 155 {
jplunkett 0:3f9d7ef7266c 156 if (!_init_done) {
jplunkett 0:3f9d7ef7266c 157 return;
jplunkett 0:3f9d7ef7266c 158 }
jplunkett 0:3f9d7ef7266c 159
jplunkett 0:3f9d7ef7266c 160 if (!_send_cmd(POWER_DOWN, 0)) {
jplunkett 0:3f9d7ef7266c 161 error("Powerdown failed");
jplunkett 0:3f9d7ef7266c 162 }
jplunkett 0:3f9d7ef7266c 163
jplunkett 0:3f9d7ef7266c 164 // Reset picture transfer variables
jplunkett 0:3f9d7ef7266c 165 picture_length = 0;
jplunkett 0:3f9d7ef7266c 166 picture_data_id = 0;
jplunkett 0:3f9d7ef7266c 167 picture_data_id_count = 0;
jplunkett 0:3f9d7ef7266c 168 memset(picture_buffer, 0, sizeof(picture_buffer));
jplunkett 0:3f9d7ef7266c 169 picture_buffer_size_limit = sizeof(picture_buffer);
jplunkett 0:3f9d7ef7266c 170 picture_buffer_pos = 0;
jplunkett 0:3f9d7ef7266c 171 picture_buffer_size = 0;
jplunkett 0:3f9d7ef7266c 172
jplunkett 0:3f9d7ef7266c 173 _serial.attach(NULL, SerialBase::RxIrq);
jplunkett 0:3f9d7ef7266c 174 _flush_rx();
jplunkett 0:3f9d7ef7266c 175
jplunkett 0:3f9d7ef7266c 176 _init_done = false;
jplunkett 0:3f9d7ef7266c 177 }
jplunkett 0:3f9d7ef7266c 178
jplunkett 0:3f9d7ef7266c 179 void CameraOV528::take_picture(void)
jplunkett 0:3f9d7ef7266c 180 {
jplunkett 0:3f9d7ef7266c 181 // Ensure driver is powered up
jplunkett 0:3f9d7ef7266c 182 powerup();
jplunkett 0:3f9d7ef7266c 183
jplunkett 0:3f9d7ef7266c 184 // Update settings if any have changed
jplunkett 0:3f9d7ef7266c 185 if ((_dev_format != _format) || (_dev_resolution != _resolution)) {
jplunkett 0:3f9d7ef7266c 186 _set_fmt_and_res(_format, _resolution);
jplunkett 0:3f9d7ef7266c 187 _dev_format = _format;
jplunkett 0:3f9d7ef7266c 188 _dev_resolution = _resolution;
jplunkett 0:3f9d7ef7266c 189 }
jplunkett 0:3f9d7ef7266c 190
jplunkett 0:3f9d7ef7266c 191 // Take snapshot
jplunkett 0:3f9d7ef7266c 192 camera_printf("Taking snapshot\r\n");
jplunkett 0:3f9d7ef7266c 193 if (!_send_cmd(SNAPSHOT, 0)) {
jplunkett 0:3f9d7ef7266c 194 error("Take snapshot failed");
jplunkett 0:3f9d7ef7266c 195 }
jplunkett 0:3f9d7ef7266c 196
jplunkett 0:3f9d7ef7266c 197 // Start picture transfer
jplunkett 0:3f9d7ef7266c 198 camera_printf("Starting transfer\r\n");
jplunkett 0:3f9d7ef7266c 199 const GetSetting request = GET_JPEG_PREVIEW_PICTURE;
jplunkett 0:3f9d7ef7266c 200 if (!_send_cmd(GET_PICTURE, request)) {
jplunkett 0:3f9d7ef7266c 201 error("Get picture command failed");
jplunkett 0:3f9d7ef7266c 202 }
jplunkett 0:3f9d7ef7266c 203 camera_command_t resp = {0};
jplunkett 0:3f9d7ef7266c 204 uint32_t size_read = _read((uint8_t*)&resp, COMMAND_LENGTH, 1000);
jplunkett 0:3f9d7ef7266c 205 if (size_read != COMMAND_LENGTH) {
jplunkett 0:3f9d7ef7266c 206 error("Get picture response invalid");
jplunkett 0:3f9d7ef7266c 207 }
jplunkett 0:3f9d7ef7266c 208 if (resp.header != 0xAA) error("Get picture response invalid sync");
jplunkett 0:3f9d7ef7266c 209 if (resp.command != DATA) error("Get picture response invalid data");
jplunkett 0:3f9d7ef7266c 210 if (resp.param[0] != request) error("Get picture response invalid content");
jplunkett 0:3f9d7ef7266c 211 picture_length = (resp.param[1] << 0) |
jplunkett 0:3f9d7ef7266c 212 (resp.param[2] << 8) |
jplunkett 0:3f9d7ef7266c 213 (resp.param[3] << 16);
jplunkett 0:3f9d7ef7266c 214 picture_data_id = 0;
jplunkett 0:3f9d7ef7266c 215 uint32_t payload_length = picture_buffer_size_limit - PIC_PAYLOAD_OVERHEAD;
jplunkett 0:3f9d7ef7266c 216 picture_data_id_count = divide_round_up(picture_length, payload_length);
jplunkett 0:3f9d7ef7266c 217 }
jplunkett 0:3f9d7ef7266c 218
jplunkett 0:3f9d7ef7266c 219 uint32_t CameraOV528::get_picture_size()
jplunkett 0:3f9d7ef7266c 220 {
jplunkett 0:3f9d7ef7266c 221 return picture_length;
jplunkett 0:3f9d7ef7266c 222 }
jplunkett 0:3f9d7ef7266c 223
jplunkett 0:3f9d7ef7266c 224 uint32_t CameraOV528::read_picture_data(uint8_t * data, uint32_t size)
jplunkett 0:3f9d7ef7266c 225 {
jplunkett 0:3f9d7ef7266c 226 uint32_t size_copied = 0;
jplunkett 0:3f9d7ef7266c 227 while (size_copied < size) {
jplunkett 0:3f9d7ef7266c 228
jplunkett 0:3f9d7ef7266c 229 // Copy any data in the picture buffer
jplunkett 0:3f9d7ef7266c 230 uint32_t size_left = size - size_copied;
jplunkett 0:3f9d7ef7266c 231 uint32_t copy_size = min(picture_buffer_size, size_left);
jplunkett 0:3f9d7ef7266c 232 memcpy(data + size_copied, picture_buffer + picture_buffer_pos, copy_size);
jplunkett 0:3f9d7ef7266c 233 picture_buffer_pos += copy_size;
jplunkett 0:3f9d7ef7266c 234 picture_buffer_size -= copy_size;
jplunkett 0:3f9d7ef7266c 235 size_copied += copy_size;
jplunkett 0:3f9d7ef7266c 236
jplunkett 0:3f9d7ef7266c 237 // If picture buffer is empty read more data
jplunkett 0:3f9d7ef7266c 238 if (0 == picture_buffer_size) {
jplunkett 0:3f9d7ef7266c 239 _read_picture_block();
jplunkett 0:3f9d7ef7266c 240 }
jplunkett 0:3f9d7ef7266c 241
jplunkett 0:3f9d7ef7266c 242 // If there is still no more data to read then break from loop
jplunkett 0:3f9d7ef7266c 243 if (0 == picture_buffer_size) {
jplunkett 0:3f9d7ef7266c 244 break;
jplunkett 0:3f9d7ef7266c 245 }
jplunkett 0:3f9d7ef7266c 246 }
jplunkett 0:3f9d7ef7266c 247
jplunkett 0:3f9d7ef7266c 248 return size_copied;
jplunkett 0:3f9d7ef7266c 249 }
jplunkett 0:3f9d7ef7266c 250
jplunkett 0:3f9d7ef7266c 251 void CameraOV528::set_resolution(CameraOV528::Resolution resolution)
jplunkett 0:3f9d7ef7266c 252 {
jplunkett 0:3f9d7ef7266c 253 const uint32_t count = sizeof(supported_resolutions) / sizeof(supported_resolutions[0]);
jplunkett 0:3f9d7ef7266c 254 bool valid_resolution = false;
jplunkett 0:3f9d7ef7266c 255 for (uint32_t i = 0; i < count; i++) {
jplunkett 0:3f9d7ef7266c 256 if (resolution == supported_resolutions[i]) {
jplunkett 0:3f9d7ef7266c 257 valid_resolution = true;
jplunkett 0:3f9d7ef7266c 258 break;
jplunkett 0:3f9d7ef7266c 259 }
jplunkett 0:3f9d7ef7266c 260 }
jplunkett 0:3f9d7ef7266c 261
jplunkett 0:3f9d7ef7266c 262 if (!valid_resolution) {
jplunkett 0:3f9d7ef7266c 263 error("Invalid resolution");
jplunkett 0:3f9d7ef7266c 264 }
jplunkett 0:3f9d7ef7266c 265
jplunkett 0:3f9d7ef7266c 266 _resolution = resolution;
jplunkett 0:3f9d7ef7266c 267
jplunkett 0:3f9d7ef7266c 268 }
jplunkett 0:3f9d7ef7266c 269
jplunkett 0:3f9d7ef7266c 270 void CameraOV528::set_format(CameraOV528::Format format)
jplunkett 0:3f9d7ef7266c 271 {
jplunkett 0:3f9d7ef7266c 272 const uint32_t count = sizeof(supported_formats) / sizeof(supported_formats[0]);
jplunkett 0:3f9d7ef7266c 273 bool valid_format = false;
jplunkett 0:3f9d7ef7266c 274 for (uint32_t i = 0; i < count; i++) {
jplunkett 0:3f9d7ef7266c 275 if (format == supported_formats[i]) {
jplunkett 0:3f9d7ef7266c 276 valid_format = true;
jplunkett 0:3f9d7ef7266c 277 break;
jplunkett 0:3f9d7ef7266c 278 }
jplunkett 0:3f9d7ef7266c 279 }
jplunkett 0:3f9d7ef7266c 280
jplunkett 0:3f9d7ef7266c 281 if (!valid_format) {
jplunkett 0:3f9d7ef7266c 282 error("Invalid format");
jplunkett 0:3f9d7ef7266c 283 }
jplunkett 0:3f9d7ef7266c 284
jplunkett 0:3f9d7ef7266c 285 _format = format;
jplunkett 0:3f9d7ef7266c 286 }
jplunkett 0:3f9d7ef7266c 287
jplunkett 0:3f9d7ef7266c 288 void CameraOV528::_set_baud(uint32_t baud)
jplunkett 0:3f9d7ef7266c 289 {
jplunkett 0:3f9d7ef7266c 290 // Baud Rate = 14.7456MHz/(2*(2nd divider +1))/(2*(1st divider+1))
jplunkett 0:3f9d7ef7266c 291 uint32_t div2 = 1;
jplunkett 0:3f9d7ef7266c 292 uint32_t div1 = PIC_CLOCK_HZ / _baud / (2 * (div2 + 1)) / 2 - 1;
jplunkett 0:3f9d7ef7266c 293 // Assert no rounding errors
jplunkett 0:3f9d7ef7266c 294 MBED_ASSERT(PIC_CLOCK_HZ / ( 2 * (div2 + 1) ) / ( 2 * (div1 + 1)) == _baud);
jplunkett 0:3f9d7ef7266c 295 if (!_send_cmd(SET_BAUD_RATE, div1, div2)) {
jplunkett 0:3f9d7ef7266c 296 error("_set_baud failed");
jplunkett 0:3f9d7ef7266c 297 }
jplunkett 0:3f9d7ef7266c 298 _serial.baud(_baud);
jplunkett 0:3f9d7ef7266c 299 }
jplunkett 0:3f9d7ef7266c 300
jplunkett 0:3f9d7ef7266c 301 void CameraOV528::_set_package_size(uint32_t size)
jplunkett 0:3f9d7ef7266c 302 {
jplunkett 0:3f9d7ef7266c 303 camera_printf("_set_package_size(%lu)\r\n", size);
jplunkett 0:3f9d7ef7266c 304 uint8_t size_low = (size >> 0) & 0xff;
jplunkett 0:3f9d7ef7266c 305 uint8_t size_high = (size >> 8) & 0xff;
jplunkett 0:3f9d7ef7266c 306 if (!_send_cmd(SET_PACKAGE_SIZE, 0x08, size_low, size_high, 0)) {
jplunkett 0:3f9d7ef7266c 307 error("_set_package_size failed");
jplunkett 0:3f9d7ef7266c 308 }
jplunkett 0:3f9d7ef7266c 309 }
jplunkett 0:3f9d7ef7266c 310
jplunkett 0:3f9d7ef7266c 311 void CameraOV528::_set_fmt_and_res(Format fmt, Resolution res)
jplunkett 0:3f9d7ef7266c 312 {
jplunkett 0:3f9d7ef7266c 313 if (!_send_cmd(INITIAL, 0x00, _format, 0x00, _resolution)) {
jplunkett 0:3f9d7ef7266c 314 error("_set_fmt_and_res failed");
jplunkett 0:3f9d7ef7266c 315 }
jplunkett 0:3f9d7ef7266c 316 }
jplunkett 0:3f9d7ef7266c 317
jplunkett 0:3f9d7ef7266c 318 void CameraOV528::_read_picture_block()
jplunkett 0:3f9d7ef7266c 319 {
jplunkett 0:3f9d7ef7266c 320 const uint32_t payload_length = picture_buffer_size_limit - PIC_PAYLOAD_OVERHEAD;
jplunkett 0:3f9d7ef7266c 321 if (picture_data_id >= picture_data_id_count) {
jplunkett 0:3f9d7ef7266c 322 // Transfer complete
jplunkett 0:3f9d7ef7266c 323 return;
jplunkett 0:3f9d7ef7266c 324 }
jplunkett 0:3f9d7ef7266c 325
jplunkett 0:3f9d7ef7266c 326 // Send an ACK to indicate that the next block id should be sent
jplunkett 0:3f9d7ef7266c 327 uint8_t id_low = (picture_data_id >> 0) & 0xff;
jplunkett 0:3f9d7ef7266c 328 uint8_t id_high = (picture_data_id >> 8) & 0xff;
jplunkett 0:3f9d7ef7266c 329 _send_ack(0x00, 0x00, id_low, id_high);
jplunkett 0:3f9d7ef7266c 330
jplunkett 0:3f9d7ef7266c 331 // Read image data
jplunkett 0:3f9d7ef7266c 332 memset(picture_buffer, 0, sizeof(picture_buffer));
jplunkett 0:3f9d7ef7266c 333 uint32_t size_left = picture_length - payload_length * picture_data_id;
jplunkett 0:3f9d7ef7266c 334 uint32_t size_to_read = min(size_left, payload_length) + PIC_PAYLOAD_OVERHEAD;
jplunkett 0:3f9d7ef7266c 335 uint32_t size_read = _read(picture_buffer, size_to_read);
jplunkett 0:3f9d7ef7266c 336 if (size_read != size_to_read) {
jplunkett 0:3f9d7ef7266c 337 error("Image data protocol error");
jplunkett 0:3f9d7ef7266c 338 }
jplunkett 0:3f9d7ef7266c 339
jplunkett 0:3f9d7ef7266c 340 // Validate checksum
jplunkett 0:3f9d7ef7266c 341 uint8_t checksum = 0;
jplunkett 0:3f9d7ef7266c 342 for (uint32_t i = 0; i < size_read - 2; i++) {
jplunkett 0:3f9d7ef7266c 343 checksum += picture_buffer[i];
jplunkett 0:3f9d7ef7266c 344 }
jplunkett 0:3f9d7ef7266c 345 if (picture_buffer[size_read - 2] != checksum) {
jplunkett 0:3f9d7ef7266c 346 error("Image data checksum failure");
jplunkett 0:3f9d7ef7266c 347 }
jplunkett 0:3f9d7ef7266c 348
jplunkett 0:3f9d7ef7266c 349 // Update buffer information
jplunkett 0:3f9d7ef7266c 350 picture_buffer_pos = PIC_PAYLOAD_DATA_BEGIN;
jplunkett 0:3f9d7ef7266c 351 picture_buffer_size = size_read - PIC_PAYLOAD_OVERHEAD;
jplunkett 0:3f9d7ef7266c 352
jplunkett 0:3f9d7ef7266c 353 // If this is the last packet then send the completion ACK
jplunkett 0:3f9d7ef7266c 354 if (picture_data_id + 1 == picture_data_id_count) {
jplunkett 0:3f9d7ef7266c 355 _send_ack(0x00, 0x00, 0xF0, 0xF0);
jplunkett 0:3f9d7ef7266c 356 }
jplunkett 0:3f9d7ef7266c 357
jplunkett 0:3f9d7ef7266c 358 // Increment position
jplunkett 0:3f9d7ef7266c 359 camera_printf("%lu of %lu\r\n", picture_data_id + 1, picture_data_id_count);
jplunkett 0:3f9d7ef7266c 360 camera_printf("size left %lu\r\n",
jplunkett 0:3f9d7ef7266c 361 picture_length - payload_length * picture_data_id +
jplunkett 0:3f9d7ef7266c 362 PIC_PAYLOAD_OVERHEAD - size_read);
jplunkett 0:3f9d7ef7266c 363 picture_data_id++;
jplunkett 0:3f9d7ef7266c 364 }
jplunkett 0:3f9d7ef7266c 365
jplunkett 0:3f9d7ef7266c 366 void CameraOV528::_send_ack(uint8_t p1, uint8_t p2, uint8_t p3, uint8_t p4)
jplunkett 0:3f9d7ef7266c 367 {
jplunkett 0:3f9d7ef7266c 368 camera_command_t cmd = {0};
jplunkett 0:3f9d7ef7266c 369 cmd.header = 0xAA;
jplunkett 0:3f9d7ef7266c 370 cmd.command = ACK;
jplunkett 0:3f9d7ef7266c 371 cmd.param[0] = p1;
jplunkett 0:3f9d7ef7266c 372 cmd.param[1] = p2;
jplunkett 0:3f9d7ef7266c 373 cmd.param[2] = p3;
jplunkett 0:3f9d7ef7266c 374 cmd.param[3] = p4;
jplunkett 0:3f9d7ef7266c 375 _send((uint8_t*)&cmd, COMMAND_LENGTH);
jplunkett 0:3f9d7ef7266c 376 }
jplunkett 0:3f9d7ef7266c 377
jplunkett 0:3f9d7ef7266c 378 void CameraOV528::_rx_irq(void)
jplunkett 0:3f9d7ef7266c 379 {
jplunkett 0:3f9d7ef7266c 380 if(_serial.readable()) {
jplunkett 0:3f9d7ef7266c 381
jplunkett 0:3f9d7ef7266c 382 // Check for overflow
jplunkett 0:3f9d7ef7266c 383 if (_read_buf_head + 1 == _read_buf_tail) {
jplunkett 0:3f9d7ef7266c 384 error("RX buffer overflow");
jplunkett 0:3f9d7ef7266c 385 }
jplunkett 0:3f9d7ef7266c 386
jplunkett 0:3f9d7ef7266c 387 // Add data
jplunkett 0:3f9d7ef7266c 388 _read_buf[_read_buf_head] = (uint8_t)_serial.getc();
jplunkett 0:3f9d7ef7266c 389 _read_buf_head = (_read_buf_head + 1) % sizeof(_read_buf);
jplunkett 0:3f9d7ef7266c 390
jplunkett 0:3f9d7ef7266c 391 // Check if thread should be woken
jplunkett 0:3f9d7ef7266c 392 if (_read_buf_head == _read_wake_pos) {
jplunkett 0:3f9d7ef7266c 393 _read_sem.release();
jplunkett 0:3f9d7ef7266c 394 _read_wake_pos = READ_WAKE_POS_INVALID;
jplunkett 0:3f9d7ef7266c 395 }
jplunkett 0:3f9d7ef7266c 396 }
jplunkett 0:3f9d7ef7266c 397 }
jplunkett 0:3f9d7ef7266c 398
jplunkett 0:3f9d7ef7266c 399 bool CameraOV528::_send(const uint8_t *data, uint32_t size, uint32_t timeout_ms)
jplunkett 0:3f9d7ef7266c 400 {
jplunkett 0:3f9d7ef7266c 401 for (uint32_t i = 0; i < size; i++) {
jplunkett 0:3f9d7ef7266c 402 _serial.putc(data[i]);
jplunkett 0:3f9d7ef7266c 403 }
jplunkett 0:3f9d7ef7266c 404 return true;
jplunkett 0:3f9d7ef7266c 405 }
jplunkett 0:3f9d7ef7266c 406
jplunkett 0:3f9d7ef7266c 407 uint32_t CameraOV528::_read(uint8_t *data, uint32_t size, uint32_t timeout_ms)
jplunkett 0:3f9d7ef7266c 408 {
jplunkett 0:3f9d7ef7266c 409 MBED_ASSERT(size < sizeof(_read_buf));
jplunkett 0:3f9d7ef7266c 410 MBED_ASSERT(0 == _read_sem.wait(0));
jplunkett 0:3f9d7ef7266c 411
jplunkett 0:3f9d7ef7266c 412 core_util_critical_section_enter();
jplunkett 0:3f9d7ef7266c 413
jplunkett 0:3f9d7ef7266c 414 // Atomically set wakeup condition
jplunkett 0:3f9d7ef7266c 415 uint32_t size_available = queue_used(_read_buf_head, _read_buf_tail, sizeof(_read_buf));
jplunkett 0:3f9d7ef7266c 416 if (size_available >= size) {
jplunkett 0:3f9d7ef7266c 417 _read_wake_pos = READ_WAKE_POS_INVALID;
jplunkett 0:3f9d7ef7266c 418 } else {
jplunkett 0:3f9d7ef7266c 419 _read_wake_pos = (_read_buf_tail + size) % sizeof(_read_buf);
jplunkett 0:3f9d7ef7266c 420 }
jplunkett 0:3f9d7ef7266c 421
jplunkett 0:3f9d7ef7266c 422 core_util_critical_section_exit();
jplunkett 0:3f9d7ef7266c 423
jplunkett 0:3f9d7ef7266c 424 // Wait until the requested number of bytes are available
jplunkett 0:3f9d7ef7266c 425 if (_read_wake_pos != READ_WAKE_POS_INVALID) {
jplunkett 0:3f9d7ef7266c 426 int32_t tokens = _read_sem.wait(timeout_ms);
jplunkett 0:3f9d7ef7266c 427 if (tokens <= 0) {
jplunkett 0:3f9d7ef7266c 428 // Timeout occurred so make sure semaphore is cleared
jplunkett 0:3f9d7ef7266c 429 _read_wake_pos = READ_WAKE_POS_INVALID;
jplunkett 0:3f9d7ef7266c 430 _read_sem.wait(0);
jplunkett 0:3f9d7ef7266c 431 } else {
jplunkett 0:3f9d7ef7266c 432 // If the semaphore was signaled then the requested number of
jplunkett 0:3f9d7ef7266c 433 // bytes were read
jplunkett 0:3f9d7ef7266c 434 MBED_ASSERT(queue_used(_read_buf_head, _read_buf_tail, sizeof(_read_buf)) >= size);
jplunkett 0:3f9d7ef7266c 435 }
jplunkett 0:3f9d7ef7266c 436 }
jplunkett 0:3f9d7ef7266c 437
jplunkett 0:3f9d7ef7266c 438 // Copy bytes
jplunkett 0:3f9d7ef7266c 439 size_available = queue_used(_read_buf_head, _read_buf_tail, sizeof(_read_buf));
jplunkett 0:3f9d7ef7266c 440 uint32_t read_size = min(size_available, size);
jplunkett 0:3f9d7ef7266c 441 for (uint32_t i = 0; i < read_size; i++) {
jplunkett 0:3f9d7ef7266c 442 data[i] = _read_buf[_read_buf_tail];
jplunkett 0:3f9d7ef7266c 443 _read_buf_tail = (_read_buf_tail + 1) % sizeof(_read_buf);
jplunkett 0:3f9d7ef7266c 444 }
jplunkett 0:3f9d7ef7266c 445
jplunkett 0:3f9d7ef7266c 446 return read_size;
jplunkett 0:3f9d7ef7266c 447 }
jplunkett 0:3f9d7ef7266c 448
jplunkett 0:3f9d7ef7266c 449
jplunkett 0:3f9d7ef7266c 450 void CameraOV528::_flush_rx(void)
jplunkett 0:3f9d7ef7266c 451 {
jplunkett 0:3f9d7ef7266c 452
jplunkett 0:3f9d7ef7266c 453 core_util_critical_section_enter();
jplunkett 0:3f9d7ef7266c 454 _read_buf_head = 0;
jplunkett 0:3f9d7ef7266c 455 _read_buf_tail = 0;
jplunkett 0:3f9d7ef7266c 456 _read_wake_pos = 0;
jplunkett 0:3f9d7ef7266c 457 core_util_critical_section_exit();
jplunkett 0:3f9d7ef7266c 458 }
jplunkett 0:3f9d7ef7266c 459
jplunkett 0:3f9d7ef7266c 460 bool CameraOV528::_send_cmd(uint8_t cmd, uint8_t p1, uint8_t p2, uint8_t p3, uint8_t p4)
jplunkett 0:3f9d7ef7266c 461 {
jplunkett 0:3f9d7ef7266c 462 _flush_rx();
jplunkett 0:3f9d7ef7266c 463
jplunkett 0:3f9d7ef7266c 464 camera_command_t command = {0};
jplunkett 0:3f9d7ef7266c 465 command.header = 0xAA;
jplunkett 0:3f9d7ef7266c 466 command.command = cmd;
jplunkett 0:3f9d7ef7266c 467 command.param[0] = p1;
jplunkett 0:3f9d7ef7266c 468 command.param[1] = p2;
jplunkett 0:3f9d7ef7266c 469 command.param[2] = p3;
jplunkett 0:3f9d7ef7266c 470 command.param[3] = p4;
jplunkett 0:3f9d7ef7266c 471 _send((uint8_t*)&command, COMMAND_LENGTH);
jplunkett 0:3f9d7ef7266c 472
jplunkett 0:3f9d7ef7266c 473 camera_command_t resp = {0};
jplunkett 0:3f9d7ef7266c 474 uint32_t len = _read((uint8_t*)&resp, COMMAND_LENGTH);
jplunkett 0:3f9d7ef7266c 475 if (COMMAND_LENGTH != len) {
jplunkett 0:3f9d7ef7266c 476 camera_printf("Wrong command response length %lu\r\n", len);
jplunkett 0:3f9d7ef7266c 477 }
jplunkett 0:3f9d7ef7266c 478 if ((resp.header != 0xAA) || (resp.command != ACK) || (resp.param[0] != command.command)) {
jplunkett 0:3f9d7ef7266c 479 camera_printf("Invalid command: %i, %i, %i\r\n", resp.header, resp.command, resp.param[0]);
jplunkett 0:3f9d7ef7266c 480 return false;
jplunkett 0:3f9d7ef7266c 481 }
jplunkett 0:3f9d7ef7266c 482 return true;
jplunkett 0:3f9d7ef7266c 483 }
jplunkett 0:3f9d7ef7266c 484
jplunkett 0:3f9d7ef7266c 485 bool CameraOV528::_init_sequence()
jplunkett 0:3f9d7ef7266c 486 {
jplunkett 0:3f9d7ef7266c 487 camera_printf("connecting to camera...");
jplunkett 0:3f9d7ef7266c 488 bool success = false;
jplunkett 0:3f9d7ef7266c 489 for (uint32_t i = 0; i < 4; i++) {
jplunkett 0:3f9d7ef7266c 490 camera_printf(".");
jplunkett 0:3f9d7ef7266c 491
jplunkett 0:3f9d7ef7266c 492 // Send SYNC command repeatedly
jplunkett 0:3f9d7ef7266c 493 if (!_send_cmd(SYNC, 0, 0, 0, 0)) {
jplunkett 0:3f9d7ef7266c 494 continue;
jplunkett 0:3f9d7ef7266c 495 }
jplunkett 0:3f9d7ef7266c 496 // Device should send back SYNC command
jplunkett 0:3f9d7ef7266c 497 camera_command_t resp = {0};
jplunkett 0:3f9d7ef7266c 498 if (_read((uint8_t*)&resp, COMMAND_LENGTH, 500) != COMMAND_LENGTH) {
jplunkett 0:3f9d7ef7266c 499 continue;
jplunkett 0:3f9d7ef7266c 500 }
jplunkett 0:3f9d7ef7266c 501 if (resp.header != 0xAA) continue;
jplunkett 0:3f9d7ef7266c 502 if (resp.command != SYNC) continue;
jplunkett 0:3f9d7ef7266c 503 if (resp.param[0] != 0) continue;
jplunkett 0:3f9d7ef7266c 504 if (resp.param[1] != 0) continue;
jplunkett 0:3f9d7ef7266c 505 if (resp.param[2] != 0) continue;
jplunkett 0:3f9d7ef7266c 506 if (resp.param[3] != 0) continue;
jplunkett 0:3f9d7ef7266c 507 success = true;
jplunkett 0:3f9d7ef7266c 508 break;
jplunkett 0:3f9d7ef7266c 509 }
jplunkett 0:3f9d7ef7266c 510 return success;
jplunkett 0:3f9d7ef7266c 511 }
jplunkett 0:3f9d7ef7266c 512
jplunkett 0:3f9d7ef7266c 513 static uint32_t divide_round_up(uint32_t dividen, uint32_t divisor)
jplunkett 0:3f9d7ef7266c 514 {
jplunkett 0:3f9d7ef7266c 515 return (dividen + divisor - 1) / divisor;
jplunkett 0:3f9d7ef7266c 516 }
jplunkett 0:3f9d7ef7266c 517
jplunkett 0:3f9d7ef7266c 518 static uint32_t min(uint32_t value1, uint32_t value2)
jplunkett 0:3f9d7ef7266c 519 {
jplunkett 0:3f9d7ef7266c 520 return value1 < value2 ? value1 : value2;
jplunkett 0:3f9d7ef7266c 521 }
jplunkett 0:3f9d7ef7266c 522
jplunkett 0:3f9d7ef7266c 523 static uint32_t queue_used(uint32_t head, uint32_t tail, uint32_t size)
jplunkett 0:3f9d7ef7266c 524 {
jplunkett 0:3f9d7ef7266c 525 if (head < tail) {
jplunkett 0:3f9d7ef7266c 526 return head + size - tail;
jplunkett 0:3f9d7ef7266c 527 } else {
jplunkett 0:3f9d7ef7266c 528 return head - tail;
jplunkett 0:3f9d7ef7266c 529 }
jplunkett 0:3f9d7ef7266c 530 }