winstarst 发表于 2007-11-10 16:35:58

SPI 通信,Atmega128 作为从端,中断方式发数据不成功

各位好,这两天在弄 ADF7020,它的数据接口只有两根线,clk 和 data,符合串行口的时序,所以想用 SPI 来访问,ADF7020 做主端,Atmega128 做从端,时钟由 ADF7020 提供,采用中断方式接收和发送数据,接收没有问题,但发送却失败,从示波器上看,发送时 data 上没有电平变化,在 AVR Studio 中和 led 灯上可以判断已经进了 SPI 中断服务程序,但数据总是发送不成功。

MISO 和 MOSI 通过 10K 电阻接在一块,然后再接到 ADF7020 的 data 线上,ss 接低电平

MISO 为输出,MOSI、SS、SClk 配置为输入,带上拉

把坛子上的帖子和手册都看了不少,至今没有解决,请大家帮帮忙,多谢~

ba_wang_mao 发表于 2007-11-10 18:50:20

楼上兄弟
 主发送时,“从”必须事先将数据准备好,然后在“从”的中断服务程序中发送即可。

winstarst 发表于 2007-11-10 19:19:56

多谢 ba_wang_mao 的回答。。

这个过程我是了解的,从 SPI 的时序图可以看出来,就一个完整的 SPI 通信过程来说,应该是由主端发起,然后从端先把数据准备好,在采样完成后放到移位寄存器中,不知道我理解的对不对??

刚刚我又跟踪了一下代码,从示波器上看,在发送的时候,data 线上已经有个电平变化,但现在还判断不了数据通信有没有成功,不知道有没有什么办法来测试,哪位介绍一个方法。

可能问题在 ADF7020 的通信方式上,我是在采样到一个高低电平变化后就往 SPDR 送数据的,不知道这样是不是符合 SPI 的要求。

winstarst 发表于 2007-11-10 19:23:33

刚刚在 2lib 上找到一个帖子,提到了一点,还没有测试,不知道是不是,先请大家确认一下,帖子内容如下:
关于8515的SPI的问题

最近在用到8515的SPI时,发现一个问题,觉得有些怪,说出来,大家讨论一下:

我一般将其SPI做为从机方式输入同步数据,数据速率一般是64K-384Kbps。以前都是收数据,一直没出现问题。最近这个项目要求在收取数据的同时要将数据从SPI口发送出去。由于要求同步,所以只能工作在从机方式。我用的是中断方式。这样问题就出来了,接收还是没有问题,但发送出现了问题:在从机模式下,发送数据必须在中断后的半个时钟周期内发送出去,否则发送的只是FFH,而不是只要保证在下一个中断来之前添入就行。我是通过程序中加入NOP用数字示波器测试到的。
这样处理就比较麻烦,因为如果是工作在384Kbps,那么半个时钟周期就是1.3us,这个时间是非常短的。除了单片机响应中断的时间,就所剩无几了,况且如果有timer中断的话,就根本不能保证了。
我仔细看了一下8515的SPI的资料,发现它实际上并没有发送缓冲寄存器,只有一个接收缓冲寄存器。发送和接收同时使用一个移位寄存器。这样当中断的时候,移位寄存器的数据装入接收缓冲器,由于没有发送寄存器,所以要求单片机尽快把数据装载入移位寄存器内,因为单片机在下一个时钟沿就要发送数据了。我想它是在中断后的半个时钟沿时对移位寄存器内的数据进行保护。所以导致了上面出现的问题。
但是当工作于主机方式时,却没有问题,因为SPI要等到数据装载入移位寄存器后才启动发送时钟。
所以我想,SPI的这种结构和UART不同,在发送方向没有缓冲器。它本来不是为了在从机模式下,而且是同步时钟,发送数据的。

winstarst 发表于 2007-11-10 19:35:23

