硬件SPI实现

emmmm,,,,话说这个硬件SPI,,,做成了,再回过头来看,也特么真的没啥,,,,,但是做不成,哪都是问题。。。。

借此仅分享下我的经验,希望您可以少走弯路。。。
嗯,,,,其实在这之前我做单片机的SPI通信没有成功,于是就先做了SPI的W25Q16的读写,,,,可以参考这里

单主单从(忽略片选)

22.1184M 9600
话补多少,上最终代码。。。

实验手札:
问题大概想了一下,只要有三个
1、具体到各个寄存器的使用,当时没弄太懂,太过心急。。。磨刀不误砍柴工,,,切记切记啊。。。
2、用李基佬的15的最小系统,特么谁知道背面有个自锁开关,,,我一不小心就按关了,,,导致调试的时候好几次情况都很意外。。。
3、关于单片机的封装没仔细看清,以及要不要用到复用引脚AUXR/AUXR1切换。。。

嗯,大概就是这样,咱们看代码!!!

主机

#include "stc15.h"       // 注意宏定义后面没分号
bit SPI_Receive;            // SPI 端口收到数据标志位 
unsigned char SPI_buffer;   // 保存SPI端口收到的数据
/**************************************************************************
 - 功能描述:STC15单片机串口1初始化,使用T1方式2自重载方式做波特率发生器 
 **************************************************************************/
void UART_init(void)       // 9600bps@22.1184MHz
{         
    // 下面代码设置定时器1
    TMOD |= 0x20;   // 0010 0000 定时器1工作于方式2(8位自动重装方式)
    TH1  = 0xFA;    // 波特率:9600 /22.1184MHZ
    TL1  = 0xFA;    // 波特率:9600 /22.1184MHZ
    TR1  = 1;
    // 下面代码设置定串口
    AUXR = 0x00;             // 很关键,使用定时器1作为波特率发生器,S1ST2=0
    SCON = 0x50;    // 01010 0000 SM0.SM1=01(最普遍的8位通信),REN=1(允许接受)
}    

void port_mode()            // 端口模式
{
    P0M1=0x00; P0M0=0x00;P1M1=0x00; P1M0=0x00;P2M1=0x00; P2M0=0x00;P3M1=0x00; P3M0=0x00;
    P4M1=0x00; P4M0=0x00;P5M1=0x00; P5M0=0x00;P6M1=0x00; P6M0=0x00;P7M1=0x00; P7M0=0x00; 
}

void main(void)
{
    unsigned char tmpdata,SPI_status;
    port_mode();      // 所有IO口设为准双向弱上拉方式。
    UART_init();      // 初始化串口,9600bps@22.1184MHz   
    SPCTL=0xF0;       // 主机 (或SPCTL=0xFC; )
    while(1)              // 主循环
    {
        if(RI)                      // 判串口是否收到数据
        {
            tmpdata=SBUF;           // 读取串口中收到的数据 
            RI=0;
            P35=!P35;               // 串口接收数据指示灯,调试时观察串口工作是否正常
            // 将数据发送到从机SPI
            SPDAT=tmpdata;          // SPI 发送数据 
            SPI_status=0;
            while(SPI_status==0)
            {
                SPI_status=SPSTAT;   // 等待SPIF=1即等待SPI发送完毕
                SPI_status=SPI_status&0x80;
            }
            SPSTAT=0xC0;             // 清0标志位SPIF和WCOL
            SPI_buffer=SPDAT;        // 保存收到的数据
            SBUF=SPI_buffer;  // 将接收到的数据从串口发送到计算机
            while(TI==0);     // 等待发送完毕
            TI=0;             // 清零串口发送中断标志 
        }
    }
}

从机

#include "stc15.h"       // 注意宏定义后面没分号
bit SPI_Receive;            // SPI 端口收到数据标志位 
unsigned char SPI_buffer;   // 保存SPI端口收到的数据
void port_mode()            // 端口模式
{
    P0M1=0x00; P0M0=0x00;P1M1=0x00; P1M0=0x00;P2M1=0x00; P2M0=0x00;P3M1=0x00; P3M0=0x00;
    P4M1=0x00; P4M0=0x00;P5M1=0x00; P5M0=0x00;P6M1=0x00; P6M0=0x00;P7M1=0x00; P7M0=0x00; 
}

void main(void)
{
    port_mode();          // 所有IO口设为准双向弱上拉方式。
    SPCTL=0xE0;           // 从机  (或SPCTL=0xEC; )
    IE2=IE2|0x02;         // ESPI(IE2.1)=1,允许SPIF产生中断   
    EA=1;                 // 开总中断  
    SPI_Receive=0;        // 清标志字
    while(1)              // 主循环
    {
        if (SPI_Receive)      // 判收是否收到主机SPI发来的数据
        {
            SPI_Receive=0;    // 清0主单片机SPI端口收到数据标志位
            SPDAT=SPI_buffer; // 将收到数据送SPDAT,准备下一次通讯时发回
        }
    }
}
void SPI(void) interrupt 9
{
    SPSTAT=0xC0;             // 清0标志位SPIF和WCOL
    SPI_buffer=SPDAT;        // 保存收到的数据
    SPI_Receive=1;           // 设置SPI端口收到数据标志 
}  

