我想做个能实时采集视频并显示的东西,全志有什么芯片合适?最好是像F1C100s这样集成DRAM的。F1C100s的CSI和LCD复用了一些引脚,没法同时使用。
离线
V3 S3 应该是H3的阉割并且sip dram的版本,我看了外设都是基本子集关系。
离线
F1C100s也可以csi摄像头+RGB666输出的啊,看到过的
离线
BGA的S3可以: https://whycan.cn/t_2227.html
不错,S3很强悍啊。唯一的障碍是BGA封装,还没画过更没焊过BGA呢……
离线
F1C100s也可以csi摄像头+RGB666输出的啊,看到过的
那有可能是SPI屏吧,带宽受限,做不到高刷新率。
离线
晕哥 说:BGA的S3可以: https://whycan.cn/t_2227.html
不错,S3很强悍啊。唯一的障碍是BGA封装,还没画过更没焊过BGA呢……
搞吧,4mil/4mil,0.2/0.4通孔,华强嘉立创走起
歪朵拉开源硬件: https://widora.cn
淘宝: https://widora.taobao.com/
离线
dgtg 说:F1C100s也可以csi摄像头+RGB666输出的啊,看到过的
那有可能是SPI屏吧,带宽受限,做不到高刷新率。
F1C100S接口不冲突,低两位才和摄像头共用引脚,不像v3s
离线
F1C100S接口不冲突,低两位才和摄像头共用引脚,不像v3s
从datasheet上看这几位都是和CSI冲突的,LCD的这几位实际用不到吗?
离线
微凉VeiLiang 说:F1C100S接口不冲突,低两位才和摄像头共用引脚,不像v3s
从datasheet上看这几位都是和CSI冲突的,LCD的这几位实际用不到吗?
https://whycan.cn/files/members/1163/Screen-Shot-2019-03-12-at-08_57_54.png
这些是RGB的低两位,显示屏这些位接地就行了。
离线
用mipi接口的摄像头不行吗?
嘉立创不接受来料加工吧?还有,嘉利创SMT接收BGA封装的吗?
离线
Quotation 说:微凉VeiLiang 说:F1C100S接口不冲突,低两位才和摄像头共用引脚,不像v3s
从datasheet上看这几位都是和CSI冲突的,LCD的这几位实际用不到吗?
https://whycan.cn/files/members/1163/Screen-Shot-2019-03-12-at-08_57_54.png
这些是RGB的低两位,显示屏这些位接地就行了。
微微凉正解!RGB666的接法,低两位也还可以并接到高两位。
离线
懂了,谢谢各位!不用很高级的屏幕,RGB666够了。
离线
懂了,谢谢各位!不用很高级的屏幕,RGB666够了。
坐等大神把 C100 的 DVP 摄像头调通。
离线
Quotation 说:懂了,谢谢各位!不用很高级的屏幕,RGB666够了。
坐等大神把 C100 的 DVP 摄像头调通。
DVP摄像头已经在XBoot上调通了。不过我只做了我目前需要的功能,代码不通用。所以先不分享了。
离线
F1C100S其实只支持666,不支持888的
小薏科技------全志F系列芯片供应、方案开发、技术支持 QQ:610943940
离线
山无棱 说:Quotation 说:懂了,谢谢各位!不用很高级的屏幕,RGB666够了。
坐等大神把 C100 的 DVP 摄像头调通。
DVP摄像头已经在XBoot上调通了。不过我只做了我目前需要的功能,代码不通用。所以先不分享了。
DVP驱动可否参考一下,正在驱动摄像头
离线
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;
}
}
离线
tianjjff 说: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; } }
多谢大神,谢谢分享!!!@Quotation
离线
感谢分享
离线
感谢,分享。
离线
请问下这个文件在哪里可以下载到,
感谢,分享。
离线
TQFP128封装的R11可以。手册坛子里应该也有了。
https://widora.io/zh/r11
歪朵拉开源硬件: https://widora.cn
淘宝: https://widora.taobao.com/
离线
tvin摄像头和rgblcd屏幕
离线