摘要:第二點與是同時完成的,說明這是全雙工通信。上表中的表示數據線空閑,該數據線無數據傳送。設置震動模式后用來控制右側的小電機,表示關,其他值為開。單片機發送了給手柄,此時手柄會返回給單片機,意味著接收到了請求,即將返回數據。
一、硬件準備:戰艦開發板、PS2手柄接收器、PS2手柄、連接線
二、硬件連接:
PS2手柄接收器有六個引腳,和單片機連接IO口連接,如下圖:
接收器信號 | 單片機IO |
---|---|
GND | GND |
VCC | 3.3V |
DI/DAT | PB12 |
DO/CMD | PB13 |
CS | PB14 |
CLK | PB15 |
三、PS2通信簡介
通訊時序如下,感覺和SPI很像,也是四線
DI與DO是一對同時傳輸的8 bit串行數據,傳輸的時候需要CS為低電平,CLK由高變低。
DO是單片機發送給接收器的信號。
DI是接收器發送給單片機的信號。
第一點:CS在數據輸出或者輸入的時候,都是低電平的,那么我們在數據傳輸的時候先把CS拉高再拉低,然后數據進行傳輸,傳輸完成之后再把CS拉高。
第二點:DI(Data Input)與DO(Data Output)是同時完成的,說明這是全雙工通信。串口是全雙工通信。IIC是半雙工通信。
第三點:在時鐘上降沿的時候,DI和DO的數據有交叉,也就是說數據進行交換(數據只有0和1),這個時候我們是不能夠讀和寫數據的,因為數據還不穩定,我們讀到的數據不準確。在時鐘為下降沿的時候,數據已經穩定了,我們在這個時候開始讀和寫數據。
第四點:由于是從0到7,可以知道有8位數據,并且是從低位到高位進行讀寫。我們可以把數據放到數組中。一個時鐘進行一個數據位(也可以叫做比特位0或1)傳輸。
時鐘頻率250KHZ(4us),數據不穩定可適當增加頻率。
當單片機發送0x01時,接收器會回復它的ID“0X41表示綠燈模式”、“0x73表示紅燈模式”;在手柄發送ID的同時,單片機將發送0X42,手柄會發送0X5A,高速單片機數據來了。
上表中的idle表示數據線空閑,該數據線無數據傳送。
所以Data[0]、Data[1]、Data[2]不能用來存放PS2搖桿的按鍵
Data[3]、Data[4]用來存放按鍵的值
Data[5]、Data[6]、Data[7]、Data[8]用來存放搖桿的模擬量
當有按鍵下,對應位為0,其他位為1
譬如,當SELECT按下,Data[3]=1111 1110B
當L3按下,Data[3]=1111 1101B
當R3按下,Data[3]=1111 1011B
當START按下,Data[3]=1111 0111B
當UP按下,Data[3]=1110 1111B
當RIGHT按下,Data[3]=1101 1111B
當DOWN按下,Data[3]=1011 1111B
當LEFT按下,Data[3]=0111 1111B
手柄有兩個模式,紅燈模式(手柄亮紅燈+綠燈)和綠燈模式(手柄只亮綠燈),可以通過按下MODE按鍵進行切換。
紅燈模式:
1.按鍵L3/R3按下有效
2.推動左右搖桿,根據行程不一樣,可輸出0x00-0xff的模擬量
綠燈模式:
1.按鍵L3/R3按下有效
2.左右搖桿不輸出模擬量,推動到上下左右的極限值,左搖桿實現的效果和UP/DOWN/RIGHT/LEFT一樣,右搖桿實現的效果和和△/X/□/○一樣。
設置震動模式后:
WW用來控制右側的小電機,0x00表示關,其他值為開。
YY用來控制左側的大電機,0x40-0xff表示電機開,值越大,震感越強烈;其他值表示電機關。
四、代碼分析
1.配置IO口,將PB12設置為下拉輸入;PB13/14/15設置為推挽輸出
void PS2_Init(void){ GPIO_InitTypeDef GPIO_InitStructure; //輸入 DI->PB12 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //使能PORTB時鐘 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //設置成上拉、下拉、浮空輸入皆可 GPIO_Init(GPIOB, &GPIO_InitStructure); //輸出 DO->PB13 CS->PB14 CLK->PB15 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //設置成推挽輸出 GPIO_Init(GPIOB, &GPIO_InitStructure);}
2.定義3個數組
u8 Comd[2]={0x01,0x42}; //開始命令。請求數據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};
3.給PB12/PB13/PB14/PB15這4個IO的狀態均進行宏定義
#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 //時鐘拉低
4.單片機向手柄發送命令
//向手柄發送命令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_us(50); CLK_L; delay_us(50); CLK_H; if(DI) Data[1] = ref|Data[1]; }}//假如單片機給接收器發送0x01=0000 0001B,接收器接受到0x01后給單片機發送了0x41=0100 0001B,ref=0x01=0000 0001B;Data[1] = 0;CMD=0000 0001BDI=0100 0001B/從低位到高位執行8次循環第一次循環:ref=0000 0001Bif(ref&CMD)為真,輸出PB13=1if(DI)為真,輸出Data[1] = ref|Data[1]=0000 0001|0000 0000=0000 0001第二次循環:ref=0000 0010Bif(ref&CMD)為假,輸出PB13=0if(DI)為假,不執行操作第三次循環:ref=0000 0100Bif(ref&CMD)為假,輸出PB13=0if(DI)為假,不執行操作第四次循環:ref=0000 1000Bif(ref&CMD)為假,輸出PB13=0if(DI)為假,不執行操作第五次循環:ref=0001 0000Bif(ref&CMD)為假,輸出PB13=0if(DI)為假,不執行操作第六次循環:ref=0010 0000Bif(ref&CMD)為假,輸出PB13=0if(DI)為假,不執行操作第七次循環:ref=0100 0000Bif(ref&CMD)為假,輸出PB13=0if(DI)為真,輸出Data[1] = ref|Data[1]=0100 0000|0000 0001=0100 0001第八次循環:ref=1000 0000Bif(ref&CMD)為假,輸出PB13=0if(DI)為假,不執行操作/所以最后的結果就是單片機將0x01按位發送了出去接收機發送的數據0x41保存到了Data[1]里面
a…volatile修飾符可以保證ref每次開始都是0x01即0000 0001B
b.ref=0x01;ref<0x0100;ref<<=1
理解這句首先需要將十六進制改為二進制,即ref=0000 0001B;ref<0000 0001 0000 000B;ref<<=1
即將ref=0x01
每次左移一位,循環八次。
c.ref&CMD
即可以通過與運算循環八次,將CMD 這個八位二進制的數按位發送出去。
d.CLK電平進行高-低-高可以產生一個周期,同時產生一個下降沿。在這個過程中,DO將信號從單片機(發送)給接收器(接收),DI將信號從接收器(發送)給單片機(接收)
e.單片機接收到的數據被保存在了Data[1]里面
5.判斷手柄是紅燈模式還是綠燈模式,通過單片機給手柄發送0x01 0x42后,手柄返回的值來判斷,如果返回的是0X41表示"綠燈模式"、0x73表示"紅燈模式"
//判斷是否為紅燈模式//返回值;0,紅燈模式//返回值;1,綠燈模式u8 PS2_RedLight(void){ CS_L; PS2_Cmd(Comd[0]); //開始命令0x01 PS2_Cmd(Comd[1]); //請求數據0x42 CS_H; if( Data[1] == 0X73) return 0 ; else return 1;}
5.單片機接收手柄數據
//讀取手柄數據void PS2_ReadData(void){ volatile u8 byte=0; volatile u16 ref=0x01; CS_L; PS2_Cmd(Comd[0]); //開始命令0x01 PS2_Cmd(Comd[1]); //請求數據0x42 for(byte=2;byte<9;byte++) //開始接受數據 { for(ref=0x01;ref<0x100;ref<<=1) { CLK_H; CLK_L; delay_us(50); CLK_H; if(DI) Data[byte] = ref|Data[byte]; } delay_us(50); } CS_H; }
a.數據傳輸必須在CS拉低期間進行,數據傳輸完成后,還要將CS拉回高電平,以便下一次的通訊。
b.單片機發送了0x01 0x42給手柄,此時手柄會返回0x5A給單片機,意味著接收到了請求,即將返回數據。所以Data[2]保存的就是手柄返回的0x5A。后面的Data[3]-Data[8]返回的都是按鍵和搖桿的狀態信息。
6.檢測按鍵狀態
//對讀出來的PS2的數據進行處理 只處理了按鍵部分 默認數據是紅燈模式 只有一個按鍵按下時//按下為0, 未按下為1u8 PS2_DataKey(){ u8 index; PS2_ClearData(); PS2_ReadData(); Handkey=(Data[4]<<8)|Data[3]; //這是16個按鍵 按下為0, 未按下為1 for(index=0;index<16;index++) { if((Handkey&(1<<(MASK[index]-1)))==0) return index+1; } return 0; //沒有任何按鍵按下}
a.上圖有給手柄按鍵標號,一共16個按鍵,包括兩個搖桿,不包括MODE鍵。16個按鍵剛好是兩個八位二進制數。所以用Data[3]和Data[4]表示所有的按鍵的狀態。
b.u16 Handkey
,通過這個變量定義可以看出來Handkey是一個16位的二進制數,Handkey=(Data[4]<<8)|Data[3]
表示的是Handkey這個變量的高八位是Data[4],低八位是Data[3]
假如我SELECT按下了,那么Data[3]=1111 1110B
如果L2按下了,那么Data[4]=1111 1110B
所以Handkey表示的就是1111 1110 1111 1110B
c.for(index=0;index<16;index++)
因為有16個按鍵,所以進行16次循環,以此判斷是被按下的是哪個鍵。
if((Handkey&(1<<(MASK[index]-1)))==0)
這個函數由內到外分析0<=index<=15
,所以1<=MASK[index]<=16
,所以0<=MASK[index]-1)<=15
,然后將1換算成16位的二進制數為:0000 0000 0000 0001B
,所以1<<(MASK[index]-1)
就是每次左移一位,循環15次。然后和Handkey進行與運算,如果為0,則說明按鍵被按下。最后index+1
是因為index是從0開始算的,而按鍵是從1開始計算的,所以最后返回的值需要+1。但這個函數只能判斷單個按鍵按下。如果有多個按鍵按下,只能檢測按鍵數較小的那個值,譬如方向上(5)和START(4)同時被按下,則返回值就是4。
#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 16u16 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};
7.檢測搖桿的狀態
#define PSS_RX 5 #define PSS_RY 6#define PSS_LX 7#define PSS_LY 8//得到一個搖桿的模擬量 范圍0~256u8 PS2_AnologData(u8 button){ return Data[button];}
a.四個搖桿反饋的是模擬量,范圍在0x00~0xFF,轉換成十進制即為0-255。搖桿的數值存放在Data[5]、Data[6]、Data[7]、Data[8]中
b.注意只有紅燈模式下搖桿才反饋模擬量,綠燈模式下搖桿不反饋模擬量。
8.其他函數
8.1清除數據緩沖
//清除數據緩沖區void PS2_ClearData(){ u8 a; for(a=0;a<9;a++) Data[a]=0x00;}
8.2手柄震動函數
/******************************************************motor1:右側小震動電機 0x00關,其他開motor2:左側大震動電機 0x40~0xFF 電機開,值越大 震動越大******************************************************/void PS2_Vibration(u8 motor1, u8 motor2){ CS_L; delay_us(16); PS2_Cmd(0x01); //開始命令 PS2_Cmd(0x42); //請求數據 PS2_Cmd(0X00); PS2_Cmd(motor1); PS2_Cmd(motor2); PS2_Cmd(0X00); PS2_Cmd(0X00); PS2_Cmd(0X00); PS2_Cmd(0X00); CS_H; delay_us(16); }
8.3發送模式設置
a.第八行PS2_Cmd(0x01)
則為紅燈模式;PS2_Cmd(0x00)
則為綠燈模式;
b.第九行PS2_Cmd(0x03)
則只可以通過第八行的指令進行紅綠燈模式切換;PS2_Cmd(0xEE)
則可以通過按MODE進行紅綠燈模式切換。
//發送模式設置void PS2_TurnOnAnalogMode(void){ CS_L; PS2_Cmd(0x01); PS2_Cmd(0x44); PS2_Cmd(0X00); PS2_Cmd(0x01); //analog=0x01;digital=0x00 軟件設置發送模式 PS2_Cmd(0xEE); //Ox03鎖存設置,即不可通過按鍵“MODE”設置模式。 //0xEE不鎖存軟件設置,可通過按鍵“MODE”設置模式。 PS2_Cmd(0X00); PS2_Cmd(0X00); PS2_Cmd(0X00); PS2_Cmd(0X00); CS_H; delay_us(16);}
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/123289.html
摘要:基于開發的軟件包導師汪禮超學員崔林威摘要騰訊物聯網操作系統是騰訊面向物聯網領域開發的實時操作系統,具有低功耗,低資源占用,模塊化,可裁剪等特性。圖中斷函數處理進行生成工程配置,按如下界面進行配置,最后點擊,并點擊。 ...
摘要:力矩控制模式電機在運行過程的電流,始終等于給定的值。設定電流為零,彈簧不被拉伸。比如機械臂從點運動到點,并限制揮舞過程中的最大速度和最大力矩。 目錄 說明一、電機...
摘要:使用庫讀寫環境光照度傳感器本文將教大家如何快速使用庫讀取光照度數據。五實驗樣機測試展示通過之前配置好的面板,通過涂鴉智能進行配網實時采集光照度傳感器的數據。 使用STM32 HAL庫讀寫環境光照度傳感器(BH1750) 本文將教大家如何快速使用STM32HAL庫讀取光照度數據。 實現功能:通...
摘要:基于的移植教程可以看這里二介紹是一種用于嵌入式應用的圖形支持軟件。適用于使用任何控制和的任何尺寸的物理和虛擬顯示。一個層,稱作驅動程序,包含了對的全部訪問。并在主函數里加入下面的代碼,測試移植是否成功。 一、環境介紹 keil:? ? 5.25 MCU:? STM32F103ZET6 UCG...
摘要:添加設備名和鑒權信息。記錄如下數據二引腳連接和接電源接地和連接至配置的串口三代碼編寫串口配置單片機需配置兩個串口,串口打印至串口助手,顯示連接狀態。串口用來發送信息至串口配置代碼如下系列配置和系列配置不同點在于口上拉和推挽配置略有不同。 ...
閱讀 3642·2021-11-15 11:37
閱讀 2310·2021-09-24 10:39
閱讀 2423·2021-07-25 21:37
閱讀 1404·2019-08-30 15:56
閱讀 2574·2019-08-30 15:55
閱讀 943·2019-08-30 15:54
閱讀 2117·2019-08-30 14:21
閱讀 846·2019-08-30 11:24