鸿蒙Harmony实战开发:Touchscreen驱动器件硬件接口使用实例

news2024/11/27 5:36:40

功能简介

Touchscreen驱动用于驱动触摸屏使其正常工作,该驱动主要完成如下工作:对触摸屏驱动IC进行上电、配置硬件管脚并初始化其状态、注册中断、配置通信接口(I2C或SPI)、设定Input相关配置、下载及更新固件等操作。

在HDF(Hardware Driver Foundation)驱动管理框架的基础上,Input驱动模型通过调用OSAL接口层和Platform接口层提供的基础接口进行开发,涉及的接口包括bus通信接口、操作系统原生接口(memory、lock、thread、timer等)。由于OSAL接口和Platform接口屏蔽了芯片平台的差异,所以基于Input驱动模型实现的Touchscreen驱动可以进行跨平台、跨OS迁移,从而实现驱动的一次开发、多端部署。

运作机制

Input模型整体的框架如图1所示。Input驱动模型基于HDF驱动框架、Platform接口、OSAL接口进行开发,向上对接规范化的驱动接口HDI(Hardware Device Interface)层,通过Input-HDI层对外提供硬件能力,即上层Input Service可以通过HDI接口层获取相应的驱动能力,进而操控Touchscreen等输入设备。

图1 基于HDF驱动框架的Input驱动模型


Input驱动模型核心部分由设备管理层、公共驱动层、器件驱动层组成。器件产生的数据借助平台数据通道能力从内核传递到用户态,驱动模型通过配置文件适配不同器件及硬件平台,提高开发者对器件驱动的开发效率。如下为模型各部分的说明:

  • Input设备管理:为各类输入设备驱动提供Input设备的注册、注销接口,同时对Input设备列表进行统一管理。
  • Input平台驱动:指各类Input设备的公共抽象驱动(例如触摸屏的公共驱动),该部分主要负责对板级硬件进行初始化、硬件中断处理、向manager注册Input设备等。
  • Input器件驱动:指各器件厂家的差异化驱动,开发者可以通过适配平台驱动预留的差异化接口进行器件驱动开发,实现器件驱动开发量最小化。
  • Input数据通道:提供一套通用的数据上报通道,各类别的Input设备驱动均可用此通道上报Input事件。
  • Input配置解析:负责对Input设备的板级配置及器件私有配置进行解析及管理。

开发指导

场景介绍

Input模块主要完成如下工作:对触摸屏驱动IC进行上电、配置硬件管脚并初始化其状态、注册中断、配置通信接口(I2C或SPI)、设定Input相关配置、下载及更新固件等操作。

接口说明

硬件接口

Touchscreen器件的硬件接口相对简单,根据PIN脚的属性,可以简单分为如下三类:

  • 电源接口

  • IO控制接口

  • 通信接口

图2 Touchscreen器件常用管脚

对于上图所示的三类接口,简要说明如下:

  1. 电源接口

    • LDO_1P8:1.8V数字电路

    • LDO_3P3:3.3V模拟电路

      通常情况下,Touchscreen驱动IC和LCD驱动IC是相互分离的,这种情况下,Touchscreen驱动IC一般同时需要1.8V和3.3V两路供电。随着芯片的演进,业内已有将Touchscreen驱动IC和LCD驱动IC集成在一颗IC中的案例,对Touchscreen而言,只需要关注1.8V供电即可,其内部需要的3.3V电源,会在驱动IC内部从LCD的VSP电源(典型值5.5V)中分出来。

  2. IO控制接口

    • Reset:reset管脚,用于在系统休眠、唤醒时,由主机侧对驱动IC进行复位操作。
    • INT:中断管脚,需要在驱动初始化时,配置为输入上拉状态。在驱动IC检测到外部触摸信号后,通过操作中断管脚来触发中断,器件驱动则会在中断处理函数中进行报点数据读取等操作。
  3. 通信接口

    • I2C:由于Touchscreen的报点数据量相对较少,所以一般选用I2C方式传输数据。I2C的具体协议及对应操作接口
    • SPI:在需要传递的数据不止包含报点坐标,还包含基础容值的情况下,由于需要传递的数据量较大,所以部分厂商会选用SPI通信方式。SPI的具体协议及对应操作接口
