STC15W4K58S4单片机SPI通讯实验1
SPI通讯实验1:
一个主机,一个从机,三线连接,不需要片选线。PC机通过串行口向主单片机发送一个字节的数据,主单片机接收到后向从机发送这个字节,从机接收后将数据返回给主机,主机再将数据通过串行口发送回PC机。
串行口波特率9600bps 无奇偶校验 8个数据位 1个停止位
单片机系统时钟频率SYS_clk=22.1184MHz
SPI通信速率SYS_clk/4
电路连接图:
两个单片机可以使用不同的供电电源,但是要共地,串行口的地线也要与两个单片机共起来。
两块单片机的SPI引脚连接时,中间一定要串联上240欧的电阻,以防止程序失误出现端口短路烧毁的情况!
采用寄存器引脚切换,SPI_S1 SPI_S0
0 1
/SS:P2.4 MOSI:P2.3 MISO:P2.2 SCLK:P2.1
主机从机都使用这组引脚作为SPI通信端口。主机采用查询方式,从机采用SPI中断方式。
使用SPI通讯需要设置 SPCTL寄存器
位号 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
位名称 | SSIG | SPEN | DORD | MSTR | CPOL | CPHA | SPR1 | SPR0 |
1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
对于主机,永远无需使用片选线所以SSIG位置1(不使用片选SS)
启动SPI硬件通讯,SPEN=1(启动)
字符传输顺序DORD=1,低位在前高位在后
设备设置为主机MSTR=1(主机)
CPOL=0,时钟极性设置为空闲时低电平
CPHA=0,时钟相位设置为前沿采样,后沿输出
SPR1 SPR0 设置为00,SPI传输速度为SYSclk/4(系统时钟的4分频)
因此 SPCTL=11110000B=0xF0;
如果是从机的话,MSTR为设置为0
因此 SPCTL=11100000B=0xE0;
由于电路连接关系的限制,需要做SPI管脚切换
AUXR1辅助功能寄存器1:
位号 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
位名称 | S1_S1 | S1_S0 | CCP_S1 | CCP_S0 | SPI_S1 | SPI_S0 | 0 | DPS |
其中SPI_S1和SPI_S0是SPI引脚切换选择位:
SPI_S1 | SPI_S0 | SPI接口引脚位置 | |||
SS/ | MOSI | MISO | SCLK | ||
0 | 0 | P1.2 | P1.3 | P1.4 | P1.5 |
0 | 1 | P2.4 | P2.3 | P2.2 | P2.1 |
1 | 0 | P5.4 | P4.0 | P4.1 | P4.3 |
1 | 1 | 无效 |
设置SPI_S1和SPI_S0为01,即切换管脚为:
SS:P2.4,MOSI:P2.3,MISO:P2.2,SCLK:P2.1
设置辅助功能寄存器AUXR1时为了不改变其它位的值,首先使用与操作清零SPI_S1 SPI_S0
AUXR1&=0xF3; //1111 0011 将位3,位2清0
之后使用或操作将SPI_S1,SPI_S0置为01
AUXR1|=0x04; //0000 0100 将位3,位2置1
这样就只改变了SPI_S1,SPI_S0的值,而其它位不变。
主机从机都做如此设置。
主要函数说明:SPI主机部分
//串行口1初始化
//系统时钟频率SYSclk=22.1184MHz,波特率9600,8个数据位无奇偶校验
//使用定时器T2作为波特率发生器
void UART1_init(void)
{
//设置SCON寄存器,使用串口1,定时器T2
//SM0 SM1 SM2 REN TB8 RB8 TI RI
// 0 1 0 1 0 0 0 0
//SM0 SM1=01:工作于8位波特率可变,使用定时器T1或T2
//REN=1:允许接收
SCON=0x50; //这个寄存器主要是设置串口工作模式,波特率可变,8位,允许接收的
//设置辅助功能寄存器AUXR
//T0x12 T1x12 UART_M0x6 T2R T2_C/T T2x12 EXTRAM S1ST2
// 1 1
AUXR|=0x01; //S1ST2=1:串口1使用定时器T2作为波特率发生器
AUXR|=0x04; //T2x12=1:T2不分频
//设置T2初值,波特率9600bps
T2L=0xC0 ; //设定定时器T2初值
T2H=0xFD;
AUXR|=0x10; //T2R=1:开启定时器T2
ES=0; //关闭串口1中断,主机采用串口查询方式,所以这里关闭中断
EA=1; //开启总中断
}
//设置SPI控制寄存器SPCTL
//使SPI工作与主机模式,无需片选
//时钟极性CPOL=0,时钟相位CPHA=0
//通讯速度位系统时钟频率的1/4
//并开启硬件SPI通讯
SPCTL=0xF0; //SPI控制寄存器 从机设置为0xE0
//做SPI管脚切换
//使用SS:P2.4 MOSI:P2.3 MISO:P2.2 SCLK:P2.1
void Switch_port()
{
AUXR1&=0xF3; //1111 0011 将位3,位2清0
AUXR1|=0x04; //0000 0100 将位3,位2置1
}
//主函数
int main(void)
{
uchar xdata i;
uchar xdata SPI_status; //用于读取SPI传输状态(是否完成)
port_mode(); //设置所有端口为准双向弱上拉的模式,就是保持与传统51单片机一至
//系统上电提示灯闪烁3次,表示程序开始执行
for(i=1; i<=3; i++)
{
LEDG=0; Delay500ms();
LEDG=1;Delay500ms();
}
LCD1602_Init(); //LCD1602初始化
UART1_init(); //串行口1初始化,9600bps,8个数据位,波特率可变,由T2产生
SPCTL=0xF0; //SPI控制寄存器 从机设置为0xE0
Switch_port(); //SPI引脚切换 使用 P2.4 P2.3 P2.2 P2.1
//在LCD1602上显示
WriteString(0,0,"===SPI Master==1");
//程序没有使用中断方式,使用查询方式
//进入主循环
while(1)
{
if(RI) //如果有串行口接收标志
{
revDat=SBUF; //读取由串口发送来的数据
RI=0; //清串口接收标志
LEDG=0; //点亮提示灯
//显示串口接到的数据
//%bx表示以16进制输出,占2个字符,不足补0
//显示格式:U S:0X** R:0X**
//其中U表示串口 S表示发送的数据 R表示接收的数据 0X表示十六进制数
sprintf(disptemp,"U S:0X%02bX R:0X%02bX",sendDat,revDat);
WriteString(0,0,disptemp);
//将串口收到的数据从SPI端口发送给从机
SPDAT=revDat;
//在屏幕的第二行显示SPI端口收发数据的情况
//S S:0X** R:0X**
//S表示SPI端口 S表示发送的数据 R 表示接收的数据 0X表示十六进制数
sprintf(disptemp,"S S:0X%02bX R:0X%02bX",revDat,SPI_buffer);
WriteString(1,0,disptemp);
//通过查询SPI状态寄存器SPSTAT,判断数据发送是否完成
SPI_status=0;
while(SPI_status==0) //等待SPIF=1即等待SPI发送完毕
{
SPI_status=SPSTAT; //读取SPSTAT状态寄存器的值
SPI_status=SPI_status&0x80; //获得第7位的值,这一位为1表明SPI传输完成
}
SPSTAT=0xC0; //写1清除SPIF和WCOL,WCOL是写冲突标志,
//写冲突是指SPI移位寄存器中的数据还没有发送完成的时候,又有新的数据写入移位寄存器
//则这个错误标志WCOL被置1
//这里不必做检测
//接收从机传来的数据
SPI_buffer=SPDAT; //保存SPI口收到的数据
//在LCD1602的第二行显示SPI收发数据
sprintf(disptemp,"S S:0X%02bX R:0X%02bX",revDat,SPI_buffer);
WriteString(0,1,disptemp);
SBUF=SPI_buffer; //将收到的数据从串口发送到计算机
sprintf(disptemp,"U S:0X%02bX R:0X%02bX",SPI_buffer,revDat); //%bx表示以16进制输出,占2个字符,不足补0
WriteString(0,0,disptemp);
while(TI==0); //等待发送完成
TI=0; //清除串口发送中断标志
}
}
return 0;
}
//从机程序
//从机使用SPI中断方式
//主要函数说明:
//SPI中断服务程序
void SPI_routine(void) interrupt 9
{
SPSTAT=0xC0; //写1清0 SPI接收中断标志和写冲突标志
SPI_buffer=SPDAT; //将SPI端口接到的数据保存到变量中
SPI_Receive=1; //设置接收完成软件标志
}
int main(void)
{
uchar xdata i;
port_mode(); //设置为准双向弱上拉的模式,就是保持与传统51单片机一至
//系统提示灯闪烁3次,表明系统上电工作
for(i=1; i<=3; i++)
{
LEDG=0; Delay500ms();
LEDG=1; Delay500ms();
}
LCD1602_Init(); //LCD1602初始化
UART1_init(); //串行口1初始化
Switch_port(); //SPI管脚切换 使用P2.4 P2.3 P2.2 P2.1
SPCTL=0xE0; //SPI控制寄存器 从机设置为0xE0
IE2=IE2|0x02; //ESPI=1,允许SPI产生中断
EA=1;
SPI_Receive=0; //清除SPI软件接收标志
WriteString(0,0,"===SPI SLAVE===1");
//进入主循环
while(1)
{
if(SPI_Receive) //判断SPI是否接收到数据
{
//%bx表示以16进制输出,占2个字符,不足补0
sprintf(disptemp,"Rev0X%02bX",SPI_buffer);
WriteString(0,1,disptemp);
SPI_Receive=0; //清除软件SPI标志
SPDAT=SPI_buffer; //接收到的数据送SPDAT,准备下一次通讯时发回
}
}
return 0;
}
源码下载:百度网盘下载
提取码:293o