为了更方便一点,把主机从机做到了一个程序里面。
只需要下载的时候主机define上master,而从机注释掉即可

手札:
1、注意自己连的是复用后的IO口,还是原装的,要不要进行端口转换。

2、注意两块SPI进行通信的时候,要MOSI、MISO、SCK、对应连接。

3、要共VCC、共GND。

看程序吧。。。

#include "STC15W4K.H"       // 注意宏定义后面没分号
#define MASTER 1            // 作为从机程序时,将该行注释掉,其余都不用修改。

bit SPI_Receive;            // SPI 端口收到数据标志位 
unsigned char SPI_buffer;   // 保存SPI端口收到的数据
/**************************************************************************
 - 功能描述:STC15单片机串口1初始化,使用T1方式2自重载方式做波特率发生器 
 **************************************************************************/
void UART_init(void)       // 9600bps@22.1184MHz
{         
    // 下面代码设置定时器1
    TMOD |= 0x20;   // 0010 0000 定时器1工作于方式2(8位自动重装方式)
    TH1  = 0xFA;    // 波特率:9600 /22.1184MHZ
    TL1  = 0xFA;    // 波特率:9600 /22.1184MHZ
    TR1  = 1;
    // 下面代码设置定串口
    AUXR = 0x00;             // 很关键,使用定时器1作为波特率发生器,S1ST2=0
    SCON = 0x50;    // 01010 0000 SM0.SM1=01(最普遍的8位通信),REN=1(允许接受)
}    
void Switch_port() // 根据硬件切换端口
{
//  AUXR1&=0XF3;    //1111 0011
//    AUXR1|=0X04;    //0000 0100
}
void port_mode()            // 端口模式
{
    P0M1=0x00; P0M0=0x00;P1M1=0x00; P1M0=0x00;P2M1=0x00; P2M0=0x00;P3M1=0x00; P3M0=0x00;
    P4M1=0x00; P4M0=0x00;P5M1=0x00; P5M0=0x00;P6M1=0x00; P6M0=0x00;P7M1=0x00; P7M0=0x00; 
}

void main(void)
{
    unsigned char tmpdata,SPI_status;
    port_mode();     // 所有IO口设为准双向弱上拉方式。
#ifdef MASTER   
        UART_init();      // 初始化串口,9600bps@22.1184MHz   
        SPCTL=0xF0;       // 主机 (或SPCTL=0xFC; )
        Switch_port();    // 端口切换
    #else
        SPCTL=0xE0;       // 从机  (或SPCTL=0xEC; )
#endif  
    SPSTAT=0xc0;          // 清0标志位SPIF和WCOL 
    IE2=IE2|0x02;         // ESPI(IE2.1)=1,允许SPIF产生中断   
    EA=1;                 // 开总中断  
    SPI_Receive=0;        // 清标志字
    while(1)              // 主循环
    {
#ifdef MASTER
        if(RI)                      // 判串口是否收到数据
        {
            tmpdata=SBUF;           // 读取串口中收到的数据 
            RI=0;
            P35=!P35;               // 串口接收数据指示灯,调试时观察串口工作是否正常
            // 将数据发送到从机SPI
            IE2&=0xfd;              // ESPI(IE2.1)=0,禁止SPIF产生中断
            SPDAT=tmpdata;          // SPI 发送数据 
            SPI_status=0;
            while(SPI_status==0)
            {
                SPI_status=SPSTAT;   // 等待SPIF=1即等待SPI发送完毕
                SPI_status=SPI_status&0x80;
            }
            IE2|=0x02;               // ESPI(IE2.1)=1,允许SPIF产生中断
            continue;                // 跳转到循环体最后结尾“}” 处执行程序
        }
        if (SPI_Receive)      // 判断是否接收到从SPI发回数据
        {
            SPI_Receive=0;    // 清0主单片机SPI端口收到从机数据标志位
            TI=0;             // 清零串口发送中断标志 
            SBUF=SPI_buffer;  // 将接收到的数据从串口发送到计算机
            while(TI==0);     // 等待发送完毕
            TI=0;             // 清零串口发送中断标志 
        }
#else
        if (SPI_Receive)      // 判收是否收到主机SPI发来的数据
        {
            SPI_Receive=0;    // 清0主单片机SPI端口收到数据标志位
            SPDAT=SPI_buffer; // 将收到数据送SPDAT,准备下一次通讯时发回
        }
#endif
    }
}
void SPI(void) interrupt 9
{
    SPSTAT=0xC0;             // 清0标志位SPIF和WCOL
    SPI_buffer=SPDAT;        // 保存收到的数据
    SPI_Receive=1;           // 设置SPI端口收到数据标志 
}  

