用STC12C5A60S2开发96路信号输出,可接485 多层主机 ,自制串口通信协议
附件中有PDF版AD原理图,源代码 开发文档。
原创。
上位机对于这些信号机的控制就是基于这块--96路信号灯板。顾名思义,96路信号灯板可以同时实现对96个信号灯的控制,操作人员只需在上位机电脑端,发送通信协议,单片机做出应答,外接设备轨道信号机实现列车指示作用。 左上角选定单片机型号,数据通信端口号;发送缓冲区发送通信协议,这块96路信号灯板的通信协议为AB 02 01 07(主机地址,可根据现场实际情况进行更改) 10(端口控制量,十六进制) FF FF FF FF FF FF FF FF FF FF FF FF(十二路控制端口控制96路灯) 00 00 00 00 02(最后一位为奇偶校验,可根据之前的数据进行更改) CD。 二、96路信号灯板原理图及PCB图 96路信号灯板所用的控制原理是:STC12C5A60S2单片机通过操作P0口的电平高低,控制SN74HC573芯片,进而控制四-十六译码器CD4514,然后译码器控制十二路信号灯的控制芯片SN74HC573和ULN2803,实现信号灯的亮灭。  
单片机源程序如下:
- /*-----------------------------------------------
- 名称:串口通信
- 日期:2019.6.10
- 内容: 多机通信,第一层机,485通信 本机为输出功能从机 YN防485信号接收冲突
- ------------------------------------------------*/
- #include<stc12c5a60s2.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
- #include<intrins.h>
-
- void datapro(); //总数据处理函数
- unsigned char a[20],c[25],b[25],ca[25]; //a[20]暂存数据包,一些计算在这个数组里面完成 c[25],ca[25]串口接收缓冲数据包
- sbit DE=P1^1; //485通信方向控制
- sbit W=P3^7; // 锁存的地址输出控制
- sbit LE=P3^6; //地址信号锁存到573
- sbit XZ=P1^0; //信号控制线
- unsigned char flag=1,i=0,m=0,flag_1=0,flag_2=0; //地址信号锁存 flag=1 串口发送完标志,i=0,m=0,全局变量 为串口接收数组小标,flag_1=0,flag_2=0串口接收数据接收完标志
- unsigned char address_1; //本机地址
- unsigned char YN=0,k=1; // k 接收数据时 转义用 ;YN 串口接收状态值
- unsigned char ZY_1=0,ZY_2=0;
- /*------------------------------------------------
- uS延时函数
- ------------------------------------------------*/
- void DelayUs2x(unsigned short int t)
- {
- while(--t);
- }
- /*------------------------------------------------
- mS延时函数
- ------------------------------------------------*/
- void DelayMs(unsigned short int t)
- {
- while(t--)
- {
- //大致延时1mS
- DelayUs2x(245);
- DelayUs2x(245);
- }
- }
- /*------------------------------------------------
- 串口和心跳定时器初始化
- ------------------------------------------------*/
- void InitUART(void)
- {
- SCON = 0x50; // SCON: 模式 1, 8-bit UART, 使能接收
- TMOD = 0x21; // TMOD: timer 1, mode 2, 8-bit 重装
- PCON=0X80;
- TH1 = 0xFD; // TH1: 重装值 253 波特率19200 晶振 11.0592MHz
- TH0 = 0x4b ; // TH0,TL0: 重装值 15536 定时约50ms
- TL0 = 0xfd ;
- WDT_CONTR=0x3f; //看门狗
- IP =0x10; //串口优先 ,心跳信息次之
- TR1 = 1; // TR1: timer 1 打开
- TR0 = 1; // TR0: timer 0 打开
-
- DE=0; //485通信默认状态为接收
-
- EA =1; //总中断开
- ET0 = 1; //定时中断开
-
-
-
- }
- /*------------------------------------------------
- 校验情况函数
- ------------------------------------------------*/
- void check(unsigned char x)
- { unsigned char j; //函数功能 告诉上位机奇偶校验的结果 暂时未用
- unsigned char d=0;
- a[1]=0x01; //ab 01 01 ad_1 x R cd
- a[2]=0x01;
- a[3]=address_1;
- a[4]=x;
-
- d=a[1]+a[2]+a[3]+a[4];
-
- if(d%2==0) a[5]=0x02;
- if(d%2==1) a[5]=0x01; //计算奇偶 奇数 01 偶数 02
-
- DE=1; //使485芯片处于发送状态
- DelayMs(1);
- flag=1 ;SBUF=0xab; while(flag==1); //发送报文头
- for(j=1;j<6;j++)
- {
- if(a[j]==0xfe||a[j]==0xab||a[j]==0xcd) { flag=1 ;SBUF=0xfe; while(flag==1); flag=1 ; SBUF=a[j]; while(flag==1); }
- else { flag=1 ;SBUF=a[j]; while(flag==1); }
- }
- flag=1;SBUF=0xcd;while(flag==1); //发送报文尾
- DE=0; //使485芯片回到接收状态
- }
- /*------------------------------------------------
- 控制输出函数
- ------------------------------------------------*/
- void control_out(void)
- {
- unsigned char A,j; //函数功能 开关量的输出
- A=b[4]; //数据帧,,要输出的字节数
- for(j=0;j<A;j++)
- {
- P0=j; //4-16译码地址
- LE=1; //573 使能引脚
- DelayMs(10);
- LE=0; //地址锁存到地址存储573芯片
- P0=b[j+5]; //要输出的数据
- W=0;
- DelayMs(10);
- W=1; //已经锁存的地址信号输出
-
- }
- }
- /*------------------------------------------------
- 接收数据处理程序
- ------------------------------------------------*/
- void datapro()
- {
- unsigned char d=0;
- unsigned char j,t;
- if((c[0]==0xab&&c[i-1]==0xcd)||(ca[0]==0xab&&ca[m-1]==0xcd)) // 数据报文头报文尾确认
- {
-
- if(flag_1==1) {for(j=0;j<25;j++) { b[j]=c[j];c[j]=0;} t=i;i=0;flag_1=0;} //数据存储区转移
- else if(flag_2==1) { for(j=0;j<25;j++) { b[j]=ca[j];ca[j]=0;} t=m;m=0;flag_2=0;} // 数据存储区转移
-
-
-
- for(j=1;j<(t-2);j++)
- {
- d=d+b[j] ;
- }
- if(d%2==0x00) d=0x02;
- else d=0x01; // 奇偶计算
-
- if(b[t-2]==d) //计算的奇偶与上位机发送过来的数据的奇偶校验字节相比较
- {
- if(b[1]==0x02&&b[2]==0x01&&b[3]==address_1) {/*check(0x01); */ control_out(); for(j=0;j<25;j++) {b[j]=0;} ;t=0; }
- //上位机发送过来的命令 本机发送给上位机校验情况,再控制输出
- else if(b[1]==0x04) { for(j=0;j<25;j++) {b[j]=0;} ;t=0; }
- //上位机发送过来的命令 本机为输出功能从机,不作反应
- else if(b[1]==0x05||b[1]==0x03){ for(j=0;j<25;j++) {b[j]=0;} ;t=0; }
- //上位机发送过来的多余数据 此为广播方式进行多级 多机通信弊端
- else if(b[1]==0x01) { for(j=0;j<25;j++) {b[j]=0;} ;t=0;}
- //上位机发送过来的上位机校验情况命令 ,本机为输出功能机,该命令是另外功能机的命令,所以不作反应,是广播通信弊端
-
- }
- else { if(b[1]==0x02) {check(0x02); } //上位机发送过来的命令 校验失败
- for(j=0;j<25;j++) {b[j]=0;} ;t=0;
- }
- }
-
- }
- /*------------------------------------------------
- 单片机心跳信息函数
- ------------------------------------------------*/
- void heartb()
- {
- unsigned char d=0;
- if(YN==0)
- {
- DE=1;
- DelayMs(1); // ab 05 ad1 R cd
- flag=1 ;
- SBUF=0xab;
- while(flag==1);
-
- flag=1 ;
- a[1]=0x05;
- SBUF=a[1];
- while(flag==1);
- flag=1 ;
- a[2]=0x01;
- SBUF=a[2];
- while(flag==1);
-
- flag=1;
- a[3]=address_1;
- if(a[3]==0xfe||a[3]==0xab||a[3]==0xcd) {SBUF=0xfe;while(flag==1);} ;
- flag=1 ;
- SBUF=a[3];
- while(flag==1);
- d=a[1]+a[2]+a[3];
- if(d%2==0x00) d=0x02;
- else d=0x01;
- flag=1;
- a[4]=d;
- SBUF=a[4];
- while(flag==1);
- flag=1 ;
- SBUF=0xcd;
- while(flag==1);
- DE=0;
- }
- }
- /*------------------------------------------------
- 主函数
- ------------------------------------------------*/
- void main (void)
- {
- unsigned char j;
- unsigned char x;
- x=5;
- P0M1=0X00;
- P0M0=0XFF;
- address_1=0x04; //本机地址
- for(j=0;j<25;j++)
- {
- a[j]=0;b[j]=0;c[j]=0; ca[j]=0; //数组初始化
- };
- InitUART();
- ES=1;
- b[4]=0x10; //数据长度 16个字节
- b[5]=0x00;
- b[6]=0x00;
- b[7]=0x00;
- b[8]=0x00;
- b[9]=0x00;
- b[10]=0x00;
- b[11]=0x00;
- b[12]=0x00;
- b[13]=0x00;
- b[14]=0x00;
- b[15]=0x00;
- b[16]=0x00;
- b[17]=0x00;
- b[18]=0x00;
- b[19]=0x00;
- b[20]=0x00;
- control_out(); //使所有信号输出为0
- x=(x-1);
- XZ=1;
- while(1)
- {
-
- DelayMs(10);
- datapro();
-
- }
- }
- /*------------------------------------------------
- 串口中断程序
- ------------------------------------------------*/
- void UART_SER (void) interrupt 4 //串行中断服务程序
- {
- unsigned char Temp; //定义临时变量
- if(RI) //判断是接收中断产生
- {
-
- RI=0; //标志位清零
- Temp=SBUF;
-
- if(flag_1==0) // flag_1==1 表示按照协议接收到了一串完整的数据
- {
- YN=1; //YN=1表示串口处于接收状态
- if(i==0&&Temp!=0xab){ YN=0;} //如果 接收到的第一个字节不是报文头0xab,则放弃接收该字节
- else{
- if(Temp==0xfe)
- { if(k==1) {k++;ZY_1=0XFE;}
- else { k=1; ZY_1=0; c[i]=Temp;i++; } //转义 处理
- }
-
- else {c[i]=Temp;
- if(c[i]==0xcd&&ZY_1!=0xfe) {flag_1=1; k=1;YN=0;} ; //判断是否处于报文尾
- if(ZY_1==0xfe)if(c[i]==0xcd||c[i]==0xab){k=1; ZY_1=0;};
- i++;
- };
-
-
-
- }
- }
- else if(flag_2==0)
- {
- YN=1;
- if(m==0&&Temp!=0xab){ YN=0;}
- else{
- if(Temp==0xfe)
- { if(k==1) {k++;ZY_2=0XFE;}
- else { k=1;ZY_2=0;ca[m]=Temp;m++; } //转义
- }
- else { ca[m]=Temp;
- if(ca[m]==0xcd&&ZY_2!=0xfe) {flag_2=1; k=1; YN=0;}
- if(ZY_2==0xfe){ if(ca[m]==0xcd||ca[m]==0xab){k=1;ZY_2=0;}; }
- m++;
- }
-
- } ;
-
- }
- }
-
-
-
- if(TI){flag=0; TI=0;}
- }
- /*------------------------------------------------
- 心跳中断程序
- ------------------------------------------------*/
- void timer0(void) interrupt 1
- { static unsigned char n=0 ;
- n++;
- if(n==110) {WDT_CONTR=0x3f;/* heartb(); n=0;*/ } // 6秒 喂看门狗,发送心跳信息
- if(n==220) {WDT_CONTR=0x3f; heartb(); n=0; }
-
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
全部资料51hei下载地址:
|