我想做个能实时采集视频并显示的东西,全志有什么芯片合适?最好是像F1C100s这样集成DRAM的。F1C100s的CSI和LCD复用了一些引脚,没法同时使用。
离线
BGA的S3可以: https://whycan.cn/t_2227.html
不错,S3很强悍啊。唯一的障碍是BGA封装,还没画过更没焊过BGA呢……
离线
F1C100s也可以csi摄像头+RGB666输出的啊,看到过的
那有可能是SPI屏吧,带宽受限,做不到高刷新率。
离线
F1C100S接口不冲突,低两位才和摄像头共用引脚,不像v3s
从datasheet上看这几位都是和CSI冲突的,LCD的这几位实际用不到吗?
离线
懂了,谢谢各位!不用很高级的屏幕,RGB666够了。
离线
Quotation 说:懂了,谢谢各位!不用很高级的屏幕,RGB666够了。
坐等大神把 C100 的 DVP 摄像头调通。
DVP摄像头已经在XBoot上调通了。不过我只做了我目前需要的功能,代码不通用。所以先不分享了。
离线
DVP驱动可否参考一下,正在驱动摄像头
CSI部分驱动如下。没有考虑什么通用性,只是针对我需要的数据格式。(UYVY -> YUV422)
只有捕捉单张图像,没有视频。
/*
* driver/csi-f1c100s.c
*/
#include <xboot.h>
#include <clk/clk.h>
#include <reset/reset.h>
#include <gpio/gpio.h>
#include <csi/csi.h>
enum {
CSI_EN = 0x000,
CSI_CFG = 0x004,
CSI_CAP = 0x008,
CSI_SCALE = 0x00c,
CSI_FIFO0_BUFA = 0x010,
CSI_FIFO0_BUFB = 0x014,
CSI_FIFO1_BUFA = 0x018,
CSI_FIFO1_BUFB = 0x01c,
CSI_FIFO2_BUFA = 0x020,
CSI_FIFO2_BUFB = 0x024,
CSI_BUF_CTL = 0x028,
CSI_BUF_STA = 0x02c,
CSI_INT_EN = 0x030,
CSI_INT_STA = 0x034,
CSI_HSIZE = 0x040,
CSI_VSIZE = 0x044,
CSI_BUF_LEN = 0x048,
};
enum {
CSI_SIZE_MASK = 0x1fff, // 13 bits
};
struct csi_f1c100s_pdata_t {
virtual_addr_t virt;
char * clk;
char * dramclk;
int reset;
int hsync;
int hsynccfg;
int vsync;
int vsynccfg;
int pclk;
int pclkcfg;
int database;
int datacfg;
int databits;
};
static void csi_f1c100s_get_size(struct csi_t * csi, int* width, int* height)
{
struct csi_f1c100s_pdata_t * pdat = (struct csi_f1c100s_pdata_t *)csi->priv;
if (width) {
*width = (read32(pdat->virt + CSI_HSIZE) >> 16) & CSI_SIZE_MASK;
}
if (height) {
*height = (read32(pdat->virt + CSI_VSIZE) >> 16) & CSI_SIZE_MASK;
}
}
static void csi_f1c100s_set_size(struct csi_t * csi, int width, int height)
{
struct csi_f1c100s_pdata_t * pdat = (struct csi_f1c100s_pdata_t *)csi->priv;
uint32_t hsize = read32(pdat->virt + CSI_HSIZE);
hsize &= ~(CSI_SIZE_MASK << 16) & ~(CSI_SIZE_MASK << 0);
hsize |= ((width * 2) & CSI_SIZE_MASK) << 16; // YUV每像素2字节
write32(pdat->virt + CSI_HSIZE, hsize);
uint32_t vsize = read32(pdat->virt + CSI_VSIZE);
vsize &= ~(CSI_SIZE_MASK << 16) & ~(CSI_SIZE_MASK << 0);
vsize |= (height & CSI_SIZE_MASK) << 16;
write32(pdat->virt + CSI_VSIZE, vsize);
// buffer len
uint32_t buflen = read32(pdat->virt + CSI_BUF_LEN);
buflen &= ~(CSI_SIZE_MASK << 0);
buflen |= (width & CSI_SIZE_MASK); // Y最长,每像素1字节
write32(pdat->virt + CSI_BUF_LEN, buflen);
}
static void csi_f1c100s_set_buffer(struct csi_t * csi, void* buffer, int len)
{
struct csi_f1c100s_pdata_t * pdat = (struct csi_f1c100s_pdata_t *)csi->priv;
// 只用BUFA,3个FIFO必须都设置上
write32(pdat->virt + CSI_FIFO0_BUFA, (uint32_t)buffer);
write32(pdat->virt + CSI_FIFO1_BUFA, (uint32_t)buffer + len * 2 / 4);
write32(pdat->virt + CSI_FIFO2_BUFA, (uint32_t)buffer + len * 3 / 4);
write32(pdat->virt + CSI_BUF_CTL, read32(pdat->virt + CSI_BUF_CTL) & ~(1 << 0));
}
static int csi_f1c100s_capture_still(struct csi_t * csi)
{
struct csi_f1c100s_pdata_t * pdat = (struct csi_f1c100s_pdata_t *)csi->priv;
// begin capture
write32(pdat->virt + CSI_CAP, read32(pdat->virt + CSI_CAP) | (1 << 0));
// clear int status
write32(pdat->virt + CSI_INT_STA, read32(pdat->virt + CSI_INT_STA));
// wait for capture start then stop
ktime_t timeout = ktime_add_ms(ktime_get(), 2000);
int captured = 0;
do {
if(read32(pdat->virt + CSI_INT_STA) & (1 << 0)) {
captured = 1;
break;
}
} while(ktime_before(ktime_get(), timeout));
return captured;
}
static struct device_t * csi_f1c100s_probe(struct driver_t * drv, struct dtnode_t * n)
{
struct csi_f1c100s_pdata_t * pdat;
struct csi_t * csi;
struct device_t * dev;
virtual_addr_t virt = phys_to_virt(dt_read_address(n));
char * clk = dt_read_string(n, "clock-name", NULL);
char * dramclk = dt_read_string(n, "dram-clock-name", NULL);
pdat = malloc(sizeof(struct csi_f1c100s_pdata_t));
if(!pdat)
return FALSE;
csi = malloc(sizeof(struct csi_t));
if(!csi)
{
free(pdat);
return FALSE;
}
pdat->virt = virt;
pdat->clk = strdup(clk);
pdat->dramclk = strdup(dramclk);
pdat->reset = dt_read_int(n, "reset", -1);
pdat->hsync = dt_read_int(n, "hsync-gpio", -1);
pdat->hsynccfg = dt_read_int(n, "hsync-gpio-config", -1);
pdat->vsync = dt_read_int(n, "vsync-gpio", -1);
pdat->vsynccfg = dt_read_int(n, "vsync-gpio-config", -1);
pdat->pclk = dt_read_int(n, "pclk-gpio", -1);
pdat->pclkcfg = dt_read_int(n, "pclk-gpio-config", -1);
pdat->database = dt_read_int(n, "data-gpio-base", -1);
pdat->datacfg = dt_read_int(n, "data-gpio-config", -1);
pdat->databits = dt_read_int(n, "data-bits", -1);
csi->name = alloc_device_name(dt_read_name(n), -1);
csi->get_size = csi_f1c100s_get_size;
csi->set_size = csi_f1c100s_set_size;
csi->set_buffer = csi_f1c100s_set_buffer;
csi->capture_still = csi_f1c100s_capture_still;
csi->priv = pdat;
clk_enable(pdat->clk);
clk_enable(pdat->dramclk);
if(pdat->reset >= 0) {
reset_deassert(pdat->reset);
}
if(pdat->hsync >= 0) {
if(pdat->hsynccfg >= 0) {
gpio_set_cfg(pdat->hsync, pdat->hsynccfg);
}
gpio_set_pull(pdat->hsync, GPIO_PULL_UP);
}
if(pdat->vsync >= 0) {
if(pdat->vsynccfg >= 0) {
gpio_set_cfg(pdat->vsync, pdat->vsynccfg);
}
gpio_set_pull(pdat->vsync, GPIO_PULL_UP);
}
if(pdat->pclk >= 0) {
if(pdat->pclkcfg >= 0) {
gpio_set_cfg(pdat->pclk, pdat->pclkcfg);
}
gpio_set_pull(pdat->pclk, GPIO_PULL_UP);
}
if (pdat->database) {
for (int i = 0; i < pdat->databits; i++) {
if(pdat->datacfg >= 0) {
gpio_set_cfg(pdat->database + i, pdat->datacfg);
}
gpio_set_pull(pdat->database + i, GPIO_PULL_UP);
}
}
// input format: YUV422
// input sequence: UYVY
// output format: planar YUV 422
// vref polarity: high
// href polarity: low
// pclk polarity: high
write32(pdat->virt + CSI_CFG,
(3 << 20) | (0 << 16) | (2 << 8) // YUV
| (1 << 2) | (1 << 1) | (0 << 0));
write32(pdat->virt + CSI_EN, read32(pdat->virt + CSI_EN) | (1 << 0));
if(!register_csi(&dev, csi))
{
clk_disable(pdat->clk);
free(pdat->clk);
clk_disable(pdat->dramclk);
free(pdat->dramclk);
free_device_name(csi->name);
free(csi->priv);
free(csi);
return NULL;
}
dev->driver = drv;
return dev;
}
static void csi_f1c100s_remove(struct device_t * dev)
{
struct csi_t * csi = (struct csi_t *)dev->priv;
struct csi_f1c100s_pdata_t * pdat = (struct csi_f1c100s_pdata_t *)csi->priv;
if(csi && unregister_csi(csi))
{
clk_disable(pdat->clk);
free(pdat->clk);
clk_disable(pdat->dramclk);
free(pdat->dramclk);
free_device_name(csi->name);
free(csi->priv);
free(csi);
}
}
static void csi_f1c100s_suspend(struct device_t * dev)
{
}
static void csi_f1c100s_resume(struct device_t * dev)
{
}
static struct driver_t csi_f1c100s = {
.name = "csi-f1c100s",
.probe = csi_f1c100s_probe,
.remove = csi_f1c100s_remove,
.suspend = csi_f1c100s_suspend,
.resume = csi_f1c100s_resume,
};
static __init void csi_f1c100s_driver_init(void)
{
register_driver(&csi_f1c100s);
}
static __exit void csi_f1c100s_driver_exit(void)
{
unregister_driver(&csi_f1c100s);
}
driver_initcall(csi_f1c100s_driver_init);
driver_exitcall(csi_f1c100s_driver_exit);
以下为SCCB部分,SCCB只是I2C的特例,所以就在I2C驱动里加了2个函数:
int i2c_sccb_write(const struct i2c_device_t * dev, uint8_t reg, uint8_t data)
{
uint8_t msg_data[2] = { reg, data };
struct i2c_msg_t msg[1];
msg[0].addr = dev->addr;
msg[0].flags = 0;
msg[0].len = 2;
msg[0].buf = msg_data;
int ret = i2c_transfer(dev->i2c, msg, 1);
return (ret == 1) ? 1 : 0;
}
int i2c_sccb_read(const struct i2c_device_t * dev, uint8_t reg, uint8_t* out)
{
uint8_t msg_data[2] = { reg, 0xee };
struct i2c_msg_t msg[2];
// write address
msg[0].addr = dev->addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = msg_data;
// read back
msg[1].addr = dev->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = 1;
msg[1].buf = msg_data + 1;
int ret = i2c_transfer(dev->i2c, msg, 2);
if (ret == 2) {
*out = msg_data[1];
return 1;
} else {
return 0;
}
}
离线