从0开始的操作系统手搓教程23:构建输入子系统——实现键盘驱动1——热身驱动

目录

所以,键盘是如何工作的

说一说我们的8042

输出缓冲区寄存器

状态寄存器

控制寄存器

动手!

注册中断

简单整个键盘驱动

Reference

ScanCode Table


我们下一步就是准备进一步完善我们系统的交互性。基于这个,我们想到的第一个可以用来进行输入的设备,就是键盘!这个不假。我们这个章节的核心内容,就是构建一个基于键盘的输入子系统。给我们之后的系统更多功能添砖加瓦!

所以,键盘是如何工作的

虽然现在的键盘可以说是日新月异,但是基本的工作原理不会发生很大的改变。我们的键盘是一个独立的设备,需要介入总线跟我们的主板系统沟通。不管怎么说,我们的主板上,存在一个Intel 8048 或兼容芯片,它的作用是:每当键盘上发生按键操作,它就向键盘控制器报告哪个键被按下,按键是否弹起。

上图的8048 是键盘上的芯片,其主要责任就是监控哪个键被按下。当键盘上发生按键操作时,8048 当然知道是哪个键被按下。但光它自己知道还不 行,它毕竟要将按键信息传给8042,必须得让8042 知道到底是按下了哪个键,为此8048 必然要和8042达成一个协议,这个协议规定了键盘上的每个物理键对应的唯一数值,说白了就是对键盘上所有的按键进行编码,为每个按键分配唯一的数字,这样双方都知道了每个数值代表哪个键。当某个键被按下时,8048 把这个键对应的数值发送给8042,8042根据这个数值便知道是哪个键被按下了。

现在,笔者就在使用键盘敲击一些字符,我松开手,按键被弹起来了,输入显然完成了,8048就会通知在主板上的8042何时按键被弹起,也就是击键操作何时结束,这样8042才知道用户在一次持续按键操作中到底输入了多少个相同的字符。因此,键盘扫描码中不仅仅要记录按键被按下时对应的编码,还要记录按键被松开(弹起)时的编码。这下事情变得显然了,我们不得不请出两个码——按键被按下时的编码叫通码,也就是表示按键上的触点接通了内部电路,使硬件产生了一个码,故通码也称为 makecode。按键在被按住不松手时会持续产生相同的码,直到按键被松开时才终止,因此按键被松开弹起时产生的编码叫断码,也就是电路被断开了,不再持续产生码了,故断码也称为breakcode。一个键的扫描码是由通码和断码组的。

郑刚老师在《操作系统真相还原》中介绍了三种扫描码,这里只介绍第二种,因为余下的已经几乎没人使用了,我们(程序员)不是不知道键盘用的是哪种扫描码吗,那好,只要 8042 知道就行。为了兼容第一套 键盘扫描码对应的中断处理程序,不管键盘用的是何种键盘扫描码,当键盘将扫描码发送到 8042 后,都 由 8042 转换成第一套扫描码,这就是我们上一节中所说的 8042 的“处理”。 因此,我们在键盘的中断处理程序中只处理第一套键盘扫描码就可以了。关于整张表,参考笔者的附录即可。

看完这个表,你仔细观察一下,大多数情况下第一套扫描码中的通码和断码都是 1 字节大小。而且不难发现:断码 = 0x80 + 通码。

所以回过头来,我们在分析一下:完整的击键操作包括两个过程,先是被按下,也许是被按下一瞬间,也许是持续保持被按下,然后是被松开,总之,按下的动作是先于松开发生的,因此每次按键时会先产生通码,再产生断码。比如我们按下字符 a 时,按照第一套键盘扫描码来说,先是产生通码 0x1e,后是产生断码 0x9e。

另一些我们注意到:一些按键的通码和断码都以0xe0 开头,它们占2 字节,甚至Pause 键以0xe1 开头, 占6字节。原因是这样的,并不是一种键盘就要用一套键盘扫描码,最初第一套键盘扫描码是由XT 键盘所使用的,它后来也被一些更新的键盘所使用。XT 键盘上的键很少,比如右边回车键附近就没有alt 和ctrl 键,这是在后来的键盘中才加进去的,因此表示扩展 extend,所以在扫描码前面加了 0xe0 作为前缀。比如在 XT 键盘上,左边有alt 键,其通码为0x38,断码为0xb8。右边的alt 键是后来在新的键盘上加进去的,因此,一方面为了表示都是同样功能的alt 键,另一方面表示不是左边那个alt,而是右边的alt,于是这个扩展的alt 键的扫描码便为“0xe0 和原来左边alt 的扫描码”。因此,右边alt 键的通码便为“0xe0,0x38”,断码为“0xe0,0xb8”。

