From c96bedf513b76bf97762ed654a4b660b3b76cf0b Mon Sep 17 00:00:00 2001 From: Anuj Deshpande Date: Wed, 9 Aug 2023 16:08:17 +0530 Subject: [PATCH 1/5] feat: Add arducam driver as a simple cmake library --- CMakeLists.txt | 3 + Kconfig | 5 + README.md | 4 +- arducam_mega.c | 463 +++++++++++++++++++++++++++++++ arducam_mega.h | 330 ++++++++++++++++++++++ dts/bindings/arducam,mega.yaml | 5 + dts/bindings/vendor-prefixes.txt | 1 + module.yml | 3 + 8 files changed, 812 insertions(+), 2 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 Kconfig create mode 100644 arducam_mega.c create mode 100644 arducam_mega.h create mode 100644 dts/bindings/arducam,mega.yaml create mode 100644 dts/bindings/vendor-prefixes.txt create mode 100644 module.yml diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..468415b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,3 @@ +zephyr_library() +zephyr_library_sources(arducam_mega.c) +zephyr_library_include_directories(.) diff --git a/Kconfig b/Kconfig new file mode 100644 index 0000000..7eb6412 --- /dev/null +++ b/Kconfig @@ -0,0 +1,5 @@ +menuconfig ARDUCAM_MEGA + bool "Arducam Mega camera for microcontrollers" + depends on SPI && GPIO + help + Enable driver for Arducam Mega camera. \ No newline at end of file diff --git a/README.md b/README.md index e1e9d2d..36a056b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# arducam-zephyr-driver -Zephyr RTOS driver for Arducam Mega +# arducam-mega +Zephyr RTOS driver for Arducam Mega - https://www.arducam.com/camera-for-any-microcontroller/ diff --git a/arducam_mega.c b/arducam_mega.c new file mode 100644 index 0000000..577485f --- /dev/null +++ b/arducam_mega.c @@ -0,0 +1,463 @@ +#include "arducam_mega.h" +#include +#include +#include +#include +#include +#include +#include + +#define LOG_MODULE_NAME arducam_mega +LOG_MODULE_REGISTER(LOG_MODULE_NAME); +#define MAX_PATH 51200 +#define SOME_REQUIRED_LEN 30 + +struct spi_config spi_cfg = { + .frequency = DT_PROP(DT_NODELABEL(spi0), clock_frequency), + .operation = SPI_LOCK_ON | SPI_HOLD_ON_CS | SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8) | SPI_LINES_SINGLE , + .cs =SPI_CS_CONTROL_INIT(DT_NODELABEL(spi0), 0u), + .slave = 0, +}; + +#define SPI_DEV_NAME DEVICE_DT_NAME(DT_NODELABEL(spi0)) +const struct device *spi; + +#define LED DT_ALIAS(csled) +struct gpio_dt_spec spec = GPIO_DT_SPEC_GET(LED, gpios); + + +uint8_t cameraBusRead(uint8_t address) +{ + int ret; + struct spi_buf tx_buf[1]; + tx_buf[0].buf = &address; + tx_buf[0].len = 1; + struct spi_buf_set tx_bufs = {.buffers = tx_buf, .count = 1}; + spi_cfg.operation |= SPI_HOLD_ON_CS; + spi_cfg.operation |= SPI_LOCK_ON; + + gpio_pin_toggle_dt(&spec); + ret = spi_write(spi,&spi_cfg, &tx_bufs); + uint8_t rxdata[2]; + struct spi_buf rx_buf[1] = { + {.buf=rxdata, .len = 2}, + }; + struct spi_buf_set rx_bufs = { + .buffers = rx_buf, + .count = 2 }; + + ret = spi_read(spi, &spi_cfg,&rx_bufs); + gpio_pin_toggle_dt(&spec); + spi_release(spi, &spi_cfg); + return rxdata[1]; +} + +uint8_t cameraReadReg(uint8_t addr) +{ + return cameraBusRead(addr & 0x7F); + +} + +uint8_t cameraBusWrite(uint8_t address, uint8_t value) +{ + + struct spi_buf tx_buf[2]; + tx_buf[0].buf = &address; + tx_buf[0].len = 1; + tx_buf[1].buf = &value; + tx_buf[1].len = 1; + struct spi_buf_set tx_bufs = {.buffers = tx_buf, .count = 2}; + spi_cfg.operation |= SPI_HOLD_ON_CS; + spi_cfg.operation |= SPI_LOCK_ON; + gpio_pin_toggle_dt(&spec); + int ret = spi_write(spi, &spi_cfg,&tx_bufs); + gpio_pin_toggle_dt(&spec); + k_sleep(K_MSEC(10)); + return 1; +} +void cameraWriteReg( uint8_t addr, uint8_t val) +{ + cameraBusWrite( addr | 0x80, val); +} + +void camera_wait_idle() +{ + while ((cameraReadReg(CAM_REG_SENSOR_STATE) & 0X03) != CAM_REG_SENSOR_STATE_IDLE) { + k_sleep(K_MSEC(2)); + + } +} + +uint8_t cameraGetBit(uint8_t addr, uint8_t bit) +{ + uint8_t temp; + temp = cameraReadReg(addr); + temp = temp & bit; + return temp; +} + +#define BUFFER_SIZE 0xff +uint8_t imageData = 0; +uint8_t imageDataNext = 0; +uint8_t headFlag = 0; +unsigned int i =0; +uint8_t imageBuff[BUFFER_SIZE] = {0}; +int received_length=0; + +uint8_t cameraReadByte(){ + int ret; + uint8_t rxdata[1]; + struct spi_buf rx_buf[1] = { + {.buf=rxdata, .len = 1}, + }; + struct spi_buf_set rx_bufs = { + .buffers = rx_buf, + .count = 1 }; + + gpio_pin_toggle_dt(&spec); + struct spi_buf tx_buf[1]; + + uint8_t send_cmd = SINGLE_FIFO_READ; + tx_buf[0].buf = &send_cmd; + tx_buf[0].len = 1; + + struct spi_buf_set tx_bufs = {.buffers = tx_buf, .count = 1}; + spi_cfg.operation |= SPI_HOLD_ON_CS; + spi_cfg.operation |= SPI_LOCK_ON; + ret = spi_write(spi,&spi_cfg, &tx_bufs); + + send_cmd = 0x00; + tx_buf[0].buf = &send_cmd; + ret = spi_write(spi,&spi_cfg, &tx_bufs); + + ret = spi_read(spi, &spi_cfg, &rx_bufs); + gpio_pin_toggle_dt(&spec); + received_length -= 1; + return rxdata[0]; +} + + +void cameraSaveFIFO(const char* base_path, uint32_t length, char* filename){ + received_length = length; + char path[MAX_PATH]; + struct fs_file_t file; + int base = strlen(base_path); + + fs_file_t_init(&file); + + if (base >= (sizeof(path) - SOME_REQUIRED_LEN)) { + LOG_ERR("Not enough concatenation buffer to create file paths"); + return; + } + + strncpy(path, base_path, sizeof(path)); + + path[base++] = '/'; + path[base] = 0; + strcat(&path[base], filename); + int ret; + uint8_t sd_write_counts=0; + uint8_t file_opened = 0; + while (received_length){ + imageData = imageDataNext; + imageDataNext = cameraReadByte(); + if (headFlag == 1) + { + imageBuff[i++]=imageDataNext; + if (i >= BUFFER_SIZE) + { + ret = fs_write(&file, imageBuff, i); + sd_write_counts++; + i = 0; + } + } + if (imageData == 0xff && imageDataNext ==0xd8 && file_opened == 0) + { + ret = fs_open(&file, path,FS_O_CREATE | FS_O_WRITE | FS_O_APPEND); + if(ret != 0){ + LOG_ERR("Failed to create file %s %d", path, ret); + return; + } + LOG_INF("Opened file successfully\n"); + file_opened = 1; + headFlag = 1; + imageBuff[i++]=imageData; + imageBuff[i++]=imageDataNext; + } + if (imageData == 0xff && imageDataNext ==0xd9) + { + headFlag = 0; + LOG_INF("Closed file with sd_write_counts %d\n", sd_write_counts); + fs_write(&file, imageBuff, i); + + fs_close(&file); + i = 0; + break; + } + } + +} + + +int arducam_mega_take_picture(CAM_IMAGE_MODE mode, CAM_IMAGE_PIX_FMT pixel_format) +{ + cameraWriteReg(CAM_REG_SENSOR_RESET, CAM_SENSOR_RESET_ENABLE); + camera_wait_idle(); + cameraWriteReg(0x04, 0x01); + cameraWriteReg(0x04, 0x02); + camera_wait_idle(); + k_sleep(K_MSEC(300)); + + /* Set format JPG */ + cameraWriteReg(CAM_REG_FORMAT, CAM_IMAGE_PIX_FMT_JPG); // set the data format + camera_wait_idle(); // Wait I2c Idle + + /* Set capture resolution */ + cameraWriteReg(CAM_REG_CAPTURE_RESOLUTION, CAM_SET_CAPTURE_MODE | mode); + camera_wait_idle(); // Wait I2c Idle + + /* Clear fifo flags */ + + cameraWriteReg(ARDUCHIP_FIFO, FIFO_CLEAR_ID_MASK); + /* Start capture */ + cameraWriteReg(ARDUCHIP_FIFO, FIFO_START_MASK); + + while (cameraGetBit(ARDUCHIP_TRIG, CAP_DONE_MASK) == 0) + ; + uint32_t len1, len2, len3, length = 0; + len1 = cameraReadReg(FIFO_SIZE1); + len2 = cameraReadReg(FIFO_SIZE2); + len3 = cameraReadReg(FIFO_SIZE3); + length = ((len3 << 16) | (len2 << 8) | len1) & 0xffffff; + LOG_INF("Image length is %d\n", length); + return length; +} +int arducam_mega_save_picture(const char* filename, const char* mount_point) +{ + LOG_INF("Saving picture\n"); + /* Begin function */ + /* reset cpld and camera */ + /* 0x87,0x40*/ + /* Read FIFO length */ + uint32_t len1, len2, len3, length = 0; + len1 = cameraReadReg(FIFO_SIZE1); + len2 = cameraReadReg(FIFO_SIZE2); + len3 = cameraReadReg(FIFO_SIZE3); + length = ((len3 << 16) | (len2 << 8) | len1) & 0xffffff; + LOG_INF("Image length is %d\n", length); + + cameraSaveFIFO(mount_point,length,filename); + + return 0; +} + +int arducam_mega_get_cameraid() +{ + uint8_t cameraID; + cameraID = cameraReadReg(CAM_REG_SENSOR_ID); + LOG_INF("Sensor camera ID is %x\n", cameraID); + return cameraID; + +} +int arducam_mega_init() +{ + int ret = gpio_pin_configure_dt(&spec, GPIO_OUTPUT_ACTIVE); + if (ret < 0) { + LOG_ERR("Couldn't configure CS pin"); + return -1; + } + + + LOG_INF("Initializing the camera"); + spi = device_get_binding(SPI_DEV_NAME); + if (!device_is_ready(spi)) { + LOG_ERR("Device SPI not ready, aborting test"); + return -1; + } + + /* struct arducam_mega_config *cfg = dev->config; */ + /* dev->config = { */ + /* .frequency = DT_PROP(DT_NODELABEL(spi0), clock_frequency), */ + /* .operation = SPI_LOCK_ON | SPI_HOLD_ON_CS | SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8) | SPI_LINES_SINGLE , */ + /* /\* This is initialized but we don't use it *\/ */ + /* .cs =SPI_CS_CONTROL_INIT(DT_NODELABEL(spi0), 0u), */ + /* .slave = 0, */ + /* }; */ + + /* LOG_INF("Camera initialized and ready to use %d\n", cfg->spi_cfg.frequency); */ + return 0; +} +CAM_IMAGE_MODE arducam_mega_get_resolution(char* resolution) +{ + if (strcmp(resolution,"2592x1944")==0) { + return CAM_IMAGE_MODE_WQXGA2; + } + else if (strcmp(resolution,"1920x1080")==0){ + return CAM_IMAGE_MODE_FHD; + } + else if (strcmp(resolution,"1600x1200")==0){ + return CAM_IMAGE_MODE_UXGA; + } + else if (strcmp(resolution,"1280x720")==0){ + return CAM_IMAGE_MODE_HD; + } + else if (strcmp(resolution,"640x480")==0){ + return CAM_IMAGE_MODE_VGA; + } + else if (strcmp(resolution,"320x240")==0){ + return CAM_IMAGE_MODE_QVGA; + } + else if (strcmp(resolution,"320x320")==0){ + return CAM_IMAGE_MODE_320X320; + } + else if (strcmp(resolution,"128x128")==0){ + return CAM_IMAGE_MODE_128X128; + } + else if (strcmp(resolution,"96x96")==0){ + return CAM_IMAGE_MODE_96X96; + } + /* Default to highest res if value is missing/malformed */ + return CAM_IMAGE_MODE_WQXGA2; +} + +CAM_SATURATION_LEVEL arducam_mega_get_saturation(char* saturation) +{ + saturation=saturation+2; + saturation[strlen(saturation)-1] = '\0'; + + if(strcmp(saturation,"-3")==0) { + return CAM_SATURATION_LEVEL_MINUS_3; + } + else if(strcmp(saturation,"-2")==0) { + return CAM_SATURATION_LEVEL_MINUS_2; + } + else if(strcmp(saturation,"-1")==0) { + return CAM_SATURATION_LEVEL_MINUS_1; + } + else if(strcmp(saturation,"0")==0) { + return CAM_SATURATION_LEVEL_DEFAULT; + } + else if(strcmp(saturation,"1")==0) { + return CAM_SATURATION_LEVEL_1; + } + else if(strcmp(saturation,"2")==0) { + return CAM_SATURATION_LEVEL_2; + } + else if(strcmp(saturation,"3")==0) { + return CAM_SATURATION_LEVEL_3; + } + /* Return default if string malformed */ + return CAM_SATURATION_LEVEL_DEFAULT; +} + +CAM_BRIGHTNESS_LEVEL arducam_mega_get_brightness(char* brightness) +{ + brightness=brightness+2; + brightness[strlen(brightness)-1] = '\0'; + if(strcmp(brightness,"-4")==0) { + return CAM_BRIGHTNESS_LEVEL_MINUS_4; + } + else if(strcmp(brightness,"-3")==0) { + return CAM_BRIGHTNESS_LEVEL_MINUS_3; + } + else if(strcmp(brightness,"-2")==0) { + return CAM_BRIGHTNESS_LEVEL_MINUS_2; + } + else if(strcmp(brightness,"-1")==0) { + return CAM_BRIGHTNESS_LEVEL_MINUS_1; + } + else if(strcmp(brightness,"0")==0) { + return CAM_BRIGHTNESS_LEVEL_DEFAULT; + } + else if(strcmp(brightness,"1")==0) { + return CAM_BRIGHTNESS_LEVEL_1; + } + else if(strcmp(brightness,"2")==0) { + return CAM_BRIGHTNESS_LEVEL_2; + } + else if(strcmp(brightness,"3")==0) { + return CAM_BRIGHTNESS_LEVEL_3; + } + else if(strcmp(brightness,"4")==0) { + return CAM_BRIGHTNESS_LEVEL_4; + } + /* Return default if string malformed */ + return CAM_BRIGHTNESS_LEVEL_DEFAULT; +} + +CAM_CONTRAST_LEVEL arducam_mega_get_contrast(char* contrast) +{ + contrast=contrast+2; + contrast[strlen(contrast)-1] = '\0'; + + if(strcmp(contrast,"-3")==0) { + return CAM_CONTRAST_LEVEL_MINUS_3; + } + else if(strcmp(contrast,"-2")==0) { + return CAM_CONTRAST_LEVEL_MINUS_2; + } + else if(strcmp(contrast,"-1")==0) { + return CAM_CONTRAST_LEVEL_MINUS_1; + } + else if(strcmp(contrast,"0")==0) { + return CAM_CONTRAST_LEVEL_DEFAULT; + } + else if(strcmp(contrast,"1")==0) { + return CAM_CONTRAST_LEVEL_1; + } + else if(strcmp(contrast,"2")==0) { + return CAM_CONTRAST_LEVEL_2; + } + else if(strcmp(contrast,"3")==0) { + return CAM_CONTRAST_LEVEL_3; + } + /* Return default if string malformed */ + return CAM_CONTRAST_LEVEL_DEFAULT; +} +int arducam_mega_set_saturation(CAM_SATURATION_LEVEL saturation) +{ + LOG_INF("Setting saturation to %d", saturation); + cameraWriteReg(CAM_REG_SATURATION_CONTROL, saturation); + camera_wait_idle(); + return 0; +} + +int arducam_mega_set_contrast(CAM_CONTRAST_LEVEL contrast) +{ + LOG_INF("Setting contrast to %d", contrast); + cameraWriteReg(CAM_REG_CONTRAST_CONTROL, contrast); + camera_wait_idle(); + return 0; +} + + +int arducam_mega_set_brightness(CAM_BRIGHTNESS_LEVEL brightness) +{ + LOG_INF("Setting brightness to %d", brightness); + cameraWriteReg(CAM_REG_BRIGHTNESS_CONTROL, brightness); + camera_wait_idle(); + return 0; +} + + +/* #define ARDUCAM_MEGA_DEFINE(inst) \ */ +/* static struct arducam_mega_data arducam_data_##inst; \ */ +/* static struct arducam_mega_config arducam_config_##inst = { \ */ +/* .spi_dt = SPI_DT_SPEC_GET(inst, \ */ +/* SPI_LOCK_ON | SPI_HOLD_ON_CS | SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8) | SPI_LINES_SINGLE, \ */ +/* 0), \ */ +/* }; \ */ +/* DEVICE_DT_DEFINE(inst, \ */ +/* arducam_init, NULL, \ */ +/* &arducam_data_##inst , &arducam_config_##inst, \ */ +/* POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ */ +/* NULL); */ + +/* DT_FOREACH_STATUS_OKAY(arducam_mega,ARDUCAM_MEGA_DEFINE); */ + /* .spi = SPI_DT_SPEC_INST_GET(inst, SPI_LOCK_ON | SPI_HOLD_ON_CS | SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8) | SPI_LINES_SINGLE,0), \ */ + /* .spi_cfg = { \ */ + /* .frequency = DT_PROP(DT_NODELABEL(spi0),clock_frequency), \ */ + /* .operation = SPI_LOCK_ON | SPI_HOLD_ON_CS | SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8) | SPI_LINES_SINGLE, \ */ + /* .slave = 0,.cs=SPI_CS_CONTROL_INIT(DT_NODELABEL(spi0), 0u) \ */ + /* }, \ */ +/* DT_INST_FOREACH_STATUS_OKAY(ARDUCAM_MEGA_DEFINE) */ diff --git a/arducam_mega.h b/arducam_mega.h new file mode 100644 index 0000000..b2db8bf --- /dev/null +++ b/arducam_mega.h @@ -0,0 +1,330 @@ +#ifndef __ARDUCAM_MEGA_H__ +#define __ARDUCAM_MEGA_H__ + +#include +#include +#include + + +#define ARDUCHIP_FRAMES 0x01 +#define ARDUCHIP_TEST1 0x00 //TEST register +#define ARDUCHIP_FIFO 0x04 //FIFO and I2C control +#define ARDUCHIP_FIFO_2 0x07 // FIFO and I2C control +#define FIFO_CLEAR_MASK 0x80 +#define FIFO_CLEAR_ID_MASK 0x01 +#define FIFO_START_MASK 0x02 +#define FIFO_RDPTR_RST_MASK 0x10 +#define FIFO_WRPTR_RST_MASK 0x20 + +#define ARDUCHIP_TRIG 0x44 //Trigger source +#define VSYNC_MASK 0x01 +#define SHUTTER_MASK 0x02 +#define CAP_DONE_MASK 0x04 + + +#define FIFO_SIZE1 0x45 //Camera write FIFO size[7:0] for burst to read +#define FIFO_SIZE2 0x46 //Camera write FIFO size[15:8] +#define FIFO_SIZE3 0x47 //Camera write FIFO size[18:16] + + +#define BURST_FIFO_READ 0x3C //Burst FIFO read operation +#define SINGLE_FIFO_READ 0x3D //Single FIFO read operation + +#define BUF_MAX_LENGTH 255 + +#define CAPRURE_MAX_NUM 0xff + +#define CAM_REG_POWER_CONTROL 0X02 +#define CAM_REG_SENSOR_RESET 0X07 +#define CAM_REG_FORMAT 0X20 +#define CAM_REG_CAPTURE_RESOLUTION 0X21 +#define CAM_REG_BRIGHTNESS_CONTROL 0X22 +#define CAM_REG_CONTRAST_CONTROL 0X23 +#define CAM_REG_SATURATION_CONTROL 0X24 +#define CAM_REG_EV_CONTROL 0X25 +#define CAM_REG_WHILEBALANCE_MODE_CONTROL 0X26 +#define CAM_REG_COLOR_EFFECT_CONTROL 0X27 +#define CAM_REG_SHARPNESS_CONTROL 0X28 +#define CAM_REG_AUTO_FOCUS_CONTROL 0X29 +#define CAM_REG_EXPOSURE_GAIN_WHILEBALANCE_CONTROL 0X2A +#define CAM_REG_MANUAL_GAIN_BIT_9_8 0X2B +#define CAM_REG_MANUAL_GAIN_BIT_7_0 0X2C +#define CAM_REG_MANUAL_EXPOSURE_BIT_19_16 0X2D +#define CAM_REG_MANUAL_EXPOSURE_BIT_15_8 0X2E +#define CAM_REG_MANUAL_EXPOSURE_BIT_7_0 0X2F +#define CAM_REG_SENSOR_ID 0x40 +#define CAM_REG_YEAR_ID 0x41 +#define CAM_REG_MONTH_ID 0x42 +#define CAM_REG_DAY_ID 0x43 +#define CAM_REG_SENSOR_STATE 0x44 +#define CAM_REG_DEBUG_DEVICE_ADDRESS 0X0A +#define CAM_REG_DEBUG_REGISTER_HIGH 0X0B +#define CAM_REG_DEBUG_REGISTER_LOW 0X0C +#define CAM_REG_DEBUG_REGISTER_VALUE 0X0D + +#define CAM_REG_SENSOR_STATE_IDLE (1<<1) +#define CAM_SENSOR_RESET_ENABLE (1<<6) +#define CAM_FORMAT_BASICS (0<<0) +#define CAM_SET_CAPTURE_MODE (0<<7) +#define CAM_SET_VIDEO_MODE (1<<7) + +#define SET_WHILEBALANCE 0X02 +#define SET_EXPOSURE 0X01 +#define SET_GAIN 0X00 + +#define CAMERA_TYPE_NUMBER 2 + +#define CAMERA_OV5640 0 +#define CAMERA_OV3640 1 + +#define FORMAT_JPEG 0X01 +#define FORMAT_RGB 0X02 +#define FORMAT_YUV 0X03 + + + + +#define RESOLUTION_160X120 (1<<0) +#define RESOLUTION_320X240 (1<<1) +#define RESOLUTION_640X480 (1<<2) +#define RESOLUTION_800X600 (1<<3) +#define RESOLUTION_1280X720 (1<<4) +#define RESOLUTION_1280X960 (1<<5) +#define RESOLUTION_1600X1200 (1<<6) +#define RESOLUTION_1920X1080 (1<<7) +#define RESOLUTION_2048X1536 (1<<8) +#define RESOLUTION_2592X1944 (1<<9) +#define RESOLUTION_320x320 (1<<10) +#define RESOLUTION_128x128 (1<<11) +#define RESOLUTION_96x96 (1<<12) + +#define SPECIAL_NORMAL (0<<0) +#define SPECIAL_BLUEISH (1<<0) +#define SPECIAL_REDISH (1<<1) +#define SPECIAL_BW (1<<2) +#define SPECIAL_SEPIA (1<<3) +#define SPECIAL_NEGATIVE (1<<4) +#define SPECIAL_GREENISH (1<<5) +#define SPECIAL_YELLOWISH (1<<8) + +/** + * @enum CamStatus + * @brief Camera status + */ +typedef enum +{ + CAM_ERR_SUCCESS = 0, /** Date: Fri, 22 Sep 2023 21:26:21 +0530 Subject: [PATCH 2/5] fix: Use underscores instead of camelCase everywhere --- arducam_mega.c | 62 +++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/arducam_mega.c b/arducam_mega.c index 577485f..b39f6f7 100644 --- a/arducam_mega.c +++ b/arducam_mega.c @@ -10,7 +10,7 @@ #define LOG_MODULE_NAME arducam_mega LOG_MODULE_REGISTER(LOG_MODULE_NAME); #define MAX_PATH 51200 -#define SOME_REQUIRED_LEN 30 +#define CONCAT_BUFF_LEN 30 struct spi_config spi_cfg = { .frequency = DT_PROP(DT_NODELABEL(spi0), clock_frequency), @@ -26,7 +26,7 @@ const struct device *spi; struct gpio_dt_spec spec = GPIO_DT_SPEC_GET(LED, gpios); -uint8_t cameraBusRead(uint8_t address) +uint8_t camera_bus_read(uint8_t address) { int ret; struct spi_buf tx_buf[1]; @@ -52,13 +52,13 @@ uint8_t cameraBusRead(uint8_t address) return rxdata[1]; } -uint8_t cameraReadReg(uint8_t addr) +uint8_t camera_read_reg(uint8_t addr) { - return cameraBusRead(addr & 0x7F); + return camera_bus_read(addr & 0x7F); } -uint8_t cameraBusWrite(uint8_t address, uint8_t value) +uint8_t camera_bus_write(uint8_t address, uint8_t value) { struct spi_buf tx_buf[2]; @@ -75,23 +75,23 @@ uint8_t cameraBusWrite(uint8_t address, uint8_t value) k_sleep(K_MSEC(10)); return 1; } -void cameraWriteReg( uint8_t addr, uint8_t val) +void camera_write_reg( uint8_t addr, uint8_t val) { - cameraBusWrite( addr | 0x80, val); + camera_bus_write( addr | 0x80, val); } void camera_wait_idle() { - while ((cameraReadReg(CAM_REG_SENSOR_STATE) & 0X03) != CAM_REG_SENSOR_STATE_IDLE) { + while ((camera_read_reg(CAM_REG_SENSOR_STATE) & 0X03) != CAM_REG_SENSOR_STATE_IDLE) { k_sleep(K_MSEC(2)); } } -uint8_t cameraGetBit(uint8_t addr, uint8_t bit) +uint8_t camera_get_bit(uint8_t addr, uint8_t bit) { uint8_t temp; - temp = cameraReadReg(addr); + temp = camera_read_reg(addr); temp = temp & bit; return temp; } @@ -137,7 +137,7 @@ uint8_t cameraReadByte(){ } -void cameraSaveFIFO(const char* base_path, uint32_t length, char* filename){ +void camera_save_fifo(const char* base_path, uint32_t length, char* filename){ received_length = length; char path[MAX_PATH]; struct fs_file_t file; @@ -145,7 +145,7 @@ void cameraSaveFIFO(const char* base_path, uint32_t length, char* filename){ fs_file_t_init(&file); - if (base >= (sizeof(path) - SOME_REQUIRED_LEN)) { + if (base >= (sizeof(path) - CONCAT_BUFF_LEN)) { LOG_ERR("Not enough concatenation buffer to create file paths"); return; } @@ -201,33 +201,33 @@ void cameraSaveFIFO(const char* base_path, uint32_t length, char* filename){ int arducam_mega_take_picture(CAM_IMAGE_MODE mode, CAM_IMAGE_PIX_FMT pixel_format) { - cameraWriteReg(CAM_REG_SENSOR_RESET, CAM_SENSOR_RESET_ENABLE); + camera_write_reg(CAM_REG_SENSOR_RESET, CAM_SENSOR_RESET_ENABLE); camera_wait_idle(); - cameraWriteReg(0x04, 0x01); - cameraWriteReg(0x04, 0x02); + camera_write_reg(0x04, 0x01); + camera_write_reg(0x04, 0x02); camera_wait_idle(); k_sleep(K_MSEC(300)); /* Set format JPG */ - cameraWriteReg(CAM_REG_FORMAT, CAM_IMAGE_PIX_FMT_JPG); // set the data format + camera_write_reg(CAM_REG_FORMAT, CAM_IMAGE_PIX_FMT_JPG); // set the data format camera_wait_idle(); // Wait I2c Idle /* Set capture resolution */ - cameraWriteReg(CAM_REG_CAPTURE_RESOLUTION, CAM_SET_CAPTURE_MODE | mode); + camera_write_reg(CAM_REG_CAPTURE_RESOLUTION, CAM_SET_CAPTURE_MODE | mode); camera_wait_idle(); // Wait I2c Idle /* Clear fifo flags */ - cameraWriteReg(ARDUCHIP_FIFO, FIFO_CLEAR_ID_MASK); + camera_write_reg(ARDUCHIP_FIFO, FIFO_CLEAR_ID_MASK); /* Start capture */ - cameraWriteReg(ARDUCHIP_FIFO, FIFO_START_MASK); + camera_write_reg(ARDUCHIP_FIFO, FIFO_START_MASK); - while (cameraGetBit(ARDUCHIP_TRIG, CAP_DONE_MASK) == 0) + while (camera_get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK) == 0) ; uint32_t len1, len2, len3, length = 0; - len1 = cameraReadReg(FIFO_SIZE1); - len2 = cameraReadReg(FIFO_SIZE2); - len3 = cameraReadReg(FIFO_SIZE3); + len1 = camera_read_reg(FIFO_SIZE1); + len2 = camera_read_reg(FIFO_SIZE2); + len3 = camera_read_reg(FIFO_SIZE3); length = ((len3 << 16) | (len2 << 8) | len1) & 0xffffff; LOG_INF("Image length is %d\n", length); return length; @@ -240,13 +240,13 @@ int arducam_mega_save_picture(const char* filename, const char* mount_point) /* 0x87,0x40*/ /* Read FIFO length */ uint32_t len1, len2, len3, length = 0; - len1 = cameraReadReg(FIFO_SIZE1); - len2 = cameraReadReg(FIFO_SIZE2); - len3 = cameraReadReg(FIFO_SIZE3); + len1 = camera_read_reg(FIFO_SIZE1); + len2 = camera_read_reg(FIFO_SIZE2); + len3 = camera_read_reg(FIFO_SIZE3); length = ((len3 << 16) | (len2 << 8) | len1) & 0xffffff; LOG_INF("Image length is %d\n", length); - cameraSaveFIFO(mount_point,length,filename); + camera_save_fifo(mount_point,length,filename); return 0; } @@ -254,7 +254,7 @@ int arducam_mega_save_picture(const char* filename, const char* mount_point) int arducam_mega_get_cameraid() { uint8_t cameraID; - cameraID = cameraReadReg(CAM_REG_SENSOR_ID); + cameraID = camera_read_reg(CAM_REG_SENSOR_ID); LOG_INF("Sensor camera ID is %x\n", cameraID); return cameraID; @@ -417,7 +417,7 @@ CAM_CONTRAST_LEVEL arducam_mega_get_contrast(char* contrast) int arducam_mega_set_saturation(CAM_SATURATION_LEVEL saturation) { LOG_INF("Setting saturation to %d", saturation); - cameraWriteReg(CAM_REG_SATURATION_CONTROL, saturation); + camera_write_reg(CAM_REG_SATURATION_CONTROL, saturation); camera_wait_idle(); return 0; } @@ -425,7 +425,7 @@ int arducam_mega_set_saturation(CAM_SATURATION_LEVEL saturation) int arducam_mega_set_contrast(CAM_CONTRAST_LEVEL contrast) { LOG_INF("Setting contrast to %d", contrast); - cameraWriteReg(CAM_REG_CONTRAST_CONTROL, contrast); + camera_write_reg(CAM_REG_CONTRAST_CONTROL, contrast); camera_wait_idle(); return 0; } @@ -434,7 +434,7 @@ int arducam_mega_set_contrast(CAM_CONTRAST_LEVEL contrast) int arducam_mega_set_brightness(CAM_BRIGHTNESS_LEVEL brightness) { LOG_INF("Setting brightness to %d", brightness); - cameraWriteReg(CAM_REG_BRIGHTNESS_CONTROL, brightness); + camera_write_reg(CAM_REG_BRIGHTNESS_CONTROL, brightness); camera_wait_idle(); return 0; } From 7bf5ec6f0d4f0ba0522f45bc22f1d59b2424f204 Mon Sep 17 00:00:00 2001 From: Anuj Deshpande Date: Fri, 22 Sep 2023 21:32:07 +0530 Subject: [PATCH 3/5] fix: move global variables to function --- arducam_mega.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/arducam_mega.c b/arducam_mega.c index b39f6f7..3b171e8 100644 --- a/arducam_mega.c +++ b/arducam_mega.c @@ -11,6 +11,9 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME); #define MAX_PATH 51200 #define CONCAT_BUFF_LEN 30 +#define BUFFER_SIZE 0xff + +int received_length; struct spi_config spi_cfg = { .frequency = DT_PROP(DT_NODELABEL(spi0), clock_frequency), @@ -96,13 +99,6 @@ uint8_t camera_get_bit(uint8_t addr, uint8_t bit) return temp; } -#define BUFFER_SIZE 0xff -uint8_t imageData = 0; -uint8_t imageDataNext = 0; -uint8_t headFlag = 0; -unsigned int i =0; -uint8_t imageBuff[BUFFER_SIZE] = {0}; -int received_length=0; uint8_t cameraReadByte(){ int ret; @@ -138,6 +134,16 @@ uint8_t cameraReadByte(){ void camera_save_fifo(const char* base_path, uint32_t length, char* filename){ + + uint8_t imageData = 0; + uint8_t imageDataNext = 0; + uint8_t headFlag = 0; + unsigned int i =0; + uint8_t imageBuff[BUFFER_SIZE] = {0}; + int ret; + uint8_t sd_write_counts=0; + uint8_t file_opened = 0; + received_length = length; char path[MAX_PATH]; struct fs_file_t file; @@ -155,9 +161,6 @@ void camera_save_fifo(const char* base_path, uint32_t length, char* filename){ path[base++] = '/'; path[base] = 0; strcat(&path[base], filename); - int ret; - uint8_t sd_write_counts=0; - uint8_t file_opened = 0; while (received_length){ imageData = imageDataNext; imageDataNext = cameraReadByte(); From 4802ccff16d4d10bd96461d582f3d25821fd5694 Mon Sep 17 00:00:00 2001 From: Anuj Deshpande Date: Sat, 23 Sep 2023 16:23:52 +0530 Subject: [PATCH 4/5] fix: minor changes to get rid of gcc warnings --- arducam_mega.c | 4 ++-- arducam_mega.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arducam_mega.c b/arducam_mega.c index 3b171e8..808a05b 100644 --- a/arducam_mega.c +++ b/arducam_mega.c @@ -73,7 +73,7 @@ uint8_t camera_bus_write(uint8_t address, uint8_t value) spi_cfg.operation |= SPI_HOLD_ON_CS; spi_cfg.operation |= SPI_LOCK_ON; gpio_pin_toggle_dt(&spec); - int ret = spi_write(spi, &spi_cfg,&tx_bufs); + spi_write(spi, &spi_cfg,&tx_bufs); gpio_pin_toggle_dt(&spec); k_sleep(K_MSEC(10)); return 1; @@ -235,7 +235,7 @@ int arducam_mega_take_picture(CAM_IMAGE_MODE mode, CAM_IMAGE_PIX_FMT pixel_forma LOG_INF("Image length is %d\n", length); return length; } -int arducam_mega_save_picture(const char* filename, const char* mount_point) +int arducam_mega_save_picture(char* filename, const char* mount_point) { LOG_INF("Saving picture\n"); /* Begin function */ diff --git a/arducam_mega.h b/arducam_mega.h index b2db8bf..3bb9308 100644 --- a/arducam_mega.h +++ b/arducam_mega.h @@ -315,7 +315,7 @@ struct arducam_mega_config { int arducam_mega_init(); int arducam_mega_take_picture(CAM_IMAGE_MODE mode, CAM_IMAGE_PIX_FMT pixel_format); -int arducam_mega_save_picture(const char* filename, const char* mount_point); +int arducam_mega_save_picture(char* filename, const char* mount_point); int arducam_mega_get_cameraid(); CAM_IMAGE_MODE arducam_mega_get_resolution(char* resolution); From 9efd29799c16aecbafb95e754fd02f5527e7149f Mon Sep 17 00:00:00 2001 From: Anuj Deshpande Date: Tue, 3 Oct 2023 15:55:22 +0530 Subject: [PATCH 5/5] fix: use burst read --- arducam_mega.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++--- arducam_mega.h | 11 +++++ 2 files changed, 114 insertions(+), 5 deletions(-) diff --git a/arducam_mega.c b/arducam_mega.c index 808a05b..cb5ecd7 100644 --- a/arducam_mega.c +++ b/arducam_mega.c @@ -1,3 +1,12 @@ +/* + * This file is part of the Arribada Time Lapse Camera project. + * + * Copyright 2023 Arribada Initiative C.I.C. + * + * This work is licensed under the MIT license, see the file LICENSE for + * details. + */ + #include "arducam_mega.h" #include #include @@ -91,6 +100,7 @@ void camera_wait_idle() } } + uint8_t camera_get_bit(uint8_t addr, uint8_t bit) { uint8_t temp; @@ -100,7 +110,7 @@ uint8_t camera_get_bit(uint8_t addr, uint8_t bit) } -uint8_t cameraReadByte(){ +uint8_t camera_read_byte(){ int ret; uint8_t rxdata[1]; struct spi_buf rx_buf[1] = { @@ -132,6 +142,94 @@ uint8_t cameraReadByte(){ return rxdata[0]; } +/* uint8_t buff[2048]; */ + +uint32_t camera_read_burst(uint8_t* buff, uint32_t length) +{ + int ret; + if(received_length < length) + length = received_length; + + /*Set fifo burst mode register*/ + struct spi_buf tx_buf[1]; + uint8_t fifo_reg = BURST_FIFO_READ; + tx_buf[0].buf = &fifo_reg; + tx_buf[0].len = 1; + struct spi_buf_set tx_bufs = {.buffers = tx_buf, .count = 1}; + spi_cfg.operation |= SPI_HOLD_ON_CS; + spi_cfg.operation |= SPI_LOCK_ON; + gpio_pin_toggle_dt(&spec); + spi_write(spi, &spi_cfg,&tx_bufs); + /* gpio_pin_toggle_dt(&spec); */ + /* k_sleep(K_MSEC(10)); */ + + /* Write a blank */ + struct spi_buf tx_buf2[1]; + uint8_t blank=0x00; + tx_buf2[0].buf = ␣ + tx_buf2[0].len = 1; + struct spi_buf_set tx_bufs2 = {.buffers = tx_buf2, .count = 1}; + spi_cfg.operation |= SPI_HOLD_ON_CS; + spi_cfg.operation |= SPI_LOCK_ON; + /* gpio_pin_toggle_dt(&spec); */ + spi_write(spi, &spi_cfg,&tx_bufs2); + /* gpio_pin_toggle_dt(&spec); */ + /* k_sleep(K_MSEC(10)); */ + + + /* Read burst */ + /* LOG_INF("Burst reading length %d", length); */ + struct spi_buf rx_buf[1] = { + {.buf=buff, .len = length}, + }; + struct spi_buf_set rx_bufs = { + .buffers = rx_buf, + .count = 2 }; + + /* gpio_pin_toggle_dt(&spec); */ + ret = spi_read(spi, &spi_cfg,&rx_bufs); + gpio_pin_toggle_dt(&spec); + spi_release(spi, &spi_cfg); + received_length -= length; + return length; + +} + +#define BUFF_SIZE 32768 +void camera_save_fifo_burst(const char* base_path, uint32_t image_length, char* filename){ + char path[MAX_PATH]; + struct fs_file_t file; + int base = strlen(base_path); + uint8_t buff[BUFF_SIZE]; + uint32_t length; + int ret, sd_write_counts=0; + fs_file_t_init(&file); + received_length = image_length; + + if (base >= (sizeof(path) - CONCAT_BUFF_LEN)) { + LOG_ERR("Not enough concatenation buffer to create file paths"); + return; + } + + strncpy(path, base_path, sizeof(path)); + + path[base++] = '/'; + path[base] = 0; + strcat(&path[base], filename); + ret = fs_open(&file, path,FS_O_CREATE | FS_O_WRITE | FS_O_APPEND); + if(ret != 0){ + LOG_ERR("Failed to create file %s %d", path, ret); + return; + } + LOG_INF("Opened file successfully\n"); + while(received_length) { + length = camera_read_burst(buff, BUFF_SIZE); + ret = fs_write(&file, buff, length); + sd_write_counts++; + } + LOG_INF("Closed file with sd_write_counts %d\n", sd_write_counts); + fs_close(&file); +} void camera_save_fifo(const char* base_path, uint32_t length, char* filename){ @@ -163,7 +261,7 @@ void camera_save_fifo(const char* base_path, uint32_t length, char* filename){ strcat(&path[base], filename); while (received_length){ imageData = imageDataNext; - imageDataNext = cameraReadByte(); + imageDataNext = camera_read_byte(); if (headFlag == 1) { imageBuff[i++]=imageDataNext; @@ -190,7 +288,7 @@ void camera_save_fifo(const char* base_path, uint32_t length, char* filename){ if (imageData == 0xff && imageDataNext ==0xd9) { headFlag = 0; - LOG_INF("Closed file with sd_write_counts %d\n", sd_write_counts); + LOG_INF("Closed file with sd_write_counts %d received length left %d\n", sd_write_counts,received_length); fs_write(&file, imageBuff, i); fs_close(&file); @@ -222,6 +320,7 @@ int arducam_mega_take_picture(CAM_IMAGE_MODE mode, CAM_IMAGE_PIX_FMT pixel_forma /* Clear fifo flags */ camera_write_reg(ARDUCHIP_FIFO, FIFO_CLEAR_ID_MASK); + camera_wait_idle(); /* Start capture */ camera_write_reg(ARDUCHIP_FIFO, FIFO_START_MASK); @@ -249,8 +348,7 @@ int arducam_mega_save_picture(char* filename, const char* mount_point) length = ((len3 << 16) | (len2 << 8) | len1) & 0xffffff; LOG_INF("Image length is %d\n", length); - camera_save_fifo(mount_point,length,filename); - + camera_save_fifo_burst(mount_point,length,filename); return 0; } diff --git a/arducam_mega.h b/arducam_mega.h index 3bb9308..211d4e9 100644 --- a/arducam_mega.h +++ b/arducam_mega.h @@ -1,3 +1,14 @@ +/* + * This file is part of the Arducam SPI Camera project. + * + * Copyright 2021 Arducam Technology co., Ltd. All Rights Reserved. + * Copyright 2023 Arribada Initiative C.I.C + * + * This work is licensed under the MIT license, see the file LICENSE for + * details. + * + */ + #ifndef __ARDUCAM_MEGA_H__ #define __ARDUCAM_MEGA_H__