STC15H2K64S4单片机P1.0-时钟 P1.1数据模拟PS2给PC发送A键
使用单片机STC15H2K64S4单片机的P1.0作为时钟,P1.1作为数据线,地线 +5电源 四根线与电脑端的键盘PS/2接口相连,想用单片机模拟键盘A键被按下时发送通码0x1c给PC机,但是在PC机上一直监测不到A字符,代码如下#include <STC15H2K64S4.H>
// 引脚定义
#define KEY_ROW_PORT P2 // 键盘行端口(P2.0~P2.5)
#define KEY_COL_PORT P0 // 键盘列端口(P0.0~P0.6)
sbit PS2_CLK = P1^0 ; // PS/2 时钟引脚(P1.0)
sbit PS2_DATA =P1^1; // PS/2 数据引脚(P1.1)
#define uchar unsigned char
uchar KeyState = {0};// 按键状态缓存(0:未按下,1:按下)
uchar LastKey = 0xFF; // 上一次按键键码(初始化为无效值)
bit KeyPressedFlag = 0; // 按键按下标志位
// 延时函数(微秒级,适配11.0592MHz晶振)
void DelayUs(uchar us)
{
while(us--)
{
_nop_();
_nop_();
_nop_();
}
}
// 延时函数(毫秒级,适配11.0592MHz晶振)
void DelayMs(unsigned int ms)
{
uchar i, j;
while(ms--)
{
i = 11;
j = 190;
do
{
while(--j);
}
while(--i);
}
}
// PS/2 发送一个字节(含起始位、8位数据、1位奇偶位、1位停止位)
void PS2_SendByte(uchar dat)
{
uchar i;
bit parity = 0;// 奇偶位(PS/2协议:数据位1的个数为奇数时,奇偶位为1,总1数为偶数)
// 1. 拉低CLK和DATA,准备发送起始位(主机主动拉低CLK至少10us)
PS2_CLK = 0;
DelayUs(15);
PS2_DATA = 0;// 起始位(低电平)
DelayUs(5);
PS2_CLK = 1; // 释放CLK,等待从机(计算机)拉低CLK
// 2. 发送8位数据(LSB先传,即从bit0开始)
for(i = 0; i < 8; i++)
{
while(PS2_CLK);// 等待从机拉低CLK(从机准备好接收)
PS2_DATA = (dat >> i) & 0x01;// 发送当前数据位
parity ^= PS2_DATA; // 计算奇偶位(异或累加1的个数)
while(!PS2_CLK); // 等待从机释放CLK(完成当前位接收)
}
// 3. 发送奇偶位
while(PS2_CLK);
PS2_DATA = parity;
while(!PS2_CLK);
// 4. 发送停止位(高电平)
while(PS2_CLK);
PS2_DATA = 1;
while(!PS2_CLK);
PS2_CLK = 1;// 释放CLK,完成1字节发送
DelayUs(10);
}
// 主函数(初始化+循环扫描+PS2发送)
void main()
{
// 端口初始化:行端口(推挽输出),列端口(上拉输入),PS2引脚(开漏输出,兼容PS2协议)
P2M0 = 0x3F;// P2.0~P2.5 推挽输出(0x3F = 00111111)
P2M1 = 0x00;
P0M0 = 0x00;// P0.0~P0.6 上拉输入(开漏输入模式)
P0M1 = 0x7F;// 0x7F = 01111111
P1M0 = 0x03;// P1.0~P1.1 开漏输出(PS2需要开漏,匹配总线电平)
P1M1 = 0x00;
PS2_CLK = 1;// 初始释放PS2 CLK和DATA(高电平)
PS2_DATA = 1;
DelayMs(100); // 系统稳定延时
while(1)
{
PS2_SendByte(0x1c); // 向计算机发送PS2键码A键
PS2_SendByte(0xF0); // 发送键码释放帧(PS2协议:释放键码=0xF0+按下键码)
PS2_SendByte(0x1c);
DelayMs(1000);// 降低扫描频率,减少CPU占用
}
}
你提出的这个问题涉及使用 STC15H2K64S4 单片机通过 PS/2 接口协议 模拟键盘按键(A键)发送通码 0x1C 给 PC。你已经实现了基本的硬件连接(P10 为时钟,P11 为数据线)和部分代码结构,但在 PC 端无法接收到预期的字符“A”。
以下是对你的问题的全面分析和优化建议:
一、PS/2 协议基础
PS/2 是一种同步串行通信协议,使用两根线:
CLK(时钟线):由主机(PC)或设备(键盘)控制。
DATA(数据线):数据在时钟的下降沿被采样。
1. 数据帧格式(设备发送给主机)
标准 PS/2 数据帧如下(11 位):
| 位序 | 内容 |
|------|------------------|
| 0 | 起始位(0) |
| 1~8| 数据位(LSB 先发)|
| 9 | 奇偶校验位 |
| 10 | 停止位(1) |
2. 发送时序要点
数据线在时钟下降沿时必须稳定。
时钟频率通常在 10~20kHz,即每个位周期约 50~100 μs。
设备必须控制时钟线(拉低 -> 释放)以发送数据。
二、代码分析与问题定位
你当前的代码片段中,仅给出了延时函数、引脚定义及全局变量,但未提供完整的 PS/2 发送函数。根据你描述的“发送 A 键通码 0x1C 但 PC 无反应”,可能的问题如下:
1. 时序控制不准确
PS/2 的时序要求非常严格,尤其是时钟的控制和数据的稳定时间。若延时不足或过长,会导致主机(PC)无法正确识别数据。
2. 数据位顺序错误(LSB vs MSB)
PS/2 协议中数据位是 LSB 先发。若代码中直接发送 0x1C(即二进制 00011100),则实际发送顺序应为:
起始位(0) + 00011100 -> LSB 先发:00011100 实际发送顺序为 00011100(不变)
但注意,如果你在代码中没有正确地逐位发送,或在位移操作时出错,可能导致数据发送错误。
3. 校验位错误
PS/2 要求使用 奇校验,即整个数据帧中 1 的个数必须为奇数。若你未正确计算奇校验位,PC 会丢弃该帧。
4. 时钟线控制错误
PS/2 设备在发送数据时必须主动控制时钟线。如果你的代码中 CLK 线始终由 PC 控制,或你未正确地在发送期间拉低 CLK,会导致 PC 无法接收数据。
三、建议解决方案
1. 完整的 PS/2 发送函数示例(基于你当前硬件)
c
void SendPS2Byte(uchar data)
{
uchar i;
uchar parity = 1; // 奇校验初始化为1,确保最终1的个数为奇数
// 拉低数据线前先拉低时钟线,确保主机不主动控制
PS2CLK = 0;
DelayUs(10);
// 起始位
PS2DATA = 0;
DelayUs(10);
PS2CLK = 1;
DelayUs(10);
PS2CLK = 0;
// 发送8位数据(LSB先发)
for(i = 0; i < 8; i++)
{
if(data & 0x01)
{
PS2DATA = 1;
parity ^= 1; // 计算奇校验
}
else
{
PS2DATA = 0;
}
data >>= 1;
DelayUs(10);
PS2CLK = 1;
DelayUs(10);
PS2CLK = 0;
}
// 发送奇校验位
PS2DATA = parity;
DelayUs(10);
PS2CLK = 1;
DelayUs(10);
PS2CLK = 0;
// 停止位
PS2DATA = 1;
DelayUs(10);
PS2CLK = 1;
DelayUs(10);
PS2CLK = 0;
// 释放数据线与时钟线
PS2DATA = 1;
PS2CLK = 1;
}
2. 使用函数发送 A 键通码(0x1C)
c
void SendAKey()
{
SendPS2Byte(0x1C); // A键通码
}
3. 注意事项
时钟线必须由设备(单片机)控制,在发送期间主动拉低并释放。
数据线在时钟下降沿时必须稳定,建议在拉低 CLK 后延时再改变 DATA。
奇校验位必须正确计算,否则 PC 会丢弃数据帧。
延时函数需适配当前主频(110592MHz),建议使用定时器或更精确的延时方式。
四、硬件连接建议
确保你的硬件连接如下:
| 单片机引脚 | 功能 | 连接至 PS/2 接口 |
|------------|----------|------------------|
| P1^0 | PS2CLK| CLK 引脚 |
| P1^1 | PS2DATA | DATA 引脚 |
| GND | 地 | GND |
| VCC | 电源 | +5V |
注意:PS/2 接口通常是 6 针 mini-DIN 接口,其中仅使用 CLK、DATA、GND、VCC 四根线。
五、调试建议
1. 使用示波器或逻辑分析仪 观察 CLK 和 DATA 波形,确认是否符合 PS/2 协议格式。
2. 尝试发送多个帧,如发送 0x1C 多次,观察是否能触发一次有效接收。
3. 尝试发送其他键码,如 0x1B(Z键)等,确认是否为码值错误。
4. 尝试使用 USB PS/2 转换器 测试,部分主板或笔记本可能不支持纯 PS/2 接口。
六、总结
你当前的问题主要集中在 PS/2 协议实现不完整 上,包括:
时钟线控制不正确
数据位顺序处理错误
校验位未正确计算
延时控制不精确
建议你根据上述完整函数进行修改,并配合逻辑分析仪进行调试。只要时序和数据格式正确,PC 端应能正确接收到“A”字符。
如需进一步帮助,欢迎继续交流。
[本文内容由国芯人工智能辅助生成,仅供参考] STC8H8K64U系列的USB支持 PS2
我看你的说明时钟频率10-20Khz,位周期50-100us,在程序里你用DelayUs(10);延时10us是什么意思? 示波器\逻辑分析仪 抓下波形和正常的对比下
Ps2接口不是即插即用的,用单片机模拟时,只是一直的给pc发送数据,我按照师傅给的建议试了,pc收不到数据
代码如下:
#include "reg51.h"
#include "intrins.h"
typedef unsigned char BYTE;
typedef unsigned int WORD;
sfr P0M1 = 0x93;
sfr P0M0 = 0x94;
sfr P1M1 = 0x91;
sfr P1M0 = 0x92;
sfr P2M1 = 0x95;
sfr P2M0 = 0x96;
sfr P3M1 = 0xb1;
sfr P3M0 = 0xb2;
sfr P4M1 = 0xb3;
sfr P4M0 = 0xb4;
#define delaym(); _nop_();_nop_(); _nop_(); _nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();
#define Delay2(); _nop_();_nop_();_nop_(); _nop_(); _nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();_nop_(); _nop_(); _nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();_nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_(); _nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_(); _nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_(); _nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_(); _nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_(); _nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_(); _nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_(); _nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_();_nop_();_nop_(); _nop_(); _nop_(); _nop_();_nop_(); _nop_();_nop_(); _nop_();
#define delays(); _nop_();_nop_();_nop_();_nop_(); _nop_();_nop_(); _nop_();
#define delayms(); _nop_();_nop_();_nop_();
sfr AUXR= 0x8e; //辅助寄存器
sfr P_SW1 = 0xA2; //外设功能切换寄存器1
#define S1_S0 0x40 //P_SW1.6
#define S1_S1 0x80 //P_SW1.7
sbit P34 = P3^4;
sbit PS2_CLK=P1^0;
sbit PS2_DATA=P1^1;
bit busy;
void SendData(BYTE dat);
void SendString(char *s);
void delay()
{
int i, j;
for (i=0; i<1000; i++)
for (j=0; j<500; j++);
}
/*延时微秒*/
void DelayUs(unsigned int us)
{
Delay2();
Delay2();
Delay2();
Delay2();
}
/*延时毫秒*/
void DelayMs(unsigned int ms)
{
unsigned char i,j;
while(ms--)
{
i=16;
j=190;
do
{
while(--j);
}
while(--i);
}
}
/*PS2发送函数 */
void PS2_SendByte(unsigned char dat)
{
unsigned char i=0;
bit parity=1;
PS2_CLK=0;
DelayUs(50);
//起始位
PS2_DATA=0;
DelayUs(50);
PS2_CLK=1;
DelayUs(50);
PS2_CLK=0;
//发送8位数据 LSB在前
for(i=0;i<8;i++)
{
if(dat&0x01)
{
PS2_DATA=1;
parity^=1;
}
else
{
PS2_DATA=0;
}
dat>>=1;
DelayUs(50);
PS2_CLK=1;
DelayUs(50);
PS2_CLK=0;
}
//发送奇偶校验位
PS2_DATA=parity;
DelayUs(50);
PS2_CLK=1;
DelayUs(50);
PS2_CLK=0;
//停止位
PS2_DATA=1;
DelayUs(50);
PS2_CLK=1;
DelayUs(50);
PS2_CLK=0;
//释放时钟和数据线
PS2_CLK=1;
PS2_DATA=1;
}
void main()
{
P1M1 = 0x00;
P1M0 = 0x00;
P3M1 = 0x00;
P3M0 = 0x00;
SCON = 0x50; //8位可变波特率
AUXR = 0x00; //定时器1为12T模式
TMOD = 0x20; //定时器1为模式2(8位自动重载)
TH1=0xfd;
TL1=0xfd;
TR1 = 1; //定时器1开始工作
ES = 1; //使能串口中断
EA = 1;
while(1)
{
PS2_SendByte(0x1c);
DelayMs(500); //500毫秒
}
}C:\Users\DELL\Desktop\微信图片_20251015135711_134_24.jpeg
页:
[1]