PS2手柄使用SPI外设读取时遇到的问题,附测试工程

背景:昨天,心血来潮想使用单片机的SPI外设读取ps2手柄,但是发现的数据根本不对。

(型号:STM32F411CEU6)

结论:

1、PS2手柄是SPI全双工通信。

2、通信速度<=100khz。

3、时钟空闲时是高电平,下降沿读取。

4、数据格式LSB,小端发送。

5、如果将SPI外设时钟频率降低至100kHZ或者以下,则可以正常读出数据,但是需要降低外设上面的的总线频率,那么在这条总线上的其他外设频率也会跟着降低。而应用IO口模拟与使用SPI外设所占用的IO口数量相同,也不会影响其他外设的时钟频率,所以,综合分析,使用IO口模拟SPI读取PS2更好一点。

过程:使用网上IO口模拟的程序,发现可以正常运行,于是示波器分别测量SPI外设和IO模拟的波形,发现是SPI外设的时钟频率太快了,导致出现问题。将IO模拟的时钟延时时间改为4us的时候,数据是不准确的,将时钟延时改为1us的时候,数据读出的都是0xff。

下面这个是用IO口模拟的程序,测量其时钟频率约100khz。时钟延时10us。

//读数据、写命令
u8 PS2_Write_Read(u8 dat)
{  //数据返回值temp    
    u8 temp = 0;  
   //循环计数值i
    u8 i = 0; 
   //命令值临时存储wt_temp
    u8 wt_temp = 0; 
    
    wt_temp = dat;
    for(i=0;i<8;i++)
    {
        PS2_SCK_H();//时钟高
        delay_us(10);//时钟延时
        if(wt_temp&0x01)//判断命令从0bit开始,循环8次。决定CMD电平。
        {
            PS2_CMD_H();
        }
        else
        {
            PS2_CMD_L();
        }
        PS2_SCK_L();//时钟低
        delay_us(10);//时钟延时
        wt_temp>>=1;
        //数据第一个是第0bit,因此需要把后接收到的数据移动到第7bit。
        temp |= GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)<<i;
    }
    return temp;//返回数据的值
}

波形:

可以正常读出数据。

将其改成延时1us。

//读数据、写命令
u8 PS2_Write_Read(u8 dat)
{  //数据返回值temp    
    u8 temp = 0;  
   //循环计数值i
    u8 i = 0; 
   //命令值临时存储wt_temp
    u8 wt_temp = 0; 
    
    wt_temp = dat;
    for(i=0;i<8;i++)
    {
        PS2_SCK_H();//时钟高
        delay_us(1);//时钟延时
        if(wt_temp&0x01)//判断命令从0bit开始,循环8次。决定CMD电平。
        {
            PS2_CMD_H();
        }
        else
        {
            PS2_CMD_L();
        }
        PS2_SCK_L();//时钟低
        delay_us(1);//时钟延时
        wt_temp>>=1;
        //数据第一个是第0bit,因此需要把后接收到的数据移动到第7bit。
        temp |= GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)<<i;
    }
    return temp;//返回数据的值
}

读出的数据就是不正常的了。

此时使用单片机SPI外设读取。配置如下:

#include "main.h"
#include "misc.h"
#include "user_spi.h"
//配置初始化
void USER_SPI1_Init(void)
{
    RCC->AHB1ENR |=0X01;//开启GPIOA时钟
    RCC->APB2ENR |=1<<12;//开启外设时钟    
    //配置GPIOA
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA,&GPIO_InitStruct);
    //CSpin
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
    GPIO_Init(GPIOA,&GPIO_InitStruct);
    
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_SPI1);

    //APB2 外设SPI1,系统时钟100MHZ
    SPI1 ->CR1 |= 1<<2;     //配置为主设备
    SPI1 ->CR1 |= 1<<1;     //配置空闲时钟为高电平
    SPI1 ->CR1 &= ~(0x01);     //配置为下降沿采样
    SPI1 ->CR1 |= 0X07<<3;  //波特率256分频 390khz
    SPI1 ->CR1 |= 0X01<<7;  //LSB
    SPI1 ->CR1 |= 0X01<<8;  //使用软件NSS管理。用来代替外部io口输入电平    
    SPI1 ->CR1 |= 0X01<<9;  //NSS为1,是设定SPI1为主设备的前提条件。如果不置位,则SPI1设备为从设备。
    SPI1 ->CR1 |= 1<<6;     //使能SPI    
}
//中断初始化
void USER_SPI1_TIM_Init(void)
{
    NVIC_InitTypeDef  NVIC_InitStruct = {0};
    NVIC_InitStruct.NVIC_IRQChannel = SPI1_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;  //0~1
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 7;     //0~7
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
}

//发送接收函数
u8 USER_SPI1_TrsRec(u8 dat)
{
    u8 temp = 0;
    u16 time_out = 0;
    while(!(SPI1->SR &1<<1))//等待上一次发送完成,1发送完成,0未完成
    {
        time_out++;
        if(time_out >2400)//100us
        { break;}
    }
    time_out = 0;
    SPI1->DR = dat;   //发送数据
    while(!(SPI1->SR &0x01))//等待收到有效数据,1有数据,0无数据
    {
        time_out++;
        if(time_out >2400)//100us
        { break;}
    }
    temp = SPI1->DR;  //读出数据
    return temp;
}

读出的数据也是不正常的,如果想要正常读出数据,其时钟频率尽量不得大于100khz。`

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值