软件接口

Input HDF驱动提供给系统服务Input Service调用的HDI驱动能力接口,按照业务范围可以分为三大模块:Input设备管理模块、Input数据上报模块、Input业务控制模块,具体的接口如下表所示,包括:输入设备打开及关闭接口、注册设备监听的回调接口、设备信息查询接口、电源状态控制接口等。

  • input_manager.h

    接口名称功能描述
    int32_t (*OpenInputDevice)(uint32_t devIndex);打开Input设备
    int32_t (*CloseInputDevice)(uint32_t devIndex);关闭Input设备
    int32_t (*GetInputDevice)(uint32_t devIndex, DeviceInfo **devInfo);获取指定ID的设备信息
    int32_t (*GetInputDeviceList)(uint32_t *devNum, DeviceInfo **devList, uint32_t size);获取所有设备列表信息
  • input_reporter.h

    接口名称功能描述
    int32_t (*RegisterReportCallback)(uint32_t devIndex, InputReportEventCb *callback);注册Input设备的回调
    int32_t (*UnregisterReportCallback)(uint32_t devIndex);注销Input设备的回调
    void (*ReportEventPkgCallback)(const EventPackage **pkgs, uint32_t count);上报数据的回调函数
  • input_controller.h

    接口名称功能描述
    int32_t (*SetPowerStatus)(uint32_t devIndex, uint32_t status);设置电源状态
    int32_t (*GetPowerStatus)(uint32_t devIndex, uint32_t *status);获取电源状态
    int32_t (*GetDeviceType)(uint32_t devIndex, uint32_t *deviceType);获取设备类型
    int32_t (*GetChipInfo)(uint32_t devIndex, char *chipInfo, uint32_t length);获取器件编码信息
    int32_t (*GetVendorName)(uint32_t devIndex, char *vendorName, uint32_t length);获取模组厂商名
    int32_t (*GetChipName)(uint32_t devIndex, char *chipName, uint32_t length);获取芯片厂商名
    int32_t (*SetGestureMode)(uint32_t devIndex, uint32_t gestureMode);设置手势模式
    int32_t (*RunCapacitanceTest)(uint32_t devIndex, uint32_t testType, char *result, uint32_t length);执行容值自检测试
    int32_t (*RunExtraCommand)(uint32_t devIndex, InputExtraCmd *cmd);执行拓展指令

开发步骤

以Touchscreen器件驱动为例,Input驱动模型的完整加载流程可以分为六步:

  1. 设备描述配置:由开发者参考已有模板进行设备描述配置,配置的信息包括驱动加载顺序、板级硬件信息、器件私有数据信息等。

  2. 加载Input设备管理驱动:由HDF驱动加载Input设备管理驱动,完成设备manager的创建并对其初始化。

  3. 加载平台驱动:平台驱动由HDF框架加载,主要完成板级配置解析及硬件初始化,并提供器件注册接口。

  4. 加载器件驱动:器件驱动也由HDF框架加载,完成器件设备的实例化,包括器件私有配置解析和平台预留的差异化接口适配。

  5. 器件设备向平台驱动注册:将实例化的器件设备注册到平台驱动,实现设备和驱动的绑定,并完成中断注册、上下电等器件初始化工作。

  6. Input设备注册:在器件初始化完成后,实例化Input设备,并将其注册到Input manager进行管理。

根据Input驱动模型的加载流程可知,Touchscreen器件驱动的开发过程主要包含以下三个步骤:

  1. 设备描述配置:目前Input驱动基于HDF驱动框架编写,驱动的加载启动由HDF驱动管理框架统一处理。首先需要在对应的配置文件中,将驱动信息注册进去,如是否加载、加载优先级,此后HDF驱动框架会逐一启动注册过的驱动模块。

  2. 板级配置及Touchscreen器件私有配置:配置对应的IO管脚功能,例如对单板上为Touchscreen设计预留的I2C Pin脚,需设置对应的寄存器,使其选择I2C的通信功能。

  3. 实现器件差异化适配接口:根据硬件单板设计的通信接口,使用Platform接口层提供的管脚操作接口配置对应的复位管脚、中断管脚以及电源操作,对于GPIO的操作

