【51单片机项目】基于51单片机自制多功能小键盘/模拟USB键盘【附源码】(STC89C52RC+CH9328)

news/2024/5/19 21:30:22

目录

一、效果展示

二、创作灵感

三、硬件电路

注意事项

工作原理

四、源码

main.c

五、附录

CH9328工作原理

CH9328的模式选择 

​编辑 全键盘键码值表

参考链接


一、效果展示

d194e591f2204266ada26961a88b6c47.gif469a9f06125742b1aeb4a047315e965d.gif

该小键盘具有三种功能:

1、自动输入开机密码

2、每隔一段时间自动按下ctrl+s(即保存)

3、具有和电脑键盘的ctrl  c  v一样的功能,可组合使用(如ctrl+c是复制)

上述的三个键均、开机密码、自动保存时间均可自定义,修改键码值即可。

建码值见附录

二、创作灵感

由于本人是学校社团“仪光实践协会”技术部部长(技术部只有我一个人,既是部长,又是部员,哈哈哈🤣),需要给大一的学弟学妹们想一些有意思的项目,所以就做了这个。

三、硬件电路

注意事项

该电路的供电接口选用了micro-usb,之所以不用Type-C接口是因为该电路涉及数据传输,而用具有数据传输功能的Type-C接口较难焊接,故用micro–usb接口。

值得注意的是:目前micro–usb数据线使用得已经不太多了,在使用该键盘时一定要用具有数据传输功能的数据线。

有些数据线只有供电功能,不能进行通信!

cd4b594c69bd4d4297e2a9181144e96c.png

219d5c6349e34fb7806ac1fb0a9560af.png

8a3a185e424242478b1179c28b72d156.png

链接:https://pan.baidu.com/s/1L4SugrenjcNNLDxNhnBYWQ?pwd=tone 
提取码:tone

晶振最好选用11.0592MHz。

一开始我使用12MHz的晶振,但是在测试键盘时偶尔会识别错按键,如按下的是v键,但是电脑却显示按下了Capslock。

我对照键码值表发现如果出错(如上述CapsLock的例子),键值总是比按下的按键的键码值多一个固定的数。

我猜是由于12MHz产生的波特率不太精准,之后换成11.0592MHz发现果然是晶振的问题,完美解决识别错按键的问题。

工作原理

单片机不断地检测按键是否按下,如按下,则与CH9328进行通信,单片机向CH9328发送相对应的键码值,之后CH9328模拟键盘输入,最终电脑显示按键按下。

四、源码

此代码以开机密码是“wang”为例。

main.c

