⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 test.c

📁 51单片机315模块双向通信代码,可直接使用.用于完成家电控制之类的小应用.
💻 C
字号:
#include <reg51.h>
#include "string.h"


sbit LED1 = P1^2;
sbit LED2 = P1^4;
sbit LED3 = P1^3;
//sbit LED1 = P3^6;
//sbit LED2 = P3^7;
sbit W_IN = P3^2;
sbit W_OUT = P3^4;

unsigned char w_timer=0;
unsigned int jiffies=0,x=0;
void clock_timer(void) interrupt 1 using 1{
	jiffies++;
	//if(x++==500){x=0;
	w_timer++;//};
}

void clock_init(void){
	jiffies = 0;
	TMOD=0x02;
//	TH0=TL0=0x9b;//12M
//	TH0=TL0=0x7a;//16M
//	TH0=TL0=0x75;//16.59M
//	TH0=TL0=0x72;//17M
//	TH0=TL0=0x37;//24M
	TH0=TL0=0x40;//22M
	EA=1;
	ET0=1;

	TR0=1;
}
void init_serialcomm(void)
{
    SCON  = 0x50;       //SCON: serail mode 1, 8-bit UART, enable ucvr 
    TMOD |= 0x20;       //TMOD: timer 1, mode 2, 8-bit reload 
    PCON |= 0x80;       //SMOD=1; 
    TH1   = 0xFF;       //Baud:4800  fosc=11.0592MHz  :f4
    //IE   |= 0x90;       //Enable Serial Interrupt 
    TR1   = 1;          // timer 1 run
	RI=0;
	TI=1; 
}

void serial_out(unsigned char d){
	while(!TI);
	TI=0;
	SBUF=(d);
}

//等待指定长度的串行数据到达,超时值为每两个字节之间的间隔时间而非等待整个串的时间.
//超时单位为time_out * 100uS
bit wait_serial(unsigned char *p, unsigned char len, unsigned char time_out){
	unsigned int time=jiffies;
	unsigned char n=0;
	do{
		if (RI){
			p[n++]=SBUF;
			RI=0;
			if(n==len)
				return 0;
			time=jiffies;
		}
	}while(jiffies-time < time_out);
	return 1;
}

#define W_LP 6	//低电平/前引导
#define W_HP 12	//高电平
#define W_FS 18	//引导/结束
#define HEAD_CODE 100	//前导码个数

#define DEBUG 0

#define W_BUF_LEN 24
unsigned char w_buf[W_BUF_LEN];