开发实例

下面以RK3568开发板的Input模块为例,说明Touchscreen器件的适配和接口使用方法。

  1. 设备描述配置

    如下配置主要包含Input驱动模型各模块层级信息,配置文件路径为vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs。具体原理可参考HDF驱动开发流程,HDF框架依据该配置信息实现对Input模型各模块的依次加载等。

    input :: host {
        hostName = "input_host";
        priority = 100;
        device_input_manager :: device {
            device0 :: deviceNode {
                policy = 2;        // 向外发布服务
                priority = 100;    // 加载优先级,在input模块内,manager模块优先级应为最高
                preload = 0;       // 加载该驱动,0:加载;1:不加载
                permission = 0660;
                moduleName = "HDF_INPUT_MANAGER";
                serviceName = "input_dev_manager";
                deviceMatchAttr = "";
            }
        }
        device_hdf_touch :: device {
            device0 :: deviceNode {
                policy = 2;
                priority = 120;
                preload = 0;
                permission = 0660;
                moduleName = "HDF_TOUCH";
                serviceName = "event1";
                deviceMatchAttr = "touch_device1";
            }
        }
    
        device_touch_chip :: device {
            device0 :: deviceNode {
                policy = 0;
                priority = 130;
                preload = 0;
                permission = 0660;
                moduleName = "HDF_TOUCH_SAMPLE";
                serviceName = "hdf_touch_sample_service";
                deviceMatchAttr = "zsj_sample_5p5";
            }
        }
    }
  2. 板级配置及器件私有配置

    如下配置包含板级硬件配置及器件私有数据配置,配置文件路径为vendor/hihope/rk3568/hdf_config/khdf/input/input_config.hcs。实际业务开发时,可根据具体需求增删及修改如下配置文件信息。

    root {
        input_config {
            touchConfig {
                touch0 {
                    boardConfig {
                        match_attr = "touch_device1";
                        inputAttr {
                            inputType = 0;           // 0代表触摸屏
                            solutionX = 480; 
                            solutionY = 960;
                            devName = "main_touch";  // 设备名称
                        }
                        busConfig {
                            busType = 0;             // 0代表I2C
                            busNum = 6;
                            clkGpio = 86;
                            dataGpio = 87;
                            i2cClkIomux = [0x114f0048, 0x403];  // i2c_clk对应pin的寄存器配置
                            i2cDataIomux = [0x114f004c, 0x403]; // i2c_data对应pin的寄存器配置
                        }
                        pinConfig {
                            rstGpio = 3;
                            intGpio = 4;
                            rstRegCfg = [0x112f0094, 0x400];  // reset对应pin的寄存器配置
                            intRegCfg = [0x112f0098, 0x400];  // interrupt对应pin的寄存器配置
                        }
                        powerConfig {
                            vccType = 2;       // 1代表LDO、2代表GPIO、3代表PMIC
                            vccNum = 20;       // GPIO号为20
                            vccValue = 1800;   // 电压幅值为1800mV
                            vciType = 1;
                            vciNum = 12;
                            vciValue = 3300;
                        }
                        featureConfig {
                            capacitanceTest = 0;
                            gestureMode = 0;
                            gloverMOde = 0;
                            coverMode = 0;
                            chargerMode = 0;
                            knuckleMode = 0;
                        }
                    }
                    chipConfig {
                        template touchChip {
                            match_attr = "";
                            chipName = "sample";
                            vendorName = "zsj";
                            chipInfo = "AAAA11222";  // 1~4字符代表产品名,5~6字符代表IC型号,7~9字符代表模型型号
                            busType = 0;
                            deviceAddr = 0x5D;
                            irqFlag = 2;             // 1代表上升沿触发,2代表下降沿触发,4代表高电平触发,8代表低电平触发
                            maxSpeed = 400;
                            chipVersion = 0;
                            powerSequence {
                                /* 上电时序的配置含义说明:
                                  [类型, 状态, 方向 , 延时]
                                  <type> 0代表空,1代表vcc电源(1.8V),2代表VCI电源(3.3V),3代表复位管脚,4代表中断管脚
                                  <status> 0代表下电或拉低,1代表上电或拉高,2代表无操作
                                  <dir> 0代表输入方向,1代表输出方向,2代表无操作
                                  <delay> 代表延时多少毫秒, 例如20代表延时20ms
                                */
                                powerOnSeq = [4, 0, 1, 0,
                                            3, 0, 1, 10,
                                            3, 1, 2, 60,
                                            4, 2, 0, 0];
                                suspendSeq = [3, 0, 2, 10];
                                resumeSeq = [3, 1, 2, 10];
                                powerOffSeq = [3, 0, 2, 10,
                                              1, 0, 2, 20];
                            }
                        }
                        chip0 :: touchChip {
                            match_attr = "zsj_sample_5p5";
                            chipInfo = "ZIDN45100";
                            chipVersion = 0;
                        }
                    }
                }
            }
        }
    }
  3. 添加器件驱动

    在器件驱动中,主要实现了平台预留的差异化接口,以器件数据获取及解析进行示例说明,代码路径为drivers/hdf_core/framework/model/input/driver/touchscreen/touch_gt911.c。具体开发过程,需要根据实际使用的单板及器件进行适配。

    /* 将从器件中读取到的报点数据解析为坐标 */
    static void ParsePointData(ChipDevice *device, FrameData *frame, uint8_t *buf, uint8_t pointNum)
    {
        int32_t resX = device->driver->boardCfg->attr.resolutionX;
        int32_t resY = device->driver->boardCfg->attr.resolutionY;
    
        for (int32_t i = 0; i < pointNum; i++) {
            frame->fingers[i].y = (buf[GT_POINT_SIZE * i + GT_X_LOW] & ONE_BYTE_MASK) |
                                  ((buf[GT_POINT_SIZE * i + GT_X_HIGH] & ONE_BYTE_MASK) << ONE_BYTE_OFFSET);
            frame->fingers[i].x = (buf[GT_POINT_SIZE * i + GT_Y_LOW] & ONE_BYTE_MASK) |
                                  ((buf[GT_POINT_SIZE * i + GT_Y_HIGH] & ONE_BYTE_MASK) << ONE_BYTE_OFFSET);
            frame->fingers[i].valid = true;
        }
    }
    /* 从器件中获取报点数据 */
    static int32_t ChipDataHandle(ChipDevice *device)
    {
        int32_t ret;
        uint8_t touchStatus = 0;
        uint8_t pointNum;
        uint8_t buf[GT_POINT_SIZE * MAX_SUPPORT_POINT] = {0};
        InputI2cClient *i2cClient = &device->driver->i2cClient;
        uint8_t reg[GT_ADDR_LEN] = {0};
        FrameData *frame = &device->driver->frameData;
        reg[0] = (GT_BUF_STATE_ADDR >> ONE_BYTE_OFFSET) & ONE_BYTE_MASK;
        reg[1] = GT_BUF_STATE_ADDR & ONE_BYTE_MASK;
        ret = InputI2cRead(i2cClient, reg, GT_ADDR_LEN, &touchStatus, 1);
        if (ret < 0 || touchStatus == GT_EVENT_INVALID) {
            return HDF_FAILURE;
        }
        OsalMutexLock(&device->driver->mutex);
        (void)memset_s(frame, sizeof(FrameData), 0, sizeof(FrameData));
        if (touchStatus == GT_EVENT_UP) {
            frame->realPointNum = 0;
            frame->definedEvent = TOUCH_UP;
            goto exit;
        }
        reg[0] = (GT_X_LOW_BYTE_BASE >> ONE_BYTE_OFFSET) & ONE_BYTE_MASK;
        reg[1] = GT_X_LOW_BYTE_BASE & ONE_BYTE_MASK;
        pointNum = touchStatus & GT_FINGER_NUM_MASK;
        if (pointNum <= 0 || pointNum > MAX_SUPPORT_POINT) {
            HDF_LOGE("%s: pointNum is invalid, %d", __func__, pointNum);
            (void)ChipCleanBuffer(i2cClient);
            OsalMutexUnlock(&device->driver->mutex);
            return HDF_FAILURE;
        }
        frame->realPointNum = pointNum;
        frame->definedEvent = TOUCH_DOWN;
        /* 从寄存器中读取报点值 */
        (void)InputI2cRead(i2cClient, reg, GT_ADDR_LEN, buf, GT_POINT_SIZE * pointNum);
        /* 解析报点值 */
        ParsePointData(device, frame, buf, pointNum);
    exit:
        OsalMutexUnlock(&device->driver->mutex);
        if (ChipCleanBuffer(i2cClient) != HDF_SUCCESS) {
            return HDF_FAILURE;
        }
        return HDF_SUCCESS;
    }
    
    static struct TouchChipOps g_sampleChipOps = {
        .Init = ChipInit,
        .Detect = ChipDetect,
        .Resume = ChipResume,
        .Suspend = ChipSuspend,
        .DataHandle = ChipDataHandle,
    };
    
    static TouchChipCfg *ChipConfigInstance(struct HdfDeviceObject *device)
    {
        TouchChipCfg *chipCfg = (TouchChipCfg *)OsalMemAlloc(sizeof(TouchChipCfg));
        if (chipCfg == NULL) {
            HDF_LOGE("%s: instance chip config failed", __func__);
            return NULL;
        }
        (void)memset_s(chipCfg, sizeof(TouchChipCfg), 0, sizeof(TouchChipCfg));
        /* 解析器件私有配置 */
        if (ParseTouchChipConfig(device->property, chipCfg) != HDF_SUCCESS) {
            HDF_LOGE("%s: parse chip config failed", __func__);
            OsalMemFree(chipCfg);
            chipCfg = NULL;
        }
        return chipCfg;
    }
    
    static ChipDevice *ChipDeviceInstance(void)
    {
        ChipDevice *chipDev = (ChipDevice *)OsalMemAlloc(sizeof(ChipDevice));
        if (chipDev == NULL) {
            HDF_LOGE("%s: instance chip device failed", __func__);
            return NULL;
        }
        (void)memset_s(chipDev, sizeof(ChipDevice), 0, sizeof(ChipDevice));
        return chipDev;
    }
    
    static void FreeChipConfig(TouchChipCfg *config)
    {
        if (config->pwrSeq.pwrOn.buf != NULL) {
            OsalMemFree(config->pwrSeq.pwrOn.buf);
        }
        if (config->pwrSeq.pwrOff.buf != NULL) {
            OsalMemFree(config->pwrSeq.pwrOff.buf);
        }
        OsalMemFree(config);
    }
    
    static int32_t HdfSampleChipInit(struct HdfDeviceObject *device)
    {
        TouchChipCfg *chipCfg = NULL;
        ChipDevice *chipDev = NULL;
        HDF_LOGE("%s: enter", __func__);
        if (device == NULL) {
            return HDF_ERR_INVALID_PARAM;
        }
        /* 器件私有配置解析 */
        chipCfg = ChipConfigInstance(device);
        if (chipCfg == NULL) {
            return HDF_ERR_MALLOC_FAIL;
        }
        /* 器件设备实例化 */
        chipDev = ChipDeviceInstance();
        if (chipDev == NULL) {
            goto freeCfg;
        }
        chipDev->chipCfg = chipCfg;
        chipDev->ops = &g_sampleChipOps;
        chipDev->chipName = chipCfg->chipName;
        chipDev->vendorName = chipCfg->vendorName;
    
      /* 器件设备注册到平台驱动 */
        if (RegisterChipDevice(chipDev) != HDF_SUCCESS) {
            goto freeDev;
        }
        HDF_LOGI("%s: exit succ, chipName = %s", __func__, chipCfg->chipName);
        return HDF_SUCCESS;
    
    freeDev:
        OsalMemFree(chipDev);
    freeCfg:
        FreeChipConfig(chipCfg);
        return HDF_FAILURE;
    }
    
    struct HdfDriverEntry g_touchSampleChipEntry = {
        .moduleVersion = 1,
        .moduleName = "HDF_TOUCH_SAMPLE",
        .Init = HdfSampleChipInit,
    };
    
    HDF_INIT(g_touchSampleChipEntry);
  4. 调用Input HDI接口

    下面通过示例代码说明上层输入系统服务调用Input HDI的步骤。

    #include "input_manager.h"
    #define DEV_INDEX 1
    
    IInputInterface *g_inputInterface;
    InputReportEventCb g_callback;
    
    /* 定义数据上报的回调函数 */
    static void ReportEventPkgCallback(const EventPackage **pkgs, uint32_t count)
    {
        if (pkgs == NULL || count > MAX_PKG_NUM) {
            return;
        }
        for (uint32_t i = 0; i < count; i++) {
            HDF_LOGI("%s: pkgs[%d] = 0x%x, 0x%x, %d", __func__, i, pkgs[i]->type, pkgs[i]->code, pkgs[i]->value);
        }
    }
    
    int InputServiceSample(void)
    {
        uint32_t devType = INIT_DEFAULT_VALUE;
    
        /* 获取Input驱动能力接口 */
        int ret = GetInputInterface(&g_inputInterface);
        if (ret != INPUT_SUCCESS) {
            HDF_LOGE("%s: get input interfaces failed, ret = %d", __func__, ret);
            return ret;
        }
    
        INPUT_CHECK_NULL_POINTER(g_inputInterface, INPUT_NULL_PTR);
        INPUT_CHECK_NULL_POINTER(g_inputInterface->iInputManager, INPUT_NULL_PTR);
        /* 打开特定的Input设备 */
        ret = g_inputInterface->iInputManager->OpenInputDevice(DEV_INDEX);
        if (ret) {
            HDF_LOGE("%s: open input device failed, ret = %d", __func__, ret);
          return ret;
        }
    
        INPUT_CHECK_NULL_POINTER(g_inputInterface->iInputController, INPUT_NULL_PTR);
        /* 获取对应Input设备的类型 */
        ret = g_inputInterface->iInputController->GetDeviceType(DEV_INDEX, &devType);
        if (ret) {
            HDF_LOGE("%s: get device type failed, ret: %d", __FUNCTION__, ret);
            return ret;
        }
        HDF_LOGI("%s: device1's type is %u\n", __FUNCTION__, devType);
    
        /* 给特定的Input设备注册数据上报回调函数 */
        g_callback.ReportEventPkgCallback = ReportEventPkgCallback;
        INPUT_CHECK_NULL_POINTER(g_inputInterface->iInputReporter, INPUT_NULL_PTR);
        ret  = g_inputInterface->iInputReporter->RegisterReportCallback(DEV_INDEX, &g_callback);
        if (ret) {
            HDF_LOGE("%s: register callback failed, ret: %d", __FUNCTION__, ret);
          return ret;
        }
        HDF_LOGI("%s: wait 10s for testing, pls touch the panel now", __FUNCTION__);
        OsalMSleep(KEEP_ALIVE_TIME_MS);
    
        /* 注销特定Input设备上的回调函数 */
        ret = g_inputInterface->iInputReporter->UnregisterReportCallback(DEV_INDEX);
        if (ret) {
            HDF_LOGE("%s: unregister callback failed, ret: %d", __FUNCTION__, ret);
            return ret;
        }
    
        /* 关闭特定的Input设备 */
        ret = g_inputInterface->iInputManager->CloseInputDevice(DEV_INDEX);
        if (ret) {
            HDF_LOGE("%s: close device failed, ret: %d", __FUNCTION__, ret);
          return ret;
        }
        return 0;
    }
