用51单片机模拟SPI串口通信#双机通信#C语言#

发布时间:2024年01月23日

涉及到的知识: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串口助手拍的照,两台电脑也可以的,都试过没问题)

文章来源:https://blog.csdn.net/2301_80415757/article/details/135442407
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。