unsigned char w_recv_step=0;
unsigned char w_recv_data;
unsigned char w_recv_last_time;	//上一次翻转的时间
unsigned char w_recv_width;	//脉冲宽度
bit w_recv_status,w_recv_last_status;
bit recv_over = 0;
#define START_RECV() {w_recv_step=0; recv_over=0;}
#define STOP_RECV() {recv_over=1;}
void w_recv(void){
	//正式写程序时,应在调用该函数前就做检测,以节省时间.以下检测应删去.
	if(recv_over)
		return;

	w_recv_status=W_IN;
	LED1=!w_recv_status;

	if (w_recv_last_status == w_recv_status){//信号脚无变化,无信号到达,直接返回,返回前仅处理超时.
		if (w_recv_step == 0)//w_recv_step==0时无需处理超时
			return;
		w_recv_width = w_timer - w_recv_last_time;
		if (w_recv_width > W_FS + W_LP/2){//超时处理
			w_recv_step = 0;
#if DEBUG
			//serial_out(0x53);
			//serial_out(w_recv_width);
#endif
		}
		return;
	}
	w_recv_last_status = w_recv_status;//记下本次电平值
	w_recv_width = w_timer - w_recv_last_time;//如果上一个信号已完成,记录信号宽度,如果当前为第一个信号,该计算结果不使用
	w_recv_last_time = w_timer;//新的信号到达,更新计时器

	if (w_recv_step == 0){//目前所处理的信号为第一个信号,不需作后续处理,因为无上一个信号信息.
		w_recv_step = 1;
		return;
	}

	//step 1: 等待前引导信号到来
	if(w_recv_step == 1){
		if(w_recv_width > W_LP - W_LP/2 && w_recv_width < W_LP + W_LP/2)//前引导脉冲到来,宽度约为8,重复发送,直到发送引导脉冲
			w_recv_step = 2;
		return;
	}

	//等待前引导信号结束及等待引导信号到来
	if(w_recv_step == 2){
		if(w_recv_width > W_LP - W_LP/2 && w_recv_width < W_LP + W_LP/2)//前引导脉冲还未结束,等待
			return;
		if(w_recv_width > W_FS - W_LP/2 && w_recv_width < W_FS + W_LP/2)//引导脉冲到来,宽度约为16
			w_recv_step = 3;
		else{
			w_recv_step = 0;
#if DEBUG
			serial_out(0x54);
			serial_out(w_recv_width);
#endif
		}
		return;
	}

	//读取一字节
	if(w_recv_step > 2 && w_recv_step < 3 + W_BUF_LEN * 8){//step 3 --- step 10: 取回8位数据
		w_recv_data >>= 1;
	
		if(w_recv_width > W_LP - W_LP/2 && w_recv_width < W_LP + W_LP/2)
			w_recv_data &= 0x7f;
		else
		if(w_recv_width > W_HP - W_LP/2 && w_recv_width < W_HP + W_LP/2)
			w_recv_data |= 0x80;
		else{
			w_recv_step = 0;
#if DEBUG
			serial_out(0x56);
			serial_out(w_recv_width);
#endif
		}
		if(((w_recv_step - 3) & 7) == 7)
			w_buf[(w_recv_step - 3) >> 3] = w_recv_data;
	}

	if(w_recv_step == 3 + W_BUF_LEN * 8){//step 11 : 结束
		if(w_recv_width > W_FS - W_LP/2 && w_recv_width < W_FS + W_LP/2)//等待结束脉冲,宽度约为20,未收到该信号则为出错
			recv_over = 1;//serial_out(0xaa);
		else
			serial_out(0x55);
	}

	w_recv_step++;
}

unsigned int w_send_step=0;
unsigned char w_send_data;
unsigned char w_send_last_time;	//上一次翻转的时间
unsigned char w_send_width;	//脉冲宽度
bit send_over=1;
#define START_SEND() {w_send_step=0; send_over=0;}
#define STOP_SEND() {send_over=1;}
void w_send(){
	//正式写程序时,应在调用该函数前就做检测,以节省时间.以下检测应删去.
	if(send_over)
		return;

	if(w_send_step == HEAD_CODE + 2 + W_BUF_LEN * 8)//数据发送完毕
		send_over = 1;

	if (w_send_step == 0){
		w_send_step = 1;
		w_send_width = W_LP;
		W_OUT = 0;
		LED2 = 0;
		return;
	}

	if(w_timer - w_send_last_time < w_send_width)
		return;

	w_send_step++;
	w_send_last_time = w_timer;
	W_OUT = !W_OUT;
	LED2 = !LED2;

	if(w_send_step == HEAD_CODE)
		w_send_width = W_FS;

	if(w_send_step > HEAD_CODE && w_send_step < HEAD_CODE + 1 + W_BUF_LEN * 8){
		if(((w_send_step - (HEAD_CODE + 1)) & 7) == 0)
			w_send_data = w_buf[(w_send_step - (HEAD_CODE + 1)) >> 3];
		if(w_send_data & 1)
			w_send_width = W_HP;
		else
			w_send_width = W_LP;
		w_send_data >>= 1;
	}

	if(w_send_step == HEAD_CODE + 1 + W_BUF_LEN * 8)
		w_send_width = W_FS;

}

sys_init(){
	clock_init();
	init_serialcomm();
}

