打印

IO口模拟SPI时序

[复制链接]
7588|22
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
时序图显示该SPI接口在CLK的上升沿读MISO 状态得到当前的输入数据位,在CLK的下降沿向MOSI输出当前数据位。
首先初始化SPI时把CLK拉低。
unsigned char SPI_Send(unsigned char data)
{
    unsigned char i,temp;
    bit loadbit = 0;
    unsigned char loaddata = 0;
    temp = data;    //首先输出最高位
    for(i=0; i<8; i++)
    {
        CLK = 1;
       MOSI = temp & 0x08;
       _nop_( ) ;         
        CLK = 0;
       loadbit = MISO;
       _nop_( ) ;         
    }
    temp = temp <<1;
    loaddata = (loaddata << 1) | loadbit;
    return (loadbit);
}

这个程序大家觉得完善吗,如果不完善,请发表一下意见

SPI.PNG (88.84 KB )

SPI.PNG

相关帖子

沙发
醉心369|  楼主 | 2013-1-8 21:44 | 只看该作者
错了,应该是return (loaddata);
大虾门发表点意见啊

使用特权

评论回复
板凳
airwill| | 2013-1-8 21:58 | 只看该作者
    temp = temp <<1;
    loaddata = (loaddata << 1) | loadbit;

这两行, 应该放循环里面

使用特权

评论回复
地板
醉心369|  楼主 | 2013-1-8 22:08 | 只看该作者
airwill 发表于 2013-1-8 21:58
temp = temp

是,这个错了,是我疏忽了。
unsigned char SPI_Send(unsigned char data)
{
     unsigned char i,temp;
     bit loadbit = 0;
     unsigned char loaddata = 0;
     temp = data;    //首先输出最高位
     for(i=0; i<8; i++)
     {
         CLK = 1;
     MOSI = temp & 0x08;
        _nop_( ) ;         
         CLK = 0;
     loadbit = MISO;
          _nop_( ) ;  
          temp = temp <<1;
          loaddata = (loaddata << 1) | loadbit;      
     }
     return (loaddata);
}

上面这个函数,是我看到别人写的一个程序。我觉得还有一些不对之处,大虾门怎么看

使用特权

评论回复
5
lzqxs89| | 2013-1-9 09:31 | 只看该作者
你这个SPI程序可以正常工作不?
       CLK = 1;
       MOSI = temp & 0x08;      //输出数据
       _nop_( ) ;         
        CLK = 0;
       loadbit = MISO;         //读数据
       _nop_( ) ;
我觉得应该把 MOSI = temp & 0x08; 与  loadbit = MISO; 的位置换一下。
你理解的下降沿、上升沿应该理解为下降沿之后,上升沿之后(单片机是顺序执行的,不能实现在下降沿的同时写数据,你自己的程序是在下降沿之前,就把数据送到MOSI了)。

使用特权

评论回复
6
joyme| | 2013-1-9 10:53 | 只看该作者
高位开始发送,不是 &0x08 应该 &0x80

使用特权

评论回复
7
醉心369|  楼主 | 2013-1-9 12:25 | 只看该作者
unsigned char SPI_Send(unsigned char data)
{
     unsigned char i,temp;
     bit loadbit = 0;
     unsigned char loaddata = 0;
     temp = data;    //首先输出最高位
     for(i=0; i<8; i++)
     {
         CLK = 1;
         MOSI = temp & 0x80;
        _nop_( ) ;
        loadbit = MISO;
          _nop_( ) ;         
         CLK = 0;
          temp = temp <<1;
          loaddata = (loaddata << 1) | loadbit;      
     }
     return (loaddata);
}
这是从时序图上我自己理解的,看一下还有什么不妥

使用特权

评论回复
8
醉心369|  楼主 | 2013-1-9 12:29 | 只看该作者
lzqxs89 发表于 2013-1-9 09:31
你这个SPI程序可以正常工作不?
       CLK = 1;
       MOSI = temp & 0x08;      //输出数据

这个程序,是别人写的,&0x08 应该 &0x80,这是我的笔误,上面是我自己理解的,与你的不一样

使用特权

