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

目录

一、效果展示

二、创作灵感

三、硬件电路

注意事项

工作原理

四、源码

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.rhkb.cn/news/313940.html

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

相关文章

通用大模型研究重点之五:llama family

LLAMA Family decoder-only类型 LLaMA&#xff08;Large Language Model AI&#xff09;在4月18日公布旗下最大模型LLAMA3&#xff0c;参数高达4000亿。目前meta已经开源了80亿和700亿版本模型&#xff0c;主要升级是多模态、长文本方面工作。 模型特点&#xff1a;采用标准的…

Unreal Engine创建Plugin

打开UE工程&#xff0c;点击编辑&#xff0c;选择插件 点击“新插件”按钮&#xff0c;选择“空白选项”填入插件名字"MultiPlayerPlugin"&#xff0c;填入插件作者、描述&#xff0c;点击“创建插件”按钮打开C工程&#xff0c;即可看到插件目录&#xff0c;编译C工…

【网络安全】安全事件管理处置 — 安全事件处置思路指导

专栏文章索引&#xff1a;网络安全 有问题可私聊&#xff1a;QQ&#xff1a;3375119339 目录 一、处理DDOS事件 1.准备工作 2.预防工作 3.检测与分析 4.限制、消除 5.证据收集 二、处理恶意代码事件 1.准备 2.预防 3.检测与分析 4.限制 5.证据收集 6.消除与恢复 …

游戏新手村18:游戏广告渠道与广告形式

上文我们说到&#xff0c;渠道为王&#xff0c;渠道可以为我们带来流量和用户&#xff0c;进而带来收入。我们可以通过哪些渠道导入用户呢&#xff1f;每个渠道有哪些优劣呢&#xff1f;在进行游戏营销推广的时候我们该如何选择呢&#xff1f; 根据付费性质&#xff0c;我们可…

鸿蒙ArkUI实战开发-如何通过上下滑动实现亮度和音量调节

场景说明 在音视频应用中通常可以通过上下滑动来调节屏幕亮度和音量大小&#xff0c;本例即为大家介绍如何实现上述UI效果。 说明&#xff1a; 由于当前亮度和音量调节功能仅对系统应用开发&#xff0c;所以本例仅讲解UI效果的实现。 效果呈现 本例效果如下&#xff1a; 当在…

iOS - 多线程-GCD-队列组

文章目录 iOS - 多线程-GCD-队列组1. 队列组1.1 基本使用步骤 iOS - 多线程-GCD-队列组 开发过程中&#xff0c;有时候想实现这样的效果 多个任务并发执行所有任务执行完成后&#xff0c;进行下一步处理&#xff08;比如回到主线程刷新UI&#xff09; 1. 队列组 可以使用GC…

区间图着色问题:贪心算法设计及实现

区间图着色问题&#xff1a;贪心算法设计及实现 1. 问题定义2. 贪心算法设计2.1 活动排序2.2 分配教室2.3 算法终止 3. 伪代码4. C语言实现5. 算法分析6. 结论7. 参考文献 在本文中&#xff0c;我们将探讨如何使用贪心算法解决一个特定的资源分配问题&#xff0c;即区间图着色问…

ruby 配置代理 ip(核心逻辑)

在 Ruby 中配置代理 IP&#xff0c;可以通过设置 Net::HTTP 类的 Proxy 属性来实现。以下是一个示例&#xff1a; require net/http// 获取代理Ip&#xff1a;https://www.kuaidaili.com/?refrg3jlsko0ymg proxy_address 代理IP:端口 uri URI(http://www.example.com)Net:…

【002_音频开发_基础篇_Linux音频架构简介】

002_音频开发_基础篇_Linux音频架构简介 文章目录 002_音频开发_基础篇_Linux音频架构简介创作背景Linux 音频架构ALSA 简介ASoC 驱动硬件架构软件架构MachinePlatformCodec ASoC 驱动 PCMALSA设备文件结构 ALSA 使用常用概念alsa-libALSA Open 流程ALSA Write 流程2种写入方法…