最后

小编在之前的鸿蒙系统扫盲中,有很多朋友给我留言,不同的角度的问了一些问题,我明显感觉到一点,那就是许多人参与鸿蒙开发,但是又不知道从哪里下手,因为资料太多,太杂,教授的人也多,无从选择。有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

为了确保高效学习,建议规划清晰的学习路线,涵盖以下关键阶段:


 鸿蒙(HarmonyOS NEXT)最新学习路线

该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案

路线图适合人群:

IT开发人员:想要拓展职业边界
零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能。
技术提升/进阶跳槽:发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术

2.视频学习资料+学习PDF文档

HarmonyOS Next 最新全套视频教程

  纯血版鸿蒙全套学习资料(面试、文档、全套视频等)              

​​

总结

参与鸿蒙开发,你要先认清适合你的方向,如果是想从事鸿蒙应用开发方向的话,可以参考本文的学习路径,简单来说就是:为了确保高效学习,建议规划清晰的学习路线

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

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

相关文章

考试:数据库系统(01)

数据库系统 ◆数据&#xff1a;是数据库中存储的基本对象&#xff0c;是描述事物的符号记录。 数据的种类&#xff1a;文本、图形、图像、音频、视频、学生的档案记录、货物的运输 情况等。 ◆数据库DB: 是长期存储在计算机内、有组织的、可共享的大量数据的集合。 ◆数据库…