那组合键呢?比如说我们嗯下Ctrl + C键,想要复制郑刚老师的教授内容,自己偷个懒的时候,这个是如何处理的呢?

现在你自己慢慢做一次Ctrl + C试一下:

  1. 你先摁下Ctrl键,毕竟你先按C就会打印出字符C了。

  2. 你保持Ctrl键不松手

  3. 一个手指按下C

  4. 然后随意的松开,比如说可能松开ctrl

  5. 松开C,然后感觉自己像是一个笨蛋一样(笑)

不开玩笑了,当我们做步骤一的时候,8048向8042发送了<L-ctrl>键的通码0x14,当然,这显然是第二套扫描码8042收到0x14后将其转换为第一套键盘扫描码0x1d,并将其保存到自己的输出缓冲区寄存器中。接着,8042向中断代理发送中断信号,处理器随后执行键盘中断处理程序。键盘中断处理程序从8042的输出缓冲区寄存器中获取扫描码0x1d,并判断这次按下的是<L-ctrl>键(实际上无论是<L-ctrl>还是<R-ctrl>,通常都被视为Ctrl键,因为它们只是位置不同,功能相同)。

我们的键盘处理程序在某个全局变量中记录Ctrl键已被按下。这个,跟大部分的键盘处理程序是一样的。

第二步的时候,我们的<L-ctrl>键持续按住不松手因此8048会持续向8042发送0x14。8042每次都会将其转换为第一套键盘扫描码0x1d,并向中断代理发送中断信号。每次键盘中断处理程序都会从8042中获取到0x1d。与步骤一相同,键盘处理程序判断这是<L-ctrl>的通码,并在全局变量中记录Ctrl键被按下。尽管Ctrl键已经被按下,键盘处理程序可能只记录最后一次按下的键,而不关心之前按下了多少次相同的键。(我们好像没必要记着,对吧)

第三步,我们终于准备发送c的第二套键盘扫描码0x21,8042将其转换为第一套键盘扫描码0x2e,并保存到输出缓冲区寄存器中,随后向中断代理发送中断信号。

键盘中断处理程序开始执行,从8042的输出缓冲区寄存器中获取0x2e。键盘处理程序判断这次按下的是c键,并检查之前Ctrl键已经被按下(全局变量中有记录),因此判断用户按下的是“Ctrl+c”组合键。Ctrl、Alt、Shift等控制键通常与后续按下的键组合使用,这是基于微软的操作习惯,即控制键先按下,普通键后按下。由于这次按下的不是控制键,键盘处理程序将记录Ctrl键是否按下的全局变量清空,并将“Ctrl+C”这一消息上报给上层模块。我们的上层接受到后,就会做对应的Hook操作。

我们在步骤四种假设你是,,<L-ctrl>键被松开,8048向8042发送它的第二套键盘扫描码0xf0和0x14(断码)。第二套键盘扫描码的断码通常由固定的前缀0xf0和其通码组成。8042将这两个字节转换为第一套键盘扫描码0x9d(断码),随后发送中断信号。键盘中断处理程序发现最高位为1,表示这是断码,意味着键被松开。无论松开的是什么键,键盘处理程序都会忽略,不做任何处理。

在步骤五中,a键被松开,8048向8042发送它的第二套键盘扫描码0xf0和0x21(断码)。8042将其转换为0xae并保存到输出缓冲区寄存器中,随后发送中断信号。键盘中断处理程序读取该扫描码,发现是键被弹起,因此忽略该事件。

说一说我们的8042

Intel 8042 芯片或兼容芯片被集成在主板上的南桥芯片中,它是键盘控制器,也就是键盘的 IO 接口, 因此它是8048的代理,也是前面所得到的处理器和键盘的“中间层”。8048通过PS/2、USB 等接口与8042通信,处理器通过端口与8042通信(IO 接就是外部硬件的代理,它和处理器都位于主机内部,因此处理器与 IO 接口可以通过端口直接通信)。

我们来看看IO口:

寄存器端口读/写
Output Buffer(输出缓冲区)0x60
Input Buffer(输入缓冲区)0x60
Status Register(状态寄存器)0x64
Control Register(控制寄存器)0x64

