打印

晕死人的SPI问题...ARM7(ADuC7026)...神啊救救我吧

[复制链接]
8504|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
    在很久很久以前(40天前),我画了个PCB,主控芯片用的是ADuC7026
然后...一个PCB竟然做了这么久,不得不佩服下这家工厂- -...

    好不容易搭好了硬件,没想到软件上又出问题了...花了N天被这个神奇的SPI接口搞得头晕目眩还是没搞定...(下面我附上了连接图)

    然后是“症状”:
    我在调试的时候使用Master模式发送,这个地方就比较奇怪了...因为发送对象是2片级联的74HC595,所以要发2个字节,那么我的程序中就这么做了,结果老不正常,然后我意外得发现在末尾跟着发8个“0”就能发送正常(第一个晕倒的地方,到现在还没弄明白..)
    然后是最头疼的Master模式接收,这个还要恶心...我设置成接收的那个8位寄存器满了,中断寄存器里的一个位就置1,然后发3个字节空数据(也就是24个时钟)出去,3片165级联所对应的数据就分3次从MISO读进来了。结果人算不如天算啊,用while去等这个1,然后运行时发现...我这辈子是等不到这个1了T_T...之后没办法,只好在发空数据把165数据从MISO上移进来的时候设置成用while查询发送中断位(就是发送寄存器发光了SPI中断寄存器里就有个1出来)结果这个1我是等到了,然后好不容易读进来数据了,然后又出现那个非常奇怪的问题...很不正常,但是在开头先发1个空字节(8个脉冲)出去,不读,然后再发3个空字节(24个脉冲出去),读3次,这样总算是读进来了,也算正常了。但是好景不长,我惊人得发现~~这个神奇的SPI在同样的输入下,连续快速得读100遍,这数据就不是整体向前就是整体向后移动几位,非常可恶...然后在这100次读函数调用的每个后面都加上while(InputDelay--);后,我又惊人得发现当InputDelay大于40的时候(我是41.78M时钟,延时大于40个周期)读进来的数据是正确的(多少遍都一样),但是没有这个延时,数据就前后移动走样........
    晕啊~~我是郁闷死了ADI你真厉害能制造出这么有创意的SPI接口...
    以下附上我的程序:

/*********************************************************************************************************
*       
*                                         这个是发送程序
*
*********************************************************************************************************/

    extern void DOutputWrite(uint32 OutputData)
    {
        SPICON = (1 << 12)|     // Continuous Transfer Enable           1 - Enable                  0 - Disable
                 (0 << 11)|     // Loop Back Enable                     1 - Connect test software   0 - Normal mode
                 (0 << 10)|     // Slave Output Enable                  1 - Enable                  0 - Disable
                 (0 <<  9)|     // Slave Select Input Enable            1 - Enable(Master Mode)     0 - Disable
                 (0 <<  8)|     // SPIRX Overflow Overwrite Enable      1 - Overwritten             0 - New byte is discarded
                 (0 <<  7)|     // SPITX Underflow Mode                 1 - Transmit 0              0 - Transmit the previous data
                 (1 <<  6)|     // Tranfer and Interrupt (Master)Mode   1 - SPITX                   0 - SPIRX
                 (1 <<  5)|     // LSB First Transfer Enable Bit        1 - LSB first               0 - MSB first
                 (0 <<  3)|     // Serial Clock Polarity Mode Bit       1 - Clock idles high        0 - Clock idles low
                 (1 <<  2)|     // Serial Clock Phase Mode Bit          1 - Begin of bit transfer   0 - End of bit transfer 
                 (1 <<  1)|     // Master Mode Enable Bit               1 - Master mode             0 - Slave mode
                 (1 <<  0);     // SPI Enable Bit                       1 - Enable                  0 - Disable

        SPITX = ~(OutputData & 0x000000FF);
        while ((SPISTA & 0x02) != 0x02);    // wait for data transfer status bit

        SPITX = ~((OutputData & 0x0000FF00) >> 8);
        while ((SPISTA & 0x02) != 0x02);

        SPITX = 0x00;                       // 这个就是那邪恶的8个空脉冲
        while ((SPISTA & 0x02) != 0x02); 

        CLR_595_STR(); 
        SET_595_STR();

    }

