嵌入式硬件实战提升篇(一)-泰山派RK3566制作多功能小手机

news2024/12/27 15:36:48

引言:主要针对于嵌入式全栈内容的知识点汇总并对于linux等相关驱动知识点进行串联,用大家参考学习,并用到了嘉立创提供的泰山派RK3566作为学习的主控。

实物演示如下所示:

目录

一、硬件设计

1.转接电路

2.背光电路

3.音频接口

4.原理图设计

5.PCB设计

二、软件开发

1.驱动开发

1.1.设备树驱动改写

1.2.GP7101背光驱动设备树编写以及配置i2c1设备树

1.3.屏幕参数设配

1.4.触摸屏驱动

2.Android系统开发

三、外壳制作


一、硬件设计

我们的主控用的是RK3566由于主板的 MIPI 接口 为 31 PIN,而我们选型的屏幕是 24 PIN 的 DSI 所以得设计一个转接板,并且转接板主要由接口转换电路、背光电路、音频电路构成。

其中,3.1寸屏幕的分辨率为 480x800,使用的是MIPI DSI接口,屏幕排线为24个引脚,其中4、5、9脚为空不需要接,接下来我们对所选型的屏幕进行分析。

1.转接电路

如上为LCD屏幕的数据手册,如下我们对此屏幕设计的转接电路图。

如下为CTP电容式触摸屏的数据手册,如下我们对此屏幕设计的转接电路图。

如下为CTP电容式触摸屏的数据手册,如下我们对此触摸屏幕设计的转接电路图。

2.背光电路

值得注意的是,他的输出电流是110mA我们3.1寸屏幕最大能承受的驱动电流是25mA所以不适合直接接到3.1寸屏幕的FPC上。

板子背光驱动电路IOUT=0.2V/R(R=(R95xR96)/(R95+R96)),最终得出IOUT = 0.2V/1.8≈110mA

驱动电路利用SY7201ABC,SY7201ABC 提供恒流输出,确保LED模块的电流稳定。这对于LED的亮度控制至关重要,因为LED的亮度与电流成正比,恒定的电流能够保证LED的亮度一致,避免因电流波动导致的亮度不均匀或闪烁问题。如下为SY7201ABC的数据手册。

针对上述引脚我们需要认识一下, LX:电感连接引脚,控制电流的转换。

GND:地引脚,电路的参考点。

FB:反馈引脚,用于设定输出电流(亮度)。

EN/PWM:使能引脚和调光控制引脚,使用 PWM 信号调节亮度。

OVP:过压保护引脚,防止电压过高对电路和 LED 模块造成损害。

IN:输入电压引脚,连接电源并使用电容去噪。

其次这里可以看出是BOOST升压电路,对于BOOST升压电路如果不了解可以回看专栏【嵌入式硬件知识汇总】

因此,下述原理图中的,IN与LX就不再赘述,OVP主要用于过压保护引脚,FB通过反馈电压来控制驱动电流。连接一个外部电阻(R1)将 FB 引脚与 GND 连接,用来设定输出电流。也就是L out = 0.2V/R100 = 0.2/10 = 20mA 其中R99为NC不贴,如果电流还是未达到阈值可以考虑。最后EN/PWM引脚,R102 R101为上下拉电阻默认下拉贴R101,我们根据需要在没有驱动控制的时候通过上拉或者下拉电阻来决定屏幕背光关闭还是打开。

最后因为泰山派没有PWM引脚引到3.1寸扩展板,但触摸接口有I2C1引到3.1寸扩展屏幕上,I2C是可以挂在多个设备的,所以为了能够实现背光调节功能,我们通过GP7101一颗I2C转PWM的芯片来实现PWM的调节,GP7101和触摸一起挂到I2C1下,这样板载背光的EN/PWM也能体现出来作用。

3.音频接口

喇叭

通过两个弹簧顶针(POGO PIN)与泰山派SPKP和SPKN连接,音频驱动电路由泰山派上的RK809-5实现。

麦克风

通过一个弹簧顶针(POGO PIN)与泰山派MIC连接,MIC相关的驱动电路集成在了泰山派上。

4.原理图设计

5.PCB设计

MIPI的差分对属于高速信号需要特殊处理,在布线中选择差分对布线来出MIPI的差分线,并且没对走线中进行各地处理,差分对的误差需要在合理范围之内所以需要在布线中选择差分对长度调节来走蛇形线使差分在合理范围之内,如下为PCB视图:

具体顶层底层如下所示:

二、软件开发

1.驱动开发

1.1.设备树驱动改写

mipi相关的设备树在tspi-rk3566-dsi-v10.dtsi中,这里面包含mipi相关的所有设备树。我们通过tspi-rk3566-user-v10.dts中使用头文件去包含tspi-rk3566-dsi-v10.dtsi来决定是否使用mipi屏幕

具体代码段如下:

/dts-v1/;

#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/display/media-bus-format.h>
#include <dt-bindings/pinctrl/rockchip.h>
#include "rk3566.dtsi"
#include <dt-bindings/display/rockchip_vop.h>


//tspi核心配置层,这里是几乎后期不需要怎么改动
#include "tspi-rk3566-core-v10.dtsi"

//【开/关】EDP 显示屏幕配置,用户可以基于此复制自己的屏幕,注意EDP与MIPI屏幕互斥,因为共用了VOP如果需要同显自行修改
// #include "tspi-rk3566-edp-v10.dtsi"

//【开/关】mipi 显示屏幕配置,用户可以基于此复制自己的屏幕,注意EDP与MIPI屏幕互斥,因为共用了VOP如果需要同显自行修改
// #include "tspi-rk3566-dsi-v10.dtsi"

//【开/关】HDMI 显示屏幕配置,里面内容几乎可以不用动,如果不需要hdmi显示直接注释掉即可
#include "tspi-rk3566-hdmi-v10.dtsi"

//【开/关】摄像头 目前视频的是ov5659
#include "tspi-rk3566-csi-v10.dtsi"

//【开/关】网口 扩展板上使用的是千兆网,不接扩展板情况下可以关闭
// #include "tspi-rk3566-gmac1-v10.dtsi"

