首页 > 解决方案 > 当接收时钟和从机选择作为输入时,尝试通过 SPI 作为从机发送 10 位数据(带缓冲模式)

问题描述

我正在尝试模拟来自 10 位 AEAT-6010 旋转编码器的数据,并将其发送到 ATtiny3217 上 SPI 协议的 MISO 引脚上。ATtiny 充当从机,接收 CLK 和 SS 信号作为输入,通过输出数据(编码器值)来响应。编码器按照SSI协议发送数据,如下图:

编码器时序特性

当尝试在 ATtiny 上发送 8 位 SPI 协议的 10 位时,就会出现问题。SPI 协议的主控芯片是 TMS320F2808 芯片,我从它接收 11 个时钟脉冲和 SS 信号。测得的信号和数据如下图所示:

示波器测量

这里我尝试发送的数据0b10只是为了测试。我可以在 MISO 线上看到正确的数据,但信号中间有三个额外的 1。这是在 ATtinyBUFEN=1BUFWR=1SPI 设置中,如下面的配置所示(没有缓冲模式,三个 1 出现在最后 3 位上,并且第一位(MSB)被读取为 1):

int8_t SPI_0_init()
{

    SPI0.CTRLA = 0 << SPI_CLK2X_bp    /* Enable Double Speed: disabled */
                 | 0 << SPI_DORD_bp   /* Data Order Setting: enabled */
                 | 1 << SPI_ENABLE_bp /* Enable Module: enabled */
                 | 0 << SPI_MASTER_bp /* SPI module in slave mode */
                 | SPI_PRESC_DIV4_gc; /* System Clock / 4 */

    SPI0.CTRLB = 1 << SPI_BUFEN_bp   /* Buffer Mode Enable: enabled */
                 | 1 << SPI_BUFWR_bp /* Buffer Write Mode: enabled */
                 | SPI_MODE_0_gc     /* SPI Mode 1 */
                 | 0 << SPI_SSD_bp;  /* Slave Select Disable: disabled */

    SPI0.INTCTRL = 0 << SPI_DREIE_bp    /* Data Register Empty Interrupt Enable: enabled */
                   | 1 << SPI_IE_bp     /* Interrupt Enable: enabled */
                   | 0 << SPI_RXCIE_bp  /* Receive Complete Interrupt Enable: disabled */
                   | 0 << SPI_SSIE_bp   /* Slave Select Trigger Interrupt Enable: disabled */
                   | 0 << SPI_TXCIE_bp; /* Transfer Complete Interrupt Enable: disabled */

    return 0;
}

我已经确认 TMS320F2808 的 SPI 模块和 ATtiny 的数据是相同的(在时钟下降沿读取)。关于 SPI 的缓冲区如何工作或 SPI 的中断,我是否遗漏了什么?当启用不同的中断(除了清除标志)时,我不确定在 ISR 中要做什么。这是我的主要功能(ISR 暂时为空):

int main(void)
{
    /* Initializes MCU, drivers and middleware */
    atmel_start_init();

    sei(); // Enable global interrupts

    /* Replace with your application code */
    while (1) {

        SPI_transmit(enc_data_L);

}

其中SPI_transmit()如下:

void SPI_transmit(uint16_t enc_data)
{
    // Then start the transmission by assigning the data to the SPI data register
    SPI0.DATA = enc_data;
    //SPI0.DATA = enc_data_H;

    // Now wait for the data transmission to complete by periodically checking the SPI status register
    //the SPI_IF is the only interrupt flag with a function in non-buffered mode.
    while(!(SPI0.INTFLAGS & (SPI_RXCIF_bm)));
    SPI0.DATA; //Dummy read to clear flag
}

我也尝试过按照这里的建议拆分 16 位数据,但问题仍然存在于 8 位数据中。希望这是足够的背景信息。我很感谢任何想法!

标签: avrspiavr-gccencoderattiny

解决方案


SPI_transmit写入缓冲区后,您正在等待传输完成。之后,输入缓冲区被完全传输,现在是空的。

  while(!(SPI0.INTFLAGS & (SPI_RXCIF_bm))); // waits until data is transmitted

RXCIF当接收缓冲区中有数据时置位,即输出缓冲区的传输也完成。因此,当主机发送下一个字节时,输出缓冲区中还没有新数据要发送。可能 SPI 模块只是重复传输前一个字节。

因此,不要等待 RXCIF 标志,而是等待 DREIF 标志,当传输缓冲区为空并准备好接收下一个字节时,该标志将被设置。您可以忽略传入的数据,因为无论如何您都不会使用它

void SPI_transmit(uint16_t enc_data)
{
    // Wait until buffer is ready to get the next data byte
    while(!(SPI0.INTFLAGS & (SPI_DREIF_bm))); 

    // Then start the transmission by assigning the data to the SPI data register
    SPI0.DATA = enc_data;

    // Don't care about read buffer
}

推荐阅读