安恒信息总裁宋端智,辞职了!活捉一枚新鲜出炉的餐饮人!

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247485367&idx1&sn837891059c360ad60db7e9ac980a3321&chksmc0e47eebf793f7fdb8fcd7eed8ce29160cf79ba303b59858ba3a6660c6dac536774afb2a6330#rd 《网安面试指南》http://mp.weixin.qq.com/s?…

雅菲奥朗 FinOps 认证培训:开启企业云财务管理转型之路

前言&#xff1a; 在当今快速变化的商业环境中&#xff0c;企业面临着前所未有的IT财务挑战。随着云计算和数字化转型的推进&#xff0c;传统的财务管理方式已经不能满足“企业上云”的需求。FinOps&#xff0c;即“云财务管理”应运而生&#xff0c;成为帮助企业实现IT财务流…

Compose TextField详解

首先明确Compose TextField的底层依赖是&#xff1a; TextField BasicTextField CoreTextField 相较于Text&#xff0c;TextField需要提供可输入的能力以及渲染效果动态更新的能力。 // CompositionLocals// If the text field is disabled or read-only, we should not d…

智能菜谱推荐系统_ct3p7

TOC springboot575智能菜谱推荐系统_ct3p7--论文 第一章 概述 1.1 研究背景 近些年&#xff0c;随着中国经济发展&#xff0c;人民的生活质量逐渐提高&#xff0c;对网络的依赖性越来越高&#xff0c;通过网络处理的事务越来越多。随着智能菜谱推荐管理的常态化&#xff0c…