//【开/关】下方是用户定义层,所有用户修改理论上在此下方修改就好了
/ {
	model = "lckfb tspi V10 Board";
	compatible = "lckfb,tspi-v10", "rockchip,rk3566";

	rk_headset: rk-headset {
		compatible = "rockchip_headset";
		headset_gpio = <&gpio0 RK_PC5 GPIO_ACTIVE_HIGH>;
		pinctrl-names = "default";
		pinctrl-0 = <&hp_det>;
	};

	leds: leds {
		compatible = "gpio-leds";
		rgb_led_r: rgb-led-r {
			gpios = <&gpio1 RK_PB2 GPIO_ACTIVE_LOW>;
			linux,default-trigger = "timer";
			linux,delay-reg = <0>;   		// 延时注册
			linux,blink-delay-on = <500>; 	// 打开时间
			linux,blink-delay-off = <500>;	// 关闭时间
		};
		rgb_led_g: rgb-led-g {
			gpios = <&gpio1 RK_PB1 GPIO_ACTIVE_LOW>;
			linux,default-trigger = "timer";
			linux,delay-reg = <100>;   		// 延时注册
			linux,blink-delay-on = <1000>; 
			linux,blink-delay-off = <1000>;
		};
		rgb_led_b: rgb-led-b {
			gpios = <&gpio1 RK_PB0 GPIO_ACTIVE_LOW>;
			linux,default-trigger = "timer";
			linux,delay-reg = <100>;  		// 延时注册
			linux,blink-delay-on = <1500>; 
			linux,blink-delay-off = <1500>;
		};
	};

};

