最近在调试荔枝派Zero Allwinner V3s + ov2640 + SPR5801
首先获取u-boot源码: git clone https://github.com/Lichee-Pi/u-boot.git -b v3s-current
修改 include/configs/sun8i.h, 使u-boot可以直接从tf卡启动:
/*
* (C) Copyright 2014 Chen-Yu Tsai
*
* Configuration settings for the Allwinner A23 (sun8i) CPU
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __CONFIG_H
#define __CONFIG_H
/*
* A23 specific configuration
*/
#ifdef CONFIG_USB_EHCI
#define CONFIG_USB_EHCI_SUNXI
#endif
#ifdef CONFIG_MACH_SUN8I_H3
#define CONFIG_SUNXI_USB_PHYS 4
#elif defined CONFIG_MACH_SUN8I_A83T
#define CONFIG_SUNXI_USB_PHYS 3
#elif defined CONFIG_MACH_SUN8I_V3S
#define CONFIG_SUNXI_USB_PHYS 1
#else
#define CONFIG_SUNXI_USB_PHYS 2
#endif
/*
* Include common sunxi configuration where most the settings are
*/
#define CONFIG_BOOTCOMMAND "setenv bootm_boot_mode sec; " \
"load mmc 0:1 0x41000000 zImage; " \
"load mmc 0:1 0x41800000 sun8i-v3s-licheepi-zero-dock.dtb; " \
"bootz 0x41000000 - 0x41800000;"
#define CONFIG_BOOTARGS "console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw vt.global_cursor_default=0"
#include
#endif /* __CONFIG_H */
下载 linux-zero-5.2.y 增加对 ov2640 的配置。
linux-zero-5.2.y/arch/arm/boot/dts$ gedit sun8i-v3s.dtsi
...
csi1: csi@1cb4000 {
compatible = "allwinner,sun8i-v3s-csi";
reg = <0x01cb4000 0x1000>;
interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_BUS_CSI>,
<&ccu CLK_CSI1_SCLK>,
<&ccu CLK_DRAM_CSI>;
clock-names = "bus", "mod", "ram";
resets = <&ccu RST_BUS_CSI>;
status = "disabled";
};
pio: pinctrl@1c20800 {
compatible = "allwinner,sun8i-v3s-pinctrl";
reg = <0x01c20800 0x400>;
interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_BUS_PIO>, <&osc24M>, <&osc32k>;
clock-names = "apb", "hosc", "losc";
gpio-controller;
#gpio-cells = <3>;
interrupt-controller;
#interrupt-cells = <3>;
emac_rgmii_pins: emac-rgmii-pins {
pins = "PD0", "PD1", "PD2", "PD3", "PD4",
"PD5", "PD7", "PD8", "PD9", "PD10",
"PD12", "PD13", "PD15", "PD16", "PD17";
function = "emac";
allwinner,drive = <SUN4I_PINCTRL_40_MA>;
allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
drive-strength = <40>;
};
i2c0_pins: i2c0-pins {
pins = "PB6", "PB7";
function = "i2c0";
};
pwm0_pins: pwm0 {
pins = "PB4";
function = "pwm0";
};
uart0_pb_pins: uart0-pb-pins {
pins = "PB8", "PB9";
function = "uart0";
};
lcd_rgb666_pins_a: lcd-rgb666-pe {
pins = "PE0", "PE1", "PE2", "PE3", "PE4", "PE5",
"PE6", "PE7", "PE8", "PE9", "PE10", "PE11",
"PE12", "PE13", "PE14", "PE15", "PE16", "PE17",
"PE18", "PE19", "PE23", "PE24";
function = "lcd";
};
mmc0_pins: mmc0-pins {
pins = "PF0", "PF1", "PF2", "PF3",
"PF4", "PF5";
function = "mmc0";
drive-strength = <30>;
bias-pull-up;
};
mmc1_pins: mmc1-pins {
pins = "PG0", "PG1", "PG2", "PG3",
"PG4", "PG5";
function = "mmc1";
drive-strength = <30>;
bias-pull-up;
};
spi0_pins: spi0-pins {
pins = "PC0", "PC1", "PC2", "PC3";
function = "spi0";
};
csi1_8bit: csi1-8bit@0 {
pins = "PE6","PE7","PE8","PE9","PE10","PE11","PE12","PE13","PE14","PE15";
bias-disable;
function = "csi";
};
csi1_clk: csi1-clk@0 {
pins = "PE0","PE2","PE3";
bias-disable;
function = "csi";
};
csi1_mclk: csi1-mclk@0 {
pins = "PE1";
bias-disable;
function = "csi";
};
i2c1_pins: i2c1 {
pins = "PE21", "PE22";
function = "i2c1";
};
};
i2c1: i2c@1c2b000 {
compatible = "allwinner,sun6i-a31-i2c";
reg = <0x01c2b000 0x400>;
interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&ccu CLK_BUS_I2C1>;
resets = <&ccu RST_BUS_I2C1>;
status = "disabled";
#address-cells = <1>;
#size-cells = <0>;
};
linux-zero-5.2.y/arch/arm/boot/dts$ gedit sun8i-v3s-licheepi-zero.dts
&csi1 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&csi1_clk &csi1_8bit>;
port {
csi1_ep: endpoint {
remote-endpoint = <&ov2640_0>;
hsync-active = <0>;
vsync-active = <0>;
bus-width = <10>;
pclk-sample = <1>;
};
};
};
&i2c1 {
pinctrl-0 = <&i2c1_pins>;
pinctrl-names = "default";
status = "okay";
ov2640: camera@30 {
compatible = "ovti,ov2640";
reg = <0x30>;
pinctrl-names = "default";
pinctrl-0 = <&csi1_mclk>;
clocks = <&ccu CLK_CSI1_MCLK>;
clock-names = "xvclk";
assigned-clocks = <&ccu CLK_CSI1_MCLK>;
assigned-clock-rates = <24000000>;
port {
ov2640_0: endpoint {
remote-endpoint = <&csi1_ep>;
bus-width = <10>;
};
};
};
};
其他的没有改动
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- licheepi_zero_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
sudo cp -r ./arch/arm/boot/dts/sun8i-v3s-licheepi-zero-dock.dtb /media/t/BOOT/
sudo cp -r ./arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dtb /media/t/BOOT/
sudo cp -r arch/arm/boot/zImage /media/t/BOOT/
insmod video、csi 、ov2640 的ko即可。
Ethernet 的支持,内核4.*的版本需是在 linux-zero-5.2.y/drivers/net/ethernet/allwinner/sun8i-emac.c,
开始我看5.2没有这个,我想着增加sun8i-emac.c Kconfig 后来发现在 sun8i-v3s.dtsi
emac: ethernet@1c30000 {
compatible = "allwinner,sun8i-h3-emac"; // 请注意这个sun8i-h3-emac
syscon = <&syscon>;
reg = <0x01c30000 0x10000>;
interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "macirq";
resets = <&ccu RST_BUS_EMAC>;
reset-names = "stmmaceth";
clocks = <&ccu CLK_BUS_EMAC>;
clock-names = "stmmaceth";
status = "disabled";
mdio: mdio {
#address-cells = <1>;
#size-cells = <0>;
compatible = "snps,dwmac-mdio";
};
mdio-mux {
compatible = "allwinner,sun8i-h3-mdio-mux";
#address-cells = <1>;
#size-cells = <0>;
mdio-parent-bus = <&mdio>;
/* Only one MDIO is usable at the time */
internal_mdio: mdio@1 {
compatible = "allwinner,sun8i-h3-mdio-internal";
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
int_mii_phy: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <1>;
clocks = <&ccu CLK_BUS_EPHY>;
resets = <&ccu RST_BUS_EPHY>;
};
};
external_mdio: mdio@2 {
reg = <2>;
#address-cells = <1>;
#size-cells = <0>;
};
};
};
然后在kernel 目录下搜索,发现 5.2 对 sun8i-emac.c 的支持换了位置,只需要配置一下即可
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
./fswebcam -S 20 -d /dev/video0 -p UYVY -r 800x600 --dumpframe dump.bin fswebcam.jpg
这样就可以抓取到图片了,声卡不用修改可以直接运行。
基于fswebcam修改,不想写代码。
修改fswebcam.c
case SRC_PAL_YUV420P:
printf("SRC_PAL_YUV420P \n");
fswc_add_image_yuv420p(&src, abitmap);
fswc_add_image_yuv420pGTI(&src, rgbbitmap);
/*
* bgrbitmap rgb 800 * 600
*
* */
bboxes_nms = (vec *)malloc(sizeof(vec));
if (!bboxes_nms) {
printf("malloc error.\n");
return -1;
}
float *feature_vector = (float *)malloc(512*4*2);
Read_registerData(feature_vector);
ssdDetectionInit();
faceRecog_init(recog_convFile, recog_fcFile);
pModel = GtiCreateModel(detect_modelFile);
if (pModel == NULL) {
printf(" ===============\n Failed to load model. detect \n");
return -1;
}
//GtiResize(bgrbitmap, src.height, src.width, gtibitmap, INPUT_IMAGE_H, INPUT_IMAGE_W);
GtiCvtColor(rgbbitmap, src.height, src.width, Inbitmap);
printf("src.height = %d , src.width = %d \n", src.height, src.width);
imageResize(Inbitmap, src.height, src.width, gtibitmap, INPUT_IMAGE_H, INPUT_IMAGE_W);
#ifdef _DEBUG
FILE *f;
f = fopen("rgb.bin", "wb");
if (!f)
ERROR("fopen: %s", strerror(errno));
else {
fwrite(rgbbitmap, 1, 800*600*3, f);
fclose(f);
}
printf("abitmap.bin success !\n");
#endif
#ifdef _DEBUG
FILE *s;
s = fopen("gti.bin", "wb");
if (!s)
ERROR("fopen: %s", strerror(errno));
else {
fwrite(gtibitmap, 1, 224*224*3, s);
fclose(s);
}
#endif
processRGBdata(pModel, gtibitmap, bboxes_nms);
GtiDestroyModel(pModel);
for (int nI=0; nI<vec_size(bboxes_nms); nI++) {
if ((vec_get_elem(bboxes_nms, nI).label) != 1)
continue;
float tmp_x1 = vec_get_elem(bboxes_nms, nI).x1 < 0 ? 0:vec_get_elem(bboxes_nms, nI).x1*800;
float tmp_y1 = vec_get_elem(bboxes_nms, nI).y1 < 0 ? 0:vec_get_elem(bboxes_nms, nI).y1*600;
float tmp_x2 = vec_get_elem(bboxes_nms, nI).x2 < 0 ? 0:vec_get_elem(bboxes_nms, nI).x2*800;
float tmp_y2 = vec_get_elem(bboxes_nms, nI).y2 < 0 ? 0:vec_get_elem(bboxes_nms, nI).y2*600;
float tmp_area = (tmp_x2 - tmp_x1) * (tmp_y2 - tmp_y1);
if (tmp_area > area) {
area = tmp_area;
x1 = tmp_x1;
y1 = tmp_y1;
x2 = tmp_x2;
y2 = tmp_y2;
}
}
if (area <= 0) {
printf("detect no face\n");
continue;
}
printf("Location: ");
printf("%.3f %.3f / %.3f %.3f.\n", x1, y1, x2, y2); // x2 W y2 H
// system("aplay amixer_src/tmp.wav ");
vec_free(bboxes_nms);
unsigned char *Pface_boxes = calloc((x2)*(y2)*3, sizeof(unsigned char));
imageCut(Inbitmap, 600, 800, Pface_boxes, x1, y1, y2, x2);
unsigned char *inface = calloc(imageLength, sizeof(unsigned char));
imageResize(Pface_boxes, y2, x2, inface, 224, 224);
if (Pface_boxes) {
free(Pface_boxes);
Pface_boxes = NULL;
}
pModel = GtiCreateModel(recog_modelFile);
printf("recog CNN time diff %lld\n", getTimeDiff(recTime_s, recTime_e));
if (pModel == NULL) {
printf("===============\n Failed to load model. \n");
}
float *feature = calloc(512*4, sizeof(float));
faceRecog_process(pModel, inface, feature);
if (config->registered != NULL) {
FILE *ptr;
ptr = fopen("tbs.bin", "wb");
if (!ptr)
ERROR("fopen: %s", strerror(errno));
else {
fwrite(feature, 1, sizeof(float) * 512, ptr);
fclose(ptr);
}
char buffer[50];
sprintf(buffer, "arecord -D hw:0,0 -d 5 -f S16_LE -r 16000 %s.wav", config->registered);
printf("%s \n", buffer);
//system(src);
printf("registered success \n");
}
#ifdef _DEBUG
FILE *t;
t = fopen("face.bin", "wb");
if (!s)
ERROR("fopen: %s", strerror(errno));
else {
fwrite(inface, 1, 224*224*3, t);
fclose(t);
}
#endif
if (inface) {
free(inface);
inface = NULL;
}
GtiDestroyModel(pModel);
float distbuf[2] = {0};
for (int i = 0; i < 2; i++) {
distbuf[i] = faceRecog_cosDistance(&feature_vector[512*i], feature, 512);
printf("=============dist = %f\n",distbuf[i]);
}
float maxvalue = distbuf[0];
int index = 0;
for (int i = 1; i < 2; i++) {
if (distbuf[i] > maxvalue) {
maxvalue = distbuf[i];
index = i;
}
}
if (maxvalue < 0.8) {
printf("result: recognize no face\n");
} else {
printf("result: index = %d \n", index);
if (index == 0) {
system("aplay ./feature/0.wav");
}
if (index == 1) {
system("aplay ./feature/1.wav");
}
}
if (feature) {
free(feature);
feature = NULL;
}
break;
运行结果:
# ./fswebcam -S 20 -d /dev/video0 -p YUV420P -r 800x600 --dumpframe dump.bin fswebcam.jpg
--- Opening /dev/video0...
Trying source module v4l2...
/dev/video0 opened.
No input was specified, using the first.
--- Capturing frame...
Skipping 20 frames...
Capturing 1 frames...
Dumping raw frame to 'dump.bin'...
src.length = 720000
SRC_PAL_YUV420P
cnnW_size = 262144, Bias_size = 512
src.height = 600 , src.width = 800
Location: 316.259 32.925 / 610.643 408.263.
result: index = 1
Playing WAVE './feature/1.wav' : Signed 16 bit Little Endian, Rate 16000 Hz, Mono