一、項目介紹
串行通信是一種常見的數據傳輸方式,允許將數據以比特流的形式在發(fā)送端和接收端之間傳輸。當前實現基于STC89C52單片機的串行通信發(fā)射機,通過紅外發(fā)射管和接收頭實現自定義協(xié)議的數據無線傳輸。
二、系統(tǒng)設計
2.1 單片機選擇
在本設計中,選擇了STC89C52作為主控芯片。單片機具有較高的性能和豐富的外設資源,適合實現串行通信發(fā)射機功能。
2.2 矩陣鍵盤
采用4x4的矩陣鍵盤,用于接收用戶輸入的指令。通過掃描矩陣鍵盤的按鍵狀態(tài),可以獲取用戶需要發(fā)送的數據。
2.3 紅外發(fā)射管和接收頭
選擇具有較高發(fā)射功率和較長發(fā)射距離的紅外發(fā)射管,并配合紅外接收頭進行數據傳輸。當紅外接收頭檢測到紅外光時,輸出低電平;沒有檢測到紅外光時,輸出高電平。
2.4 矩陣鍵盤掃描
利用矩陣鍵盤的行列掃描原理,實時檢測用戶按鍵狀態(tài),并將按鍵值保存在變量中供后續(xù)使用。
2.5 數據轉換和紅外發(fā)送
根據自定義的協(xié)議格式,將用戶按鍵值轉換為紅外控制碼。通過IO口驅動紅外發(fā)射管發(fā)送紅外控制碼。
三、協(xié)議的約定
【1】自定義發(fā)送協(xié)議: 自定義發(fā)送協(xié)議需要約定以下內容:
- 幀格式:確定每一幀數據的起始標志、數據長度和校驗信息等。常見的幀格式包括起始位、數據位、停止位和校驗位。
- 數據編碼:確定將要發(fā)送的數據轉換為比特流進行傳輸的方式。常見的編碼方式有Manchester編碼和Pulse-Width Modulation(PWM)編碼。
- 校驗機制:確定是否需要添加校驗位,以保證數據傳輸的準確性和完整性。常見的校驗方式有奇偶校驗、循環(huán)冗余校驗(CRC)等。
例如,可以采用以下的幀格式作為示例:
- 幀頭:起始位,一個特定的比特用于標識幀的開始。
- 數據字段:包含要發(fā)送的數據。
- 校驗位:用于檢驗幀數據的準確性。
- 幀尾:停止位,一個特定的比特用于標識幀的結束。
【2】接收原理: 接收端通過紅外接收頭實現對發(fā)送端發(fā)送的紅外控制碼的接收和解碼。接收原理包括以下步驟:
- 紅外信號接收:紅外接收頭接收紅外光,并將接收到的光信號轉換為電流信號。
- 弱信號放大:對接收到的電流信號進行放大,以便進行后續(xù)處理。
- 數據解碼:根據約定的幀格式和編碼方式,將接收到的比特流解碼為原始數據。
- 校驗校準:對接收到的數據進行校驗和校準,確保數據的準確性。
下面是發(fā)送端和接收端的代碼:
發(fā)送端代碼:
#include <reg52.h>
// 定義紅外發(fā)射管IO口
#define IR_LED P1
// 發(fā)送一幀數據
void sendFrame(unsigned char data) {
unsigned char i;
// 發(fā)送起始位
IR_LED = 0;
DelayUs(300);
for (i = 0; i < 8; i++) {
// 發(fā)送數據位
IR_LED = data & 0x01;
DelayUs(300);
data >>= 1;
}
// 發(fā)送停止位
IR_LED = 1;
DelayUs(300);
}
// 主函數
void main() {
unsigned char sendData = 0x55; // 要發(fā)送的數據
while (1) {
sendFrame(sendData); // 發(fā)送一幀數據
DelayMs(1000);
}
}
接收端代碼:
#include <reg52.h>
// 定義紅外接收頭IO口
#define IR_RECV P2
// 接收一幀數據
unsigned char receiveFrame() {
unsigned char i;
unsigned char data = 0;
while (IR_RECV); // 等待起始位
DelayUs(150);
for (i = 0; i < 8; i++) {
DelayUs(300);
data >>= 1;
if (IR_RECV) {
data |= 0x80;
}
}
return data;
}
// 主函數
void main() {
unsigned char receivedData;
while (1) {
receivedData = receiveFrame(); // 接收一幀數據
// 處理接收到的數據
}
}
四、代碼實現
下面是基于STC89C52單片機的串行通信發(fā)射機和接收機的整體代碼,其中包括了4x4矩陣鍵盤的讀取和紅外數據傳輸的功能:
發(fā)射機代碼:
#include <reg52.h>
#define IR_LED P1
#define KEYBOARD P2
// 發(fā)送一幀數據
void sendFrame(unsigned char data) {
unsigned char i;
// 發(fā)送起始位
IR_LED = 0;
DelayUs(300);
for (i = 0; i < 8; i++) {
// 發(fā)送數據位
IR_LED = data & 0x01;
DelayUs(300);
data >>= 1;
}
// 發(fā)送停止位
IR_LED = 1;
DelayUs(300);
}
// 讀取矩陣鍵盤
unsigned char readKeyboard() {
unsigned char row, col, keyVal;
KEYBOARD = 0xF0; // 設置行為高電平,列為低電平
if (KEYBOARD != 0xF0) { // 檢測是否有按鍵按下
keyVal = KEYBOARD;
switch (keyVal) {
case 0xE0: row = 0; break;
case 0xD0: row = 1; break;
case 0xB0: row = 2; break;
case 0x70: row = 3; break;
default: return 0xFF;
}
KEYBOARD = 0x0F; // 設置列為高電平,行為低電平
keyVal = KEYBOARD;
switch (keyVal) {
case 0x0E: col = 0; break;
case 0x0D: col = 1; break;
case 0x0B: col = 2; break;
case 0x07: col = 3; break;
default: return 0xFF;
}
// 根據行列計算鍵值
return 4 * row + col + 1;
}
return 0xFF; // 返回無效鍵值
}
// 主函數
void main() {
unsigned char sendData;
while (1) {
sendData = readKeyboard(); // 讀取鍵盤數據
if (sendData != 0xFF) {
sendFrame(sendData); // 發(fā)送一幀數據
}
}
}
接收機代碼:
#include <reg52.h>
#define IR_RECV P3
// 接收一幀數據
unsigned char receiveFrame() {
unsigned char i;
unsigned char data = 0;
while (IR_RECV); // 等待起始位
DelayUs(150);
for (i = 0; i < 8; i++) {
DelayUs(300);
data >>= 1;
if (IR_RECV) {
data |= 0x80;
}
}
return data;
}
// 主函數
void main() {
unsigned char receivedData;
while (1) {
receivedData = receiveFrame(); // 接收一幀數據
// 處理接收到的數據
}
}