8042 是连接 8048 和处理器的桥梁,8042 存在的目的是:为了处理器可以通过它控制 8048 的工作方式,然后让8048 的工作成果通过8042 回传给处理器。此时8042 就相当于数据的缓冲区、中转站,根据数据被发送的方向,8042 的作用分别是输入和输出。

处理器把对 8048 的控制命令临时放在 8042 的寄存器中,让 8042 把控制命令发送给 8048,此时 8042 充当了 8048 的参数输入缓冲区。 8048 把工作成果临时提交到8042 的寄存器中,好让处理器能从 8042 的寄存器中获取它(8048)的工作成果,此时 8042 充当了 8048 的结果输出缓冲区。

当需要把数据从处理器发到8042 时(数据传送尚未发生时),0x60 端口的作用是输入缓冲区,此时应该用out 指令写入0x60 端口。

当数据已从 8048 发到 8042 时,0x60 端口的作用是输出缓冲 区,此时应该用in指令从8042 的0x60 端口(输出缓冲区寄存器)读 取8048 的输出结果。

最后,出于编程目的,还差寄存器说明:

寄存器宽度读写属性描述
输入缓冲区寄存器8 位只写键盘驱动程序通过 out 指令向此寄存器写入对 8048 的控制命令、参数等。对于 8042 本身的控制命令也是写入此寄存器。
状态寄存器8 位只读反映 8048 和 8042 的内部工作状态。各位意义详见描述。
控制寄存器8 位只写用于写入命令控制字。每个位都可以设置一种工作方式,意义详见描述。
输出缓冲区寄存器

8042的输出缓冲区寄存器是一个8位宽度的寄存器,只读,键盘驱动程序从此寄存器中通过in指令读取来自8048 的扫描码、来自8048 的命令应答以及对8042 本身设置时,8042 自身的应答也从该寄存器中获取。

注意,输出缓冲区寄存器中的扫描码是给处理器准备的,在处理器未读取之前,8042 不会再往此寄 存器中存入新的扫描码。

8042 是怎样知道输出缓冲区寄存器中的值是否被读取了呢?这个简单,8042 也有个 智能芯片,它为处理器提供服务,当处理器通过端口跟它要数据的时候它当然知道了,因此,每当有 in 指令来读取此寄存器时,8042 就将状态寄存器中的第0位置成0,这就表示寄存器中的扫描码数据已经被取走,可以继续处理下一个扫描码了。当再次往输出缓冲寄存器存入新的扫描码时,8042 就将状态寄存器中的第 0 位置为 1,这表示输出缓冲寄存器已满,可以读取了。

状态寄存器
描述
位 0置 1 时表示输出缓冲区寄存器已满,处理器通过 in 指令读取后该位自动置 0。
位 1置 1 时表示输入缓冲区寄存器已满,8042 将值读取后该位自动置 0。
位 2系统标志位,最初加电时为 0,自检通过后置为 1。
位 3置 1 时,表示输入缓冲区中的内容是命令,置 0 时,输入缓冲区中的内容是普通数据。
位 4置 1 时表示键盘启用,置 0 时表示键盘禁用。
位 5置 1 时表示发送超时。
位 6置 1 时表示接收超时。
位 7来自 8048 的数据在奇偶校验时出错。
控制寄存器
描述
位 0置 1 时启用键盘中断。
位 1置 1 时启用鼠标中断。
位 2设置状态寄存器的位 2。
位 3置 1 时,状态寄存器的位 4 无效。
位 4置 1 时禁止键盘。
位 5置 1 时禁止鼠标。
位 6将第二套键盘扫描码转换为第一套键盘扫描码。
位 7保留位,默认为 0。

动手!

注册中断

这里我们先把中断一次性注册了,省事

; -------------------------------------------------------------------------
;   Part 2 Table Page for the interrupt for kernel
; -------------------------------------------------------------------------
INTR_VECTOR 0x20, PUSH_ZERO  ; Entry for the timer interrupt.
INTR_VECTOR 0x21, PUSH_ZERO  ; Entry for the keyboard interrupt.
INTR_VECTOR 0x22, PUSH_ZERO  ; Cascade interrupt.
INTR_VECTOR 0x23, PUSH_ZERO  ; Entry for serial port 2.
INTR_VECTOR 0x24, PUSH_ZERO  ; Entry for serial port 1.
INTR_VECTOR 0x25, PUSH_ZERO  ; Entry for parallel port 2.
INTR_VECTOR 0x26, PUSH_ZERO  ; Entry for the floppy disk.
INTR_VECTOR 0x27, PUSH_ZERO  ; Entry for parallel port 1.
INTR_VECTOR 0x28, PUSH_ZERO  ; Entry for the real-time clock.
INTR_VECTOR 0x29, PUSH_ZERO  ; Redirect.
INTR_VECTOR 0x2a, PUSH_ZERO  ; Reserved.
INTR_VECTOR 0x2b, PUSH_ZERO  ; Reserved.
INTR_VECTOR 0x2c, PUSH_ZERO  ; PS/2 mouse.
INTR_VECTOR 0x2d, PUSH_ZERO  ; FPU floating-point unit exception.
INTR_VECTOR 0x2e, PUSH_ZERO  ; Hard disk.
INTR_VECTOR 0x2f, PUSH_ZERO  ; Reserved.