//pdata unsigned char this_node_id = 0x11;
unsigned char this_node_id = 0x11;

typedef struct pack_head_s{
	unsigned char source_id;
	unsigned char target_id;
	unsigned char package_len;
	unsigned char check_sum;
}package_head_t;
typedef struct package_s{
	package_head_t head;
	unsigned char package_data[1];
}package_t;

//发送函数返回值
#define ERROR_SEND_BUSY 0x10	//前一次发送未完成
#define ERROR_SEND_NULL 0x23		//发送包长为0,错误包

//接收函数返回值
#define ERROR_RECV_EMPTY 0x20	//当前未收到数据
#define ERROR_RECV_CHECKSUM 0x21	//校验和错误
#define ERROR_RECV_NULL 0x22		//收到包长为0,错误包


//由于计算校验合会占用大量时间,因此在调用该函数时接收程序后台部分会受影响,可能造成正在接收的数据报文丢失.
//发送程序后台部分同样会受影响,但由于此时后台发送肯定是空闲,所以忽略影响.
//所以,调用原则是:发送时要确认发送与接收已全部停止
unsigned char send_package(package_t *package){
	unsigned char check_sum;
	unsigned char len;
	unsigned char *p;

	package->head.package_len = W_BUF_LEN;//目前是将数据包长度定死,设包长的目的是为了将来扩展.

	len = package->head.package_len;	//为将来而设.今后发送包长可能在进放该函数前设定.
	if (len == 0)
		return ERROR_SEND_NULL;

	//填写报文头
	package->head.source_id = this_node_id;

	//计算校验和,计算完后,报文所有字节(包括报文头)之和应为0
	package->head->check_sum = 0;
	check_sum = 0;
	p = (unsigned char*)package;
	do{
		check_sum += *p;
		p++;
	}while(--len);
	package->head->check_sum = ~check_sum;

	//发送
	START_SEND();

	while(!send_over)
		w_send();

	return 0;
}

//由于计算校验合会占用大量时间,因此在调用该函数时发送程序后台部分会受影响,可能造成正在发送的数据报文时序错乱.
//接收程序后台部分同样会受影响,但由于此时后台接收肯定是已经完成的状态,所以忽略影响.
//所以,调用原则是:发送时要确认发送程序已停止
unsigned char recv_package(package_t *package){
	unsigned char check_sum;
	unsigned char len;
	unsigned char *p;

	if (len == 0)
		return ERROR_RECV_NULL;

	//计算校验和
	len = package->head.package_len;
	check_sum = 0;
	p = (unsigned char*)package;
	do{
		check_sum += *p;
		p++;
	}while(--len);
	if(check_sum)
		return ERROR_RECV_CHECKSUM;

	return 0;
}

void sys_thread(){
	if(!recv_over)
		w_recv();
	//后面加上键盘扫描等驱动程序,但所有代码执行最长时间不得高于100微秒
}

typedef struct command_s{
	unsigned char command_id;
	unsigned char command_data[1];
}command_t;

#define GET_DEVICE_STATUS		0x11	//读取节点控制器所控制的设备的参数
#define SET_DEVICE_STATUS		0x12	//设置节点控制器所控制的设备的参数
#define GET_NODE_STATUS			0x13	//读取节点控制器自身参数
#define SET_NODE_STATUS			0x14	//设置节点控制器自身参数

#define ERROR_COMMAND_INVAILED	0x11	//错误的命令码

typedef struct device_ctl_s{
	unsigned char device_id;
	unsigned char device_status;
}device_ctl_t;