/*********************************************************************************************************
*       
*                                          这个是可恶的接收程序
*
*********************************************************************************************************/

    extern uint32 DInputRead(void)
    {
        uint32 InputData = 0, InputDelay = 100;

        SPICON = (1 << 12)|     // Continuous Transfer Enable           1 - Enable                  0 - Disable
                 (0 << 11)|     // Loop Back Enable                     1 - Connect test software   0 - Normal mode
                 (0 << 10)|     // Slave Output Enable                  1 - Enable                  0 - Disable
                 (0 <<  9)|     // Slave Select Input Enable            1 - Enable(Master Mode)     0 - Disable
                 (1 <<  8)|     // SPIRX Overflow Overwrite Enable      1 - Overwritten             0 - New byte is discarded
                 (0 <<  7)|     // SPITX Underflow Mode                 1 - Transmit 0              0 - Transmit the previous data
                 (1 <<  6)|     // Tranfer and Interrupt (Master)Mode   1 - SPITX                   0 - SPIRX
                 (1 <<  5)|     // LSB First Transfer Enable Bit        1 - LSB first               0 - MSB first
                 (0 <<  3)|     // Serial Clock Polarity Mode Bit       1 - Clock idles high        0 - Clock idles low
                 (0 <<  2)|     // Serial Clock Phase Mode Bit          1 - Begin of bit transfer   0 - End of bit transfer 
                 (1 <<  1)|     // Master Mode Enable Bit               1 - Master mode             0 - Slave mode
                 (1 <<  0);     // SPI Enable Bit                       1 - Enable                  0 - Disable

        SET_165_SHIFT();                    
        CLR_165_CINH();

        SPITX = 0x00;                       // 那可恶的8个脉冲又登场了
        while ((SPISTA & 0x02) != 0x02);    // wait for data transfer status bit
                                                 
        SPITX = 0x00;
        while ((SPISTA & 0x02) != 0x02);
        InputData += SPIRX;

        SPITX = 0x00;
        while ((SPISTA & 0x02) != 0x02);
        InputData += (SPIRX << 8);
        
        SPITX = 0x00;
        while ((SPISTA & 0x02) != 0x02); 
        InputData += (SPIRX << 16);

        CLR_165_SHIFT();
        SET_165_CINH(); 

        while(InputDelay--);    //这个地方要等大于40个以上周期,不等就出错,郁闷

        return InputData; 
    }

/*********************************************************************************************************
*       
*                                      这个是初始化程序,应该没问题吧..
*
*********************************************************************************************************/
    
    extern void DIOInit(void)
    {
        // Set GPIO Mode 
        GP1CON &= 0x0FFFFFFF;           // SHIFT_165(P1.7), 
        GP2CON &= 0x0FFFF00F;           // CINH_165(P2.7), OE_595(P2.2), STR_595(P2.1)

        // Initiatory State
        CLR_165_SHIFT();                // 74HC165 Pin                                   
        SET_165_CINH();   
        CLR_595_OE();                   // 74HC595 Pin
        SET_595_STR();

        SET_165_SHIFT_OUT();            // GPIO's Direvtion Set
        SET_165_CINH_OUT();
        SET_595_OE_OUT();
        SET_595_STR_OUT();  

        GP1CON = (GP1CON & 0xF000FFFF) | 0x02220000;        
                                        // MOSI(P1.6), MISO(P1.5), CLK(P1.4)
        SPIDIV = 0x1F;                  // Set SPI clock 40960000/(2×(1+SPIDIV))
                                        // 0x1F = 625KHz
    }