评论回复
9
lzqxs89| | 2013-1-9 13:08 | 只看该作者
醉心369 发表于 2013-1-9 12:25
unsigned char SPI_Send(unsigned char data)
{
     unsigned char i,temp;

CLK = 1;
MOSI = temp & 0x80;你这两条语句会导致 MOSI 的电平在 CLK=1 的时候发生变化,不符合时序图了。
改成这样就可以啦。
MOSI = temp & 0x80;
CLK = 1;
SPI时序高电平期间数据是稳定的(不变化),所以在 CLK=1之前  你就要把数据放到MOSI上面去,          同理   CLK = 1 之后  你读MISO的数据也才是正确的。

使用特权

评论回复
10
afei9527| | 2013-1-9 13:59 | 只看该作者
本帖最后由 afei9527 于 2013-1-9 14:01 编辑

/*******************************/
#define _CPOL     0 //模式配置
#define _CPHA     0 //模式配置
/*******************************/
void delay(unsigned char ms )
{
unsigned char m;
    while(ms--)
    for(m=0;m<100;m++);
}
/************************************************
        端口方向配置  与输出初始化
************************************************/
void SPI_Init(void)
{
SCK_IO(1)   ;  
MOSI_IO(1)  ;
MISO_IO(0)  ;  
SSEL_IO(1)  ;
SSEL_D(1);
MOSI_D(1);
#if _CPOL==0
SCK_D(0);
#else
SCK_D(1);
#endif
}
/**********************************************
模式零           
***********************************************/
#if _CPOL==0&&_CPHA==0          //MODE   0  0   
unsigned char SPI_Send_Dat(unsigned char dat)
{
//第一数据sck上升之前 其他下降沿  数据采样 上升沿
unsigned char n,rx_dat;
for(n=0;n<8;n++)
{
  SCK_D(0);//下降沿
  if(dat&0x80)MOSI_D(1);
  else MOSI_D(0);
  dat<<=1;
  SCK_D(1); //上升沿
  rx_dat<<=1;
  if(MISO_I())rx_dat|=0x01;
  else rx_dat&=0xfe;
}
  SCK_D(0);
return rx_dat;
}
#endif
/**********************************************
模式二         
***********************************************/
#if _CPOL==1&&_CPHA==0           //MODE   1  0
unsigned char SPI_Send_Dat(unsigned char dat)
{
//第一数据sck下降沿之前 其他上升沿  数据采样 下降沿
unsigned char n,rx_dat;
for(n=0;n<8;n++)
{
  SCK_D(1);
  if(dat&0x80)MOSI_D(1);
  else MOSI_D(0);
  dat<<=1;
  SCK_D(0);
  rx_dat<<=1;
  if(MISO_I())rx_dat|=0x01;
  else rx_dat&=0xfe;
}
  SCK_D(1);
return rx_dat;
}
#endif
/*********************************************
模式一      
*********************************************/
#if _CPOL==0&&_CPHA==1           //MODE  0  1
unsigned char SPI_Send_Dat(unsigned char dat)
{
//第一数据sck上升 其他上升沿  数据采样 下降沿
unsigned char n,rx_dat;
SCK_D(0);
for(n=0;n<8;n++)
{
  SCK_D(1);
  if(dat&0x80)MOSI_D(1);
  else MOSI_D(0);
  dat<<=1;
  SCK_D(0);
  rx_dat<<=1;
  if(MISO_I())rx_dat|=0x01;
  else rx_dat&=0xfe;
}
return  rx_dat;
}
#endif
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
#if _CPOL==1&&_CPHA==1            //MODE  1  1
unsigned char SPI_Send_Dat(unsigned char dat)
{
//第一数据下降沿  其他下降沿   数据采样 上升沿
unsigned char n,rx_dat;
SCK_D(1);
for(n=0;n<8;n++)
{
  SCK_D(0);
  if(dat&0x80)MOSI_D(1);
  else MOSI_D(0);
  dat<<=1;
  SCK_D(1);
  rx_dat<<=1;
  if(MISO_I())rx_dat|=0x01;
  else rx_dat&=0xfe;
}
return rx_dat;
}
/*************************************
*************************************/
/*int main(void)
{
//DDRB=0Xff;
SPI_Init();
while(1)
{
SSEL_D(0);
P2=SPI_Send_Dat(0xaa);
SSEL_D(1);
//delay(100);
}
return 0;
} */


使用特权

评论回复
11
醉心369|  楼主 | 2013-1-9 18:28 | 只看该作者
lzqxs89 发表于 2013-1-9 13:08
CLK = 1;
MOSI = temp & 0x80;你这两条语句会导致 MOSI 的电平在 CLK=1 的时候发生变化,不符合时序图 ...

我先前可能理解错了,下降沿输出就以为是:高电平的时候先把数据写上,然后下降沿来的时候就会把数据输出。那下降沿输出和上升沿读取,怎样理解呢

使用特权

评论回复
12
醉心369|  楼主 | 2013-1-9 22:15 | 只看该作者
afei9527 发表于 2013-1-9 13:59
/*******************************/
#define _CPOL     0 //模式配置
#define _CPHA     0 //模式配置

很感谢,现在有一个DS1302的时序,函数如下:
/**************DS1302一个写字节******************/
void DS1302InputByte(uchar d)  //实时时钟写入一字节(内部函数)
{
    uchar i;
    ACC=d;
    for (i=8;i>0;i--)
    {
        DS1302_IO=ACC0;    //相当于汇编中的 RRC
        DS1302_CLK=1;
        DS1302_CLK=0;    //发一个高跳变到低的脉冲
        ACC=ACC>>1;
    }
}
/**************DS1302读一个字节******************/
uchar DS1302OutputByte(void)  //实时时钟读取一字节(内部函数)
{
    uchar i;
    for (i=8;i>0;i--)
    {
        ACC=ACC>>1;     //相当于汇编中的 RRC
        ACC7=DS1302_IO;
        DS1302_CLK=1;
        DS1302_CLK=0;    //发一个高跳变到低的脉冲
    }
    return(ACC);
}

这个时序图正好与我先前发的时序图,正好相反,上面两个函数也是看到别人写的,是否正确?(我认为正确)
这个好像都是在低电平的时候写和读的,怎样理解先前SPI的时序上升沿读,下降沿写呢?
又怎样理解这个DS1302上升沿写和读呢?
按照他们的时序编程,函数的高低应该正好相反啊。

1302.PNG (30.49 KB )

1302.PNG

使用特权

评论回复
13
ZOUWEN1| | 2013-1-9 23:04 | 只看该作者
本帖最后由 ZOUWEN1 于 2013-1-9 23:07 编辑

针对SPI上面的时序,MCU到SPI:在时钟的下降沿准备好数据,确保数据稳定后,切换时钟从底电平到高电平,此时从器件就会锁存这一位数据。SPI到MCU:在时钟为高电平期间,从器件输出对应的数据,时钟从高电平转到低电平,在低电平期间,些时读从器件输出的数据就好了。
这是我的理解,不对处请指正。
补充一点:做驱动时一定要多花时间读器件手册,理解器件的时序,做出程序后使用罗辑分析仪来检查时序是否正确。

使用特权

评论回复
14
guangods| | 2013-1-10 08:16 | 只看该作者
,多看看数据手册就知道了,有可能不同的芯片架构的关系,一些运算可能有小的差别

使用特权

评论回复
15
醉心369|  楼主 | 2013-1-11 11:16 | 只看该作者
lzqxs89 发表于 2013-1-9 13:08
CLK = 1;
MOSI = temp & 0x80;你这两条语句会导致 MOSI 的电平在 CLK=1 的时候发生变化,不符合时序图 ...

经过最近琢磨,我的理解为:
对于输出,要把数据线上的数据准备好后,时钟线上再给予锁存时钟沿。
对于输入,先给予锁存时钟沿使得从设备准备好数据,再从数据线上读入有效数据。
即对于上面的SPI时序,我还是认为,对于写在高电平时准备数据,下降沿输出数据。对于读,当上升沿来的时候锁存数据然后输入。

使用特权

评论回复
16
醉心369|  楼主 | 2013-1-11 11:46 | 只看该作者
guangods 发表于 2013-1-10 08:16
,多看看数据手册就知道了,有可能不同的芯片架构的关系,一些运算可能有小的差别 ...

再有差别,也是上升沿和下降沿锁存数据啊

使用特权

评论回复
17
lzqxs89| | 2013-1-11 12:44 | 只看该作者
本帖最后由 lzqxs89 于 2013-1-11 12:48 编辑
醉心369 发表于 2013-1-11 11:16
经过最近琢磨,我的理解为:
对于输出,要把数据线上的数据准备好后,时钟线上再给予锁存时钟沿。
对于输 ...

你明白你的意思, 你意思是在高电平的时候准备好,在时钟下降沿的时候把数据输出来。但是跟你说单片机不是按你想的这样工作的哦,你可以用示波器、逻辑分析仪看看波形。MOSI = temp & 0x80;一执行完,数据就到IO上去啦,并不是像你想得那样,Do you understand!

使用特权

评论回复
18
醉心369|  楼主 | 2013-1-12 17:30 | 只看该作者
airwill 发表于 2013-1-8 21:58
temp = temp

SPI 有四种模式时序,对于DS1302的时序图用哪个模式啊,为什么,这几天老是看时序了,越看越晕

使用特权

评论回复
19
afei9527| | 2013-1-14 14:13 | 只看该作者
一般用第一种  #define _CPOL     0 //模式配置   #define _CPHA     0 //模式配置  ds1302是第一种模式

使用特权

评论回复
20
醉心369|  楼主 | 2013-1-19 16:55 | 只看该作者
lzqxs89 发表于 2013-1-11 12:44
你明白你的意思, 你意思是在高电平的时候准备好,在时钟下降沿的时候把数据输出来。但是跟你说单片机不是 ...

用逻辑分析仪或示波器抓出来的波形可能存在误差,也许由于总线不等长造成的时序误差

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

60

主题

283

帖子

2

粉丝