STM32與PS2的無線通信和相關函數介紹

来源:https://www.cnblogs.com/Sound-Sleep/archive/2022/12/16/STM32_PS2.html
-Advertisement-
Play Games

個人名片: 對人間的熱愛與歌頌,可抵歲月冗長:sun_with_face: Github👨🏻‍💻:念舒_C.ying CSDN主頁✏️:念舒_C.ying 個人博客:earth_asia: :念舒_C.ying 1 配置環境 掛載系統ISO,並配置好本地dnf源。(前面的文章講過,這裡就略過. ...


PS2採用SPI通信協議


接收器介面

  1. DI:手柄->主機,時鐘的下降沿傳送信號,信號的讀取在時鐘由髙到低的變化過程中完成
  2. DO:主機->手柄,同步傳送於時鐘的下降沿
  3. 空埠
  4. GND
  5. VDD:3~5V
  6. CS:低電平被選中
  7. CLK
  8. 空埠
  9. ACK:一般不用

時鐘頻率

250Khz ~ 4us

數據不穩定可以適當增加頻率


通信流程

  • 拉低 CS 線電平,併發出一個命令“0x01”
  • 手柄會回覆它的 ID “0x41=綠燈模式, 0x73=紅燈模式”
  • 手柄發送 ID 的同時,單片機將傳送0x42,請求數據
  • 手柄發送出 0x5A, 告訴單片機“數據來了”

下麵是數據意義對照表,其中idle表示空閑


順序3~8的解析

  • 按鍵按下時為0,未按下為1

紅燈模式和綠燈模式

  • 紅燈模式:左右搖桿發送模擬值, 0x00〜OxFF 之間,且搖桿按下的鍵值 L3、 R3 有效
    ID = 0x73
  • 綠燈模式:左右搖桿模擬值為無效,推到極限時,對應發送 UP、 RIGHT、 DOWN、LEFT、△、 〇、 X、 □
    按鍵 L3、 R3 無效
    ID = 0x41

連接使用說明

  • 接收器和單片機共用一個電源

  • 自動配對
  • 未配對的情況下,兩邊的燈都會不停的閃
  • 燈常亮則配對成功

  • 在一定時間內未搜索到接收器,手柄將進入待機模式
  • 待機模式下手柄的燈將滅掉,可以通過“START” 鍵,喚醒手柄。
  • 按鍵 “MODE” (“ANALOG”) , 可以選擇紅燈模式和綠燈模式

pstwo.c部分函數詳解

void PS2_Init(void)

初始化GPIO介面

  • 介面配置
    • DI->PB12
    • DO->PB13
    • CS->PB14
    • CLK->PB15
void PS2_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	//使能PORTB時鐘
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	//配置 PB13 PB14 PB15 為 通用推輓輸出,速度為50mMhz
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStruct);

	//配置 PB12 為 下拉輸入模式
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPD;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_12;
	GPIO_Init(GPIOB, &GPIO_InitStruct);									  
}


void PS2_Cmd(u8 CMD)

發送數據給PS2的同時接收PS2的數據

  • 涉及到的頭文件
#define DI   PBin(12)           //PB12  輸入

#define DO_H PBout(13)=1        //命令位高
#define DO_L PBout(13)=0        //命令位低

#define CLK_H PBout(15)=1     	//時鐘拉高
#define CLK_L PBout(15)=0      	//時鐘拉低
  • 涉及到的全局變數
//數據存儲數組
u8 Data[9]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 

void PS2_Cmd(u8 CMD)
{
	volatile u16 ref=0x01;
	//重置數據
	Data[1] = 0;
	for(ref=0x01;ref<0x0100;ref<<=1)
	{
		//檢測是否有指令需要發送,有指令則拉高電平
		if(ref&CMD)	DO_H;                   
		else DO_L;
		
		//先拉高時鐘線電平,然後降低,然後再拉高,從而同步發送與接收數據
		CLK_H;                       
		DELAY_TIME;
		CLK_L;
		DELAY_TIME;
		CLK_H;

		//若接受到數據,則在對應數據位寫1
		if(DI)
			Data[1] = ref|Data[1];
	}
	//發送完八位數據之後延時一段時間
	delay_us(16);
}

  • ref由0x00000001(8bit)變成0x10000000(8bit),模擬從低位開始的串列通信
  • 時鐘電平每次出現一次下降沿,DO_H、DO_L同時發送一bit數據

void PS2_ReadData(void)

讀取手柄數據

  • 涉及到的頭文件
#define DI   PBin(12)           //PB12  輸入

#define DO_H PBout(13)=1        //命令位高
#define DO_L PBout(13)=0        //命令位低

#define CS_H PBout(14)=1       	//CS拉高
#define CS_L PBout(14)=0       	//CS拉低

#define CLK_H PBout(15)=1     	//時鐘拉高
#define CLK_L PBout(15)=0      	//時鐘拉低
  • 涉及到的全局變數
//數據存儲數組
u8 Data[9]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 
//用於存儲兩個命令,分別是開始命令和請求數據命令
u8 Comd[2]={0x01,0x42};

