涉及到的知识:SPI通信协议,UART通信协议,串口中断,定时器
本文只有程序哦!!!
程序讲解:
? ? ? ? 电脑与51单片机之间是UART通信,并且运用了中断,单片机与单片机之间是模拟SPI通信。
? ? ? ? 程序运行的过程主要是,电脑A向51单片机A发送一个数据,单片机接收到了RI+1,触发中断,在中断函数中,将数据发给单片机B,单片机B收到了数据,存入SBUF寄存器发送给电脑B,电脑B的串口助手收到数据。(B->A同理)
(1)头文件与引脚定义:
# include <regx51.h>
# include <intrins.h>
sbit SCLK = P0^0;
sbit MOSI = P0^1;
sbit MISO = P0^2;
sbit CS = P0^3;
//引脚定义,模拟SPI通信
(2)初始化函数:
void INIT()
{
SCON = 0x50;//串口寄存器,方式一,8位UART波特率可变
PCON = 0;//没有校验位所以不设置,没有这一步也可以
TMOD = 0x20;//定时器模式,方式二,8位自动重载
TH1 = 0xFD;
TL1 = 0xFD;//波特率9600
ES = 1;//串口中断开关
ET1 = 0;//禁止定时器中断
EA = 1;//打开中断总开关
TR1 = 1;//打开定时器
}
(3)延时函数和发送函数:
void Delay5us()
{
_nop_();//一个机器周期约5us
}
void SPI_Send(unsigned char date1)//这个是单片机收到来自电脑的数据
{
unsigned char i;
CS = 0;//拉低片选线
SCLK = 1;//时钟线空闲时置一
for(i=0;i<8;++i)
{
SCLK = 0;//拉低时钟线
if(date1 & 0x80)//判断数据最低位是否为1
{
MOSI = 1;//把date1的最低位通过电平传到另一台单片机去
}
else
{
MOSI = 0;//最低位为0,把0传给另一个单片机
}
SCLK = 1;//拉高时钟线,处理数据
date1 <<= 1;//date1左移一位
void Delay5us();//每发送一位数据延时5us
}
}
(4)接收函数
unsigned char SPI_Receive()//有返回值,返回的值给SBUF,然后SBUF会发给另一台电脑
{
unsigned char i,date2;
date2 = 0;//初始化
for(i=0;i<8;++i)
{
date2 <<= 1;//左移一位
while(SCLK == 1);//判断时钟线
//SCLK = 1会卡在这里表示要么还没发数据要么发完了一位数据了
while(SCLK == 0);//表示现在正在发送数据,SCLk = 1会进入下一步
date2 |= MISO;//把MISO上的数据给date2的最低为
}
return (date2);
}
(5)中断函数
void UART_Routine() interrupt 4
{
if(RI == 1)//收到数据
{
RI = 0;//清零
SPI_Send(SBUF);//把收到的数据发给另一台单片机
}
else if(TI == 1)
{
TI = 0;//清零
}
}
(6)主函数
void main()
{
void INIT();//初始化
while(1)
{
SBUF = SPI_Receive();
//未收到数据会卡在这里
//收到数据时会将收到的数据放入SBUF寄存器中,然后TI+1,进入中断将数据发给另一台电脑
}
}
????????这个程序会有一点点小小的误差(当两边同时拉低时钟信号的时候,但是由于我们5us发一个数据所以误差率相当的小,在isp上自动发送也不会出现问题),就是没有分主从只是利用了SPI原理,在51单片机上模拟双机通信。原理上来说,从机是不能拉低时钟信号的,但是我们拉低了,就相当于说没有主从之分。
????????两个单片机的程序有一点点区别,下面这个程序在两个单片机之间运行过,是OK的!
# include <regx51.h>
# include <intrins.h>
sbit SCLK = P0^0;
sbit MOSI = P0^1;
sbit MISO = P0^2;
sbit CS = P0^3;
void UART_INIT()
{
SCON = 0x50;//串口寄存器方式1,八位自动重载
PCON = 0;//串口寄存器
TMOD = 0x20;//定时器寄存器
TL1 = 0xFD;
TH1 = 0xFD;//波特率为9600
ET1 = 0;//禁止定时器1中断
ES = 1;//串口中断
EA = 1;//中断总开关
TR1 = 1;//启动定时器1
}
void Delay5us()
{
_nop_();
}
//CPOL = 1;CPHA = 0;
void SPI_Send(unsigned char date1)
{
unsigned char i;
for(i=0;i<8;++i)
{
SCLK = 0;
if(date1 & 0x80)
{
MOSI = 1;//另一台就是MISO = 1;
}
else
{
MOSI = 0;//另一台是MISO
}
SCLK = 1;
date1 <<= 1;
Delay5us();
}
}
unsigned char SPI_Receive()
{
unsigned char i,date2;
date2 = 0x00;
for(i=0;i<8;++i)
{
date2 <<= 1;
while(SCLK == 1);
while(SCLK == 0);
date2 |= MISO;//另一台是MOSI
}
return (date2);
}
void SPI_Routine() interrupt 4
{
if(RI == 1)
{
SPI_Send(SBUF);
RI = 0;
}
else if(TI == 1)
{
TI = 0;
}
}
void main()
{
UART_INIT();
while(1)
{
SBUF = SPI_Receive();
}
}
(照片有点糊,当时从机的有一个MOSI没改所以收到的数据有错误,上面的代码是改过的,放心使用,我是在一台电脑上用了两个isp串口助手拍的照,两台电脑也可以的,都试过没问题)