概要
基于51单片机的一主多从交互通信方案。使用protues仿真成功,暂未实物验证。
整体架构流程
在平常使用串口时,经常觉得串口不够使用,特别是在多硬件外设的情况下。随着现在社会发展,一些单片机价格非常便宜(stc8只要8毛一片)
那我们是不是能使用多片芯片来减少我们主机负担或者不用采购高价格的多串口芯片呢?
所以我按照这个想法想了一种通过软件和多单片机的方案。
上图中更改自定义命令字的定义即可自定义其更多功能,实现相互通信只要使用一位标志位即可。
其中发送函数和接收函数都是主机软件应该解决的,接收中断则时从机要干的事情!
主机可接收特定从机信息,从机也可接收主机的数据和命令。
我认为如果你要改善应该注意主机占有绝对领导权,这个问题。小心处理出现多个大脑这种危险操作。
具体代码实现可以看下面的技术细节
技术细节
主机代码:
#include <STC89C5xRC.H> #include "UART.h" #include "Timer0.h" #include "Key.h" #include "Delay.h" unsigned char R_Slave_Num=0;//标志接收命令已经发出准备好接受从机的数据 unsigned char R_Buff[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//判断发送的状态存放数据 unsigned char KeyNum=0,temp; /** * @brief 主机向指定从机发送一个字节数据 * @param Slave_Num-从机的编号*****范围1~16 * @param message-要发送的数据 * @retval 无 */ void Master_SendByte(unsigned char Slave_Num,unsigned char message) { UART_SendByte(Slave_Num<<4); Delay(20); UART_SendByte(message); } /** * @brief 主机接收从机的一个字节数据 * @param Slave_Num-从机的编号*****范围1~16 * @retval 无 */ void Master_ReciveByte(unsigned char Slave_Num) { R_Slave_Num=Slave_Num; UART_SendByte((Slave_Num<<4)|0x08); } void keyscan(void) { KeyNum=Key(); if(KeyNum) temp=KeyNum; switch (temp) { case 1:Master_SendByte(1,0x55);temp=0;break; case 2:Master_ReciveByte(4);temp=0;break; case 3:Master_SendByte(3,0x55);temp=0;break; case 4:Master_SendByte(3,0xaa);temp=0;break; } } void main(void) { UART_Init(); Timer0_Init(); while(1) { keyscan(); P2=R_Buff[4]; } } void UART_Routine() interrupt 4 //串口中断 { if(RI==1)//过滤发出中断干扰 { if(R_Slave_Num) { R_Buff[R_Slave_Num]=SBUF; R_Slave_Num=0; } RI=0;//软件复位 } } void Timer0_Routine() interrupt 1 { static unsigned int T0Count1,T0Count2; TL0 = 0x66; //设置定时初值 TH0 = 0xFC; //设置定时初值 T0Count1++;T0Count2++;//每1ms进行一次++; if(T0Count1>=20)//20ms调用一次Key_Loop()函数 { T0Count1=0; Key_Loop(); } }
从机代码:
#include <STC89C5xRC.H> #include "UART.h" unsigned char temp,R_Buff=0xff,T_Buff=0x33;//存放串口接收到的数据 unsigned char uart_flag=0;//0代表收命令1代表接收数据 void main(void) { UART_Init(); while(1) { P2=R_Buff; } } void UART_Routine() interrupt 4 //串口中断 { if(RI==1)//过滤发出中断干扰 { temp=SBUF; if(uart_flag==1)//如果接收标志为1,则把数据接收到R—buff里 { R_Buff=SBUF; uart_flag=0;//并把标志位置0,为下一次主机命令做准备 } else//否则接收标志为0,表示当前状态为收命令 { if((temp&0xF8)==0x48)//判断命令为本地址且为发数据 { UART_SendByte(T_Buff); } else if((temp&0xF8)==0x40)//判断命令为本地址且为收数据则置标志位 { //为下一次做准备 uart_flag=1; } } } RI=0;//软件复位 }
Timer0.c内容:
/** * @brief 定时器初始化 * @param 无 * @retval 无 */ void Timer0_Init(void)//1毫秒@11.0592MHz { AUXR &= 0x7F; //定时器时钟12T模式 TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x01; //设置定时器模式 TL0 = 0x66; //设置定时初值 TH0 = 0xFC; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0=1; EA=1; PT0=0; }
Uart.c内容:
/** * @brief 串口初始化 * @param 无 * @retval 无 */ void UART_Init()//9600bps@11.0592MHz { PCON &= 0x7F; //波特率不倍速 SCON = 0x50; //8位数据,可变波特率 AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T AUXR &= 0xFE; //串口1选择定时器1为波特率发生器 TMOD &= 0x0F; //设置定时器模式 TMOD |= 0x20; //设置定时器模式 TL1 = 0xFD; //设置定时初始值 TH1 = 0xFD; //设置定时重载值 ET1 = 0; //禁止定时器%d中断 TR1 = 1; //定时器1开始计时 EA=1; //启动所有中断 ES=1; //启动串口中断 } /** * @brief 串口发送一个字节数据 * @param Byte 要发送的一个字节数据 * @retval 无 */ void UART_SendByte(unsigned char Byte) { SBUF=Byte; while(TI==0);//检测发送完成标志位 TI=0;//该位必须软件复位 }
小结
该方式只是我短暂思考后得出的方法,我觉得肯定是有更优的方案解决这个问题。在单片机越来越便宜的趋势下,我觉得除了增加主机的引脚这种多从机的方案也是能占一方势力!希望我的想法能给大家带来一丝帮助或者是灵感,欢迎讨论交流!