&pinctrl {
	headphone {
		hp_det: hp-det {
			rockchip,pins = <0 RK_PC5 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};
};

//用户三色灯
&leds {
	status = "okay";
};

//耳机插入检测,不使用扩展板情况需关闭,否则默认会检测到耳机插入
&rk_headset {
	status = "disabled";
};

//用户串口3
&uart3 {
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&uart3m1_xfer>;
};

//用户I2C2
&i2c2 {
	status = "okay";

	/*添加你的I2C设备参考
	gt1x: gt1x@14 {
		compatible = "goodix,gt1x";
		reg = <0x14>;
		pinctrl-names = "default";
		pinctrl-0 = <&touch_gpio>;
		goodix,rst-gpio = <&gpio0 RK_PB6 GPIO_ACTIVE_HIGH>;
		goodix,irq-gpio = <&gpio0 RK_PB5 IRQ_TYPE_LEVEL_LOW>;
	};*/
};

&i2c3 {
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&i2c3m1_xfer>;
	/*添加你的I2C设备参考
	gt1x: gt1x@14 {
		compatible = "goodix,gt1x";
		reg = <0x14>;
		pinctrl-names = "default";
		pinctrl-0 = <&touch_gpio>;
		goodix,rst-gpio = <&gpio0 RK_PB6 GPIO_ACTIVE_HIGH>;
		goodix,irq-gpio = <&gpio0 RK_PB5 IRQ_TYPE_LEVEL_LOW>;
	};*/
};


&spi3 {
	status = "okay";
	max-freq = <48000000>;
	dma-names = "tx","rx";
	pinctrl-names = "default", "high_speed";
	pinctrl-0 = <&spi3m1_cs0 &spi3m1_pins>;
	pinctrl-1 = <&spi3m1_cs0 &spi3m1_pins_hs>;
	spi_test@10 {
		compatible ="rockchip,spi_test_bus1_cs0";
		reg = <0>;
		spi-max-frequency = <24000000>;
		status = "okay";
	};
};

&pwm8 {
	status = "okay";
};

&pwm9 {
	status = "okay";
};

&pwm14 {
	status = "okay";
};

//pwd 15遥控器
&pwm15 {
	status = "okay";
	compatible = "rockchip,remotectl-pwm";
	remote_pwm_id = <3>;
	handle_cpu_id = <1>;
	remote_support_psci = <0>;
	pinctrl-names = "default";
	pinctrl-0 = <&pwm15m0_pins>;

	//用户自定方法:adb设置输出日志并通过dmesg确定usercode=address与key_table=command
	//echo 1 > sys/module/rockchip_pwm_remotectl/parameters/code_print
	//键值可在 include/dt-bindings/input/linux-event-codes.h 中查找
	ir_key1 {
		rockchip,usercode = <0xff00>;
		rockchip,key_table =
			<0xf2	KEY_MENU>,
			<0xe9	KEY_BACK>,
			<0xe3	KEY_ENTER>,
			<0xe7	KEY_UP>,
			<0xad	KEY_DOWN>,
			<0xf7	KEY_LEFT>,
			<0xa5	KEY_RIGHT>,
			<0xba	KEY_1>,
			<0xb9	KEY_2>,
			<0xb8	KEY_3>,
			<0xbb	KEY_4>,
			<0xbf	KEY_5>,
			<0xbc	KEY_6>,
			<0xf8	KEY_7>,
			<0xea	KEY_8>,
			<0xf6	KEY_9>,
			<0xe6	KEY_0>;
	};
};

把下述位置打开即可

1.2.GP7101背光驱动设备树编写以及配置i2c1设备树

从原理图中可知GP7101和触摸共同挂在道I2C下,从数据手册中我们可以得知GP7101的I2C地址是0XB0,0xB0是包含了读写位的所以我们实际填写中还需要右移一位最终地址为0X58。

tspi-rk3566-dsi-v10.dtsi中添加GP7101相关设备树驱动,首先引用I2C1并往设备树I2C1节点中添加GP7101子节点并指定I2C地址、最大背光,默认背光等。

&i2c1 {              // 引用名为i2c1的节点  
    status = "okay"; // 状态为"okay",表示此节点是可用和配置正确的  
    GP7101@58 {      // 定义一个子节点,名字为GP7101,地址为58  
        compatible = "gp7101-backlight";   // 该节点与"gp7101-backlight"兼容,  
        reg = <0x58>;                      // GP7101地址0x58  
        max-brightness-levels = <255>;     // 背光亮度的最大级别是255  
        default-brightness-level = <100>;  // 默认的背光亮度级别是100  
    };  
};

一般背光驱动都放在/kernel/drivers/video/backlight目录下,所以我们在此路径下创建一个my_gp7101_bl目录用来存放Makefilegp7101_bl.c文件,编译利用 obj-y += gp7101_bl.o 即可。

接下来就是 gp7101_bl.c驱动编写 ,如下为IIC驱动框架。

#include "linux/stddef.h"
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/input/mt.h>
#include <linux/random.h>

#if 1
#define MY_DEBUG(fmt,arg...)  printk("gp7101_bl:%s %d "fmt"",__FUNCTION__,__LINE__,##arg);
#else
#define MY_DEBUG(fmt,arg...)
#endif

#define BACKLIGHT_NAME "gp7101-backlight"

static int gp7101_bl_probe(struct i2c_client *client,
            const struct i2c_device_id *id)
{
    MY_DEBUG("locat");
    return 0;
}

static int gp7101_bl_remove(struct i2c_client *client)
{
    MY_DEBUG("locat");
    return 0;
}

static const struct of_device_id gp7101_bl_of_match[] = {
    { .compatible = BACKLIGHT_NAME, },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, gp7101_bl_of_match);

static struct i2c_driver gp7101_bl_driver = {
    .probe      = gp7101_bl_probe,
    .remove     = gp7101_bl_remove,
    .driver = {
        .name     = BACKLIGHT_NAME,
     .of_match_table = of_match_ptr(gp7101_bl_of_match),
    },
};

static int __init my_init(void)
{
    MY_DEBUG("locat");
    return i2c_add_driver(&gp7101_bl_driver);
}

static void __exit my_exit(void)
{
    MY_DEBUG("locat");
    i2c_del_driver(&gp7101_bl_driver);
}

module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("My touch driver");
MODULE_AUTHOR("wucaicheng@qq.com");

因为驱动过程中会有很多参数,我们不可能创建全局变量去保存他们,在linux驱动中一般都是通过创建一个结构体来保存驱动相关的参数,所以这里我创建一个gp7101_backlight_data结构体。

/* 背光控制器设备数据结构 */
struct gp7101_backlight_data {
    /* 指向一个i2c_client结构体的指针*/
    struct i2c_client *client;
    /*......其他成员后面有用到再添加........*/
};

当驱动中of_match_table = of_match_ptr(gp7101_bl_of_match)和设备树匹配成功以后会执行探针函数,探针函数中我们会去初始化驱动。

// gp7101_bl_probe - 探测函数,当I2C总线上的设备与驱动匹配时会被调用
static int gp7101_bl_probe(struct i2c_client *client,
            const struct i2c_device_id *id)
{
    struct backlight_device *bl; // backlight_device结构用于表示背光设备
    struct gp7101_backlight_data *data; // 自定义的背光数据结构
    struct backlight_properties props; // 背光设备的属性
    struct device_node *np = client->dev.of_node; // 设备树中的节点

    MY_DEBUG("locat"); // 打印调试信息

    // 为背光数据结构动态分配内存
    data = devm_kzalloc(&client->dev, sizeof(struct gp7101_backlight_data), GFP_KERNEL);
    if (data == NULL){
        dev_err(&client->dev, "Alloc GFP_KERNEL memory failed."); // 内存分配失败,打印错误信息
        return -ENOMEM; // 返回内存分配错误码
    } 

    // 初始化背光属性结构
    memset(&props, 0, sizeof(props));
    props.type = BACKLIGHT_RAW; // 设置背光类型为原始类型
    props.max_brightness = 255; // 设置最大亮度为255

    // 从设备树中读取最大亮度级别
    of_property_read_u32(np, "max-brightness-levels", &props.max_brightness);

    // 从设备树中读取默认亮度级别
    of_property_read_u32(np, "default-brightness-level", &props.brightness);

    // 确保亮度值在有效范围内
    if(props.max_brightness>255 || props.max_brightness<0){
        props.max_brightness = 255;
    }
    if(props.brightness>props.max_brightness || props.brightness<0){
        props.brightness = props.max_brightness;
    }

    // 注册背光设备
    bl = devm_backlight_device_register(&client->dev, "backlight", &client->dev, data, &gp7101_backlight_ops,&props);
    if (IS_ERR(bl)) {
        dev_err(&client->dev, "failed to register backlight device\n"); // 注册失败,打印错误信息
        return PTR_ERR(bl); // 返回错误码
    }
    data->client = client; // 保存i2c_client指针
    i2c_set_clientdata(client, data); // 设置i2c_client的客户端数据

    MY_DEBUG("max_brightness:%d brightness:%d",props.max_brightness, props.brightness); // 打印最大亮度和当前亮度
    backlight_update_status(bl); // 更新背光设备的状态

    return 0; // 返回成功
}

devm_backlight_device_register这个函数非常重要,他是 Linux 内核中用于动态注册背光设备的一个函数。前缀带devm的一般都会在设备被销毁时自动释放相关资源,无需手动调用 backlight_device_unregister。

这个函数的主要作用是创建并注册一个 backlight_device 实例,这个实例代表了系统中的一个背光设备。背光设备通常用于控制显示屏的亮度。函数原型如下:

struct backlight_device *devm_backlight_device_register(
    struct device *dev, const char *name, struct device *parent,
    void *devdata, const struct backlight_ops *ops,
    const struct backlight_properties *props);

参数说明:

  • dev:指向父设备的指针,通常是一个 struct i2c_clientstruct platform_device

  • name:背光设备的名称。

  • parent:背光设备的父设备,通常与 dev 参数相同。

  • devdata:私有数据,会被传递给背光操作函数。

  • ops:指向 backlight_ops 结构的指针,这个结构定义了背光设备的行为,包括设置亮度、获取亮度等操作。

  • props:指向 backlight_properties 结构的指针,这个结构包含了背光设备的属性,如最大亮度、当前亮度等。

ops参数非常重要,因为我们就是通过这个参数指向的结构成员中的函数去实现获取背光更新背光的。函数的原型如下:

struct backlight_ops {
    unsigned int options;

#define BL_CORE_SUSPENDRESUME   (1 << 0)

    /* Notify the backlight driver some property has changed */
    int (*update_status)(struct backlight_device *);
    /* Return the current backlight brightness (accounting for power,
       fb_blank etc.) */
    int (*get_brightness)(struct backlight_device *);
    /* Check if given framebuffer device is the one bound to this backlight;
       return 0 if not, !=0 if it is. If NULL, backlight always matches the fb. */
    int (*check_fb)(struct backlight_device *, struct fb_info *);
};

通过backlight_ops定义了一个名为gp7101_backlight_opsbacklight_ops结构体实例,并且只初始化了.update_status成员,它指向了一个名为gp7101_backlight_set的函数,这个函数负责更新背光设备的亮度状态。

static struct backlight_ops gp7101_backlight_ops = {
    .update_status = gp7101_backlight_set,
};

这就是我们更新背光的核心函数了,每次背光被改动的时候系统都会回调这个函数,在函数中我们通过I2C1去写GP7101实现修改背光。 GP7101两种操作方法第一种是8位PWM,第二种是16位数PWM,刚好我们背光是从0~255所以,我们就选择8位PWM,八位PWM模式需要写寄存器0x03。

/* I2C 背光控制器寄存器定义 */
#define BACKLIGHT_REG_CTRL_8  0x03  
#define BACKLIGHT_REG_CTRL_16 0x02
/* 设置背光亮度 */
static int gp7101_backlight_set(struct backlight_device *bl)
{
    struct gp7101_backlight_data *data = bl_get_data(bl);  // 获取背光数据结构指针
    struct i2c_client *client = data->client;  // 获取I2C设备指针
    u8 addr[1] = {BACKLIGHT_REG_CTRL_8};  // 定义I2C地址数组
    u8 buf[1] = {bl->props.brightness};  // 定义数据缓冲区,用于存储背光亮度值

    MY_DEBUG("pwm:%d", bl->props.brightness);  // 输出背光亮度值

    // 将背光亮度值写入设备
    i2c_write(client, addr, sizeof(addr), buf, sizeof(buf));

    return 0;  // 返回成功
}

i2c_write函数

s32 i2c_write(struct i2c_client *client, u8 *addr, u8 addr_len, u8 *buf, s32 len)
{
    struct i2c_msg msg; // 定义i2c消息结构,用于传输数据
    s32 ret = -1; // 初始化返回值为-1,表示失败
    u8 *temp_buf; // 定义临时缓冲区指针

    msg.flags = !I2C_M_RD; // 标志位,表示写操作
    msg.addr = client->addr; // 设备地址
    msg.len = len + addr_len; // 写入数据的总长度(地址长度+数据长度)

    // 分配临时缓冲区
    temp_buf = kzalloc(msg.len, GFP_KERNEL);
    if (!temp_buf) {
        goto error; // 如果分配失败,跳转到错误处理
    }

    // 装填地址到临时缓冲区
    memcpy(temp_buf, addr, addr_len);
    // 装填数据到临时缓冲区(紧随地址之后)
    memcpy(temp_buf + addr_len, buf, len);
    msg.buf = temp_buf; // 设置消息的缓冲区为临时缓冲区

    // 发送消息并写入数据
    ret = i2c_transfer(client->adapter, &msg, 1);
    if (ret == 1) {
        kfree(temp_buf); // 释放临时缓冲区
        return 0; // 如果消息成功传输,返回0表示成功
    }

error:
    // 如果写入失败,打印错误信息
    if (addr_len == 2) {
        MY_DEBUG("I2C Write: 0x%04X, %d bytes failed, errcode: %d! Process reset.", (((u16)(addr[0] << 8)) | addr[1]), len, ret);
    } else {
        MY_DEBUG("I2C Write: 0x%02X, %d bytes failed, errcode: %d! Process reset.", addr[0], len, ret);
    }
    if (temp_buf) {
        kfree(temp_buf); // 释放临时缓冲区
    }
    return -1; // 返回-1表示失败
}

屏蔽原有背光设备树节点。

/ {
    
    /*backlight: backlight {
        compatible = "pwm-backlight";
        pwms = <&pwm5 0 25000 0>;
        brightness-levels = <
              0  20  20  21  21  22  22  23
             23  24  24  25  25  26  26  27
             27  28  28  29  29  30  30  31
             31  32  32  33  33  34  34  35
             35  36  36  37  37  38  38  39
             40  41  42  43  44  45  46  47
             48  49  50  51  52  53  54  55
             56  57  58  59  60  61  62  63
             64  65  66  67  68  69  70  71
             72  73  74  75  76  77  78  79
             80  81  82  83  84  85  86  87
             88  89  90  91  92  93  94  95
             96  97  98  99 100 101 102 103
            104 105 106 107 108 109 110 111
            112 113 114 115 116 117 118 119
            120 121 122 123 124 125 126 127
            128 129 130 131 132 133 134 135
            136 137 138 139 140 141 142 143
            144 145 146 147 148 149 150 151
            152 153 154 155 156 157 158 159
            160 161 162 163 164 165 166 167
            168 169 170 171 172 173 174 175
            176 177 178 179 180 181 182 183
            184 185 186 187 188 189 190 191
            192 193 194 195 196 197 198 199
            200 201 202 203 204 205 206 207
            208 209 210 211 212 213 214 215
            216 217 218 219 220 221 222 223
            224 225 226 227 228 229 230 231
            232 233 234 235 236 237 238 239
            240 241 242 243 244 245 246 247
            248 249 250 251 252 253 254 255
        >;
        default-brightness-level = <255>;
    };*/
};

在dsi1中也需要屏蔽掉否则找不到引用节点编译时候会报错。

&dsi1 {
    status = "okay";
    rockchip,lane-rate = <1000>;
    dsi1_panel: panel@0 {
        /*省略*/
        // backlight = <&backlight>;
        /*省略*/
    };
};
1.3.屏幕参数设配

tspi-rk3566-dsi-v10.dtsi配置

  • 修改lanes数

3.1寸屏幕硬件上只用了2lanes的差分对,设备树中默认配置的是4lanes所以我们需要把lanes修改为2。

dsi,lanes  = <4>;
改为
dsi,lanes  = <2>;
  • 配置初始化序列

panel-init-sequence = [
    // init code
    05 78 01 01
    05 78 01 11
    39 00 06 FF 77 01 00 00 11
    15 00 02 D1 11
    15 00 02 55 B0 // 80 90 b0
    39 00 06 FF 77 01 00 00 10
    39 00 03 C0 63 00
    39 00 03 C1 09 02
    39 00 03 C2 37 08
    15 00 02 C7 00 // x-dir rotate 0:0x00,rotate 180:0x04
    15 00 02 CC 38
    39 00 11 B0 00 11 19 0C 10 06 07 0A 09 22 04 10 0E 28 30 1C
    39 00 11 B1 00 12 19 0D 10 04 06 07 08 23 04 12 11 28 30 1C
    39 00 06 FF 77 01 00 00 11 // enable  bk fun of  command 2  BK1
    15 00 02 B0 4D
    15 00 02 B1 60 // 0x56  0x4a  0x5b
    15 00 02 B2 07
    15 00 02 B3 80
    15 00 02 B5 47
    15 00 02 B7 8A
    15 00 02 B8 21
    15 00 02 C1 78
    15 00 02 C2 78
    15 64 02 D0 88
    39 00 04 E0 00 00 02
    39 00 0C E1 01 A0 03 A0 02 A0 04 A0 00 44 44
    39 00 0D E2 00 00 00 00 00 00 00 00 00 00 00 00
    39 00 05 E3 00 00 33 33
    39 00 03 E4 44 44
    39 00 11 E5 01 26 A0 A0 03 28 A0 A0 05 2A A0 A0 07 2C A0 A0
    39 00 05 E6 00 00 33 33
    39 00 03 E7 44 44
    39 00 11 E8 02 26 A0 A0 04 28 A0 A0 06 2A A0 A0 08 2C A0 A0
    39 00 08 EB 00 01 E4 E4 44 00 40
    39 00 11 ED FF F7 65 4F 0B A1 CF FF FF FC 1A B0 F4 56 7F FF
    39 00 06 FF 77 01 00 00 00
    15 00 02 36 00 //U&D  Y-DIR rotate 0:0x00,rotate 180:0x10
    15 00 02 3A 55
    05 78 01 11           
    05 14 01 29                
];
  • 配置屏幕时序

disp_timings1: display-timings {
    native-mode = <&dsi1_timing0>;
    dsi1_timing0: timing0 {
        clock-frequency = <27000000>;
        hactive = <480>;       //与 LCDTiming.LCDH 对应
        vactive = <800>;       //与 LCDTiming.LCDV 对应
        hfront-porch = <32>;   //与 LCDTiming.HFPD 对应 
        hsync-len = <4>;       //与 LCDTiming.HSPW 对应
        hback-porch = <32>;    //与 LCDTiming.HBPD 对应
        vfront-porch = <9>;    //与 LCDTiming.VEPD 对应
        vsync-len = <4>;       //与 LCDTiming.VsPW 对应
        vback-porch = <3>;     //与 LCDTiming.VBPD 对应
        hsync-active = <0>;
        vsync-active = <0>;
        de-active = <0>;
        pixelclk-active = <0>;
    };
};
1.4.触摸屏驱动

配置i2c1设备树

&i2c1 {
    status = "okay";             // 表示这个i2c1设备是可用的
    clock-frequency = <400000>;  // 设置i2c1的时钟频率为400kHz
    myts@38 {                    // 定义一个i2c设备,设备地址为0x38,设备名称为myts
        compatible = "my,touch"; // 表示这个设备是触摸屏设备,驱动名称为my,touch
        reg = <0x38>;            // i2c设备地址
        tp-size = <89>;          // 触摸屏的大小
        max-x = <480>;           // 触摸屏支持的最大X坐标值
        max-y = <800>;           // 触摸屏支持的最大Y坐标值
        touch-gpio = <&gpio1 RK_PA0 IRQ_TYPE_LEVEL_LOW>; // 触摸屏的触摸中断引脚,连接到gpio1的第0个引脚,触发方式为低电平触发
        reset-gpio = <&gpio1 RK_PA1 GPIO_ACTIVE_HIGH>;   // 触摸屏的复位引脚,连接到gpio1的第1个引脚,有效电平为高电平
    };
   /****省略****/
};

也是利用 obj-y   += my_touch.o 去编译。

my_touch.c驱动

static int my_touch_ts_probe(struct i2c_client *client,
            const struct i2c_device_id *id)
{
    return 0;
}

static int my_touch_ts_remove(struct i2c_client *client)
{
    MY_DEBUG("locat");
    return 0;
}

static const struct of_device_id my_touch_of_match[] = {
    { .compatible = "my,touch", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_touch_of_match);

static struct i2c_driver my_touch_ts_driver = {
    .probe      = my_touch_ts_probe,
    .remove     = my_touch_ts_remove,
    .driver = {
        .name     = "my-touch",
     .of_match_table = of_match_ptr(my_touch_of_match),
    },
};

static int __init my_ts_init(void)
{
    MY_DEBUG("locat");
    return i2c_add_driver(&my_touch_ts_driver);
}

static void __exit my_ts_exit(void)
{
    MY_DEBUG("locat");
    i2c_del_driver(&my_touch_ts_driver);
}

module_init(my_ts_init);
module_exit(my_ts_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("My touch driver");
MODULE_AUTHOR("wucaicheng@qq.com");

驱动中的结构体

// 定义一个表示触摸设备的结构体
struct my_touch_dev {
    struct i2c_client *client; // 指向与触摸设备通信的 I2C 客户端结构体的指针
    struct input_dev *input_dev; // 指向与输入设备关联的 input_dev 结构体的指针,用于处理输入事件
    int rst_pin; // 触摸设备的复位引脚编号
    int irq_pin; // 触摸设备的中断引脚编号
    u32 abs_x_max; // 触摸设备在 X 轴上的最大绝对值
    u32 abs_y_max; // 触摸设备在 Y 轴上的最大绝对值
    int irq; // 触摸设备的中断号
};

当驱动中of_match_table = of_match_ptr(my_touch_of_match)和设备树匹配成功以后会执行探针函数,探针函数中我们会去初始化驱动。

static int my_touch_ts_probe(struct i2c_client *client,
            const struct i2c_device_id *id)
{
    int ret; // 定义一个返回值变量
    struct my_touch_dev *ts; // 定义一个结构体指针,用来指向my_touch_dev结构体
    struct device_node *np = client->dev.of_node; // 获取设备节点
    // 打印调试信息
    MY_DEBUG("locat"); // 调用MY_DEBUG函数打印调试信息,此处打印"locat"

    ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); // 使用devm_kzalloc分配内存,减少内存申请操作
    if (ts == NULL){ // 检查内存分配是否成功
        dev_err(&client->dev, "Alloc GFP_KERNEL memory failed."); // 内存分配失败,打印错误信息
        return -ENOMEM; // 返回内存申请错误的码
    }
    ts->client = client; // 触摸屏设备的客户端指针指向i2c_client结构体
    i2c_set_clientdata(client, ts); // 将my_touch_dev结构体的指针设置为i2c客户端的数据

    // 从设备树中读取触摸屏的最大X和Y值
    if (of_property_read_u32(np, "max-x", &ts->abs_x_max)) {
        dev_err(&client->dev, "no max-x defined\n"); // 如果读取最大X值失败,打印错误信息
        return -EINVAL; // 返回参数无效的错误码
    }
    MY_DEBUG("abs_x_max:%d",ts->abs_x_max); // 打印X值

    if (of_property_read_u32(np, "max-y", &ts->abs_y_max)) {
        dev_err(&client->dev, "no max-y defined\n"); // 如果读取最大Y值失败,打印错误信息
        return -EINVAL; // 返回参数无效的错误码
    }
    MY_DEBUG("abs_x_max:%d",ts->abs_y_max); // 打印Y值

    // 获取并请求复位GPIO管脚
    ts->rst_pin = of_get_named_gpio(np, "reset-gpio", 0); // 从设备树中获取复位管脚
    ret = devm_gpio_request(&client->dev,ts->rst_pin,"my touch touch gpio"); // 请求使用复位管脚
    if (ret < 0){ // 如果请求失败
        dev_err(&client->dev, "gpio request failed."); // 打印错误信息
        return -ENOMEM;                                 // 返回内存申请错误的码
    }
    
    ts->irq_pin = of_get_named_gpio(np, "touch-gpio", 0); // 从设备树中获取中断管脚
    ret = devm_gpio_request_one(&client->dev, ts->irq_pin, // 请求使用中断管脚
                GPIOF_IN, "my touch touch gpio");
    if (ret < 0)
        return ret; // 如果请求失败,直接返回错误码

    // 复位触摸屏设备
    gpio_direction_output(ts->rst_pin,0); // 设置复位管脚输出低电平
    msleep(20); // 等待20毫秒
    gpio_direction_output(ts->irq_pin,0); // 设置中断管脚输出低电平
    msleep(2); // 等待2毫秒
    gpio_direction_output(ts->rst_pin,1); // 设置复位管脚输出高电平
    msleep(6); // 等待6毫秒
    gpio_direction_output(ts->irq_pin, 0); // 设置中断管脚输出低电平
    msleep(50); // 等待50毫秒

    // 申请中断服务
    ts->irq = gpio_to_irq(ts->irq_pin); // 将GPIO管脚转换为中断号
    if(ts->irq){ // 检查中断号是否有效
        ret = devm_request_threaded_irq(&(client->dev), ts->irq, // 请求线程化中断
                NULL, my_touch_irq_handler,                      // 中断服务函数
                IRQF_TRIGGER_FALLING | IRQF_ONESHOT,             // 中断触发方式为下降沿触发,且只触发一次
                client->name, ts);
        if (ret != 0) {
            MY_DEBUG("Cannot allocate ts INT!ERRNO:%d\n", ret); // 如果中断请求失败,打印错误信息
            return ret; // 返回错误码
        }
    }

    // 使用devm_input_allocate_device分配输入设备对象
    ts->input_dev = devm_input_allocate_device(&client->dev); 
    if (!ts->input_dev) { // 检查输入设备对象是否分配成功
        dev_err(&client->dev, "Failed to allocate input device.\n"); // 打印错误信息
        return -ENOMEM; // 返回内存申请错误的码
    }

    // 设置输入设备的名称
    ts->input_dev->name = "my touch screen"; 
    // 设置输入设备的总线类型为I2C
    ts->input_dev->id.bustype = BUS_I2C; 
    
    // 设置X轴的最大值为480
    input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, 480, 0, 0); 
    // 设置Y轴的最大值为800
    input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, 800, 0, 0); 

    // 初始化5个多点触摸槽位,直接模式
    ret = input_mt_init_slots(ts->input_dev, 5, INPUT_MT_DIRECT); 
    if (ret) {
        dev_err(&client->dev, "Input mt init error\n"); // 打印错误信息
        return ret; // 返回错误码
    }

    // 注册输入设备
    ret = input_register_device(ts->input_dev); // 注册输入设备
    if (ret)
        return ret; // 返回错误码

    // 读取版本号
    gt9271_read_version(client); 

    return 0; // 如果一切顺利,返回0
}