Android Studio查看viewtree

前言&#xff1a;之前开发过程一直看的是手机上开发者选项中的显示布局边界&#xff0c;开关状态需要手动来回切换&#xff0c;今天偶然在Android Studio中弄出了布局树觉得挺方便的。

【STM32F407+CUBEMX+FreeRTOS+lwIP之TCP记录】

STM32F407CUBEMXFreeRTOSlwIP之TCP记录 注意TCP client(socket)示例 TCP_server(socket)效果 注意 如果连接失败&#xff0c;建议关一下代理软件。 配置方面可以参考一下上一篇UDP的文章 STM32F407CUBEMXFreeRTOSlwIP之UDP记录 TCP client(socket) #define LWIP_DEMO_PORT 8…

【C语言__指针02__复习篇12】

目录 前言 一、数组名的理解 二、使用指针访问数组 三、一维数组传参的本质 四、冒泡排序 五、二级指针 六、指针数组 七、指针数组模拟二维数组 前言 本篇主要讨论以下问题&#xff1a; 1. 数组名通常表示什么&#xff0c;有哪两种例外情况&#xff0c;在例外情况中…

【论文浅尝】Phi-3-mini:A Highly Capable Language Model Locally on Your Phone

Phi-3-mini phi-3-mini&#xff0c;一个3.8亿个参数的语言模型&#xff0c;训练了3.3万亿个token&#xff0c;其总体性能&#xff0c;通过学术基准和内部测试进行衡量&#xff0c;可以与Mixtral 8x7B和GPT-3.5等模型相媲美(在MMLU上达到69%&#xff0c;在MT-bench上达到8.38)&…

Maya vs Blender:制作3D动画首选哪一个?

就 3D 动画而言&#xff0c;有两款3D软件引发了最多的争论&#xff1a;Blender 与 Maya。这两个强大的平台都提供强大的工具集&#xff0c;使动画故事和角色栩栩如生。但作为一名3D动画师&#xff0c;您应该投入时间学习和创作哪一个呢&#xff1f;下面我将从以下六点给您一个清…

用Python将原始边列表转换为邻接矩阵

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 在图论和网络分析中&#xff0c;图是一种非常重要的数据结构&#xff0c;它由节点&#xff…

C++感受6-Hello World 交互版

变量、常量输入、输出、流getline() 函数读入整行输入Hello() 函数复习新定义函数 Input() 实现友好的人机交互还有 “痘痘” 为什么挤不到的分析…… 1. DRY 原则简介 上一节课&#xff0c;我们写了两版“问候”程序。第一版的最大问题是重复的内容比较多&#xff0c;每一次问…

运行django

确保app被注册 urls.py中编写url 视图对应关系 命令行启动 python manage.py runserver

完美运营版商城/拼团/团购/秒杀/积分/砍价/实物商品/虚拟商品等全功能商城

源码下载地址&#xff1a;完美运营版商城.zip 后台可以自由拖曳修改前端UI页面 还支持虚拟商品自动发货等功能 挺不错的一套源码 前端UNIAPP 后端PHP 一键部署版本

Python浅谈清朝秋海棠叶版图

1、清朝疆域概述&#xff1a; 清朝是我国最后一个封建王朝&#xff0c;其始于1616年建州女真部努尔哈赤建立后金&#xff0c;此后统一女真各部、东北地区。后又降服漠南蒙古&#xff0c;1644年入关打败农民起义军、灭南明&#xff0c;削三藩&#xff0c;复台湾。后又收外蒙&am…

网络安全之防范钓鱼邮件

随着互联网的快速发展&#xff0c;新的网络攻击形式“网络钓鱼”呈现逐年上升的趋势&#xff0c;利用网络钓鱼进行欺骗的行为越来越猖獗&#xff0c;对互联网的安全威胁越来越大。网络钓鱼最常见的欺骗方式就是向目标群体发送钓鱼邮件&#xff0c;而邮件标题和内容&#xff0c;…