上面这幅图就是主机和从机的接口连接图。
上面这幅图就是关于SPI接口的主从机得管脚连接图。左边是主机的接口,右边是从机的接口。
void SPI_MasterInit(void)//这个就是主机SPI接口的初始化的函数了
{
/* Set MOSI and SCK output, all others input */
DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK);
/* Enable SPI, Master, set clock rate fck/16 */
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
}
void SPI_MasterTransmit(char cData)//主机发送字节函数
{
/* Start transmission */
SPDR = cData;
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)));
}
上面的图是官方手册上的例子程序,说到底还是人家造芯片的都给出了使用方法了,还是按照他的手册上程序来看懂吧。
上面这个图是SPI控制寄存器的位图。下面我就分析下这个SPI控制寄存器里面每一位的功能吧.当然我还是结合数据手册里面的说明来写的。
SPI中断允许位,可读可写,在寄存器SPSR中SPIF位置1以及SREG寄存器中的总中断允许位开启的情况下SPIE位可以触发SPI中断执行。那么再看看SPSR寄存器吧。
上面这个就是SPSR状态寄存器的内部图了,然后看看SPIF这位的说明吧.
上图就是SPIF位的说明了。当串行数据发送完毕时标志位SPIF会被置位,在状态寄存器中的SPIF位被置位并且总中断允许一个中断就会产生。还有一种情况就是当SS管脚被设置为输入状态并且被置低以及在SPI处于主机模式下也可以将标志位SPIF置高。当执行相应的中断处理后标志位SPIF会被硬件清零,另外,当标志位SPIF置高后并且状态寄存器被第一次读的情况下也会被清零。然后才可以访问SPDR数据寄存器。
第六位WCOL写冲突标志位,这位会被置高当数据寄存器SPDR在数据传送的过程中被写时。在WCOL位被置高时并且当SPI状态寄存器被第一次读时,WCOL和SPIF会被清零。然后才访问SPI的数据寄存器。就是当被写的时候这个WCOL置高表示正在写的过程还不能读SPDR,当WCOL清零后才可以对SPDR读取数据。
上图中表示BIT5..1是保留位,当被读的时候会被读取为0。
然后最后这个位就是确定传输速率SCK Frequence时钟频率的了。先看看一个配置表格吧。
这个表格是反应SCK和晶振频率的关系。从最上面的位说明得出如果SPI2X置高的话那么SPI的速度也就是SCK Frequence会翻倍并且要再SPI处于主机模式下才行。也就是说SCK Frequence最小的周期会是CPU时钟周期的2倍。当此时SPI被确定为从机模式时则只能确保是在或者比更低。
接下来再看看数据寄存器吧。
还有一个就是SS
(1) 从机模式(SLAVE MODE)
当SPI配置为从机模式时,这个SS脚一般都是输入状态,SS为低将激活SPI口MISO成为输出(用户必须对相应端口进行配置)当SS为高时,所有引脚成为输入,SPI逻辑复位,不再接收数据。
(2) 主机模式
当SPI配置为主机模式时,用户可以决定SS引脚的方向,当SS配置为输出时不影响SPI系统的传输。典型应用是用来驱动从机的SS引脚。
如果配置为输入,必须保持为高以保证SPI的正常工作,若系统配置为主机,SS为输入,但被外设拉低,则SPI系统会将此低电平解释为一个外部主机将自己选择为从机。为了防止总线冲突,SPI系统将实现如下动作:
1、清零SPCR的MSTR位,使SPI成为从机,从而MOSI和SCK变为输入。
2、SPSR的SPIF置位,若SPI的中断和全局中断开放,则中断服务程序将得到执行。
因此说来,使用中断方式处理SPI主机的数据传输,并且存在SS被拉低的可能性时,中断服务函数应该检查MSTR是否为1.若被清零用户必将其置位,以便重新配置为主机模式。
下面看看时序图吧
上图是主机模式的时序图。下图为从机模式的时序图。
下面介绍下数据传输格式
上图主要是设置采样是在上升沿还是下降沿,还有就是一开始的SCK的极性。
接下来就是分析原来的例子程序了,呵呵,我比较懒点很少自己写^_^
先看看电路图吧:
首先看看主程序:
#include <iom16v.h>
#include <macros.h>
#include "delay.h"
#include "spi.h"
/*数码管0,1,2,3,4,5,6,7,8,9显示*/
unsigned char tab[]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90};
void port_init(void)
{
DDRA =0X00; //上拉
PORTA=0XFF;
DDRB =0XFF; //低四位输出低电平,使四位数码管均作显示
PORTB=0XF0; //MISO输出高电平,关闭595移位寄存器的数据清零功能
DDRC =0X00; //上拉
PORTC=0XFF;
DDRD =0X00; //上拉
}
/*-----------------------------------------------------------------
函数名称: void HC595out(uchar i)
函数功能: 74HC595数据输出显示
参 数:
返 回 值: 无
-----------------------------------------------------------------*/
void HC595out(unsigned char i)
{
PORTB&=~(1<<4); //等待数据传输,595存储寄存器数据不变
SPIMasterTransmit(i); //传输数据
PORTB|=(1<<4); //595移位寄存器的数据进入数据存储寄存器,并在数码管上显示
}
void main(void)
{
unsigned char i=0;
port_init();//端口初始化操作
PORTD=0XFF;
SPImasterInit(); //SPI初始化
while(1)
{
i++;
if(i>9)
{
i=0;
}
HC595out(tab[i]);//传送数据到595
DelayMs(1000);//延时一段时间是数码管显示数字之间的时间差
}
}
接下来看看SPI的初始化函数和SPI传送数据的函数
void SPImasterInit(void) //SPI初始化
{
DDRB|=(1<<7)|(1<<5); //设置SCK,MOSI为输出,其他为输入
SPCR|=(1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);//使能SPI,主模式,Fck/128
}
/*-----------------------------------------------------------------
函数名称: void SPIMasterTransmit(uchar TranData)
函数功能: SPI数据传输
参 数:
返 回 值: 无
-----------------------------------------------------------------*/
void SPIMasterTransmit(unsigned char TranData)
{
SPDR=TranData; //数据放入SPI寄存器中
while(!(SPSR&(1<<SPIF))); //查询发送完毕的标志位等待数据传输完毕
}
还有一个延时函数
void Delay(void)
{
unsigned char a, b, c;
for (a = 1; a; a++)
for (b = 1; b; b++)
for (c = 0; c<10; c++) //循环次数=255*255*10
;
}
但是我现在还没看懂这个语法是怎么玩的, for (a = 1; a; a++)就是这个FOR循环,但是意思还是知道的,就是那个中间的a就是a小于等于255的意思。错了,看来我的猜测是错了,刚才查了下,是这个意思,只有当表达式2中的值为真是继续执行循环体内语句,否则为假时则退出。那么就是这样的了,首先我们定义的a是0—255的,然后当加到255时下个就是0了为假,网上是这样说的,我刚刚查了下,比如if(数字) 其实隐藏了原始是if(数字!=0)这样就很容易理解了。^_^
文章评论(0条评论)
登录后参与讨论