读取触摸数据有很多方法比如轮询但是这样效率太低了,所以我们这里通过中断方式实现触摸数据读取,当屏幕被触控时,屏幕会通过irq引脚输出中断信号。

devm_request_threaded_irq(&(client->dev), ts->irq,   // 请求线程化中断
                NULL, my_touch_irq_handler,          // 中断服务函数
                IRQF_TRIGGER_FALLING | IRQF_ONESHOT, // 中断触发方式为下降沿触发,且只触发一次
                client->name, ts);

上面中断以后当屏幕被触摸会触发中断线程服务函数my_touch_irq_handler,在这个函数里面我们通过i2c去读取屏幕相关的参数。

static irqreturn_t my_touch_irq_handler(int irq, void *dev_id)
{
    s32 ret = -1;                        // 定义一个返回值,初始化为-1
    struct my_touch_dev *ts = dev_id;    // 获取指向设备数据的指针
    u8 addr[1] = {0x02};                 // 定义一个寄存器地址数组对应数据手册TD_STATUS 
    u8 point_data[1+6*5]={0};            // 定义一个点数据数组,预留足够空间 for 5 touch points
    u8 touch_num = 0;                    // 定义一个变量来存储触摸点的数量
    u8 *touch_data;                       // 定义一个指针用于指向点数据
    int i = 0;                           // 定义一个循环变量
    int event_flag, touch_id, input_x, input_y; // 定义一些用于存储事件信息的变量

    MY_DEBUG("irq");                    // 打印中断信息

    ret = my_touch_i2c_read(ts->client, addr, sizeof(addr), point_data, sizeof(point_data)); // 尝试读取触摸屏设备的数据
    if (ret < 0){                        // 如果读取失败
        MY_DEBUG("I2C write end_cmd error!"); // 打印错误信息
    }
    touch_num = point_data[0]&0x0f;     // 获取触摸点的数量
    MY_DEBUG("touch_num:%d",touch_num); // 打印触摸点数量

    // 遍历触摸点数据
    for(i=0; i<5; i++){
        // 获取触摸点数据
        touch_data = &point_data[1+6*i];
        /*
        解析触摸点的事件标志位
        00b: 按下
        01b: 抬起
        10b: 接触
        11b: 保留
        */
        event_flag = touch_data[0] >> 6;
        if(event_flag == 0x03)continue; // 如果事件标志位不是按下或抬起,则跳过此循环
        touch_id = touch_data[2] >> 4;    // 获取触摸点ID

        MY_DEBUG("i:%d touch_id:%d event_flag:%d",i,touch_id,event_flag); // 打印调试信息
        input_x  = ((touch_data[0]&0x0f)<<8) | touch_data[1]; // 计算X坐标
        input_y  = ((touch_data[2]&0x0f)<<8) | touch_data[3]; // 计算Y坐标

        // MY_SWAP(input_x,input_y); // 如果需要交换X和Y坐标,可以取消注释此行
        MY_DEBUG("i:%d,x:%d,y:%d",i,input_x,input_y); // 打印调试信息
        // 设置输入设备的触摸槽位
        input_mt_slot(ts->input_dev, touch_id);

        if(event_flag == 0){ // 如果是按下
            // 上报按下事件和坐标
            input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); // 设置为按下状态
            input_report_abs(ts->input_dev, ABS_MT_POSITION_X, 480-input_x); // 报告X坐标
            input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); // 报告Y坐标
        }else if (event_flag == 2){ // 如果是长按
            // 直接上报数据
            input_report_abs(ts->input_dev, ABS_MT_POSITION_X, 480-input_x); // 报告X坐标
            input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); // 报告Y坐标
        else if(event_flag == 1){ // 如果是触摸抬起
            // 上报事件
            input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); // 设置为抬起状态
        }
    }
    // 报告输入设备的指针仿真信息
    input_mt_report_pointer_emulation(ts->input_dev, true);
    // 同步输入事件
    input_sync(ts->input_dev);
    // 返回IRQ_HANDLED,表示中断已经被处理
    return IRQ_HANDLED;
}

