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

news2024/9/24 13:13:12

目录

一、效果展示

二、创作灵感

三、硬件电路

注意事项

工作原理

四、源码

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; // 设置为工作方式1
    TMOD = 0x20; // 设置计数器工作方式2
    PCON = 0x80; // 波特率加倍
    TH1 = 0xFA; // 计数器初始值设置,波特率9600
    TL1 = 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);//enter
    P[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);//w
	  P[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);//a
	  P[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);//n
	  P[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);//g
		P[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);//enter
		P[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;//Ctrl
				SAVE[2] = 0x16;//S
				for (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.coloradmin.cn/o/1616101.html

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

相关文章

在Mac M1笔记本上跑大语言模型llama3的4个步骤?(install、pull、run、ask)

要点 Ollama一个功能强大的本地大语言模型LLM运行工具&#xff0c;支持很多模型&#xff0c;并且操作极其简单快速回忆步骤&#xff1a; 下载ollama工具&#xff1a;https://ollama.com/download 下载模型&#xff1a;ollama pull llama3 #根据libs列表直接指定名字 运行模型…

【软考】设计模式之适配器模式

目录 1. 说明2. 应用场景3. 结构图4. 构成5. 优缺点5.1 优点5.2 缺点 6. 适用性7. java示例7.1 类适配器模式7.2 对象适配器模式 1. 说明 1.Adapter&#xff08;适配器&#xff09;。2.将一个类的接口转换成客户希望的另外一个接口。3.Adapter模式使得原本由于接口不兼容而不能…

visionTransformer window平台下报错

错误&#xff1a; KeyError: Transformer/encoderblock_0/MlpBlock_3/Dense_0kernel is not a file in the archive解决方法&#xff1a; 修改这个函数即可&#xff0c;主要原因是Linux系统与window系统路径分隔符不一样导致 def load_from(self, weights, n_block):ROOT f&…

物理隔离条件下的数据安全导入导出方案,哪种最安全可控?

数据安全在当今信息化社会中扮演着至关重要的角色&#xff0c;尤其像政府、军工等单位&#xff0c;有比较多的核心数据要保护&#xff0c;一旦出现数据泄漏&#xff0c;将造成不可估量的后果。因此为了保护数据安全&#xff0c;政府、军工等单位一般会采取纯物理隔离&#xff0…

day07 51单片机-串口通信

51 单片机-串口通信 1 串口通信 1.1 需求描述 本案例讲解如何通过串口和PC以9600波特率,无校验位、1停止位通信。最终实现PC向单片机发送字符串,单片机回复PC。本案例中采用串口1通信。 1.2 硬件设计 1.2.1 串口工作原理 串口是将数据按照比特逐一发送的通信接口。在串…

视频教程下载:ChatGPT驱动的SEO、网络营销、生产力提升

用户遇到的一个常见问题是在ChatGPT对话过程中难以保持清晰的目的和专注。这可能导致互动无效和浪费时间。这门课程将教给各种创意人士——艺术家、制造者、博主、讲师和内容创作者——如何制定理想的提示配方&#xff0c;从而产生更有成效的对话和更高的回报。 这是一门关于如…

《R语言与农业数据统计分析及建模》学习——数据框的向量化操作

1、向量化操作的概念和有时 向量化操作是指对整个数据结构进行一次性操作&#xff0c;而不需要使用显式的循环结构&#xff08;即同时处理整个数据框的元素&#xff0c;而不需要使用for循环逐个处理每个元素&#xff09;。优势如下&#xff1a; 代码简洁&#xff1a;不需要编写…

3.Docker常用镜像命令和容器命令详解

文章目录 1、Docker镜像命令1.1 获取镜像1.2 查看镜像1.2.1、images命令列出镜像1.2.2、tag命令添加镜像标签1.2.3、inspect命令查看详细信息1.2.4、history命令查看镜像历史 1.3 搜索镜像1.4 删除和清理镜像1.4.1、使用标签删除镜像1.4.2、清理镜像 1.5 创建镜像1.5.1、基于已…

node.js如何实现留言板功能?

一、实现效果如下&#xff1a; 20240422_160404 二、前提配置&#xff1a; 配置&#xff1a;需要安装并且导入underscore模板引擎 安装&#xff1a;在控制台输入npm install underscore -save 文件目录配置&#xff1a; 1》在文件里建一个data文件夹&#xff0c;此文件夹下…

SLS 查询新范式:使用 SPL 对日志进行交互式探索

作者&#xff1a;无哲 引言 在构建现代数据和业务系统的过程中&#xff0c;可观测性已经变得至关重要&#xff0c;日志服务&#xff08;SLS&#xff09;为 Log/Trace/Metric 数据提供了大规模、低成本、高性能的一站式平台服务&#xff0c;并提供数据采集、加工、投递、分析、…

【PhpStorm的环境配置与应用的简单介绍】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

AWD线下攻防万字最完整战术(记第一届“长城杯”半决赛战术)

目录 准备阶段 1.登录比赛平台&#xff08;获取资产&#xff09; 查看账号账号修改 服务器SSH口令mysqlWEB服务口令(后台密码)数据库后台管理员密码 账号用户检查 2.dump源码&#xff08;方便应急响应恢复靶机&#xff09; 网站源码备份 压缩文件解压文件备份到服务器本地上传…

探索大型语言模型(LLM)在人类性格个性评估(MBTI)中的前景与应用

1.概述 大型语言模型&#xff08;LLM&#xff09;如ChatGPT在各个领域的应用确实越来越广泛&#xff0c;它们利用庞大的数据集进行训练&#xff0c;以模拟人类的语言理解和生成能力。这些模型在提供信息、解答问题、辅助决策等方面表现出了强大的能力&#xff0c;但它们并不具…

Docker pull镜像名称 把本地镜像推送到远程详解

Docker pull镜像名称 把本地镜像推送到远程详解&#xff1a; Docker 镜像 仓库 容器介绍 以及镜像仓库详解 下载一个alpine的镜像演示&#xff0c;alpine是一个比较小的的linux镜像。 docker pull alpinedocker tag d4ff818577bc docker.io/itying/alpine:v1.0.1docker tag d4…

Macs Fan Control Pro for Mac:全面优化Mac风扇控制软件

Macs Fan Control Pro for Mac是一款专为苹果电脑用户设计的风扇控制软件&#xff0c;旨在通过精确的风扇速度调节&#xff0c;全面优化Mac的散热性能&#xff0c;确保系统始终运行在最佳状态。 Macs Fan Control Pro for Mac中文版下载 该软件具备实时监控功能&#xff0c;能够…

ESP32与SD卡交互实现:文件读写实战与初始化详解及引脚定义

本代码实现ESP32与SD卡的交互&#xff0c;包括定义SPI引脚、创建自定义SPI类实例、编写WriteFile与ReadFile函数进行文件读写。setup函数初始化串口、SPI、SD卡&#xff0c;向“/test.txt”写入“myfirstmessage”&#xff0c;读取并打印其内容。loop函数留空待扩展。 1. 需要…

区块链安全应用-------压力测试

基于已有的链进行测试&#xff08;build_chain默认建的链 四个节 点&#xff09;&#xff1a; 第一步&#xff1a;搭链 1. 安装依赖 在ubuntu操作系统中&#xff0c;操作步骤如下&#xff1a; sudo apt install -y openssl curl 2. 创建操作目录, 下载安装脚本 ## 创建操作…

HDFS分布式文件存储系统

1-1 HDFS的存储机制 按块&#xff08;block&#xff09;存储 hdfs在对文件数据进行存储时&#xff0c;默认是按照128M(包含)大小进行文件数据拆分&#xff0c;将不同拆分的块数据存储在不同datanode服务器上 拆分后的块数据会被分别存储在不同的服务器上 副本机制 为了保证hdfs…

标题Selenium IDE 常见错误笔记

Selenium IDE 常见错误笔记 错误1&#xff1a;Failed:Exceeded waiting time for new window to appear 2000ms 这个错误通常出现在第一次运行时&#xff0c;有两个原因&#xff1a; Firefox阻止了弹出式窗口&#xff0c;在浏览器设置里允许这个操作即可。 有些网站设置了反…

轻松上手,无缝对接:详述如何接入企讯通空号检测接口API

企讯通空号检测接口API作为一款高效、精准的手机号码状态检测工具&#xff0c;能够帮助企业及开发者快速识别手机号码的有效性&#xff0c;优化通讯资源&#xff0c;提升营销效果。本篇文章将带领您一步步了解如何轻松、无缝地对接企讯通空号检测接口API&#xff0c;让您的业务…