记得修改一下支持的中断数

#define IDT_DESC_CNT (0x30) // The number of interrupt descriptors in the IDT

然后在pci.c种,记得只打开键盘的中断

    // Mask interrupts to disable all IRQsoutb(PCI_MASTER_DATA_PORT, 0xfd);      // Mask all IRQs on the master PIC (set bit 0)outb(PCI_SLAVE_DATA_PORT, 0xff);       // Mask all IRQs on the slave PIC (set all bits)

简单整个键盘驱动

实际上就是直接读缓存端口就好了哈哈

#include "include/device/keyboard.h"
#include "include/library/ccos_print.h"
#include "include/kernel/interrupt.h"
#include "include/device/configs/keyboard_ascii.h"
#include "include/device/configs/keyboard_mappings.h"
#include "include/io/io.h"
​
static void keyboard_intr_handler(void)
{// hey don't use puts here, gs is not switched, else we will visit// wrong place__ccos_putchar('C');// uint8_t scancode = inb(KEYBOARD_BUF_PORT);// __ccos_display_int(scancode);return;
}
​
// register the interrupt here
void init_basic_input_subsystem(void)
{ccos_puts("initing subsystem of input: from keyboard!\n");register_intr_handler(KEYBOARD_INTERRUPT_N, keyboard_intr_handler);ccos_puts("init subsystem of input: from keyboard done!\n");
}

这里呢,#include "include/device/configs/keyboard_ascii.h" #include "include/device/configs/keyboard_mappings.h"两个文件就具体到笔者的代码种看先。

嘿!我们上电试试看,不用担心线程的事情,我们把时钟中断关闭了。

可以看到我们摁下摁键的时候,这些字符就会蹦出来了!注意,嗯下一次,弹起一次。这就说明了通码和断码的存在了。

小小的修改一下代码哈:

static void keyboard_intr_handler(void)
{// hey don't use puts here, gs is not switched, else we will visit// wrong place// __ccos_putchar('C');uint8_t scancode = inb(KEYBOARD_BUF_PORT);__ccos_display_int(scancode);__ccos_putchar(' ');return;
}

看看!现在外面收到了scancode了!

代码

CCOperateSystem/Documentations/9_Boost_BasicInputSubsystem/9.2_finish_input_subsystem_1_code at main · Charliechen114514/CCOperateSystemhttps://github.com/Charliechen114514/CCOperateSystem/tree/main/Documentations/9_Boost_BasicInputSubsystem/9.2_finish_input_subsystem_1_code

下一篇

nullhttps://blog.csdn.net/charlie114514191/article/details/146105521

Reference

ScanCode Table

通码断码通码断码
<esc>0181<caps lock>3aba
F13bbba1e9e
F23cbcs1f9f
F33dbdd20a0
F43ebef21a1
F53fbfg22a2
F640c0h23a3
F741c1j24a4
F842c2k25a5
F943c3l26a6
F1044c4:;27a7
F1157d7"'28a8
F1258d8<enter>1c9c
29a9<L-Shift>2aaa
!10282z2cac
@20383x2dad
#30484c2eae
$40585v2faf
%50686b30b0
^60787n31b1
&70888m32b2
*80989<,33b3
(90a8a>.34b4
)00b8b?/35b5
_-0c8c<R-shift>36b6
+=0d8d<L-ctrl>1d9d
<backspace>0e8e<L-alt>38b8
<tab>0f8f<space>39b9
q1090<R-alt>e0,38e0,b8
w1191<R-ctrl>e0,1de0,9d
e1212
r1393
t1494
y1595
u1696
i1797
o1898
p1999
{[1a9a
}]1b9b
|\2bab
通码断码通码断码
PrintScreen SysRqe0,2a,e0,37e0,b7,e0,aaNumLock45c5
Scroll Lock46c6/e0,35e0,b5
Pause Breake1,1d,45e1,9d,c5*37b7
Inserte0,52e0,d2-4aca
Homee0,47e0,c77Home47c7
Page Upe0,49e0,c98Up48c8
Deletee0,53e0,d39PgUp49c9
Ende0,4fe0,cf4Left4bcb
Page Downe0,51e0,d154ccc
e0,46e0,c66Right4dcd
e0,4de0,cd1End4fcf
e0,48e0,c82Down50d0
e0,50e0,d03PgDn51d1
0Ins52d2.Del53d3
+4eceEntere0,1ce0,9c

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【JavaEE】-- 多线程(初阶)4