读取数据从TD_STATUS开始,一个触摸点包含6个数据分别是TOUCH1_XH、TOUCH1_XL、TOUCH1_YH、TOUCH1_YL、TOUCH1_WEIGHT,共计支持5点触控,所以连续读取的长度为1(TD_STATUS)+6(数据)*5。

ret = my_touch_i2c_read(ts->client, addr, sizeof(addr), point_data, sizeof(point_data));

2.Android系统开发

修改屏幕密度

# 修改密度这里是240 PRODUCT_PROPERTY_OVERRIDES += ro.sf.lcd_density=240

系统添加屏幕旋转

diff --git a/rk3566_tspi/BoardConfig.mk b/rk3566_tspi/BoardConfig.mk index 4f702c3..bbad87a 100755 --- a/rk3566_tspi/BoardConfig.mk +++ b/rk3566_tspi/BoardConfig.mk @@ -37,3 +37,6 @@ ifeq ($(strip $(BOARD_USES_AB_IMAGE)), true) include device/rockchip/common/BoardConfig_AB.mk TARGET_RECOVERY_FSTAB := device/rockchip/rk356x/rk3566_tspi/recovery.fstab_AB endif +TARGET_RECOVERY_DEFAULT_ROTATION := ROTATION_RIGHT +SF_PRIMARY_DISPLAY_ORIENTATION := 90

