/* * Copyright (c) 2006-2023, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2023-03-24 spaceman the first version */ #include "board.h" #ifdef BSP_USING_OV2640 #include #include #include #include #include #include "drv_dcmi.h" #include "drv_ov2640.h" #include "drv_ov2640_cfg.h" #define DRV_DEBUG //#define CAMERA_DUMP #define LOG_TAG "drv.ov2640" #include #define CHIP_ADDRESS 0x30 /* OV2640 address */ // #define CHIP_ADDRESS 0x3C /* OV5640 address */ #define I2C_NAME "i2c1" #define PWDN_PIN GET_PIN(D, 14) struct rt_i2c_bus_device *i2c_bus = RT_NULL; #if defined(CAMERA_DUMP) #define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ') static void dump_hex(const rt_uint8_t *ptr, rt_size_t buflen) { unsigned char *buf = (unsigned char *)ptr; int i, j; for (i = 0; i < buflen; i += 16) { rt_kprintf("%08x:", i); for (j = 0; j < 16; j++) { if (i + j < buflen) { rt_kprintf("%02x", buf[i + j]); } else { rt_kprintf(" "); } } rt_kprintf(" "); for (j = 0; j < 16; j++) { if (i + j < buflen) { rt_kprintf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.'); } } rt_kprintf("\n"); } } #endif /* i2c read reg */ static rt_err_t read_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t len, rt_uint8_t *buf) { struct rt_i2c_msg msg[2] = {0, 0}; RT_ASSERT(bus != RT_NULL); msg[0].addr = CHIP_ADDRESS; msg[0].flags = RT_I2C_WR; msg[0].buf = ® msg[0].len = 1; msg[1].addr = CHIP_ADDRESS; msg[1].flags = RT_I2C_RD; msg[1].len = len; msg[1].buf = buf; if (rt_i2c_transfer(bus, msg, 2) == 2) { return RT_EOK; } return -RT_ERROR; } /* i2c write reg */ static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t data) { rt_uint8_t buf[2]; struct rt_i2c_msg msgs; RT_ASSERT(bus != RT_NULL); buf[0] = reg; buf[1] = data; msgs.addr = CHIP_ADDRESS; msgs.flags = RT_I2C_WR; msgs.buf = buf; msgs.len = 2; if (rt_i2c_transfer(bus, &msgs, 1) == 1) { return RT_EOK; } return -RT_ERROR; } static rt_err_t ov2640_read_id(struct rt_i2c_bus_device *bus, rt_uint16_t *id) { rt_uint8_t read_value[2]; write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_SENSOR); // 选择 SENSOR 寄存器组 read_reg(bus, OV2640_SENSOR_PIDH, 1, &read_value[0]); // 读取ID高字节 read_reg(bus, OV2640_SENSOR_PIDL, 1, &read_value[1]); // 读取ID低字节 *id = ((rt_uint16_t)(read_value[0] << 8) & 0xFF00); *id |= ((rt_uint16_t)(read_value[1]) & 0x00FF); if ((*id != OV2640_ID1) && (*id != OV2640_ID2)) { LOG_E("ov2640 init error, id: 0x%04x", *id); return -RT_ERROR; } LOG_I("ov2640 init success, id: 0x%04x", *id); return RT_EOK; } static rt_err_t ov2640_reset(struct rt_i2c_bus_device *bus) { rt_pin_mode(PWDN_PIN, PIN_MODE_OUTPUT); rt_thread_mdelay(5); // 等待模块上电稳定,最少5ms,然后拉低PWDN rt_pin_write(PWDN_PIN, PIN_LOW); // PWDN 引脚输出低电平,不开启掉电模式,摄像头正常工作,此时摄像头模块的白色LED会点亮 // 根据OV2640的上电时序,硬件复位的持续时间要>=3ms,反客的OV2640采用硬件RC复位,持续时间大概在6ms左右 // 因此加入延时,等待硬件复位完成并稳定下来 rt_thread_mdelay(5); write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_SENSOR); // 选择 SENSOR 寄存器组 write_reg(bus, OV2640_SENSOR_COM7, 0x80); // 启动软件复位 // 根据OV2640的软件复位时序,软件复位执行后,要>=2ms方可执行SCCB配置,此处采用保守一点的参数,延时10ms rt_thread_mdelay(10); return RT_EOK; } static rt_err_t ov2640_config(struct rt_i2c_bus_device *bus, const rt_uint8_t (*configdata)[2]) { rt_uint32_t i = 0; for (i = 0; configdata[i][0]; i++) { write_reg(bus, configdata[i][0], configdata[i][1]); // 进行参数配置 } return RT_EOK; } void ov2640_set_pixformat(struct rt_i2c_bus_device *bus, rt_uint8_t pixformat) { const rt_uint8_t(*configdata)[2]; uint32_t i; // 计数变量 switch (pixformat) { case Pixformat_RGB565: configdata = OV2640_RGB565_Config; break; case Pixformat_JPEG: configdata = OV2640_JPEG_Config; break; default: break; } for (i = 0; configdata[i][0]; i++) { write_reg(bus, configdata[i][0], configdata[i][1]); // 进行参数配置 } } rt_err_t ov2640_set_framesize(struct rt_i2c_bus_device *bus, rt_uint16_t width, rt_uint16_t height) { if ((width % 4) || (height % 4)) // 输出图像的大小一定要能被4整除 { return -RT_ERROR; // 返回错误标志 } write_reg(bus, OV2640_SEL_Registers,OV2640_SEL_DSP); // 选择 dsp寄存器组 write_reg(bus, 0x5a, width / 4 & 0xff); // 实际图像输出的宽度(outw),7~0 bit,寄存器的值等于实际值/4 write_reg(bus, 0x5b, height / 4 & 0xff); // 实际图像输出的高度(outh),7~0 bit,寄存器的值等于实际值/4 write_reg(bus, 0x5c, (width / 4 >> 8 & 0x03) | (height / 4 >> 6 & 0x04)); // 设置zmhh的bit[2:0],也就是outh 的第 8 bit,outw 的第 9~8 bit, write_reg(bus, OV2640_DSP_RESET, 0x00); // 复位 return RT_EOK; // 成功 } rt_err_t ov2640_set_horizontal_mirror(struct rt_i2c_bus_device *bus, rt_uint8_t configstate) { rt_uint8_t ov2640_reg; // 寄存器的值 write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_SENSOR); // 选择 sensor 寄存器组 read_reg(bus, OV2640_SENSOR_REG04, 1, &ov2640_reg); // 读取 0x04 的寄存器值 // reg04,寄存器组4,寄存器地址为 0x04,该寄存器的bit[7],用于设置水平是否镜像 if (configstate == OV2640_Enable) // 如果使能镜像 { ov2640_reg |= 0x80; // bit[7]置1则镜像 } else // 取消镜像 { ov2640_reg &= ~0x80; // bit[7]置0则是正常模式 } return write_reg(bus, OV2640_SENSOR_REG04, ov2640_reg); // 写入寄存器 } rt_err_t ov2640_set_vertical_flip(struct rt_i2c_bus_device *bus, rt_uint8_t configstate) { rt_uint8_t ov2640_reg; // 寄存器的值 write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_SENSOR); // 选择 sensor 寄存器组 read_reg(bus, OV2640_SENSOR_REG04, 1, &ov2640_reg); // 读取 0x04 的寄存器值 // reg04,寄存器组4,寄存器地址为 0x04,该寄存器的第bit[6],用于设置水平是垂直翻转 if (configstate == OV2640_Enable) { // 此处设置参考openmv的驱动 // bit[4]具体的作用是什么手册没有说,如果垂直翻转之后,该位不置1的话,颜色会不对 ov2640_reg |= 0x40 | 0x10; // bit[6]置1时,图像会垂直翻转 } else // 取消翻转 { ov2640_reg &= ~(0x40 | 0x10); // 将bit[6]和bit[4]都写0 } return write_reg(bus, OV2640_SENSOR_REG04, ov2640_reg); // 写入寄存器 } void ov2640_set_saturation(struct rt_i2c_bus_device *bus, rt_int8_t saturation) { write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_DSP); // 选择 dsp寄存器组 switch (saturation) { case 2: write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x02); write_reg(bus, OV2640_DSP_BPADDR, 0x03); write_reg(bus, OV2640_DSP_BPDATA, 0x68); write_reg(bus, OV2640_DSP_BPDATA, 0x68); break; case 1: write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x02); write_reg(bus, OV2640_DSP_BPADDR, 0x03); write_reg(bus, OV2640_DSP_BPDATA, 0x58); write_reg(bus, OV2640_DSP_BPDATA, 0x58); break; case 0: write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x02); write_reg(bus, OV2640_DSP_BPADDR, 0x03); write_reg(bus, OV2640_DSP_BPDATA, 0x48); write_reg(bus, OV2640_DSP_BPDATA, 0x48); break; case -1: write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x02); write_reg(bus, OV2640_DSP_BPADDR, 0x03); write_reg(bus, OV2640_DSP_BPDATA, 0x38); write_reg(bus, OV2640_DSP_BPDATA, 0x38); break; case -2: write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x02); write_reg(bus, OV2640_DSP_BPADDR, 0x03); write_reg(bus, OV2640_DSP_BPDATA, 0x28); write_reg(bus, OV2640_DSP_BPDATA, 0x28); break; default: break; } } void ov2640_set_brightness(struct rt_i2c_bus_device *bus, rt_int8_t brightness) { write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_DSP); // 选择 dsp寄存器组 switch (brightness) { case 2: write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x04); write_reg(bus, OV2640_DSP_BPADDR, 0x09); write_reg(bus, OV2640_DSP_BPDATA, 0x40); write_reg(bus, OV2640_DSP_BPDATA, 0x00); break; case 1: write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x04); write_reg(bus, OV2640_DSP_BPADDR, 0x09); write_reg(bus, OV2640_DSP_BPDATA, 0x30); write_reg(bus, OV2640_DSP_BPDATA, 0x00); break; case 0: write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x04); write_reg(bus, OV2640_DSP_BPADDR, 0x09); write_reg(bus, OV2640_DSP_BPDATA, 0x20); write_reg(bus, OV2640_DSP_BPDATA, 0x00); break; case -1: write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x04); write_reg(bus, OV2640_DSP_BPADDR, 0x09); write_reg(bus, OV2640_DSP_BPDATA, 0x10); write_reg(bus, OV2640_DSP_BPDATA, 0x00); break; case -2: write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x04); write_reg(bus, OV2640_DSP_BPADDR, 0x09); write_reg(bus, OV2640_DSP_BPDATA, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x00); break; default: break; } } void ov2640_set_contrast(struct rt_i2c_bus_device *bus, rt_int8_t contrast) { write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_DSP); // 选择 dsp寄存器组 switch (contrast) { case 2: write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x04); write_reg(bus, OV2640_DSP_BPADDR, 0x07); write_reg(bus, OV2640_DSP_BPDATA, 0x20); write_reg(bus, OV2640_DSP_BPADDR, 0x28); write_reg(bus, OV2640_DSP_BPDATA, 0x0c); write_reg(bus, OV2640_DSP_BPDATA, 0x06); break; case 1: write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x04); write_reg(bus, OV2640_DSP_BPADDR, 0x07); write_reg(bus, OV2640_DSP_BPDATA, 0x20); write_reg(bus, OV2640_DSP_BPADDR, 0x24); write_reg(bus, OV2640_DSP_BPDATA, 0x16); write_reg(bus, OV2640_DSP_BPDATA, 0x06); break; case 0: write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x04); write_reg(bus, OV2640_DSP_BPADDR, 0x07); write_reg(bus, OV2640_DSP_BPDATA, 0x20); write_reg(bus, OV2640_DSP_BPADDR, 0x20); write_reg(bus, OV2640_DSP_BPDATA, 0x20); write_reg(bus, OV2640_DSP_BPDATA, 0x06); break; case -1: write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x04); write_reg(bus, OV2640_DSP_BPADDR, 0x07); write_reg(bus, OV2640_DSP_BPDATA, 0x20); write_reg(bus, OV2640_DSP_BPADDR, 0x1c); write_reg(bus, OV2640_DSP_BPDATA, 0x2a); write_reg(bus, OV2640_DSP_BPDATA, 0x06); break; case -2: write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x04); write_reg(bus, OV2640_DSP_BPADDR, 0x07); write_reg(bus, OV2640_DSP_BPDATA, 0x20); write_reg(bus, OV2640_DSP_BPADDR, 0x18); write_reg(bus, OV2640_DSP_BPDATA, 0x34); write_reg(bus, OV2640_DSP_BPDATA, 0x06); break; default: break; } } void ov2640_set_effect(struct rt_i2c_bus_device *bus, rt_uint8_t effect_mode) { write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_DSP); // 选择 dsp寄存器组 switch (effect_mode) { case OV2640_Effect_Normal: // 正常模式 write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x00); write_reg(bus, OV2640_DSP_BPADDR, 0x05); write_reg(bus, OV2640_DSP_BPDATA, 0x80); write_reg(bus, OV2640_DSP_BPDATA, 0x80); break; case OV2640_Effect_Negative: // 负片模式,也就是颜色全部取反 write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x40); write_reg(bus, OV2640_DSP_BPADDR, 0x05); write_reg(bus, OV2640_DSP_BPDATA, 0x80); write_reg(bus, OV2640_DSP_BPDATA, 0x80); break; case OV2640_Effect_BW: // 黑白模式 write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x18); write_reg(bus, OV2640_DSP_BPADDR, 0x05); write_reg(bus, OV2640_DSP_BPDATA, 0x80); write_reg(bus, OV2640_DSP_BPDATA, 0x80); break; case OV2640_Effect_BW_Negative: // 黑白+负片模式 write_reg(bus, OV2640_DSP_BPADDR, 0x00); write_reg(bus, OV2640_DSP_BPDATA, 0x58); write_reg(bus, OV2640_DSP_BPADDR, 0x05); write_reg(bus, OV2640_DSP_BPDATA, 0x80); write_reg(bus, OV2640_DSP_BPDATA, 0x80); break; default: break; } } int rt_hw_ov2640_init(void) { extern rt_err_t ov2640_dcmi_crop(uint16_t displey_xsize, uint16_t displey_ysize, uint16_t sensor_xsize, uint16_t sensor_ysize); static rt_uint16_t id = 0; rt_device_t dcmi_dev = RT_NULL; i2c_bus = rt_i2c_bus_device_find(I2C_NAME); if (i2c_bus == RT_NULL) { LOG_E("can't find %c deivce", I2C_NAME); return -RT_ERROR; } /* dcmi init */ dcmi_dev = rt_device_find("dcmi"); if (dcmi_dev == RT_NULL) { LOG_E("can't find dcmi device!"); return -RT_ERROR; } rt_device_open(dcmi_dev, RT_DEVICE_FLAG_RDWR); ov2640_reset(i2c_bus); ov2640_read_id(i2c_bus, &id); ov2640_config(i2c_bus, OV2640_SVGA_Config); // 配置 SVGA模式 ------> 800*600, 最大帧率30帧 // ov2640_config(i2c_bus, OV2640_UXGA_Config); // 配置 UXGA模式 ------> 1600*1200,最大帧率15帧 ov2640_set_framesize(i2c_bus, OV2640_Width, OV2640_Height); // 设置OV2640输出的图像大小 // 将OV2640输出图像裁剪成适应屏幕的大小 struct stm32_dcmi_cropsize cropsize = {Display_Width, Display_Height, OV2640_Width, OV2640_Height}; rt_device_control(dcmi_dev, DCMI_CTRL_CROP, &cropsize); ov2640_set_pixformat(i2c_bus, Pixformat_RGB565); // ov2640_set_pixformat(i2c_bus, Pixformat_JPEG); ov2640_set_saturation(i2c_bus, 0); ov2640_set_brightness(i2c_bus, 0); ov2640_set_contrast(i2c_bus, 0); ov2640_set_effect(i2c_bus, OV2640_Effect_Normal); return RT_EOK; } INIT_APP_EXPORT(rt_hw_ov2640_init); #ifdef DRV_DEBUG #ifdef FINSH_USING_MSH #ifdef BSP_USING_LCD_SPI #include "drv_lcd_spi.h" int camera_sample(int argc, char **argv) { rt_device_t dcmi_dev = RT_NULL; rt_uint8_t fps = 0; dcmi_dev = rt_device_find("dcmi"); if (dcmi_dev == RT_NULL) { LOG_E("can't find dcmi device!"); return -RT_ERROR; } struct stm32_dcmi* stm32_dcmi_dev = DCMI_DEVICE(dcmi_dev); // malloc dma memory struct rt_memheap* axi_sram = (struct rt_memheap*)rt_object_find("axi_sram", RT_Object_Class_MemHeap); void* buff_ptr = rt_memheap_alloc(axi_sram, OV2640_BufferSize); // 启动DMA连续传输 struct stm32_dcmi_dma_transmitbuffer transmitbuffer = {(uint32_t)buff_ptr, OV2640_BufferSize}; rt_device_control(dcmi_dev, DCMI_CTRL_TRANSMIT_CONTINUOUS, &transmitbuffer); while (1) { rt_sem_take(&stm32_dcmi_dev->cam_semaphore, RT_WAITING_FOREVER); // rt_device_control(dcmi_dev, DCMI_CTRL_SUSPEND, RT_NULL); // 将图像数据复制到屏幕 lcd_copybuffer(0, 0, Display_Width, Display_Height, (uint16_t *)buff_ptr); // rt_device_control(dcmi_dev, DCMI_CTRL_RESUME, RT_NULL); rt_device_control(dcmi_dev, DCMI_CTRL_GET_FPS, &fps); LOG_D("fps: %d", fps); } rt_memheap_free(buff_ptr); } MSH_CMD_EXPORT(camera_sample, record picture to lcd); #endif /* BSP_USING_LCD_SPI */ #endif /* FINSH_USING_MSH */ #endif /* DRV_DEBUG */ #endif