openharmony GPIO 驱动开发

news2024/10/5 16:20:24

openharmony GPIO 驱动开发

  • GPIO 基础知识
    • GPIO 基础知识——概念
    • GPIO 基础知识——IO 复用
    • GPIO 基础知识——GPIO 分组和编号
    • GPIO 基础知识——用户态测试
  • HDF 框架下 GPIO 驱动
    • HDF 框架下的 GPIO 驱动——案例描述(以 HI3516DV300 平台为例,提供代码)
    • HDF 框架下的 GPIO 驱动——应用绑定服务
    • HDF 框架下的 GPIO 驱动——用户态 HdfSBuf
    • HDF 框架下的 GPIO 驱动——应用和驱动通信1
    • HDF 驱动框架下的 GPIO 驱动——应用和驱动通信2
    • HDF 框架下的 GPIO 驱动——驱动入口
    • HDF 框架下的 GPIO 驱动——驱动接收和发送数据
    • HDF 框架下的 GPIO 驱动——GPIO 配置
    • HDF 框架下的 GPIO 驱动——GPIO 配置(中断)
    • HDF 框架下的 GPIO 驱动——防抖和浮空
    • HDF 框架下的 GPIO 驱动——LED 控制
    • HDF 框架下的 GPIO 驱动——驱动程序目录和结构
    • HDF 框架下的 GPIO 驱动——应用程序目录结构
  • 总结
  • 参考链接


GPIO 基础知识

GPIO 基础知识——概念

GPIO:输入或输出高低电平,任意的高低电平的数量和波形组合,无任何协议要求,可以驱动 LED、按键等外设专用 IO:有协议约束的 IO,输入和输出的高低电平的数量、波形组合、波形的持续时间遵循相应的协议,如 I2C、SPI、UART、PWM

  • PWM
    在这里插入图片描述
  • i2c
    在这里插入图片描述

GPIO 基础知识——IO 复用

芯片应提供尽可能多的功能和外部接口,但是芯片的管脚(Pin)数量有限,使用很多 IO 管脚具有多个功能,通过软件配置实现对同一个管脚的分时复用。以 HI3516DV300 为例,共 92 个 GPIO, GPIO3_6 的复用关系如下图:

在这里插入图片描述
在这里插入图片描述

不是所有 IO 管脚都可以作为 GPIOI,有些只能作为专用 IO,如外接存储芯片,而有些管脚只能作为 GPIO。

GPIO 基础知识——GPIO 分组和编号

数量众多的 GPIO 通过分组管理,因此每个 GPIO 都有一个组号和组内号(组内偏移,offset),不同芯片的 GPIO 分组数量和组内 GPIO 管脚数量定义不同。

例如:比如 RK3399/RK3399Pro 提供 5 组 GPIO(GPIO0~GPIO4)共 122 个,所有的 GPIO 都可以用作中断,GPIO0/GPIO1 可以作为系统唤醒脚,所有 GPIO 都可以软件配置为上拉或者下拉,所有 GPIO 默 认为输入,GPIO 的驱动能力软件可以配置。 关于原理图上的 GPIO 跟 dts 里面的 GPIO 的对应关系,例如GPIO4c0,那么对应的 dts 里面应该是“gpio4 16”。因为 GPIO4A 有 8 个 pin,GPIO4B 也有 8 个 pin,以此计算可得 c0 口就是 16,c1 口就是 17,以此类推; GPIO 的 使用请参考 docs\Kernel\Pin-Ctrl\目录下 《Rockchip Pin-Ctrl 开 发指南 V1.0-20160725.pdf》。

GPIO 基础知识——用户态测试

  • 确定 GPIO 管脚编号和电平状态
  • 将管脚复用为 GPIO 功能(复位后默认为 GPIO)
  • 比如 GPIO3_6 管脚计算得到为 GPIO30,可以执行 echo 30 > export
  • 此时,会在 gpio 下新增 gpio30 目录,可以在该目录下执行操作进行 GPIO30 管脚的控制,比如 direction 方向(in、out),电平高低 value(1/0)

HDF 框架下 GPIO 驱动