#include "reg52.h"sbit k1 = P2^5;
sbit k2 = P2^6;
sbit k3 = P2^7;void sendbyte(unsigned char b) // 串口发送字符
{SBUF = b;while (!TI);TI = 0;
}void init() // 初始化函数
{SCON = 0x50; // 设置为工作方式1TMOD = 0x20; // 设置计数器工作方式2PCON = 0x80; // 波特率加倍TH1 = 0xFA; // 计数器初始值设置,波特率9600TL1 = TH1;TR1 = 1; // 打开计数器
}void Timer0Init(void)
{TMOD &= 0xF0;		//设置定时器模式TMOD |= 0x01;		//设置定时器模式TL0 = 0xCD;		//设置定时初始值TH0 = 0xD4;		//设置定时初值TF0 = 0;		//清除TF0标志TR0 = 1;		//定时器0开始计时ET0=1;EA=1;PT0=0;
}void delay(unsigned int xms)
{unsigned char i, j;while(xms--){i = 2;j = 239;do{while (--j);} while (--i);}
}void main(void) // 主函数
{		unsigned char key[8] = 0x00,P[8] = 0x00,i;Timer0Init();// 初始化init(); // 初始化 delay(1500);
//以下是开机自动输入密码的程序,一直到第二个enter结束		P[2] = 0x00;P[2] = 0x28;for (i = 0; i < 8; i++) sendbyte(P[i]);delay(100);//enterP[2] = 0x00; // 按键松开后for (i = 0; i < 8; i++) sendbyte(P[i]);delay(100);P[2] = 0x00;P[2] = 0x1A;for (i = 0; i < 8; i++) sendbyte(P[i]);delay(100);//wP[2] = 0x00; // 按键松开后for (i = 0; i < 8; i++) sendbyte(P[i]);delay(100);P[2] = 0x00;P[2] = 0x04;for (i = 0; i < 8; i++) sendbyte(P[i]);delay(100);//aP[2] = 0x00; // 按键松开后for (i = 0; i < 8; i++) sendbyte(P[i]);delay(100);P[2] = 0x00;P[2] = 0x11;for (i = 0; i < 8; i++) sendbyte(P[i]);delay(100);//nP[2] = 0x00; // 按键松开后for (i = 0; i < 8; i++) sendbyte(P[i]);delay(100);P[2] = 0x00;P[2] = 0x0A;for (i = 0; i < 8; i++) sendbyte(P[i]);  delay(100);//gP[2] = 0x00; // 按键松开后for (i = 0; i < 8; i++) sendbyte(P[i]);delay(100);P[2] = 0x00;P[2] = 0x28;for (i = 0; i < 8; i++) sendbyte(P[i]);delay(100);//enterP[2] = 0x00; // 按键松开后for (i = 0; i < 8; i++) sendbyte(P[i]);P2 = 0xFF;while (1) {  delay(20); // 按键消抖处理if (k1 == 0) {   delay(20);key[8] = 0x00;delay(1);key[0] = 0x01; // 按下ctrl键do {if(k2 == 0){delay(20);  //消抖key[2] = 0x00;delay(1);key[2] = 0x06;for (i = 0; i < 8; i++) sendbyte(key[i]);while (k2 == 0);  //等待按键松开delay(10);  //消抖key[2] = 0x00; // 按键松开后delay(10);  //消抖for (i = 0; i < 8; i++) sendbyte(key[i]);while (!k2);}delay(100);if(k3 == 0){delay(20);  //消抖key[2] = 0x00;delay(1); key[2] = 0x19;for (i = 0; i < 8; i++) sendbyte(key[i]);while (k3 == 0);  //等待按键松开delay(10);  //消抖key[2] = 0x00; // 按键松开后delay(10);  //消抖for (i = 0; i < 8; i++) sendbyte(key[i]);while (!k3);	}            delay(20);  //消抖for (i = 0; i < 8; i++) sendbyte(key[i]);} while (k1 == 0);  //等待按键松开key[0] = 0x00; key[2] = 0x00; // 按键松开后for (i = 0; i < 8; i++) sendbyte(key[i]);while (!k1);}delay(10);if (k2 == 0) {   delay(20);key[0] = 0x00;delay(1); key[2] = 0x00;delay(20);  //按键消抖key[2] = 0x06; // 按下c键for (i = 0; i < 8; i++) sendbyte(key[i]);while (k2 == 0);  //等待按键松开delay(10);  //消抖key[2] = 0x00; // 按键松开后delay(10);  //消抖for (i = 0; i < 8; i++) sendbyte(key[i]);while (!k2);}delay(10);if (k3 == 0) {  	delay(20);key[2] = 0x00;delay(1); key[0] = 0x00;delay(20);  //按键消抖key[2] = 0x19; // 按下v键for (i = 0; i < 8; i++) sendbyte(key[i]);while (k3 == 0);  //等待按键松开delay(20);  //消抖key[2] = 0x00; // 按键松开后for (i = 0; i < 8; i++) sendbyte(key[i]);while (!k3);}}
}void Save() interrupt 1  //自动保存
{static unsigned int T0Count=0,i = 0;unsigned char SAVE[8] = 0x00;TL0 = 0xCD;		//设置定时初始值TH0 = 0xD4;	//设置定时初值T0Count++;if(T0Count>=40000)	//定时器分频,1s{T0Count=0;SAVE[0] = 0x01;//CtrlSAVE[2] = 0x16;//Sfor (i = 0; i < 8; i++) sendbyte(SAVE[i]);SAVE[0] = 0x00;SAVE[2] = 0x00; // 按键松开后for (i = 0; i < 8; i++) sendbyte(SAVE[i]);}
}

五、附录

CH9328工作原理

键盘发送给PC的数据每次8个字节
BYTE1 BYTE2 BYTE3 BYTE4 BYTE5 BYTE6 BYTE7 BYTE8
定义分别是:
BYTE1 --
       |--bit0:   Left Control  是否按下,按下为1 
       |--bit1:   Left Shift  是否按下,按下为1 
       |--bit2:   Left Alt    是否按下,按下为1 
       |--bit3:   Left GUI    是否按下,按下为1 
       |--bit4:   Right Control是否按下,按下为1  
       |--bit5:   Right Shift   是否按下,按下为1 
       |--bit6:   Right Alt   是否按下,按下为1 
       |--bit7:   Right GUI   是否按下,按下为1 