修改触摸方向

diff --git a/my_touch.c b/my_touch.c index 9acab2d..608914a 100755 --- a/my_touch.c +++ b/my_touch.c @@ -143,7 +143,7 @@ static irqreturn_t my_touch_irq_handler(int irq, void *dev_id) input_x = ((touch_data[0]&0x0f)<<8) | touch_data[1]; input_y = ((touch_data[2]&0x0f)<<8) | touch_data[3]; - // MY_SWAP(input_x,input_y); + MY_SWAP(input_x,input_y); MY_DEBUG("i:%d,x:%d,y:%d",i,input_x,input_y); // 设定输入设备的触摸槽位 input_mt_slot(ts->input_dev, touch_id); @@ -151,11 +151,11 @@ static irqreturn_t my_touch_irq_handler(int irq, void *dev_id) if(event_flag == 0){ // 如果是按下上报按下和坐标 input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); - input_report_abs(ts->input_dev, ABS_MT_POSITION_X, 480-input_x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); }else if (event_flag == 2){ // 如果是长按直接上报数据 - input_report_abs(ts->input_dev, ABS_MT_POSITION_X, 480-input_x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); }else if(event_flag == 1){ // 触摸抬起,上报事件 @@ -277,8 +277,8 @@ static int my_touch_ts_probe(struct i2c_client *client, /*设置触摸 x 和 y 的最大值*/ // 设置输入设备的绝对位置参数 - input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, 480, 0, 0); - input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, 800, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, 800, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, 480, 0, 0); // 初始化多点触摸设备的槽位 ret = input_mt_init_slots(ts->input_dev, 5, INPUT_MT_DIRECT);