F1 F4 Fn lock 指示灯不亮 联想笔记本 thinkpad

问题描述&#xff1a;F1 F4 Fn lock 指示灯开机的时候亮&#xff0c;但是使用的时候虽然能够发挥正常功能&#xff0c;但是指示灯一直熄灭&#xff0c;指示灯不亮。 电脑型号&#xff1a;联想笔记本 thinkpad E14 Gen 2 。本方案应该适用于所有联想电脑。 解决方法&#xff1a;…

嵌入式和单片机有什么区别?

目录 &#xff08;1&#xff09;什么是嵌入式&#xff1f; &#xff08;2&#xff09;什么是单片机&#xff1f; &#xff08;3&#xff09;嵌入式和单片机的共同点 &#xff08;4&#xff09;嵌入式和单片机的区别 &#xff08;1&#xff09;什么是嵌入式&#xff1f; 关…

掉毛不愁!浮毛怎么去掉比较干净?这宠物空气净化器用上真能解决

这阵子天气热得让人只想宅家&#xff0c;门窗紧闭&#xff0c;空调一开就是一整天。室内凉爽宜人&#xff0c;但一出门再回来&#xff0c;那满屋的浮毛和异味简直让人措手不及&#xff0c;仿佛从天堂跌入地狱。幸好&#xff0c;我家有台宠物空气净化器这位“救星”&#xff0c;…