/*********************************************************************************************************
*
*                            这个是测试用的主程序,每个输入和输出都有LED的所以我是直接看它们有没有发光来确定是否读取/发送成功的
*
*********************************************************************************************************/ 


    int main(void)
    {
        uint32 temp[100], i;

        SysClkInit();
        PortInit();
        ADCInit();
        DACInit();
        DIOInit();
        
        DAC_Output(1, 1500);
        DAC_Output(2, 1700);
          
        DOutputWrite(0x0000CCCC);
        Delay_x_ms(500);

        DOutputWrite(0x00003333);
        Delay_x_ms(500);

        DOutputWrite(0x00005555);
        Delay_x_ms(500);

        DOutputWrite(0x0000AAAA);
        Delay_x_ms(500);

        for(i = 0; i < 100; i++)
        {
            temp = DInputRead();
        }

        while(1)
        {
                for(i = 0; i < 100; i++)
                {
                    DOutputWrite(temp);
                }
        }
    }

相关帖子

沙发
wolver| | 2008-11-6 20:43 | 只看该作者

别郁闷了...

我当初用ADuC7026的硬件SPI时,发现必须发两次数据才是正确的...
问ADI技术支持,他们也没说出个道道...
我郁闷之下,为保证系统可靠,改GPIO仿真SPI...

使用特权

评论回复
板凳
mikejx| | 2008-11-7 10:14 | 只看该作者

几年前用过ADuC812

看清说明书上的时序图应该没什么大问题,那个时序是很严格的,反正我是搞这个SPI接口时候也经过一番波折,最后问题只是看说明书不仔细,不过就是晶振不能达到说明书上的频率

使用特权

评论回复
地板
古道热肠| | 2008-11-7 10:47 | 只看该作者

用软件模块确保硬件可行。

注意SPI总线一般最少有4种工作模块,都试试看吧,俺用硬件SPI通讯不上时,就挨个试,总共就4遍便知是否有可用的模式了。

使用特权

评论回复
5
TRUE_ARM| | 2008-11-7 12:58 | 只看该作者

准确的说:

应该是状态指示有些问题,我在现在应用中,已经放弃它的状态指示。

使用特权

评论回复
6
zq1987731|  楼主 | 2008-11-7 18:38 | 只看该作者

........

   我都把说明书翻烂了...这本厚达92页的说明书害得我英文水平都上升不少,其中Mr.古道热肠所说的工作模式...就是时钟相位及上升下降沿的4种模式~~~确实只有一种是“差不多”正确的,以595发送举例...只有在发送完有效数据后紧跟着再发【8个空脉冲】的情况下才是“正确”的...接受的话就如文中所说...乱七八糟了~~可能正如Mr.TRUE_ARM所说...状态指示有误吧,也可能...是传说中的芯片BUG...不会这么巧被我碰上了吧...- -...................

使用特权

评论回复
7
mikejx| | 2008-11-8 21:21 | 只看该作者

好象是要作一些无用的写入或者读出动作

记得ADUC812也是这样的,反正说明书肯定没错,也不是bug.因为是几年前的东西,我也忘记了,ADUC是标准的SPI接口,如果外围器件不够标准的话,就要用一些技巧把标准变为不标准了.

使用特权

评论回复
8
zq1987731|  楼主 | 2008-11-8 23:07 | 只看该作者