三、外壳制作

2024.11.11.15.04待更新

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2241012.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用两行命令快速搭建深度学习环境(Docker/torch2.5.1+cu118/命令行美化+插件),包含完整的 Docker 安装步骤

深度学习环境的配置过于繁琐&#xff0c;所以我制作了两个基础的镜像&#xff0c;希望可以帮助大家节省时间&#xff0c;你可以选择其中一种进行安装&#xff0c;版本说明&#xff1a; base 版本基于 pytorch/pytorch:2.5.1-cuda11.8-cudnn9-devel&#xff0c;默认 python 版本…

11个c语言编程练习题

0. 钞票和硬币 money.c 读取一个带有两个小数位的浮点数&#xff0c;代表货币价值。将该值分解为多种钞票和硬币的和&#xff0c;要求使用的钞票和硬币的总数量尽可能少。 货币面值有100&#xff0c;50&#xff0c;20&#xff0c;10&#xff0c;5&#xff0c;1&#xff0c;0.…

LaTeX之四:如何兼容中文(上手中文简历和中文论文)、在win/mac上安装新字体。

改成中文版 如果你已经修改了.cls文件和主文档&#xff0c;但编译后的PDF仍然显示英文版本&#xff0c;可能有以下几个原因&#xff1a; 编译器问题&#xff1a;确保你使用的是XeLaTeX或LuaLaTeX进行编译&#xff0c;因为它们对Unicode和中文支持更好。你可以在你的LaTeX编辑器…

K8S如何基于Istio实现全链路HTTPS

K8S如何基于Istio实现全链路HTTPS Istio 简介Istio 是什么?为什么选择 Istio?Istio 的核心概念Service Mesh(服务网格)Data Plane(数据平面)Sidecar Mode(边车模式)Ambient Mode(环境模式)Control Plane(控制平面)Istio 的架构与组件Envoy ProxyIstiod其他组件Istio 的流量管…

