目录
一、效果展示
二、创作灵感
三、硬件电路
注意事项
工作原理
四、源码
main.c
五、附录
CH9328工作原理
CH9328的模式选择
编辑 全键盘键码值表
参考链接
一、效果展示
该小键盘具有三种功能:
1、自动输入开机密码
2、每隔一段时间自动按下ctrl+s(即保存)
3、具有和电脑键盘的ctrl c v一样的功能,可组合使用(如ctrl+c是复制)
上述的三个键均、开机密码、自动保存时间均可自定义,修改键码值即可。
建码值见附录
二、创作灵感
由于本人是学校社团“仪光实践协会”技术部部长(技术部只有我一个人,既是部长,又是部员,哈哈哈🤣),需要给大一的学弟学妹们想一些有意思的项目,所以就做了这个。
三、硬件电路
注意事项
该电路的供电接口选用了micro-usb,之所以不用Type-C接口是因为该电路涉及数据传输,而用具有数据传输功能的Type-C接口较难焊接,故用micro–usb接口。
值得注意的是:目前micro–usb数据线使用得已经不太多了,在使用该键盘时一定要用具有数据传输功能的数据线。
有些数据线只有供电功能,不能进行通信!
链接: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博客https://blog.csdn.net/qishi3250/article/details/83344176
原理篇4、CH9328使用-CSDN博客https://blog.csdn.net/qq_44817843/article/details/112124822