void PS2_ReadData(void)
{
	volatile u8 byte=0;
	volatile u16 ref=0x01;
	
	//片選線拉低電平以選中接收器
	CS_L;

	//發送請求命令和請求數據命令
	PS2_Cmd(Comd[0]);  
	PS2_Cmd(Comd[1]);  

	//依次讀取數組Data的後七個位置
	for(byte=2;byte<9;byte++)         
	{
		//將數據寫入Data的後七個位置
		for(ref=0x01;ref<0x100;ref<<=1)
		{
			CLK_H;
			DELAY_TIME;
			CLK_L;
			DELAY_TIME;
			CLK_H;
		      if(DI)
		      Data[byte] = ref|Data[byte];
		}
		
		//每發送完八位數據之後延時一段時間
        delay_us(16);
	}
	
	//拉高片選線電平結束通信
	CS_H;
}

  • Data[1]用於存儲每次執行PS2_Cmd函數時DI返回的信號數據了
    剩下的Data[2]~Data[9]共7個位置就用來存儲需要返回單片機處理的有效數據了
  • 如果沒有進行任何操作,則Data的後7個位置的每一個位都會被寫入1

u8 PS2_RedLight(void)

判斷是否為紅燈模式,return0則為紅燈模式
紅燈的ID為“0x73”,綠燈的ID為“0x41”

  • 涉及到的頭文件
#define CS_H PBout(14)=1       	//CS拉高
#define CS_L PBout(14)=0       	//CS拉低
  • 涉及到的全局變數
//數據存儲數組
u8 Data[9]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 
//用於存儲兩個命令,分別是開始命令和請求數據命令
u8 Comd[2]={0x01,0x42};

u8 PS2_RedLight(void)
{
	CS_L;
	PS2_Cmd(Comd[0]);  
	PS2_Cmd(Comd[1]);  
	CS_H;

	//判斷是否是紅燈模式的ID
	if( Data[1] == 0X73)   return 0 ;
	else return 1;

}

  • 在發送comd[2],也就是0x42的同時,DI會用8次迴圈將ID的每一位返回到Data[1]中
  • Data[1] = 0x73,也就是等於紅燈模式的ID,則return0,否則return1

void PS2_ClearData()

重置Data數組的所有位

void PS2_ClearData()
{
	u8 a;
	for(a=0;a<9;a++)
		Data[a]=0x00;
}

u8 PS2_DataKey()

返回按鍵的對應鍵值 ,鍵值用按鍵名的巨集去定義
按鍵按下為0,未按下為1

  • 涉及到的全局變數
//用於儲存按鍵值
u16 Handkey;
//數據存儲數組
u8 Data[9]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; 
u16 MASK[]={
    PSB_SELECT,
    PSB_L3,
    PSB_R3 ,
    PSB_START,
    PSB_PAD_UP,
    PSB_PAD_RIGHT,
    PSB_PAD_DOWN,
    PSB_PAD_LEFT,
    PSB_L2,
    PSB_R2,
    PSB_L1,
    PSB_R1 ,
    PSB_GREEN,
    PSB_RED,
    PSB_BLUE,
    PSB_PINK
	};
  • 涉及到的頭文件聲明
//PS2按鍵鍵值的巨集定義
#define PSB_SELECT      1
#define PSB_L3          2
#define PSB_R3          3
#define PSB_START       4
#define PSB_PAD_UP      5
#define PSB_PAD_RIGHT   6
#define PSB_PAD_DOWN    7
#define PSB_PAD_LEFT    8
#define PSB_L2          9
#define PSB_R2          10
#define PSB_L1          11
#define PSB_R1          12
#define PSB_GREEN       13
#define PSB_RED         14
#define PSB_BLUE        15
#define PSB_PINK        16

#define PSB_TRIANGLE    13
#define PSB_CIRCLE      14
#define PSB_CROSS       15
#define PSB_SQUARE      16

u8 PS2_DataKey()
{
	u8 index;
	
	PS2_ClearData();
	PS2_ReadData();
	
	//將所有按鍵對應的位整合成一個16bit的數據
	Handkey=(Data[4]<<8)|Data[3];    
	
	for(index=0;index<16;index++)
	{	    

		//遍歷這個16bit的數據,並返回被按下按鍵的值,按鍵的值被巨集定義
		if((Handkey&(1<<(MASK[index]-1)))==0)
		return index+1;
	}
	return 0;          
}
  • 遍歷Handkey,返回按鍵對應的鍵值的邏輯如下:
    • 首先我們知道按鍵被按下時會朝對應的數據位寫入0,沒被按下則寫入1
    • 我們想要檢測被寫入0的位置
    • 而任何數&=0都會被清0
    • 所以可以用 1&按鍵名在Handkey中對應位 並判斷結果是否為0,從而判斷按鍵是否被按下
    • 所以將1左移到與Handkey中的按鍵名的對應位 對齊,進行&操作
    • 由於1左移後其他位都為0,所以&了以後其他位都是0,所以整個數字是否為0就取決於按鍵名在Handkey中的對應位是否為0
    • 接下來就是設定好1左移的量為(Mask[index] - 1)