BYTE2 -- 保留位,暂填0x00
BYTE3--BYTE8 -- 这六个为普通按键
例如:键盘发送一帧数据  02 00 04 00 00 00 00 00
表示同时按下了左Shift + ‘a’2个键;

效果:键盘无限循环显示大写字母A(因为包含了Shift键)

因为此时只模拟了按下,没有发送松开A键,所以会一直显示。因此自己模拟的时候再把松开按键也加上去。

CH9328的模式选择 

我用的是模式三。

 全键盘键码值表

参考链接

基于51单片机模拟键盘---超级简单-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qishi3250/article/details/83344176

原理篇4、CH9328使用-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_44817843/article/details/112124822


http://www.mrgr.cn/p/00467255

相关文章

【Qt 专栏】DateTime日期时间组件

本文转载自:https://cloud.tencent.com.cn/developer/article/2371799 本章将重点介绍QDateTime日期与时间组件的常用方法及灵活运用。在Qt中,日期和时间的处理通常使用 QDateTime 类。QDateTime 是一个用于表示日期和时间的类,而与之相关的组件还包括 QDate 、 QTime以及QD…

【threejs教程7】threejs聚光灯、摄影机灯和汽车运动效果

【图片完整效果代码位于文章末】 在上一篇文章中我们实现了汽车模型的加载&#xff0c;这篇文章主要讲如何让汽车看起来像在运动。同时列出聚光灯和摄像机灯光的加载方法。 查看上一篇&#x1f449;【threejs教程6】threejs加载glb模型文件&#xff08;小米su7&#xff09;&…

七段数码管

import turtle, datetime def drawGap(): # 绘制数码管间隔 turtle.penup() turtle.fd(5) def drawLine(draw): # 绘制单段数码管 drawGap() turtle.pendown() if draw else turtle.penup() turtle.fd(40) drawGap() turtle.right(90) def drawDigit(d): # 根据数字绘制七段…

微软如何打造数字零售力航母系列科普02 --- 微软低代码应用平台加速企业创新 - 解放企业数字零售力

微软低代码应用平台推动企业创新- 解放企业数字零售力 微软在2023年GARTNER发布的魔力象限图中处于头部领先&#xff08;leader&#xff09;地位。 其LCAP产品是Microsoft Power Apps&#xff0c;扩展了AI Builder、Dataverse、Power Automate和Power Pages&#xff0c;这些都包…

git 命令怎么回退到指定的某个提交 commit hash 并推送远程分支?

问题 如下图&#xff0c;我要回退到 【002】Babel 的编译流程 这一次提交 解决 1、先执行下面命令&#xff0c;输出日志&#xff0c;主要就是拿到提交 commit 的 hash&#xff0c;上图红框即可 git log或者 vscode 里面直接右击&#xff0c;copy sha 2、执行下面命令回退 g…

条件格式实例