【Linux】线程控制|POSIX线程库|多线程创建|线程终止|等待|线程分离|线程空间布局

目录 ​编辑 POSIX线程库 多线程创建 独立栈结构 获取线程ID pthread_self 线程终止 return终止线程 pthread_exit pthread_cancel 线程等待 退出码问题 线程分离 测试 线程ID及地址空间布局 ​编辑 POSIX线程库 pthread线程库是 POSIX线程库的一部分&#xf…

MySQL运维学习(1):4种日志

1.错误日志 mysql错误日志记录了mysql发生任何严重错误时的信息&#xff0c;若数据库无法正常使用时&#xff0c;可以先查看错误日志 默认情况下错误日志是开启的&#xff0c;文件名为/var/log/mysqld.log&#xff0c;如果文件不在默认位置&#xff0c;可以通过下面的命令查看…

【Java】Junit的使用

Java系列文章目录 补充内容 Windows通过SSH连接Linux 第一章 Linux基本命令的学习与Linux历史 文章目录 Java系列文章目录一、前言二、学习内容&#xff1a;三、问题描述四、解决方案&#xff1a;4.1 Junit测试方法的使用4.2 测试规范 五、总结 一、前言 学习测试JunitMock后…

LLM如何理解图数据? Graph+LLM综述

对图推理&#xff08;RoG&#xff09;&#xff1a;忠实可解释的大语言模型推理方法&#xff08;ICLR2024&#xff09; https://github.com/RManLuo/reasoning-on-graphs 推理图&#xff08;Reasoning on Graphs, RoG&#xff09;提出了一个计划-检索-推理框架&#xff0c;该…