u8 PS2_AnologData(u8 button)

返回搖桿的狀態數值

u8 PS2_AnologData(u8 button)
{
	return Data[button];
}

  • 不同的button的值所讀取的數據:

    • 5:右邊搖桿的X方向
    • 6:右邊搖桿的Y方向
    • 7:左邊搖桿的X方向
    • 8:左邊搖桿的Y方向
  • 返回的搖桿的模擬值在0~255之間

  • x方向最左邊為0,最右邊為255

  • y方向最上方為0,最右邊為255


void PS2_SetInit(void)

手柄配置初始化

void PS2_SetInit(void)
{
	PS2_ShortPoll();
	PS2_ShortPoll();
	PS2_ShortPoll();
	PS2_EnterConfing();			//進入配置模式
	PS2_TurnOnAnalogMode();	//“紅綠燈”配置模式,並選擇是否保存
	//PS2_VibrationMode();	//開啟震動模式
	PS2_ExitConfing();			//完成並保存配置
}
  • 主函數里要寫在PS_Init( )之後

void PS2_TurnOnAnalogMode(void)

設置發送模式

void PS2_TurnOnAnalogMode(void)
{
	CS_L;
	PS2_Cmd(0x01);  //設置成0x01為紅燈模式,0x00為綠燈模式
	PS2_Cmd(0x44);  
	PS2_Cmd(0X00);
	PS2_Cmd(0x01); 	
	PS2_Cmd(0x03); 	//Ox03鎖存設置,即不可通過按鍵“MODE”設置模式。
					//0xEE不鎖存軟體設置,可通過按鍵“MODE”設置模式。
	PS2_Cmd(0X00);
	PS2_Cmd(0X00);
	PS2_Cmd(0X00);
	PS2_Cmd(0X00);
	CS_H;
	delay_us(16);
}


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 摘要:常用於消除雜訊的圖像平滑方法包括三種線性濾波(均值濾波、方框濾波、高斯濾波)和兩種非線性濾波(中值濾波、雙邊濾波),本文將詳細講解兩種非線性濾波方法。 本文分享自華為雲社區《[Python從零到壹] 五十六.圖像增強及運算篇之圖像平滑(中值濾波、雙邊濾波)》,作者:eastmount。 常用於 ...
  • 前言 本文給大家分享的是如何通過利用Python製作桌面寵物,廢話不多直接開整~ 開發工具 Python版本: 3.6 相關模塊: random模塊 os模塊 cfg模塊 sys模塊 PyQt5模塊 環境搭建 安裝Python並添加到環境變數,pip安裝需要的相關模塊即可。 文中圖片素材實戰教程,評 ...
  • 今天分享自己在項目中用到的一個小技巧,就是使用 EntityFrameworkCore 時我們在通過代碼去 Update-Database 生成資料庫時如何自動將代碼模型上的註釋和欄位上的註釋攜帶到資料庫中,方便後續在資料庫直接查看各個表和各個欄位的含義。 實現效果如下: 可以看到我們每張表都有明確 ...
  • 當我們在使用MediaElement控制項播放HDR視頻時會遇到顏色發灰、泛黃的情況,難道是因為控制項做的有問題? 其實並不是程式問題,只是我們普通的應用程式工作在8bit色深的環境中,而HDR色深為10bit起步。 HDR視頻色彩空間為BT.2020 10bit色深。 SDR視頻(一般視頻)色彩空間基 ...
  • 大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是恩智浦MCX系列MCU的新品MCXN947。 自 2015 年恩智浦和飛思卡爾合併成新恩智浦之後,關於它們各自的 Arm Cortex-M 內核通用微控制器代表作系列 LPC 和 Kinetis 接下來怎麼發展一直沒有定論(兩個系列都在 ...
  • 文件系統是一種存儲和組織電腦數據的方法,它使得對電腦的訪問和查找變得容易,文件系統使用文件和樹形目錄的抽象邏輯概念代替了硬碟和光碟等物理設備使用數據塊的概念,用戶使用文件系統來保存數據不必關心數據實際保存在硬碟(或者光碟)的地址為多少的數據塊上,只需要記住這個文件所屬目錄和文件名。在寫入新數據之... ...
  • 這是我準備新開的學習記錄系列之一 今天寫一個插件的介紹吧—— Helium —— 視頻懸浮插件 youtube/b站/本地視頻都可以導入 主要功能就是你在看視頻時,要記一些學習筆記,隨著不斷往下寫,視頻會出現被擋住的情況。 這個時候Helium就能幫上大忙!首先點擊右上角三個點點,打開插件市場下載H ...
  • 個人名片: 對人間的熱愛與歌頌,可抵歲月冗長:sun_with_face: Github👨🏻‍💻:念舒_C.ying CSDN主頁✏️:念舒_C.ying 個人博客:earth_asia: :念舒_C.ying Web伺服器也稱為WWW(World Wide Web)伺服器,主要功能是提供網頁 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...