“无用的写入或读出动作”正是我所担心的...

    因为这样会产生个问题:为什么要加些无用的操作该SPI才能正常?加哪些无用的操作?
    这个应该是从实践中试出来的吧,但为什么要这样做,只有ADI自己才知道...毕竟这个芯片对用户来说就是个黑匣子,用户关心的是如何去用而不是内部硬件如何实现,那么通过推敲而得出的“无用操作”就成了不稳定因素,虽然平时用都正常,但我碰到过数次Bit丢失的现象,也就是正常情况下读入的数据整体右移或左移几位,复位肯定是充分的,因为我不怀疑FM31256的复位能力(复位线PCB宽度10mil,长度1500mil)那么会明显出这类隐患的硬件SPI我怎么敢用呢...
    最后用GPIO模拟,同样用595输出16bit,用之前的不稳定SPI,设置SPI时钟为基本不出错的最高时钟频率需要35μs,使用GPIO模拟的方式仅需20μs,效果还不错...
    而且我写了个简单的测试程序,用GPIO模拟SPI从3片级联的74HC165那边接收光耦侧传入的数据(这个已用N根导线接通输入信号,收到的永远是0x123456,然后用预先准备的0x123456去比较,有不同的话就触发一下FM31256事件计数器的输入脚,然后用两片级联的74HC595输出错误数量(事件计数器的计数值),挂了一晚上...之前的硬件SPI方式,错得那叫一个离谱,我第二天还看见那数字在跳呢,说明一点...一位错了后面就错到底了,而且输入还是输出错我都不知道,搞不好还可能是IIC总线也有问题呢,但后面用了模拟SPI的方式,同样是用这个测试程序,第二天计数器值还是0,让我确定了IIC接口和模拟的输入输出都没问题,为了防止已经处于死机状态,干脆就拔掉一路输入,输出的错误计数马上狂跳...说明还正常。
    最后得出个心得...ADI的ADuC7026的硬件SPI接口有问题...这是我N天的血泪史啊...不过ADuC7026的模拟部分真是无敌,12路12位A/D和4路12位D/A,效果奇好,唯一的不足就是D/A设置为0的时候也有4.7mA~5.1mA的输出,真是莫名其妙,不知道大家碰到过这情况没...

使用特权

评论回复
9
pauvre77| | 2010-3-5 17:56 | 只看该作者
我使用示波器观察了一下,事实上,那个spista里面的rx部分使用正常,除了在对于其功能的描述里有些偏差。
我也是用while不过不是去等那个1,而是去等那个0,然后用P4.2来输出那个status的结果
SPICON = 0x100B;
GP4DAT ^= 0x00040000;
received_data = SPIRX;
while ((SPISTA & 0x08) != 0x08);
GP4DAT ^= 0x00040000;
根据波形结果我得出结论,当我用received_data = spirx命令时,激活了8个时钟脉冲去读取过程中,那个相应的位等于1,当我读完之后,也就是说8个时钟脉冲结束后,那个位归零。我认为,事实上spirx里面始终是有数据的,那个位真正的功能是告诉你在你去读那个spirx命令发出后,什么时候你去读的数据才是真正的有效数据。
但是当我用示波器去做写操作时,反而遇到一些问题。我这里用的都是产品本身的exemple。用的spimaster文件。这里程序要求输出30个byte的数据。我同样用4.1去显示那个spista相应位的情况:
SPICON = 0x104B;     
     for (i=0;i<30;i++)   
    {   
    GP4DAT ^=0x00020000;
    SPITX = results[i];               // transmit command or any dummy data   
    while ((SPISTA & 0x01) != 0x00) ; // wait for data received status bit
    GP4DAT ^=0x00020000;
    }
发现在写第一个数据的时候,相应位的status实际上是失效的,4.1在第一次的8个脉冲前就完成了一次周期,但是从第二个数据起,4.1的时序都正常,但是由于第一个周期太快结束,所以其实所有的4.1的周期都向前走了一个周期。换句话说,在最后一个byte写的过程中,所有的4.1的周期都走完了。所以那个status不做任何变化。

使用特权

评论回复
10
wangwo| | 2010-3-5 21:41 | 只看该作者
前车之鉴,得好好看一下

使用特权

评论回复
11
pauvre77| | 2010-3-5 22:52 | 只看该作者
根据之前所描述的状态,我觉得可以大胆假设,所有的status其实只是针对那些缓存的读写状态的。特别是spitx对应的位的变化状态更加说明了这一点。因为写入spitx的时间极短,所以在八个时钟周期之前就完成了一次写入spitx的周期变化,而之后每次要写入的时候,必须等到8时钟结束后才可以重新操作。因此在最后一个数据写过程中,那个对应的位不再显示忙。那么问题来了,对于我们来说其实就是要知道什么时候真的写完,什么时候真的读完。我想到的方法是,因为不论你做写或读的操作,其是mosi和miso一直在读写同时进行,因此我们大可以把对应于读和写的spista的位同时监控起来。当然这个目的就不再是看他是否读完或者是否写完,而是理解为看spi总线何时才空闲。

使用特权

评论回复
12
pauvre77| | 2010-3-10 21:28 | 只看该作者
多次的实验后得出的结果是spitx对应的statu位不好用,所以我采用在写的同时做一个读操作,由于都是8个时钟内完成,所以可以很容易控制读写的时间,如果你有示波器的话,可以看看输出的结果非常完美,顺便说一下,在spicon=1047换到1007也就是说写模式换到读模式的时候需要给一些时间延迟(具体看我的程序),但是在读模式换到写模式时,不需要这个延迟:
#include<ADUC7026.H>   
#include<AD9959.H>     
void CPUCLKCON (void);   
void main(void) {   
    int i,j = 0;
        char received_data;
        CPUCLKCON;
    GP4DAT = 0x04040000;                  
    GP1CON = 0x22220000;                // configure SPI on SPM   
    SPIDIV = 0xCC;                      // set SPI clock 40960000/(2x(1+SPIDIV))   
    SPICON = 0x1047;                    // enable SPI master in continuous transfer mode
        SPITX = 0x00;
        received_data = SPIRX;
    while ((SPISTA & 0x08) == 0x00);         // en train de lire la valeur   
        SPITX = 0x10;
        received_data = SPIRX;
    while ((SPISTA & 0x08) == 0x00);         // en train de lire la valeur   
        SPITX = 0x80;
        received_data = SPIRX;
    while ((SPISTA & 0x08) == 0x00);         // en train de lire la valeur   
        for (i=0;i<20;i++);   // délais obligatoire pour changer le mode d’écriture à lecture
        SPICON = 0x1007;
        received_data = SPIRX;
        while ((SPISTA & 0x08) == 0x00);
        if (received_data == 0x00)
            GP4DAT ^= 0x00040000;
        else
          {
          SPICON = 0x1047;
          SPITX = received_data;
          while ((SPISTA & 0x02) == 0x00 );
          }
    }
       
void CPUCLKCON (void)
{
     PLLKEY1 = 0xAA;
     PLLCON = 0x21;   // PLL
     PLLKEY2 = 0x55;
     POWKEY1 = 0x01;
     POWCON = 0x00;  //
     POWKEY2 = 0xF4;
         return;
         }

使用特权

评论回复
13
pauvre77| | 2010-3-10 21:32 | 只看该作者
本帖最后由 pauvre77 于 2010-3-10 21:34 编辑

miso口的默认电平在设置spi后是1,所以以下第一个是我把miso接地后的结果,同时4.2灯亮,后一个是完全空置miso后的结果,4.2灯暗

使用特权

评论回复
14
lbx_00| | 2010-3-11 10:44 | 只看该作者
lz别吓人,我要用这个片子

使用特权

评论回复
15
wanghongjie321| | 2013-4-14 11:48 | 只看该作者
你好,我想问一下:在接收和发送模式下,spicon的第六位(发送和中断模式选择)是不是应该不一样啊?应该是发送时置一,让其spitx为空时产生中断,接收时清零,让spirx读完后产生中断? 请各位前辈解答疑惑。

使用特权

评论回复
16
余寒| | 2013-4-15 20:59 | 只看该作者

使用特权

评论回复
17
matao0711| | 2013-5-3 15:03 | 只看该作者
楼主,我最近正在搞aduc7026 spi通讯控制led驱动,看了你5年前的帖子,请问能否借鉴一下当时spi的例程?

使用特权

评论回复
18
ayb_ice| | 2013-5-3 15:22 | 只看该作者
不太可能是ADI问题吧,
参照官方例程吧

使用特权

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

本版积分规则

95

主题

759

帖子

3

粉丝