PS2 sử dụng giao thức truyền thông SPI.
- Mã nguồn và file tham khảo có tại: https://github.com/Sound-Sleep/PS2_Based_On_STM32
giao diện thu
- DI: Handle -> Host, cạnh xuống của đồng hồ truyền tín hiệu và việc đọc tín hiệu được hoàn thành trong quá trình thay đổi đồng hồ từ cao xuống thấp.
- DO: Máy chủ->tay cầm, được truyền đồng bộ trên cạnh xuống của đồng hồ
- cổng trống
- GND
- VDD: 3~5V
- CS: Mức thấp được chọn
- CLK
- cổng trống
- ACK: thường không được sử dụng
tần số đồng hồ
250Khz ~ 4us 。
Nếu dữ liệu không ổn định, tần số có thể được tăng lên một cách thích hợp.
quá trình giao tiếp
- Kéo dòng CS xuống thấp và ra lệnh "0x01"
- Tay cầm sẽ trả lời với ID "0x41=chế độ đèn xanh, 0x73=chế độ đèn đỏ"
- Khi bộ điều khiển gửi ID, bộ vi điều khiển sẽ gửi 0x42 để yêu cầu dữ liệu.
- Tay cầm gửi 0x5A để báo cho bộ vi điều khiển biết "dữ liệu đang đến"
Sau đây là bảng so sánh ý nghĩa dữ liệu, trong đó nhàn rỗi có nghĩa là nhàn rỗi.
Phân tích trình tự 3~8.
- Nó là 0 khi nhấn nút và 1 khi không nhấn.
Chế độ đèn đỏ và chế độ đèn xanh
- Chế độ đèn đỏ: Cần điều khiển bên trái và bên phải gửi các giá trị tương tự, trong khoảng 0x00 ~ OxFF và các giá trị khóa L3 và R3 được nhấn bởi cần điều khiển là hợp lệ
Mã số = 0x73
- Chế độ đèn xanh: Các giá trị analog của cần điều khiển bên trái và bên phải không hợp lệ. Khi được đẩy đến giới hạn, LÊN, PHẢI, XUỐNG, TRÁI, △, 〇, X, □ được gửi tương ứng.
Nút L3 và R3 không hợp lệ
Mã số = 0x41
Hướng dẫn kết nối
- Bộ thu và vi điều khiển dùng chung một nguồn điện
- ghép nối tự động
- Khi không ghép nối, đèn hai bên sẽ nhấp nháy liên tục.
- Nếu đèn vẫn sáng nghĩa là ghép nối thành công.
- Nếu không tìm thấy máy thu trong một khoảng thời gian nhất định, tay cầm sẽ chuyển sang chế độ chờ.
- Ở chế độ chờ, đèn trên tay cầm sẽ tắt và bạn có thể đánh thức tay cầm bằng cách nhấn phím "BẮT ĐẦU".
- Nhấn nút "MODE" ("ANALOG") để chọn chế độ đèn đỏ và chế độ đèn xanh
Giải thích chi tiết một số chức năng của pstwo.c
lệnh PS2_Init(void)
Khởi tạo giao diện GPIO.
- Cấu hình giao diện
- AT->PB12
- LÀM->PB13
- CS->PB14
- CLK->PB15
void PS2_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; //Bật đồng hồ PORTB RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //Định cấu hình PB13 PB14 PB15 làm đầu ra kéo đẩy phổ quát với tốc độ 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_50 MHz; //Định cấu hình PB12 cho chế độ đầu vào kéo xuống GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPD; GPIO_InitStruct.GPIO_Pin=GPIO_Pin_12;
lệnh PS2_Cmd(u8 CMD)
Gửi dữ liệu tới PS2 và nhận dữ liệu từ PS2 cùng một lúc.
- Các tập tin tiêu đề liên quan
#define DI PBin(12) // Đầu vào PB12 #define DO_H PBout(13)=1 // Bit lệnh cao #define DO_L PBout(13)=0 // Bit lệnh thấp #define CLK_H PBout(15)=1 // Đồng hồ được kéo lên cao #define CLK_L PBout(15)=0 //Đồng hồ được kéo xuống thấp
- Các biến toàn cầu liên quan
// Mảng lưu trữ dữ liệu u8 Data[9]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
void PS2_Cmd(u8 CMD) {volatile u16 ref=0x01; //Đặt lại dữ liệu Data[1] = 0; for(ref=0x01;ref<0x0100;ref<<=1) { //Phát hiện xem có lệnh nào không được gửi đi, nếu có lệnh, hãy kéo mức cao if(ref&CMD) DO_H; // Đầu tiên kéo mức đồng hồ lên cao, sau đó hạ thấp xuống rồi lại kéo lên cao, từ đó gửi và nhận dữ liệu đồng bộ CLK_TIME; CLK_L; bit if( DI) Data[1] = ref|Data[1] } // Trì hoãn một khoảng thời gian sau khi gửi dữ liệu 8 bit delay_us(16 }
- ref thay đổi từ 0x00000001 (8bit) thành 0x10000000 (8bit), mô phỏng giao tiếp nối tiếp bắt đầu từ bit thấp
- Mỗi khi mức đồng hồ xuất hiện cạnh giảm, DO_H và DO_L sẽ gửi một bit dữ liệu cùng một lúc.
không có PS2_ReadData(không có)
Đọc dữ liệu điều khiển.
- Các tập tin tiêu đề liên quan
#define DI PBin(12) // Đầu vào PB12 #define DO_H PBout(13)=1 // Bit lệnh cao #define DO_L PBout(13)=0 // Bit lệnh thấp #define CS_H PBout(14)=1 // CS được kéo lên cao#define CS_L PBout(14)=0 //CS được kéo xuống mức thấp#define CLK_H PBout(15)=1 //Đồng hồ được kéo lên cao#define CLK_L PBout(15)=0 //Đồng hồ được kéo xuống mức thấp
- Các biến toàn cầu liên quan
//Mảng lưu trữ dữ liệu u8 Data[9]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; //Dùng để lưu trữ hai lệnh, đó là lệnh bắt đầu và lệnh yêu cầu dữ liệu u8 Comd [2]={0x01,0x42};
void PS2_ReadData(void) { biến động u8 byte=0; biến động u16 ref=0x01; // Kéo dòng chọn chip xuống mức thấp để chọn bộ thu CS_L; // Gửi lệnh yêu cầu và lệnh yêu cầu dữ liệu PS2_Cmd(Comd[0]); (Comd[1]); //Đọc bảy vị trí cuối cùng của mảng Dữ liệu theo thứ tự for(byte=2;byte<9;byte++) { // Ghi dữ liệu vào bảy vị trí cuối cùng của Dữ liệu for(ref=0x01;ref<0x100;ref<<=1) { CLK_H; CLK_L; DELAY_TIME; [byte]; } // Trì hoãn trong một khoảng thời gian sau khi mỗi dữ liệu 8 bit được gửi delay_us(16); } // Kéo chip chọn mức cao đến cuối giao tiếp CS_H }
- Dữ liệu[1] được sử dụng để lưu trữ dữ liệu tín hiệu được DI trả về mỗi khi hàm PS2_Cmd được thực thi.
7 vị trí còn lại của Data[2]~Data[9] được sử dụng để lưu trữ dữ liệu hợp lệ cần được trả về vi điều khiển để xử lý.
- Nếu không có thao tác nào được thực hiện thì mỗi 7 bit cuối cùng của Dữ liệu sẽ được ghi bằng 1
u8 PS2_RedLight(void)
Xác định xem đó có phải là chế độ đèn đỏ hay không. Return0 có nghĩa là chế độ đèn đỏ là "0x73" và ID của đèn xanh là "0x41".
- Các tập tin tiêu đề liên quan
#define CS_H PBout(14)=1 //CS được kéo lên cao #define CS_L PBout(14)=0 //CS được kéo xuống mức thấp
- Các biến toàn cầu liên quan
//Mảng lưu trữ dữ liệu u8 Data[9]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; //Dùng để lưu trữ hai lệnh, đó là lệnh bắt đầu và lệnh yêu cầu dữ liệu u8 Comd [2]={0x01,0x42};
u8 PS2_RedLight(void) { CS_L; PS2_Cmd(Comd[0]); PS2_Cmd(Comd[1]); // Xác định xem đó có phải là ID của chế độ đèn đỏ hay không if( Data[1] == 0X73) return 0 ; ngược lại trả về 1 }
- Trong khi gửi comd[2], là 0x42, DI sẽ sử dụng 8 chu kỳ để trả về từng bit của ID cho Dữ liệu[1]
- Data[1] = 0x73, bằng ID của chế độ đèn đỏ thì trả về 0, ngược lại trả về 1
lệnh PS2_ClearData() vô hiệu hóa
Đặt lại tất cả các bit của mảng Dữ liệu.
void PS2_ClearData() { u8 a; cho(a=0;a<9;a++) Dữ liệu[a]=0x00; }
u8 PS2_DataKey()
Trả về giá trị khóa tương ứng của nút. Giá trị khóa được xác định bởi macro của tên nút là 0 khi nút được nhấn và 1 khi không nhấn nút.
- Các biến toàn cầu liên quan
//Dùng để lưu trữ các giá trị key u16 Handkey; //Mảng lưu trữ dữ liệu u8 Data[9]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; , 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};
- Khai báo tệp tiêu đề liên quan
//Chỉnh sửa 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_VUÔNG 16
u8 PS2_DataKey() { u8 index; PS2_ClearData(); PS2_ReadData(); //Tích hợp các bit tương ứng với tất cả các khóa vào dữ liệu 16-bit Handkey=(Data[4]<<8)|Data[3]; chỉ mục =0;chỉ mục<16;chỉ mục++) { //Truyền dữ liệu 16 bit này và trả về giá trị của phím được nhấn. Giá trị của khóa được xác định bởi macro if((Handkey&(1<<(MASK[index]-1)))==0) return chỉ số+1 } trả về 0 }
- Logic duyệt Handkey và trả về giá trị key tương ứng với key như sau:
- Trước hết, chúng ta biết rằng khi nhấn nút thì 0 sẽ được ghi vào bit dữ liệu tương ứng, còn nếu không nhấn thì 1 sẽ được ghi.
- Chúng tôi muốn phát hiện nơi 0 được viết
- Mọi số &=0 sẽ bị xóa thành 0
- Do đó, bạn có thể sử dụng tên 1&key để tương ứng với bit trong Handkey và xác định xem kết quả có bằng 0 hay không, từ đó xác định được phím đó có được nhấn hay không.
- Vì vậy hãy di chuyển 1 sang trái để căn chỉnh với bit tương ứng của tên khóa trong Handkey và thực hiện thao tác &
- Vì các bit khác đều là 0 sau 1 được dịch sang trái nên các bit khác đều là 0 sau &, do đó toàn bộ số có bằng 0 hay không phụ thuộc vào bit tương ứng của tên nút trong Handkey có phải là 0 hay không.
- Bước tiếp theo là đặt mức độ dịch chuyển trái là 1 là (Mask[index] - 1)
Trả về giá trị trạng thái của cần điều khiển.
u8 PS2_AnologData(nút u8) { trả về Dữ liệu[nút]; }
-
Dữ liệu được đọc theo các giá trị nút khác nhau:
- 5: Hướng X của cần điều khiển bên phải
- 6: Hướng Y của cần điều khiển bên phải
- 7: Hướng X của cần điều khiển bên trái
- 8: Hướng Y của cần điều khiển bên trái
-
Giá trị tương tự của phím điều khiển được trả về nằm trong khoảng từ 0 đến 255.
-
Giá trị ngoài cùng bên trái theo hướng x là 0 và giá trị ngoài cùng bên phải là 255.
-
Giá trị trên cùng theo hướng y là 0 và giá trị ngoài cùng bên phải là 255.
void PS2_SetInit(void)
Khởi tạo cấu hình bộ điều khiển.
void PS2_SetInit(void) { PS2_ShortPoll(); PS2_ShortPoll(); PS2_ShortPoll(); PS2_EnterConfing(); //Nhập chế độ cấu hình PS2_TurnOnAnalogMode(); // Chế độ cấu hình "Đèn giao thông" và chọn có lưu hay không //Bật chế độ rung PS2_ExitConfing(); //Hoàn thành và lưu cấu hình }
- Hàm chính phải được viết sau PS_Init()
vô hiệu PS2_TurnOnAnalogMode(vô hiệu)
Đặt chế độ gửi.
void PS2_TurnOnAnalogMode(void) { CS_L; PS2_Cmd(0x01); // Đặt thành 0x01 cho chế độ đèn đỏ và 0x00 cho chế độ đèn xanh PS2_Cmd(0x44); // Cài đặt chốt Ox03, nghĩa là không thể đặt chế độ bằng cách nhấn nút "MODE". // 0xEE không chốt cài đặt phần mềm và có thể đặt chế độ bằng cách nhấn nút "MODE". PS2_Cmd(0X00); PS2_Cmd(0X00); PS2_Cmd(0X00); PS2_Cmd(0X00);
- thẩm quyền giải quyết:
- hướng dẫn giao tiếp giải mã ps2 V1.5.pdf
- Tìm hiểu giao tiếp giữa bộ điều khiển từ xa PS2 và vi điều khiển stm32 (kết hợp với hướng dẫn và quy trình của Balance Car Home)_Blog của Catherine Pro-CSDN Blog
Cuối cùng, bài viết về giao tiếp không dây giữa STM32 và PS2 cũng như phần giới thiệu các chức năng liên quan sẽ kết thúc tại đây. Nếu bạn muốn biết thêm về giao tiếp không dây giữa STM32 và PS2 cũng như phần giới thiệu các chức năng liên quan, vui lòng tìm kiếm các bài viết của CFSDN hoặc tiếp tục duyệt. Bài viết liên quan, hy vọng bạn sẽ ủng hộ blog của tôi trong tương lai! .
Tôi là một lập trình viên xuất sắc, rất giỏi!