单主单从(加入片选)

22.1184M 9600

#include "STC15W4K.H"   // 注意宏定义后面没分号
#define MASTER 1            // 作为从机程序时,将该行注释掉,其余都不用修改。

bit SPI_Receive;            // SPI 端口收到数据标志位 
unsigned char SPI_buffer;   // 保存SPI端口收到的数据
sbit P2_4=P2^4;

/**************************************************************************
 - 功能描述:STC15单片机串口1初始化,使用T1方式2自重载方式做波特率发生器 
 **************************************************************************/
void UART_init(void)       // 9600bps@22.1184MHz
{         
    // 下面代码设置定时器1
    TMOD |= 0x20;   // 0010 0000 定时器1工作于方式2(8位自动重装方式)
    TH1  = 0xFA;    // 波特率:9600 /22.1184MHZ
    TL1  = 0xFA;    // 波特率:9600 /22.1184MHZ
    TR1  = 1;
    // 下面代码设置定串口
    AUXR = 0x00;             // 很关键,使用定时器1作为波特率发生器,S1ST2=0
    SCON = 0x50;    // 01010 0000 SM0.SM1=01(最普遍的8位通信),REN=1(允许接受)
}    
void Switch_port() // 根据硬件切换端口
{
//  AUXR1&=0XF3;    //1111 0011
//    AUXR1|=0X04;    //0000 0100
}
void port_mode()            // 端口模式
{
    P0M1=0x00; P0M0=0x00;P1M1=0x00; P1M0=0x00;P2M1=0x00; P2M0=0x00;P3M1=0x00; P3M0=0x00;
    P4M1=0x00; P4M0=0x00;P5M1=0x00; P5M0=0x00;P6M1=0x00; P6M0=0x00;P7M1=0x00; P7M0=0x00; 
}
void main(void)
{
    unsigned char tmpdata,SPI_status;
    port_mode();     // 所有IO口设为准双向弱上拉方式。
#ifdef MASTER   
        UART_init();      // 初始化串口,9600bps@22.1184MHz   
        SPCTL=0xF3;       // 主机 (或SPCTL=0xFC; )
        Switch_port();    // 端口切换
    #else
        SPCTL=0x63;       // 从机  (或SPCTL=0xEC; )
#endif  
    SPSTAT=0xc0;          // 清0标志位SPIF和WCOL 
    IE2=IE2|0x02;         // ESPI(IE2.1)=1,允许SPIF产生中断   
    EA=1;                 // 开总中断  
    SPI_Receive=0;        // 清标志字
    while(1)              // 主循环
    {
#ifdef MASTER
        if(RI)                      // 判串口是否收到数据
        {
            tmpdata=SBUF;           // 读取串口中收到的数据 
            RI=0;
            P35=!P35;               // 串口接收数据指示灯,调试时观察串口工作是否正常
            // 将数据发送到从机SPI
            IE2&=0xfd;              // ESPI(IE2.1)=0,禁止SPIF产生中断
            P2_4=0;                 // 打开从机片选
            SPDAT=tmpdata;          // SPI 发送数据 
            SPI_status=0;
            while(SPI_status==0)
            {
                SPI_status=SPSTAT;   // 等待SPIF=1即等待SPI发送完毕
                SPI_status=SPI_status&0x80;
            }
            P2_4=1;                  // 关闭从机片选
            IE2|=0x02;               // ESPI(IE2.1)=1,允许SPIF产生中断
            continue;                // 跳转到循环体最后结尾“}” 处执行程序
        }
        if (SPI_Receive)      // 判断是否接收到从SPI发回数据
        {
            SPI_Receive=0;    // 清0主单片机SPI端口收到从机数据标志位
            TI=0;             // 清零串口发送中断标志 
            SBUF=SPI_buffer;  // 将接收到的数据从串口发送到计算机
            while(TI==0);     // 等待发送完毕
            TI=0;             // 清零串口发送中断标志 
        }
#else
        if (SPI_Receive)      // 判收是否收到主机SPI发来的数据
        {
            SPI_Receive=0;    // 清0主单片机SPI端口收到数据标志位
            SPDAT=SPI_buffer; // 将收到数据送SPDAT,准备下一次通讯时发回
        }
#endif
    }
}
void SPI_ISR(void) interrupt 9
{
    SPSTAT=0xC0;             // 清0标志位SPIF和WCOL
    SPI_buffer=SPDAT;        // 保存收到的数据
    SPI_Receive=1;           // 设置SPI端口收到数据标志 
}  
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ReCclay

如果觉得不错,不妨请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值