文章目录 8.多线程案例8.1 单例模式8.1.1 饿汉模式8.1.2 懒汉模式 8.2 阻塞队列8.2.1 什么是阻塞队列8.2.2 生产者消费者模型8.2.3 标准库中的阻塞队列8.2.4 阻塞队列的应用场景8.2.4.1 消息队列 8.2.5 异步操作8.2.5 自定义实现阻塞队列8.2.6 阻塞队列--生产者消费者模型 8.3 …

用Python分割并高效处理PDF大文件

在处理大型PDF文件时&#xff0c;将它们分解成更小、更易于管理的块通常是有益的。这个过程称为分区&#xff0c;它可以提高处理效率&#xff0c;并使分析或操作文档变得更容易。在本文中&#xff0c;我们将讨论如何使用Python和为Unstructured.io库将PDF文件划分为更小的部分。…

Python——计算机网络

一.ip 1.ip的定义 IP是“Internet Protocol”的缩写&#xff0c;即“互联网协议”。它是用于计算机网络通信的基础协议之一&#xff0c;属于TCP/IP协议族中的网络层协议。IP协议的主要功能是负责将数据包从源主机传输到目标主机&#xff0c;并确保数据能够在复杂的网络环境中正…

【MySQL】事务|概念|如何回滚|基本特性|MySQL事务隔离性具体怎么实现的

目录 1.为啥引入 2.是啥 3.如何回滚&#xff08;日志&#xff09; &#x1f525;4.面试题&#xff1a;谈谈事务的基本特性 &#xff08;1&#xff09;原子性 &#xff08;2&#xff09;一致性&#xff08;收入和支出相匹配&#xff09; &#xff08;3&#xff09;持久性…

deepseek 本地部署

deepseek 本地部署 纯新手教学&#xff0c;手把手5分钟带你在本地部署一个私有的deepseek&#xff0c;再也不用受网络影响。流畅使用deepseek&#xff01;&#xff01;&#xff01; 如果不想看文章&#xff0c;指路&#xff1a;Deep seek R1本地部署 小白超详细教程 &#xff0…

⭐算法OJ⭐N-皇后问题 II【回溯剪枝】(C++实现)N-Queens II

⭐算法OJ⭐N-皇后问题【回溯剪枝】&#xff08;C实现&#xff09;N-Queens 问题描述 The n-queens puzzle is the problem of placing n n n queens on an n n n \times n nn chessboard such that no two queens attack each other. Given an integer n, return the num…

关联封号率降70%!2025最新IP隔离方案实操手册

高效运营安全防护&#xff0c;跨境卖家必看的风险规避指南 跨境账号管理的核心挑战&#xff1a;关联封号风险激增 2024年&#xff0c;随着全球电商平台对账号合规的审查日益严苛&#xff0c;“关联封号”已成为跨境卖家最头疼的问题之一。无论是同一IP登录多账号、员工操作失误…

pytest框架 核心知识的系统复习

1. pytest 介绍 是什么&#xff1a;Python 最流行的单元测试框架之一&#xff0c;支持复杂的功能测试和插件扩展。 优点&#xff1a; 语法简洁&#xff08;用 assert 替代 self.assertEqual&#xff09;。 自动发现测试用例。 丰富的插件生态&#xff08;如失败重试、并发执…

搭建BOA服务器

BOA服务器是嵌入式常用的服务器类型&#xff0c;嵌入式程序作为后端时候如果想配合网页进行显示&#xff0c;利用BOA服务器搭建网络界面是不错的选择 首先下载boa官方安装包 Boa Webserver 下载后传输到Ubuntu随便文件夹&#xff0c;解压 tar -xvf boa-0.94.13.tar.gz 进入…

C# OPC DA获取DCS数据(提前配置DCOM)