安全见闻-泷羽sec课程笔记

编程语言 C语言&#xff1a;一种通用的、面向过程的编程语言&#xff0c;广泛应用于系统软件和嵌入式开发。 C:在C语言基础上发展而来&#xff0c;支持面向对象编程&#xff0c;常用于尊戏开发、高性能计算等领域。 Java:一种广泛使用的面问对象编程语言&#xff0c;具有跨平台…

论文笔记(五十六)VIPose: Real-time Visual-Inertial 6D Object Pose Tracking

VIPose: Real-time Visual-Inertial 6D Object Pose Tracking 文章概括摘要I. INTRODACTIONII. 相关工作III. APPROACHA. 姿态跟踪工作流程B. VIPose网络 文章概括 引用&#xff1a; inproceedings{ge2021vipose,title{Vipose: Real-time visual-inertial 6d object pose tra…

LeetCode 热题100(八)【二叉树】(3)

目录 8.11二叉树展开为链表&#xff08;中等&#xff09; 8.12从前序与中序遍历序列构造二叉树&#xff08;中等&#xff09; 8.13路径总和III&#xff08;中等&#xff09; 8.14二叉树的最近公共祖先&#xff08;中等&#xff09; 8.15二叉树中的最大路径和&#xff08;困…

【C语言】Union

一.Union的用法 1.什么是Union? union 共用体名{ 成员列表 }; union&#xff0c;“联合体、共用体”&#xff0c;在某种程度上类似结构体struct的一种数据结构&#xff0c;共用体(union)和结构体(struct)同样可以包含很多种数据类型和变量。 2.为什么使用union&#xff1…

Postgresql源码(138)alter table增加列的执行流程分析

alter table 逻辑比较繁琐&#xff0c;但并不复杂&#xff0c;这里以增加列为例简单梳理流程。 测试用例 drop table t_echo; create table t_echo(a int,b int); insert into t_echo select t.i, t.i*10 from generate_series(1,10) t(i); alter table t_echo add c varchar…

MobaXterm 软件及如何设置取消自动断开连接

MobaXterm 软件及如何设置取消自动断开连接 背景软件资料取消自动断开连接找打设置找到SSH Setting 背景 其实很容易找到类似的资料, 主要为了简单的记录一下, 还是写一下这个博客吧, 也算是自己的输出了. 当练习打字也行吧, 反正自己打字也是一坨, 打字太慢了. 软件资料 官…

【代码审计】常见漏洞专项审计-业务逻辑漏洞审计

❤️博客主页&#xff1a; iknow181 &#x1f525;系列专栏&#xff1a; 网络安全、 Python、JavaSE、JavaWeb、CCNP &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐评论✍ 0x01 漏洞介绍 1、 原理 业务逻辑漏洞是一类特殊的安全漏洞&#xff0c;业务逻辑漏洞属于设计漏洞而非实…

YOLOv11实战垃圾分类

本文采用YOLOv11作为核心算法框架&#xff0c;结合PyQt5构建用户界面&#xff0c;使用Python3进行开发。YOLOv11以其高效的特征提取能力&#xff0c;在多个图像分类任务中展现出卓越性能。本研究针对4种垃圾数据集进行训练和优化&#xff0c;该数据集包含丰富的垃圾图像样本&am…

Kafka集群中数据的存储是按照什么方式存储的?

1&#xff09;Topic 数据的存储机制 Topic是逻辑上的概念&#xff0c;而partition是物理上的概念&#xff0c;每个partition对应于一个log文件&#xff0c;该log文件中存储的就是Producer生产的数据。Producer生产的数据会被不断追加到该log文件末端&#xff0c;为防止log文件…

渗透测试靶场 - vulhub -CVE漏洞 Weblogic 任意文件上传漏洞(CVE-2018-2894) 漏洞讲解 漏洞复现 亲测步骤能通过哟

漏洞说明 Weblogic管理端未授权的两个页面存在任意上传jsp文件漏洞&#xff0c;可直接获取服务器权限。 两个页面 /ws_utc/begin.do /ws_utc/config.do Oracle 在2018年7月更新中&#xff0c;修复了Weblogic Web Service Test Page中一处任意文件上传漏洞&#xff0c; Web Se…

使用 PyTorch 实现 AlexNet 进行 MNIST 图像分类

AlexNet 是一种经典的深度学习模型&#xff0c;它在 2012 年的 ImageNet 图像分类比赛中大放异彩&#xff0c;彻底改变了计算机视觉领域的格局。AlexNet 的核心创新包括使用深度卷积神经网络&#xff08;CNN&#xff09;来处理图像&#xff0c;并采用了多个先进的技术如 ReLU 激…

基于图的去中心化社会推荐过滤器

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;编程探索专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年11月11日19点20分 点击开启你的论文编程之旅https://www.aspiringcode.com/content?id17176636216843&uideba758a1550b46bb…

深度学习模型评价指标介绍

模型评价指标 模型评价指标1.混淆矩阵2.Overall Accuracy3.Average accuracy4.Kappa系数5.Recall6.Precision7.F18.PR曲线9.置信度10.IOU11.AP12.mAP 模型评价指标 在我们学习机器学习以及深度学习&#xff0c;甚至在计算机视觉领域&#xff0c;我们不可避免的要利用一些指标评…

k8s 1.28.2 集群部署 docker registry 接入 MinIO 存储

文章目录 [toc]docker registry 部署生成 htpasswd 文件生成 secret 文件 生成 registry 配置文件创建 service创建 statefulset创建 ingress验证 docker registry docker registry 监控docker registry ui docker registry dockerfile docker registry 配置文件 S3 storage dr…

【自用】0-1背包问题与完全背包问题的Java实现

引言 背包问题是计算机科学领域的一个经典优化问题&#xff0c;分为多种类型&#xff0c;其中最常见的是0-1背包问题和完全背包问题。这两种问题的核心在于如何在有限的空间内最大化收益&#xff0c;但它们之间存在一些关键的区别&#xff1a;0-1背包问题允许每个物品只能选择…

Zookeeper的安装与使用

一、简介 1.1、概念 ZooKeeper 是一个开源的分布式协调服务&#xff0c;主要用于解决分布式系统中的数据一致性问题。它提供了一种可靠的机制来管理和协调分布式系统的各个节点。ZooKeeper 的设计目标是简化分布式应用的开发&#xff0c;提供简单易用的接口和高性能、高稳定性…