原创 ATmega16之SPI接口分析及SPI接口来控制595实现数码管

2011-8-18 20:54 5123 7 7 分类: MCU/ 嵌入式

第四章    SPI接口

20110818042535008.jpg

       上面这幅图就是主机和从机的接口连接图。

20110818042535009.jpg

       上面这幅图就是关于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)));

}

       上面的图是官方手册上的例子程序,说到底还是人家造芯片的都给出了使用方法了,还是按照他的手册上程序来看懂吧。

20110818042535010.jpg

       上面这个图是SPI控制寄存器的位图。下面我就分析下这个SPI控制寄存器里面每一位的功能吧.当然我还是结合数据手册里面的说明来写的。

20110818042535011.jpg

SPI中断允许位,可读可写,在寄存器SPSRSPIF位置1以及SREG寄存器中的总中断允许位开启的情况下SPIE位可以触发SPI中断执行。那么再看看SPSR寄存器吧。

20110818042535012.jpg

上面这个就是SPSR状态寄存器的内部图了,然后看看SPIF这位的说明吧.

20110818042535013.jpg

上图就是SPIF位的说明了。当串行数据发送完毕时标志位SPIF会被置位,在状态寄存器中的SPIF位被置位并且总中断允许一个中断就会产生。还有一种情况就是当SS管脚被设置为输入状态并且被置低以及在SPI处于主机模式下也可以将标志位SPIF置高。当执行相应的中断处理后标志位SPIF会被硬件清零,另外,当标志位SPIF置高后并且状态寄存器被第一次读的情况下也会被清零。然后才可以访问SPDR数据寄存器。

20110818042535014.jpg

第六位WCOL写冲突标志位,这位会被置高当数据寄存器SPDR在数据传送的过程中被写时。在WCOL位被置高时并且当SPI状态寄存器被第一次读时,WCOLSPIF会被清零。然后才访问SPI的数据寄存器。就是当被写的时候这个WCOL置高表示正在写的过程还不能读SPDR,当WCOL清零后才可以对SPDR读取数据。

20110818042535015.jpg

上图中表示BIT5..1是保留位,当被读的时候会被读取为0

20110818042535016.jpg

然后最后这个位就是确定传输速率SCK Frequence时钟频率的了。先看看一个配置表格吧。

20110818042535017.jpg

这个表格是反应SCK和晶振频率的关系。从最上面的位说明得出如果SPI2X置高的话那么SPI的速度也就是SCK Frequence会翻倍并且要再SPI处于主机模式下才行。也就是说SCK Frequence最小的周期会是CPU时钟周期的2倍。当此时SPI被确定为从机模式时则只能确保是在20110818042535018.jpg或者比20110818042535019.jpg更低。

接下来再看看数据寄存器吧。

20110818042535020.jpg

还有一个就是SS

(1)    从机模式(SLAVE MODE

SPI配置为从机模式时,这个SS脚一般都是输入状态,SS为低将激活SPIMISO成为输出(用户必须对相应端口进行配置)当SS为高时,所有引脚成为输入,SPI逻辑复位,不再接收数据。

(2)    主机模式

SPI配置为主机模式时,用户可以决定SS引脚的方向,当SS配置为输出时不影响SPI系统的传输。典型应用是用来驱动从机的SS引脚。

 

如果配置为输入,必须保持为高以保证SPI的正常工作,若系统配置为主机,SS为输入,但被外设拉低,则SPI系统会将此低电平解释为一个外部主机将自己选择为从机。为了防止总线冲突,SPI系统将实现如下动作:

1、清零SPCRMSTR位,使SPI成为从机,从而MOSISCK变为输入。

2、SPSRSPIF置位,若SPI的中断和全局中断开放,则中断服务程序将得到执行。

因此说来,使用中断方式处理SPI主机的数据传输,并且存在SS被拉低的可能性时,中断服务函数应该检查MSTR是否为1.若被清零用户必将其置位,以便重新配置为主机模式。

下面看看时序图吧

20110818042535021.jpg

上图是主机模式的时序图。下图为从机模式的时序图。

20110818042535022.jpg

下面介绍下数据传输格式

20110818042536023.jpg20110818042536024.jpg

20110818042536025.jpg

上图主要是设置采样是在上升沿还是下降沿,还有就是一开始的SCK的极性。

接下来就是分析原来的例子程序了,呵呵,我比较懒点很少自己写^_^

先看看电路图吧:
20110818042536026.jpg

首先看看主程序:

#include <iom16v.h>

#include <macros.h>

#include "delay.h"

#include "spi.h"

 

/*数码管0123456789显示*/

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中的值为真是继续执行循环体内语句,否则为假时则退出。那么就是这样的了,首先我们定义的a0—255的,然后当加到255时下个就是0了为假,网上是这样说的,我刚刚查了下,比如if(数字) 其实隐藏了原始是if(数字!=0)这样就很容易理解了。^_^

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
7
关闭 站长推荐上一条 /3 下一条