HDF 框架下的 GPIO 驱动——案例描述(以 HI3516DV300 平台为例,提供代码)

  • GPIO0_6 外接 LED,输出低电平点亮 LED、高电平熄灭 LED
  • GPIO3_6 外接 KEY,配置为中断,触发方式为双边沿触发
  • 用户态程序发送指令到驱动实现点亮和熄灭 LED 操作,驱动程序返回 LED 对应管脚的电平状态到用户态,驱动程序通过形参和事件两种方式实现与应用程序的数据交互
  • 按键触发外部中断,中断服务程序可以点亮或熄灭 LED

HDF 框架下的 GPIO 驱动——应用绑定服务

  • Linux 系统下应用程序通过 open 系统调用打开 /dev/ 目录下的设备节点,获取设备文件句柄,通过这个文件句柄调用 read/write/ioctl 等系统调用接口,实现对设备的操作
  • HDF 框架下用户态应用程序调用特定接口获取驱动程序提供的服务,实现应用和驱动的绑定,应用程序获取到服务后,可基于该服务实现对驱动和设备的操作
//应用程序绑定服务
struct HdfIoService *serv = HdfIoServiceBind("GPIO_TEST_SERVICE");
if(serv == NULL){
    HDF_LOGE("fail to get service %s", "GPIO_TEST_SERVICE");
    return HDF_FAILURE;
}

gpio_drv_test_host::host{
    hostName = "gpio_drv_test";
    priority = 100;
    device_test_driver::device{
        device0::deviceNode{
            policy = 2;
            priority = 100;
            preload = 0;
            permission = 0664;
            moduleName = "GPIO_TEST_DRIVER";
            serviceName = "GPIO_TEST_SERVICE";
        }
    }
}

HDF 框架下的 GPIO 驱动——用户态 HdfSBuf

  • 应用程序获取驱动服务后,就可以利用服务实现和驱动的通信。通信数据的载体是 HdfSBuf,应用程序调用 HdfSBufObtainDefaultSize 可以获取一个默认大小为 256 字节的内存堆空间, HDF 将该内存空间组织为一个环形队列。应用程序会将数据写入该队列,驱动程序可以从队列中读取数据,反之亦然。由于是环形队列,需要保证读取数据的顺序、读取的数据类型与写入数据的一致,遵循先进先出的原则。
//用户态申请空间
struct HdfSBuf *data = HdfSBufObtainDefaultSize();
if(data == NULL){
    printf("fail to obtain sbuf data\n");
    ret = HDF_DEV_ERR_NO_MEMORY;
    goto out;
}

//用户态写数据
if(!HdfSbufWriteString(data, eventData)){
    printf("fail to write data\n");
    goto HDF_FAILURE;
}

//用户态读数据
char *replyData = HdfSbufReadString(data);
if(replyData == NULL){
    printf("fail to read data\n");
    goto HDF_FAILURE;
}

HDF 框架下的 GPIO 驱动——应用和驱动通信1

  • 应用程序申请两个缓存区(环形队列),用于和驱动程序进行数据交互
  • 应用程序向 data 缓冲区写入 String 类型数据
  • 应用程序通过服务的 Dispatch 函数向驱动程序发送数据,导致驱动的 Dispatch 函数被执行
  • 用户程序读取驱动返回的数据:首先获取 String 类型,再获取 uint16 类型
struct HdfSBuf  *data = HdfSBufObtainDefaultSize();
struct HdfSBuf *reply = HdfSBufObtainDefaultSize();

if(id == LED_ON){
    SbufWriteString(data, eventData);
    ret = ser->dispatcher->Dispatch(&serv->object, LED_ON, data, reply);
    string = HdfSbufReadString(data)l;
    HdfSbufReadUint16(reply, &pin_val);
}

HDF 驱动框架下的 GPIO 驱动——应用和驱动通信2

应用程序通过已获取的服务注册一个事件监听器,当驱动程序调用事件发送函数 HdfDeviceSendEvent 后,会触发事件监听器的 callBack 回调函数的执行,在该回调函数中接收驱动发送的数据

static struct HdfDevEventlistener listener = {
    .callBack = OnDevEventReceived,
    .priv = "Service0"
};

//应用程序注册服务
if(HdfDeviceRegisterEventListener(serv, &listener) != HDF_SUCESS){
    HDF_LOGE("fail to register event listener");
    return HDF_FAILURE;
}
  • 驱动程序调用事件发送接口 HdfDeviceSendEvent 向用户态程序发送事件,触发用户态事件监听器执行
  • 应用程序按照驱动程序写入缓冲区的顺序读取数据:首先读取 string 类型数据;再读取 uint16 类型数据