unsigned char command_process(package_t *package){
	command_t *command = (command_t *)(package->package_data);

	switch(command->command_id){
		case GET_DEVICE_STATUS:
			package->head.target_id = package->head.source_id;
			send_package(package);
			break;
		case GET_NODE_STATUS:
			package->head.target_id = package->head.source_id;
			send_package(package);
			break;
		case SET_DEVICE_STATUS:
			package->head.target_id = 0xbb;
			send_package(package);
			START_RECV();//数据包处理完成,重新开启接收程序
			break;
		case SET_NODE_STATUS:
			package->head.target_id = 0xcc;
			send_package(package);
			break;
		default:
//			START_RECV();//数据包处理完成,重新开启接收程序
			package->head.target_id = 0xdd;
			send_package(package);
			START_RECV();//数据包处理完成,重新开启接收程序
			return ERROR_COMMAND_INVAILED;
	}

	START_RECV();//数据包处理完成,重新开启接收程序

	return 0;
}

#if 1
void sleep(unsigned int time_out){
	unsigned int time=jiffies;
	unsigned char n=0;
	do{
		;
	}while(jiffies-time < time_out);
	return;
}

void send_test(){
	unsigned char device_id, device_status;
	package_t *package = w_buf;
	command_t *command = package->package_data;
	device_ctl_t *device_ctl = command->command_data;
	this_node_id = 0x20;

	while(1){
		for(device_id=0; device_id<2; device_id++){
			for(device_status=0; device_status<2; device_status++){
				STOP_RECV();
				memset(package, 0, W_BUF_LEN);
				package->head.target_id = 0x30;
				command->command_id = SET_DEVICE_STATUS;
				device_ctl->device_id=device_id;
				device_ctl->device_status=device_status;
				send_package(package);
				START_RECV();
				sleep(20000);
			}
		}
	}
}

void recv_test(){
	this_node_id = 0x30;
	while(1){
		unsigned char i;
		START_RECV();//数据包处理完成,重新开启接收程序
		while(!recv_over)//等待数据包
			sys_thread();
		for(i=0; i<W_BUF_LEN; i++)
			serial_out(w_buf[i]);
	}
}

#endif

#if 0	//中央控制器
void main(){
	sys_init();

	while(1){
		unsigned char scmd;
		sys_thread();
		if(recv_over){
			unsigned char i;
			for(i=0; i<W_BUF_LEN; i++)
				serial_out(w_buf[i]);
			START_RECV();
		}
		if(RI && wait_serial(&scmd, 1, 0) == 0){//串口命令处理程序
			switch(scmd){
				case 'f':{		//fill w_buf and send
					unsigned char ret, len;
	
					STOP_RECV();//停止接收,因为接收与发送是共用缓冲区,运行接收会干扰缓冲区.但目前该行程序暂是多余,因为目前取消了并行收发.
	
					if(wait_serial(&len, 1, 1000))
						break;
					if(wait_serial(w_buf, len, 1000))
						break;
					ret = send_package((package_t *)w_buf);
					serial_out(ret);
	
					break;
				}
				case 'g':{		//get w_buf
					unsigned char len = W_BUF_LEN;
					unsigned char *p = w_buf;
	
					if(recv_over){
						do {
							serial_out(*p);//将数据传给主机
							p++;
						}while(--len);
					}else{
						serial_out(ERROR_RECV_EMPTY);//目前缓冲区内空,无数据可取
					}
					break;
				}
			}
			START_RECV();
		}
	}
}

#else	//节点

void main(){
	sys_init();

	while(1){
		unsigned char ret;
		package_t *package = (package_t*)w_buf;
		while(!recv_over)//等待数据包
			sys_thread();

		//sleep(3000);
		send_package(package);
		START_RECV();//数据包处理完成,重新开启接收程序
		continue;

		ret = recv_package((package_t*)w_buf);//处理接收到的数据包,包括检查数据包的正确性

		if(ret){
			package_t *package = (package_t*)w_buf;
			START_RECV();//数据包处理完成,重新开启接收程序
			package->head.target_id = 0xaa;
			send_package(package);
			START_RECV();//数据包处理完成,重新开启接收程序
		}else{
			ret = command_process((package_t*)w_buf);
		}
	}
}
#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -