【得捷电子Follow me第1期】自选5-使用C++实现OV2640摄像头的访问
[复制链接]
使用C++实现OV2640摄像头的访问
1. 这次得捷电子的活动使用MicroPython实现了很多功能,逻辑清楚,效果显著,不过美中不足的是对于PicoW的资源利用不充分,比如DMA等高速访问都不能简单实现。所以,还是要考虑用C++的方法来实现,这里对于摄像头的访问就需要比较大的数据交换,最适合体现这个过程。
2. 安装开发环境。
对于Pico W的使用,推荐在windows下的visual code实现,也可以用树莓派4B在linux环境下实现,也是很有意思的,不过在windows实现也是大同小异。访问 下载并安装,可以直接创建PICO SDK的开发环境并直接实现。如果手动,就要比较麻烦,需要安装以下工具,
然后可以直接使用Powershell环境,输入
code .
直接进入开发环境,
2 摄像头OV2640和Pico W连接
2.1 摄像头的端口是SCCB协议和并行数据口,因为PicoW的端口有限,所以实际上还是用I2C来实现SCCB协议的模拟实现,用SCCB来传输数据包,这样的速度不如并行端口来实现的快,
使用的SIOC,SIOD作为SCK和SDIO,另外是XCLK等行同步命令和列同步信号,实现扫描,
对应连接的PicoW的接口可以直接在丝印上找到,
按照这个设置连接对应线
连接如下,
3 代码和编译
主函数Main.c的逻辑简单
#include <stdio.h>
#include "pico/stdlib.h"
#include "ov2640.h"
const int PIN_LED = 25;
const int PIN_CAM_SIOC = 5; // I2C0 SCL
const int PIN_CAM_SIOD = 4; // I2C0 SDA
const int PIN_CAM_RESETB = 2;
const int PIN_CAM_XCLK = 3;
const int PIN_CAM_VSYNC = 16;
const int PIN_CAM_Y2_PIO_BASE = 6;
const uint8_t CMD_REG_WRITE = 0xAA;
const uint8_t CMD_REG_READ = 0xBB;
const uint8_t CMD_CAPTURE = 0xCC;
uint8_t image_buf[352*288*2];
int main() {
stdio_uart_init();
uart_set_baudrate(uart0, 1000000);
printf("\n\nBooted!\n");
gpio_init(PIN_LED);
gpio_set_dir(PIN_LED, GPIO_OUT);
struct ov2640_config config;
config.sccb = i2c0;
config.pin_sioc = PIN_CAM_SIOC;
config.pin_siod = PIN_CAM_SIOD;
config.pin_resetb = PIN_CAM_RESETB;
config.pin_xclk = PIN_CAM_XCLK;
config.pin_vsync = PIN_CAM_VSYNC;
config.pin_y2_pio_base = PIN_CAM_Y2_PIO_BASE;
config.pio = pio0;
config.pio_sm = 0;
config.dma_channel = 0;
config.image_buf = image_buf;
config.image_buf_size = sizeof(image_buf);
ov2640_init(&config);
ov2640_reg_write(&config, 0xff, 0x01);
uint8_t midh = ov2640_reg_read(&config, 0x1C);
uint8_t midl = ov2640_reg_read(&config, 0x1D);
printf("MIDH = 0x%02x, MIDL = 0x%02x\n", midh, midl);
while (true) {
uint8_t cmd;
uart_read_blocking(uart0, &cmd, 1);
gpio_put(PIN_LED, !gpio_get(PIN_LED));
if (cmd == CMD_REG_WRITE) {
uint8_t reg;
uart_read_blocking(uart0, ®, 1);
uint8_t value;
uart_read_blocking(uart0, &value, 1);
ov2640_reg_write(&config, reg, value);
} else if (cmd == CMD_REG_READ) {
uint8_t reg;
uart_read_blocking(uart0, ®, 1);
uint8_t value = ov2640_reg_read(&config, reg);
uart_write_blocking(uart0, &value, 1);
} else if (cmd == CMD_CAPTURE) {
ov2640_capture_frame(&config);
uart_write_blocking(uart0, config.image_buf, config.image_buf_size);
}
}
return 0;
}
这里其实是混合编程,还采用了image.pio的汇编代码实现快速的DMA初始化
.program image
.wrap_target
wait 1 pin 9 // wait for hsync
wait 1 pin 8 // wait for rising pclk
in pins 8
wait 0 pin 8
.wrap
% c-sdk {
void image_program_init(PIO pio, uint sm, uint offset, uint pin_base) {
pio_sm_set_consecutive_pindirs(pio, sm, pin_base, 10, false);
pio_sm_config c = image_program_get_default_config(offset);
sm_config_set_in_pins(&c, pin_base);
sm_config_set_in_shift(&c, false, true, 8);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}
访问的方法是通过i2c的地址0x60访问
static const uint8_t OV2640_ADDR = 0x60 >> 1;
这样,写入不同的OV2640地址,分别确定为命令和数据,这样在完成初始化之后,读取I2C的通道数据直接入DMA写入设定的内存buff中,
void ov2640_capture_frame(struct ov2640_config *config) {
dma_channel_config c = dma_channel_get_default_config(config->dma_channel);
channel_config_set_read_increment(&c, false);
channel_config_set_write_increment(&c, true);
channel_config_set_dreq(&c, pio_get_dreq(config->pio, config->pio_sm, false));
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
dma_channel_configure(
config->dma_channel, &c,
config->image_buf,
&config->pio->rxf[config->pio_sm],
config->image_buf_size,
false
);
// Wait for vsync rising edge to start frame
while (gpio_get(config->pin_vsync) == true);
while (gpio_get(config->pin_vsync) == false);
dma_channel_start(config->dma_channel);
dma_channel_wait_for_finish_blocking(config->dma_channel);
}
4 编译和下载
4.1 在VScode中的编译非常简单,因为在安装PiCo SDK的过程中都配置好了开发环境,直接编译就可以,结果顺利通过,
不过下载的过程中openocd没有找到写入CMSIS-dap端口,这个也支持jlink写入,在sdk的配置中选择就可以。查看编译的结果,已经生成了多种格式的二进制文件,
不过,pico W还有一个功能就是用大容量外存的方式,选择uf2文件直接进入bootloader模式后复制进入就相当于写入了。
写入后顺利完成。
5 小结
这个开发的过程中,使用c++比micropython负责很多,但是即使这样,也是非常好用的,同时还可以支持更多的资源和能力,是对于复杂一些程序的更优选择。
|