static int32_t OnDevEventReceived(void *priv, uint32_t id, struct HdfSBuf *reply)
{
    uint16_t pin_val = 0;
    
    const char *string  = HdfSbufReadString(reply);
    if(string == NULL){
        printf("fail to read string in event reply\n");
        return HDF_FAILURE;
    }

    if(!HdfSbufReadUint16(reply, &pin_val){
        printf("fail to read uint16 in event reply\n");
        return HDF_FAILURE;
    }

    printf("%s: event reply received : id %u, string %s, pin val %u\n", (char *)priv, id, string, pin_val);
    return HDF_SUCCESS;
}

HDF 框架下的 GPIO 驱动——驱动入口

  • 驱动入口 g_GPIODriverEntry 中定义了三个函数和驱动模块名字 moduleName;
  • device_info.hcs 中新增了一个节点 gpio_drv_test_host, 包含一个名为 moduleName 的属性;
  • 两个 moduleName 的值相等时表示 hcs 和驱动匹配成功,进而调用驱动入口的 Bind 函数、Init 函数。

该过程类似于 dts 和 Linux 驱动中的 compatible 字段,当两者匹配时调用驱动中的 probe 函数

struct HdfDriverEntry g_GPIODriverEntry = {
    .moduleVersion = 1,
    .moduleName = "GPIO_TEST_DRIVER",
    .Bind = HdfGPIODriverBind,
    .Init = HdfGPIODriverInit,
    .Release = HdfGPIODriverRelease,
}

HDF_INIT(g_GPIODriverEntry);

//device_info.hcs
gpio_drv_test_host :: host{
    hostName = "gpio_drv_test";
    priority = 100;
    device_test_driver :: device{
        device0 :: deviceNode{
            policy = 2;
            priority = 100;
            preload = 0;
            permission = 0664;
            moduleName = "GPIO_TEST_DRIVER";
            serviceName = "GPIO_TEST_SERVICE";
        }
    }
}
int32_t HdfGPIODriverBind(strutc HdfDeviceObject *deviceObject)
{
    if(deviceObject == NULL){
        return HDF_FAILURE;
    }
    static struct IDeviceIoService gpioTestService = {
        .Dispatch = HdfGPIODriverDispatch,
    };
    
    deviceObject->service = &gpioTestService;
    HDF_LOGE("GPIO driver bind success");
    return HDF_SUCCESS;
}
* Bind 函数中定义了一个结构体,它是一个名为 gpioTestService 的服务,需要实现 Dispatch 成员函数,该函数用来接收用户态程序发送到内核态的消息,实现用户态和内核态之间的通信。
* 驱动中定义了一个服务,即驱动可以对外提供服务,应用程序可以使用该服务。

HDF 框架下的 GPIO 驱动——驱动接收和发送数据

  • 应用程序首先获取服务,调用服务中定义的 Dispatch 接口可触发该函数的执行,通过参数 id 区分来自用户程序的指令

  • 读取用户态发送的 string 类型数据,执行点亮或者熄灭 LED 的操作

  • 返回一个字符串和 LED 对应管脚的电平状态给用户程序,用户态程序应该首先读取第一个 string 类型数据,再读取uint16 类型数据,遵循 FIFO 原则

  • 注意驱动程序向用户程序返回数据的两种方式:

      * 返回值和发送事件,针对不同的方式
      * 用户态获取驱动数据的方式也不同
    
int32_tHdfGPIODriverDispatch(struct HdfDeviceIoClient *client, int32_t id, struct HdfSBuf *data, struct HdfSBuf *reply)
{
    //驱动程序通过接口读取来自用户态的数据,并向用户态返回数据

    if(id == LED_ON){
        const char *readData = HdfSbufReadString(data);
        if(readData != NULL){
            HDF_LOGE("%s: read data is : %s", __func__, readData);
            led_on();
            GpioRead(LED_PIN, &led_pin_val);
        }
        HdfSbufWriteString(reply, "ledon");
        HdfSbufWriteUint16(reply, led_pin_val);
    }
    else if(id == LED_OFF){
        const char *readData = HdfSbufReadString(data);
        if(readData != NULL){
            HDF_LOGE("%s: read data is :%s", __func__, readData);
            led_off();
            GpioRead(LED_PIN, &led_pin_val);
        }
        HdfSbufWriteString(reply, "ledoff");
        HdfSbufWriteUint16(reply, led_pin_val);
        if(HdfDeviceSend(client->device, id, reply) != HDF_SUCCESS)
            return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}

HDF 框架下的 GPIO 驱动——GPIO 配置

功能分类接口名描述
GPIO 读写GpioRead读管脚电平值
GpioWrite写管脚电平值
GPIO 配置GpioSetDir设置管脚方向
GpioGetDir获取管脚方向
GPIO 中断设置GpioSetIrq设置管脚对应的中断服务函数
GpioUnSetIrq取消管脚对应的中断服务函数
GpioEnableIrq使能管脚中断
GpioDisableIrq禁止管脚中断
#define LED_PIN    6// GPIO0_6,    0*8+6 = 6
#define IRQ_PIN    30//GPIO3_6    3*8+6 = 30

static int32_t GpioSetup()
{
    //驱动程序,配置 GPIO
    if(GpioSetDir(LED_PIN, GPIO_DIR_OUT) != HDF_SUCCESS){
        HDF_LOGE("GPIOsetDir: LED_PIN failed\n");
        return HDF_FAILURE;
    }

    GpioSetDir(IRQ_PIN, GPIO_DIR_IN);
    GpioDisableIrq(IRQ_PIN);
    GpioSetIrq(IRQ_PIN, OSAL_IRQF_IRIGGER_RISING | OSAL_IRQF_TRIGGER_FALLING, gpio_test_irq, NULL);
    GpioEnableIrq(IRQ_PIN);
}

何处调用 GpioSetup, Init() 还是 Dispatch ?

HDF 框架下的 GPIO 驱动——GPIO 配置(中断)

  • 中断触发方式
参数中断触发方式
OSAL_IRQF_TRIGGER_RISING上升沿触发
OSAL_IRQF_TRIGGER_FALLING下降沿触发
OSAL_IRQF_TRIGGER_HIGH高电平触发
OSAL_IRQF_TRIGGER_LOW低电平触发

在这里插入图片描述

int32_t gpio_testr_irq(uint16_t gpio, void *data)
{
    //驱动,中断服务程序
    if(GpioDisableIrq(gpio) != HDF_SUCCESS){
        HDF_LOGE("%s: disable irq failed", __func__);
        return HDF_FAILURE;
    }

    GpioRead(IRQ_PIN, &irq_pin_val);
    if(irq_pin_val == 0)
        led_off();
    else
        led_on();

    GpioEnableIrq(gpio);
}

HDF 框架下的 GPIO 驱动——防抖和浮空

中断抖动常见于使用按键作为 GPIO 中断触发源,由于按键的机械性质,很难从根本上消除抖动,需要屏蔽抖动带来的影响,这种技术称为防抖:

  • 硬件:某平台支持 GPIO 去毛刺、可配置中断触发电平值等技术
  • 软件:在中断服务程序中多次读取中断管脚的电平值,直到电平稳定

GPIO 管脚外部既不拉高、也不拉低时的状态称为浮空状态,浮空状态下的 GPIO 是不稳定的,程序读取 GPIO 对应值时,可能会出现高低频繁跳变。若浮空管脚作为外部中断,会频繁触发中断,要避免这种情况的发生:

  • GPIO 外部电路明确接 GND 或者 VCC
  • 使用上拉或者下拉电阻

在这里插入图片描述

HDF 框架下的 GPIO 驱动——LED 控制

#define LED_PIN    6// GPIO0_6,    0*8+6 = 6
#define IRQ_PIN    30//GPIO3_6    3*8+6 = 30

//高电平熄灭LED
static int32_t led_off(void)
{
    if(GpioWrite(LED_PIN, 1) != HDF_SUCCESS){
        HDF_LOGE("GpioWrite: LED_PIN failed\n");
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}

//低电平点亮 LED
static int32_t led_on(void)
{
    if(GpioWrite(LED_PIN, 0) != HDF_SUCCESS){
        HDF_LOGE("GpioWrite: LED_PIN failed\n");
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}

HDF 框架下的 GPIO 驱动——驱动程序目录和结构

  • 驱动源码目录: drivers/adapter/khdf/linux/gpio_test_drv/
  • 在上一层目录的 Makefile 添加编译目标: drivers/adapter/khdf/linux/Makefile
obj-$(CONFIG_DRIVERS_HDF)    += gpio_test_drv/
  • hcs 配置文件中添加设备节点定义:vendor/hisilicon/Hi3516DV300/hdf_config/khdf/device_info/device_info.hcs

HDF 框架下的 GPIO 驱动——应用程序目录结构

  • 在 openharmony 源码根目录下创建子目录 examples/gpio_test_app/,其中 examples 作为一个子系统,gpio_test_app 作为该子系统下的一个组件
  • 在上述目录下创建应用程序源文件和构建文件
  • 在产品定义文件 productdefine/common/products/Hi3516DV300.json 中添加 examples 子系统和 gpio_test_app 组件,使其被编译
  • 清空 out 目录,编译全量代码,驱动编译进内核,测试程序 gpio_test_app 位于 bin 目录下

总结

  • GPIO:通用和专用 IO 的区别、不同平台下的 GPIO 的分组和编号、GPIO 常用调试手段
  • HDF 驱动:GPIO 接口的配置方式、读写操作、中断,两种方式实现应用和驱动的通信,缓冲区的基本操作,基本覆盖了全部的 GPIO 接口
  • 提供一套完整的驱动程序和应用程序,并给出其目录结构

参考链接

openharmony 官方网站:https://www.openharmony.cn/mainPlay
openharmony 官方视频链接:https://www.bilibili.com/video/BV1z34y1t76h/

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

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

相关文章

为什么jvm需要有栈协程?

旧有的servlet生态的线程模型 首先我们先要聊一聊现在我们用的最多的servlet的执行模型是什么: 这个dispatch其实就是一个EventLoop或者说是一个selector来检测注册到其上的链接状态发生的变化 以Tomcat为例子,当这个selector发现存在一个链接可读时&…

【node.js】fs\path\http模块的使用

🥳博 主:初映CY的前说(前端领域) 🌞个人信条:想要变成得到,中间还有做到! 🤘本文核心:Node.js的fs\path\http模块的使用,模块化开发概念 目录 一、node.js概念与作…

一个曾经分享动态(2021)的回顾和解释-2023-

虽然看过一些典故,里面有名言道: 解释永远是多余的,理解的人不需要,不理解的更不需要。 但是,误会还是需要沟通来消除的。 例如,曾经分享过: 如下都是误会 ↓↓↓↓↓↓↓↓↓ 有朋友联系我&a…

解决东方财富数据接口激活后仍显示reactive的问题

首先确保代码可以在python中导入这个包: from EmQuantAPI import c如果无法导入,就是python没有配置好东方财富的接口,可以参考: Mac版本:Mac使用Python接入东方财富量化接口Choice,调试与获取数据Window…

北京智和信通:信创运维自动化,全栈适配国产软硬件环境

近年来,新基建和信创产业政策东风席卷神州,国产CPU、操作系统、关键应用软件等核心技术步入发展快车道,一批优秀软硬件产品走进政府机关、国企事业单位。在国产软硬件核心技术崛起的过程中,如何迅速搭建起成熟的生态环境是行业面临…

LeetCode135之分发糖果(相关话题:数组,贪心思想)

题目描述 n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。 你需要按照以下要求,给这些孩子分发糖果: 每个孩子至少分配到 1 个糖果。相邻两个孩子评分更高的孩子会获得更多的糖果。 请你给每个孩子分发糖果,计算并返回需…

【node.js】跨域的解决办法(CORS方法、同源策列的理解)

🥳博 主:初映CY的前说(前端领域) 🌞个人信条:想要变成得到,中间还有做到! 🤘本文核心:面对cors跨域、同源策略的处理 下图为本文的核心 目录 一、 跨域介绍 二、同源策略 三…

正态分布与numpy.random.normal函数

文章目录1. 正态分布2. numpy.random.normal函数3. 示例在Numpy中,有一个专门用于生成符合正态分布的随机数函数:numpy.random.normal,本文我们梳理一下它的使用方法,在梳理前,需要先了解一下什么是正态分布。 1. 正态…

黑马Hive+Spark离线数仓工业项目-任务流调度工具AirFlow(1)

任务流调度工具AirFlow 1. AirFlow介绍【了解】 - 功能、特点 - 架构角色、安装部署 2. **AirFlow使用【掌握】** - 核心:调度脚本【Python | Shell】 - 定时调度:Linux Crontab表达式 - 邮件告警:配置 3. 回顾Spark核心概念 - 存…

【10个基本网络故障排查工具-每个IT专业人员应了解】

网络故障排除工具是每个网络管理员的必需品。 在网络领域入门时,重要的是要积累一些可用于解决各种不同网络状况的工具。 虽然特定工具的使用确实是主观的并且由工程师自行决定,但本文中的工具选择是基于它们的一般性和通用性。 本文回顾了可帮助您解决大…

聊聊业务项目如何主动感知mysql是否存活

前言 先前写过一篇文章聊聊如何利用redis实现多级缓存同步,里面讲到业务部门因数据库宕机,有技术提出当数据库宕机,切换到redis,今天我们就来聊聊如何触发这个切换动作? 1、方案一:利用异常机制 伪代码如下&#xf…

大三寒假人生第一次面试失败

2022/12/28,今天是人生第一次面试。坐了2个小时的地铁去面试结果却很惨。一开始进门就笔试,当看到笔试题时发现很多基础,平时耳熟能详的词汇却怎么样也回答不出来。做了一个多小时,当面试官把题改了以后一句笔试没过。说真的在出门…

RocketMQ消息队列的下载、配置、启动、测试

目录 下载 环境变量的配置 新建一个变量 配置path 新建变量 启动 命名服务器 启动broker 测试是否启动成功 下载 地址:RocketMQ 官方网站 | RocketMQ 切换到中文模式很容易看的 下载那一列就行了 安装很容易的。 环境变量的配置 新建一个变量 就是你的bin文…

No.181# 点直播简要架构梳理走查

引言直播带货、潮流电商、短视频不断融合,本文走查下音视频直播的简要架构和角色。选择UDP,注重传输实时性,在线教育、音视频会议等。选择TCP,注重画面质量、是否卡顿等,娱乐直播、直播带货等。本文主要内容有&#xf…

RPA:帮助企业完成财务数字化转型

为什么要做财务的数字化转型 a. 传统企业财务的现状 “重复性强、耗时耗力、效率低下”是目前大家对传统企业财务的固有印象。很多企业的财务部门仍然采用传统的手工操作模式,财务流程繁琐分散,且财务部门缺乏获取、处理数据的工具。绝大部分的人力都投…

2022LOL微博杯模糊问题,1080p高清看微博杯the shy比赛直播

2022LOL微博杯的直播模糊,看着不爽 观看方法 1.打开下面在线播放m3u8文件的地址 http://www.m3u8.zone/ 如图 2.输入播放地址 微博杯的播放地址: (1月三号的地址 如果失效往下看解决方法) https://plwb01.live.weibo.com/ali…

前端数据结构与算法

前端数据结构与算法 文章宝典 链表 可以快速删除和插入节点,只用修改节点的引用 实例 队列 实例 栈 实例 树 并且左节点的值和后续节点的值都要小于等于该节点的值 图 根据图的节点之间的边是否有方向,可以分为有向图和无向图。 在有向图…

数字调制系列:如何理解IQ ?

最近在筹划写一系列关于数字IQ 调制的短文,以帮助初学者能够更好地理解和掌握。虽然IQ 调制技术已经非常广泛地应用于各种无线通信应用中,但是究其细节,仍有很多人存在疑惑,尤其对于初学者。作者从事测试工作多年,对IQ…

强化学习的Sarsa与Q-Learning的Cliff-Walking对比实验

强化学习的Sarsa与Q-Learning的Cliff-Walking对比实验Cliff-Walking问题的描述Sarsa和Q-Learning算法对比代码分享需要改进的地方引用和写在最后Cliff-Walking问题的描述 悬崖行走:从S走到G,其中灰色部分是悬崖不可到达,求可行方案 建模中&am…

(Java)【深基9.例4】求第 k 小的数

【深基9.例4】求第 k 小的数 一、题目描述 输入 nnn&#xff08;1≤n<50000001 \le n < 50000001≤n<5000000 且 nnn 为奇数&#xff09;个数字 aia_iai​&#xff08;1≤ai<1091 \le a_i < {10}^91≤ai​<109&#xff09;&#xff0c;输出这些数字的第 kk…