ISR( SPI_STC_vect)
{
    UINT8 temp;

    // Handle SPI interrupt
    if (dd_data_port_state == DATA_PORT_TRANSMITTING)   // 数据发送
    {
      switch (dd_data_packet_phase)
      {
            case PACKET_PHASE_PREAMBLE: // 前导码
            case PACKET_PHASE_SYNCWORD: // 同步字符
                temp = p_mac_active_tx_frame;
                SPDR = temp;

                dd_port_data_tx_frame_idx++;
                if (dd_port_data_tx_frame_idx >= 8 )       
                {
                  dd_data_packet_phase = PACKET_PHASE_HEADER;
                  p_mac_active_tx_frame = ((pUINT8) &mac_tx_packet_header.payload_length) - 3;
                  dd_port_data_tx_frame_idx = 0;
                  break;
                }
                break;

            case PACKET_PHASE_HEADER:
                temp = p_mac_active_tx_frame;
                SPDR = temp;
                crc16(temp);

                if (dd_port_data_tx_frame_idx >= sizeof(MAC_PACKET_HEADER_T) )       
                {
                  dd_data_packet_phase = PACKET_PHASE_PAYLOAD;
                  p_mac_active_tx_frame = &dd_port_data_tx_buffer;
                  dd_port_data_tx_frame_idx = 0;
                  break;
                }
                break;

            case PACKET_PHASE_LENGTH:
                break;

            case PACKET_PHASE_PAYLOAD:
                temp = p_mac_active_tx_frame;
                SPDR = temp;
                crc16(temp);

                if (dd_port_data_tx_frame_idx >= mac_tx_packet_header.payload_length)
                {
                  dd_data_packet_phase = PACKET_PHASE_CRC;
                  dd_port_data_tx_frame_idx = 0;
                  break;
                }
                break;

            case PACKET_PHASE_CRC:
                if (dd_port_data_tx_frame_idx == 0)
                {
                  SPDR = crc_lo;
                  dd_port_data_tx_frame_idx++;
                }
                else if (dd_port_data_tx_frame_idx == 1)
                {
                  SPDR = crc_hi;
                  dd_port_data_tx_frame_idx++;
                }
                else if (dd_port_data_tx_frame_idx >= 2)
                {       
                  dd_port_data_tx_frame_idx = 0;
                  DISABLE_SPI_INT();
                  DISABLE_SPI();
                }
                break;

            default:                                       
                break;
      }// switch
    }//DATA_PORT_TRANSMITTING

    if (dd_data_port_state == DATA_PORT_RECEIVING)
    {
      SPDR = 0xFF;
      switch (dd_data_packet_phase)
      {
            case PACKET_PHASE_HEADER:
                temp = SPDR;
                dd_port_data_rx_buffer = temp;
                crc16(temp);

                dd_port_data_rx_frame_idx++;
                if (dd_port_data_rx_frame_idx >= sizeof(MAC_PACKET_HEADER_T) )       
                {
                  dd_port_data_rx_payload_length = temp;
                  dd_data_packet_phase = PACKET_PHASE_PAYLOAD;
                  dd_port_data_rx_frame_idx = 0;
                  break;
                }
                break;

            case PACKET_PHASE_PAYLOAD:
                temp = SPDR;
                dd_port_data_rx_buffer = temp;
                crc16(temp);
                dd_port_data_rx_frame_idx++;

                if (dd_port_data_rx_frame_idx >dd_port_data_rx_payload_length )
                {
                  dd_data_packet_phase = PACKET_PHASE_CRC;
                  dd_port_data_tx_frame_idx = 0;
                  break;
                }
                break;

            case PACKET_PHASE_CRC:
                temp = SPDR;
                dd_port_data_rx_buffer = temp;
               
                crc16(temp);
                dd_port_data_rx_frame_idx++;
                if (dd_port_data_rx_frame_idx >=2 )       
                {
                  DISABLE_SPI_INT();         // Disable SPI interrupt                                               
                  DISABLE_SPI();                // Disable SPI
                  dd_has_received_whole_frame = TRUE;
                  break;
                }
                break;
            default:                                       
                break;
      }// for switch.. case..
    }// DATA_PORT_RECEIVING
}

winstarst 发表于 2007-11-12 08:45:19

各位,帮帮忙啊,上面是 SPI 中断服务程序,也参照了 Paul 的那份代码,结构上应该没有问题,触发的那部分代码如下:

/*****************************************************************************
Function:                    transmit_constant_frame
==============================================================================
Description:
Transmit a frame including preamble & sync word, payload length,
payload and CRC
*****************************************************************************/
void transmit_constant_frame(void
{
    ENABLE_SPI_INT();          // Enable SPI interrupt       

    /* This prevents stray interrupts and SPDR overruns when priming */
    while ( get_sclock() );// 采样 clk 时钟
    while ( !get_sclock() );

    /* Enable SPI*/
    ENABLE_SPI();

    p_mac_active_tx_frame = &mac_preamble_syncword;// 同步字符
    dd_port_data_tx_frame_idx = 1;
    // 启动 SPI,数据已经放在 p_mac_active_tx_frame 里面
    SPDR = p_mac_active_tx_frame;
    dd_port_data_tx_frame_idx++;

    INTERRUPTS_ON;
}

winstarst 发表于 2007-11-12 18:10:32

看样子这个问题又要和以前一样了,我不知道怎么回事,本来坛子里人气不错,可每次的问题都没有人帮忙,让我很怀疑是不是我的问题太菜了,可我看到许多比的问题更菜的,也有人回答,还是说水平菜的太多,真正的高手一般不上来,现在浮在上面的都是跟我一样的菜鸟。

说实话,坛子上的资料不少,不过新东西不多,比较适合入门,想进步还是得自己下工夫,我看来看去,真正起到帮助作用的,还是前两年的帖子,像马老师的、HJJOURAVR、菜农的,还有很多,我记不上名了,我相信高手是有的,只是我运气不好,没遇到,呵呵。。

我向来喜欢自力更生,信奉自已动手,丰衣足食,Linus 不是有句话嘛,“让我们看代码吧!”。

本来就没报什么希望,没想到竟和想的一样,看样子以后还是尽量潜水,顺便帮帮比我更菜的兄弟,推他们一把,毕竟咱也算是入门了,以前走的弯路,不要让别人再走,能帮的就帮吧!!

路漫漫兮其修远,吾将上下而求索!!

自助者天助,相信自己永远也不会错!!

winstarst 发表于 2007-11-12 18:13:36

顺便说一句,前面的关于 SPI 的论述,我手头上没有数据示波器,也没有逻辑分析仪,所以不知道对不对,也没心情做实验,哪位如果看到了,请先确认一下,不要拿来就用,不加思考的接收,出了问题别找我,呵呵。。

ba_wang_mao 发表于 2007-11-13 12:58:47

这是我写的TLV5617-SPI发送程序

/////////////////////////////////////////////////////////////////
//模出芯片TLC5617,SCK在闲置时是高电平(时序图上有一根SCK为高时持平的线),
//由于模出芯片在下降沿锁存数据,由于CPOL=1,因此CPHA=0
/////////////////////////////////////////////////////////////////
void TLV5617_SPI_Init(void)
{
        SPCR = (1<<SPE) | (1<<MSTR) | (1<<CPOL) | (0<<CPHA) | (0 <<SPR1) | (1<<SPR0);
        SPSR = (1<<SPI2X);
}

//////////////////////////////////////////////////////////////////////////////
//操作步骤:
//        1.写重叠缓冲区锁存器
//        2.把串行接口寄存器的数据写入锁存器A并用缓冲区锁存数据更新锁存器B
//////////////////////////////////////////////////////////////////////////////
void SPI_TLV5617(unsigned int cha_num,unsigned int chb_num)
{
        unsigned char i;
        unsigned int cha_COMMAND = (cha_num << 2) | 0x8000;
        unsigned int chb_COMMAND = (chb_num << 2) | 0x1000;

        ADS7841_CS_1();
        TLV5617_SPI_Init();       
        TLV5617_CS_0();
        SPDR = chb_COMMAND >> 8;
        for (i = 0 ; i < 200 ; i++)        if (SPSR & 0x80) break;
        SPDR = chb_COMMAND & 0xff;
        for (i = 0 ; i < 200 ; i++)        if (SPSR & 0x80) break;
        TLV5617_CS_1();
       
        TLV5617_CS_0();
        SPDR = cha_COMMAND >> 8;
        for (i = 0 ; i < 200 ; i++)        if (SPSR & 0x80) break;
        SPDR = cha_COMMAND & 0xff;
        for (i = 0 ; i < 200 ; i++)        if (SPSR & 0x80) break;
        TLV5617_CS_1();
}

winstarst 发表于 2007-11-13 14:18:18

呵呵,又是 ba_wang_mao 兄,谢谢关注!!

仔细看了一下您的代码,代码结构上不错,尤其是 CPOL 和 CPHA 的说明让我长了见识,稍微的把代码修改一下,就可以作为 SPI 发送数据或命令的典型函数。

代码中用的是查询方式来发送数据的,配置成主端,通过 CS 引脚来发起通信。

如果是这样的话,我没有问题,问题的关键就在从端、中断、发送,接收也没有问题,已经实现了,就是通过中断发送没有搞定。

winstarst 发表于 2007-11-13 17:44:53

呵呵,问题搞定,没时间了在这个问题上深究了,最后还是用查询方式,不明白为什么中断发送就是失败,有空再测试一下

wwff2007wwff 发表于 2014-10-15 12:22:06

老兄,你的ADF7020采用什么调制方式啊,如果是GFSK,那么在发送时,时钟管脚是没有时钟的,这时发送会失败!
页: [1]
查看完整版本: SPI 通信,Atmega128 作为从端,中断方式发数据不成功