『基础』OS-1计算机系统概述_操作系统发展历程及它的运行环境

操作系统发展历程 常考的三种操作系统对比 批操作系统脱机使用计算机&#xff1b;作业是分批处理的&#xff1b;系统内多道程序并发执行&#xff1b;交互能力差分时操作系统多个用户同时使用计算机&#xff1b;人机交互强&#xff1b;具有每个用户独立使用计算机的独占性&…

学习大数据DAY42 hive 分桶表

目录 分桶表 分桶表注意事项 hive 分桶表-创建分桶表 hive 排序关键字 hive 排序语句 上机练习 分桶表 分区提供一个隔离数据和优化查询的便利方式。不过&#xff0c;并非所有的数据集都可形 成合理的分区。对于一张表或者分区&#xff0c;Hive 可以进一步组织成桶&…

8.21T1 草莓蛋糕(拆max + 权值线段树)

http://cplusoj.com/d/senior/p/NODSX2302A 看到式子&#xff1a; 我们就应该想到拆max 若 我们可以整理推出&#xff1a; 记&#xff1a; 由 L L L 算 C C C&#xff0c;我们满足 h a ≤ h b h_a\le h_b ha​≤hb​&#xff0c;找 c c c 的最小值 C C C 算 L L L 同…

05、Redis实战:优惠券秒杀、全局唯一ID、超买乐观悲观锁、一人一单逻辑、分布式锁、分布式锁的原子性

3、优惠卷秒杀 3.1 -全局唯一ID 每个店铺都可以发布优惠券&#xff1a; 当用户抢购时&#xff0c;就会生成订单并保存到tb_voucher_order这张表中&#xff0c;而订单表如果使用数据库自增ID就存在一些问题&#xff1a; id的规律性太明显受单表数据量的限制 场景分析&#x…

第2章-02-网页中的Document元素

🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌,CSDN博客专家,阿里云社区专家博主,2023年CSDN全站百大博主。 🏆数年电商行业从业经验,历任核心研发工程师,项目技术负责人。 🏆本文已收录于专栏:Web爬虫入门与实战精讲,后续完整更新内容如下。 文章…

2024思维导图工具评测:性能、易用性全面对比

现在工作日常要处理的数据纷繁复杂&#xff0c;如何高效地组织、理解和记忆这些信息&#xff0c;成为了每个人都需要面对的挑战。不知道你有没有尝试过使用思维导图软件呢&#xff1f;这次我们看看它们是如何帮助我们优化思维、提升效率的。 1.福晰思维导图 链接一下&#xf…

数据结构day04(队列 Queue 循环队列、链式队列)

目录 【1】队列 Queue 1》 队列的定义 2》循环队列 3》链式队列 【1】队列 Queue 1》 队列的定义 队列&#xff08;queue&#xff09;是只允许在一端进行插入操作&#xff0c;而在另一端进行删除操作的线性表。 队列是一种先进先出&#xff08;First In First Out&#xf…

Day23 第十站 文件IO的多路复用

#include <myhead.h>void insert_client(int *client_arr,int *len,int client) {//client_arr[n]{3,4} len&client_count,client_count2;//添加 5 client_arr[2(*len)]5(client)client_arr[*len]client;(*len); } int find_client(int *client_arr,int len,int clie…