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端口收到数据标志
}
962






