661|2

404

帖子

2

TA的资源

纯净的硅(初级)

 

【得捷电子Follow me第1期】自选5-使用C++实现OV2640摄像头的访问 [复制链接]

 

使用C++实现OV2640摄像头的访问

1. 这次得捷电子的活动使用MicroPython实现了很多功能,逻辑清楚,效果显著,不过美中不足的是对于PicoW的资源利用不充分,比如DMA等高速访问都不能简单实现。所以,还是要考虑用C++的方法来实现,这里对于摄像头的访问就需要比较大的数据交换,最适合体现这个过程。

2. 安装开发环境。

    对于Pico W的使用,推荐在windows下的visual code实现,也可以用树莓派4B在linux环境下实现,也是很有意思的,不过在windows实现也是大同小异。访问

链接已隐藏,如需查看请登录或者注册
 下载并安装,可以直接创建PICO SDK的开发环境并直接实现。如果手动,就要比较麻烦,需要安装以下工具,

Capture.PNG

然后可以直接使用Powershell环境,输入

code .

直接进入开发环境,

Capture.PNG

2 摄像头OV2640和Pico W连接

2.1 摄像头的端口是SCCB协议和并行数据口,因为PicoW的端口有限,所以实际上还是用I2C来实现SCCB协议的模拟实现,用SCCB来传输数据包,这样的速度不如并行端口来实现的快,

使用的SIOC,SIOD作为SCK和SDIO,另外是XCLK等行同步命令和列同步信号,实现扫描,

1902046911.jpg

对应连接的PicoW的接口可以直接在丝印上找到,

192332093.jpg

按照这个设置连接对应线

Capture.PNG 连接如下,

306572805.jpg

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的过程中都配置好了开发环境,直接编译就可以,结果顺利通过,

Capture.PNG 不过下载的过程中openocd没有找到写入CMSIS-dap端口,这个也支持jlink写入,在sdk的配置中选择就可以。查看编译的结果,已经生成了多种格式的二进制文件,

Capture.PNG 不过,pico W还有一个功能就是用大容量外存的方式,选择uf2文件直接进入bootloader模式后复制进入就相当于写入了。

写入后顺利完成。

 

5 小结

这个开发的过程中,使用c++比micropython负责很多,但是即使这样,也是非常好用的,同时还可以支持更多的资源和能力,是对于复杂一些程序的更优选择。

 

 

 

 

 

 

 

 

最新回复

给力!   详情 回复 发表于 2023-6-21 19:50
 
 
 

回复
举报

5132

帖子

2

TA的资源

版主

 

相对于micropython我也比较喜欢用C/C++

 
 
 

回复

117

帖子

0

TA的资源

一粒金砂(中级)

 

给力!

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表