import pandas as pd import numpy as np import datetime todaystr(datetime.date.today())filepath/Users/kangyongqing/Documents/kangyq/202311/班均及合班储备/z拆班校验/ file102查询结果.csv file2工具-国家对应大洲表格_副本.xlsxdf1pd.read_csv(filepathfile1,dtypeob…

golang学习笔记(net/http库基本使用)

关于net/http库 我们先看看标准库net/http如何处理一个请求。 import ("fmt""log""net/http" )var count 0func main() {http.HandleFunc("/", handler)http.HandleFunc("/count", counter)log.Fatal(http.ListenAndServ…

OpenHarmony音视频—opus

简介 Opus是一种用于在互联网上进行交互式语音和音频传输的编解码器。它可以从低比特率窄带语音扩展到非常高的高品质立体声音乐。 下载安装 直接在OpenHarmony-SIG仓中搜索opus并下载。 使用说明 以OpenHarmony 3.1 Beta的rk3568版本为例 将下载的opus库代码存在以下路径&a…

猿人学内部练习平台6~10题

第六题:session保持检测 使用session请求即可:session = requests.Session() 第七题:请求规律检测1-post 根据请求接口找规律,在每一页请求前请求一次https://www.python-spider.com/cityjson即可 requests.post(https://www.python-spider.com/cityjson, verify=False) re…

Sound Siphon for Mac:音频处理与录制工具

Sound Siphon for Mac是一款专为Mac用户设计的音频处理与录制工具&#xff0c;以其出色的性能、丰富的功能和简便的操作而备受赞誉。 Sound Siphon for Mac v3.6.8激活版下载 该软件支持多种音频格式&#xff0c;包括MP3、WAV、AAC、FLAC等&#xff0c;用户可以轻松导入各种音频…

qt 使用hid开源库 ,搜索所有的hid设备

效果: cmakeLists.txt :cmake_minimum_required(VERSION 3.5)project(TF_Hid_UseANDTest VERSION 0.1 LANGUAGES CXX)set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON)set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON)file(GLOB include…

在PostgreSQL中如何创建和使用自定义函数,包括内置语言(如PL/pgSQL)和外部语言(如Python、C等)?

文章目录 一、使用内置语言 PL/pgSQL 创建自定义函数示例代码使用方法 二、使用外部语言 Python 创建自定义函数安装 PL/Python 扩展示例代码使用方法 三、使用外部语言 C 创建自定义函数编写 C 代码编译为共享库在 PostgreSQL 中注册函数注意事项 总结 PostgreSQL 是一个强大的…

上班族必备的便签备忘录app 好用的便签备忘录软件

上班族每天都需要处理大量的工作事项&#xff0c;从策划方案到客户沟通&#xff0c;每一个环节都至关重要。然而&#xff0c;仅仅依靠大脑记忆&#xff0c;很容易遗漏某些细节&#xff0c;甚至可能错过重要的截止日期。为了避免这种情况&#xff0c;许多人选择使用一款优秀的便…

探索 DTD 在 XML 中的作用及解析:深入理解文档类型定义

DTD 是文档类型定义(Document Type Definition)的缩写。DTD 定义了 XML 文档的结构以及合法的元素和属性。为什么使用 DTD 通过使用 DTD,独立的团体可以就数据交换的标准 DTD 达成一致。 应用程序可以使用 DTD 来验证 XML 数据的有效性。 内部 DTD 声明 如果 DTD 在 XML 文件…

ubuntu16安装docker及docker-compose

ubuntu16安装docker及docker-compose 一、环境前期准备 检查系统版本 系统版本最好在16及以上&#xff0c;可以确保系统的兼容性 lsb_release -a查看内核版本及系统架构 建议用 x86_64的系统架构&#xff0c;安装是比较顺利的 uname -a32的系统不支持docker&#xff0c;安…

鸿蒙HarmonyOS实战-ArkUI事件(键鼠事件)

🚀前言 键鼠事件是指在计算机操作中,用户通过键盘和鼠标来与计算机进行交互的行为。常见的键鼠事件包括按下键盘上的键、移动鼠标、点击鼠标左键或右键等等。键鼠事件可以触发许多不同的操作,比如在文本编辑器中输入文字、在游戏中移动角色、在网页上点击链接等等。计算机操…

linux下 Mysql8.0 离线安装

环境&#xff1a;centos7.9 MysqlL8.0.36安装包 链接&#xff1a;https://pan.baidu.com/s/1bKwHr05z8Ye82dT9tntdUA 提取码&#xff1a;3a5z 参考Centos安装MYSQL8(离线可用) 文章目录 1、解压安装2、配置启动2.1 修改配置文件2.2 mysql 启动 3、mysql 测试 1、解压安装 #…

uniapp-css:拼图(不规则图片拼插)、碎片

拼图案例样式 高斯模糊的地方可以对应的使用fliter属性和opacity来调节样式。 其余碎片和图片对应: 这段代码实现了一个拼图效果的Vue组件。以下是对代码的详细解析: 模板部分: 在模板中使用v-for指令遍历imgs数组中的每个图片对象,为每个图片创建一个元素。 使用:cla…

基于DEAP数据集的四种机器学习方法的情绪分类

在机器学习领域&#xff0c;KNN&#xff08;K-Nearest Neighbors&#xff09;、SVM&#xff08;Support Vector Machine&#xff09;、决策树&#xff08;Decision Tree&#xff09;和随机森林&#xff08;Random Forest&#xff09;是常见且广泛应用的算法。 介绍 1. KNN&am…

程序员学CFA——数量分析方法(四)

数量分析方法&#xff08;四&#xff09; 常见概率分布基本概念离散型随机变量与连续型随机变量离散型随机变量连续型随机变量 分布函数概率密度函数&#xff08;PDF&#xff09;累积分布函数&#xff08;CDF&#xff09; 离散分布离散均匀分布伯努利分布二项分布定义股价二叉树…