找回密码
 立即注册
查看: 72|回复: 5

STC15H2K64S4单片机P1.0-时钟 P1.1数据模拟PS2给PC发送A键

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:3
  • 最近打卡:2025-10-15 14:00:35
已绑定手机

1

主题

2

回帖

23

积分

新手上路

积分
23
发表于 5 天前 | 显示全部楼层 |阅读模式
使用单片机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[6][7] = {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占用
    }
}


回复

使用道具 举报 送花

3

主题

1880

回帖

608

积分

等待验证会员

积分
608
发表于 5 天前 | 显示全部楼层
你提出的这个问题涉及使用 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),则实际发送顺序应为:
  1. 起始位(0) + 00011100 -> LSB 先发:00011100 实际发送顺序为 00011100(不变)
复制代码

但注意,如果你在代码中没有正确地逐位发送,或在位移操作时出错,可能导致数据发送错误。

3. 校验位错误

PS/2 要求使用 奇校验,即整个数据帧中 1 的个数必须为奇数。若你未正确计算奇校验位,PC 会丢弃该帧。

4. 时钟线控制错误

PS/2 设备在发送数据时必须主动控制时钟线。如果你的代码中 CLK 线始终由 PC 控制,或你未正确地在发送期间拉低 CLK,会导致 PC 无法接收数据。

三、建议解决方案

1. 完整的 PS/2 发送函数示例(基于你当前硬件)
  1. c
  2. void SendPS2Byte(uchar data)
  3. {
  4.     uchar i;
  5.     uchar parity = 1; // 奇校验初始化为1,确保最终1的个数为奇数
  6.     // 拉低数据线前先拉低时钟线,确保主机不主动控制
  7.     PS2CLK = 0;
  8.     DelayUs(10);
  9.     // 起始位
  10.     PS2DATA = 0;
  11.     DelayUs(10);
  12.     PS2CLK = 1;
  13.     DelayUs(10);
  14.     PS2CLK = 0;
  15.     // 发送8位数据(LSB先发)
  16.     for(i = 0; i < 8; i++)
  17.     {
  18.         if(data & 0x01)
  19.         {
  20.             PS2DATA = 1;
  21.             parity ^= 1; // 计算奇校验
  22.         }
  23.         else
  24.         {
  25.             PS2DATA = 0;
  26.         }
  27.         data >>= 1;
  28.         DelayUs(10);
  29.         PS2CLK = 1;
  30.         DelayUs(10);
  31.         PS2CLK = 0;
  32.     }
  33.     // 发送奇校验位
  34.     PS2DATA = parity;
  35.     DelayUs(10);
  36.     PS2CLK = 1;
  37.     DelayUs(10);
  38.     PS2CLK = 0;
  39.     // 停止位
  40.     PS2DATA = 1;
  41.     DelayUs(10);
  42.     PS2CLK = 1;
  43.     DelayUs(10);
  44.     PS2CLK = 0;
  45.     // 释放数据线与时钟线
  46.     PS2DATA = 1;
  47.     PS2CLK = 1;
  48. }
复制代码

2. 使用函数发送 A 键通码(0x1C)
  1. c
  2. void SendAKey()
  3. {
  4.     SendPS2Byte(0x1C); // A键通码
  5. }
复制代码

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”字符。

如需进一步帮助,欢迎继续交流。

[本文内容由国芯人工智能辅助生成,仅供参考]
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:208
  • 最近打卡:2025-10-15 11:03:15

787

主题

1万

回帖

1万

积分

管理员

积分
19770
发表于 5 天前 | 显示全部楼层
STC8H8K64U系列的USB支持 PS2
截图202510111953029017.jpg

截图202510111953355606.jpg
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:3
  • 最近打卡:2025-10-15 14:00:35
已绑定手机

1

主题

2

回帖

23

积分

新手上路

积分
23
发表于 3 天前 来自手机 | 显示全部楼层
我看你的说明时钟频率10-20Khz,位周期50-100us,在程序里你用DelayUs(10);延时10us是什么意思?
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:328
  • 最近打卡:2025-10-15 08:50:20
已绑定手机

85

主题

3256

回帖

7196

积分

荣誉版主

无情的代码机器

积分
7196
发表于 3 天前 | 显示全部楼层
示波器\逻辑分析仪 抓下波形和正常的对比下
三天不学习,赶不上刘少奇~
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:3
  • 最近打卡:2025-10-15 14:00:35
已绑定手机

1

主题

2

回帖

23

积分

新手上路

积分
23
发表于 昨天 14:00 | 显示全部楼层
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
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-10-16 05:38 , Processed in 0.134467 second(s), 75 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表