OPC DA配置操作手册 配置完成后&#xff0c;访问远程ip&#xff0c;就能获取到服务 C#使用Interop.OPCAutomation采集OPC DA数据&#xff0c;支持订阅&#xff08;数据变化&#xff09;、单个读取、单个写入、断线重连

Ubuntu20.04搭建gerrit code review

一、环境准备 1. 安装 Java 环境‌ Gerrit 依赖 Java 运行环境&#xff08;推荐 JDK 8&#xff09;&#xff1a; sudo apt install openjdk-11-jdk 验证安装&#xff1a; java -version ‌2. 安装 Git sudo apt install git ‌3. 可选依赖 数据库‌&#xff1a;Gerrit …

【FSM-3: 串行序列】

FSM-3&#xff1a;串行序列 1 Serial receiver FSM使用总结&#xff1a; 所有涉及输出的driver原则上用cur_sta&#xff1b;若是使用nxt_sta的相当于是提前一拍知道结果&#xff0c;所以对于输出必须要使用clocked reg&#xff0c;这样才能和cur_sta对应起来&#xff1b;描述声…

蓝桥杯 之 前缀和与查分

文章目录 题目求和棋盘挖矿 前缀和有利于快速求解 区间的和、异或值 、乘积等情况差分是前缀和的反操作 前缀和 一维前缀和&#xff1a; # 原始的数组num,下标从1到n n len(num) pre [0]*(n1) for i in range(n):pre[i1] pre[i] num[i] # 如果需要求解num[l] 到num[r] 的区…

国产化板卡设计原理图:2330-基于FMC接口的JFM7K325T PCIeX4 3U PXIe接口卡

基于FMC接口的JFM7K325T PCIeX4 3U PXIe接口卡 一、板卡概述 本板卡基于 FPGAJFM7K325T 芯片&#xff0c;pin_to_pin兼容FPGAXC7K410T-2FFG900 &#xff0c;支持PCIeX8、64bit DDR3容量2GByte&#xff0c;HPC的FMC连接器&#xff0c;板卡支持PXIE标准协议&#xff0c;其中XJ3…

计算机视觉之dlib人脸关键点绘制及微笑测试

dlib人脸关键点绘制及微笑测试 目录 dlib人脸关键点绘制及微笑测试1 dlib人脸关键点1.1 dlib1.2 人脸关键点检测1.3 检测模型1.4 凸包1.5 笑容检测1.6 函数 2 人脸检测代码2.1 关键点绘制2.2 关键点连线2.3 微笑检测 1 dlib人脸关键点 1.1 dlib dlib 是一个强大的机器学习库&a…

一周学会Flask3 Python Web开发-SQLAlchemy连接Mysql数据库

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili app.py下&#xff0c;我们先配置数据库连接&#xff0c;然后写一个简单sql测试。 连接配置&#xff0c;包括用户名&#xff…

blender看不到导入的模型

参考&#xff1a;blender 快捷键 常见问题_blender材质预览快捷键-CSDN博客 方法一&#xff1a;视图-裁剪起点&#xff0c;设置一个很大的值 方法二&#xff1a;选中所有对象&#xff0c;对齐视图-视图对齐活动项-选择一个视图

CES Asia 2025增设未来办公教育板块,科技变革再掀高潮

作为亚洲消费电子领域一年一度的行业盛会&#xff0c;CES Asia 2025&#xff08;第七届亚洲消费电子技术贸易展&#xff09;即将盛大启幕。今年展会规模再度升级&#xff0c;预计将吸引超过500家全球展商参展&#xff0c;专业观众人数有望突破10万。除了聚焦人工智能、物联网、…

【目标检测】【NeuralPS 2023】Gold-YOLO:通过收集与分发机制实现的高效目标检测器

Gold-YOLO&#xff1a; Efficient Object Detector via Gather-and-Distribute Mechanism Gold-YOLO&#xff1a;通过收集与分发机制实现的高效目标检测器 0.论文摘要 在过去的几年中&#xff0c;YOLO系列模型已成为实时目标检测领域的领先方法。许多研究通过修改架构、增强数…

利用python实现对Excel文件中数据元组的自定义排序

问题引入&#xff1a; 假设你是一个浙江省水果超市的老板&#xff0c;统筹11个下辖地市的水果产量。假设11个地市生产的水果包括&#xff1a;苹果、香蕉和西瓜。你如何快速得到某种水果产量突出&#xff08;排名前几&#xff09;的地市&#xff1f;产量落后&#xff08;排名后…