一、项目介绍
串行通信是一种常见的数据传输方式,允许将数据以比特流的形式在发送端和接收端之间传输。当前实现基于STC89C52单片机的串行通信发射机,通过红外发射管和接收头实现自定义协议的数据无线传输。
二、系统设计
2.1 单片机选择
在本设计中,选择了STC89C52作为主控芯片。单片机具有较高的性能和丰富的外设资源,适合实现串行通信发射机功能。
2.2 矩阵键盘
采用4x4的矩阵键盘,用于接收用户输入的指令。通过扫描矩阵键盘的按键状态,可以获取用户需要发送的数据。
2.3 红外发射管和接收头
选择具有较高发射功率和较长发射距离的红外发射管,并配合红外接收头进行数据传输。当红外接收头检测到红外光时,输出低电平;没有检测到红外光时,输出高电平。
2.4 矩阵键盘扫描
利用矩阵键盘的行列扫描原理,实时检测用户按键状态,并将按键值保存在变量中供后续使用。
2.5 数据转换和红外发送
根据自定义的协议格式,将用户按键值转换为红外控制码。通过IO口驱动红外发射管发送红外控制码。
三、协议的约定
【1】自定义发送协议: 自定义发送协议需要约定以下内容:
- 帧格式:确定每一帧数据的起始标志、数据长度和校验信息等。常见的帧格式包括起始位、数据位、停止位和校验位。
- 数据编码:确定将要发送的数据转换为比特流进行传输的方式。常见的编码方式有Manchester编码和Pulse-Width Modulation(PWM)编码。
- 校验机制:确定是否需要添加校验位,以保证数据传输的准确性和完整性。常见的校验方式有奇偶校验、循环冗余校验(CRC)等。
例如,可以采用以下的帧格式作为示例:
- 帧头:起始位,一个特定的比特用于标识帧的开始。
- 数据字段:包含要发送的数据。
- 校验位:用于检验帧数据的准确性。
- 帧尾:停止位,一个特定的比特用于标识帧的结束。
【2】接收原理: 接收端通过红外接收头实现对发送端发送的红外控制码的接收和解码。接收原理包括以下步骤:
- 红外信号接收:红外接收头接收红外光,并将接收到的光信号转换为电流信号。
- 弱信号放大:对接收到的电流信号进行放大,以便进行后续处理。
- 数据解码:根据约定的帧格式和编码方式,将接收到的比特流解码为原始数据。
- 校验校准:对接收到的数据进行校验和校准,确保数据的准确性。
下面是发送端和接收端的代码:
发送端代码:
#include <reg52.h>
// 定义红外发射管IO口
#define IR_LED P1
// 发送一帧数据
void sendFrame(unsigned char data) {
unsigned char i;
// 发送起始位
IR_LED = 0;
DelayUs(300);
for (i = 0; i < 8; i++) {
// 发送数据位
IR_LED = data & 0x01;
DelayUs(300);
data >>= 1;
}
// 发送停止位
IR_LED = 1;
DelayUs(300);
}
// 主函数
void main() {
unsigned char sendData = 0x55; // 要发送的数据
while (1) {
sendFrame(sendData); // 发送一帧数据
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单片机的串行通信发射机和接收机的整体代码,其中包括了4x4矩阵键盘的读取和红外数据传输的功能:
发射机代码:
#include <reg52.h>
#define IR_LED P1
#define KEYBOARD P2
// 发送一帧数据
void sendFrame(unsigned char data) {
unsigned char i;
// 发送起始位
IR_LED = 0;
DelayUs(300);
for (i = 0; i < 8; i++) {
// 发送数据位
IR_LED = data & 0x01;
DelayUs(300);
data >>= 1;
}
// 发送停止位
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); // 发送一帧数据
}
}
}
接收机代码:
#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(); // 接收一帧数据
// 处理接收到的数据
}
}