This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] MSP430F5438A:在 ISR 中取消选择芯片选择时丢失最后几位

Guru**** 2538950 points
Other Parts Discussed in Thread: MSP430F5438A

请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1082676/msp430f5438a-missing-last-few-bits-when-chip-select-is-deselected-in-isr

部件号:MSP430F5438A
“线程: 测试”中讨论的其它部件

我正在与 MSP430F5438A 微控制器合作,我正在使用 FreeRTOS 合作模式,勾选时间为1毫秒。
我有一个场景,需要确保在传输完成后(1毫秒内)尽快将芯片选择拉高(取消选择)。
因此,在最后一个字节被发送出去后,我试图将 ISR 内的 CS 值拉高。

但是,我在示波器上注意到,CS 在最后一个字节完全传输之前被拉高。
我尝试以两种不同的 SPI 时钟频率(12.8KHz 和128KHz)观察结果,并附加了示波器图像。

SPI 时钟(黄色)
芯片选择(粉红色)
莫西语(绿色)


带 SPI 时钟12.8KHz (UCA1BR0设置为100)


带 SPI 时钟128KHz (UCA1BR0设置为10)

从图像中可以看出,在 CS 被拉高后,最后几位仍在传输中。 漏掉的位数似乎随 SPI 时钟速率的不同而不同。

#include <msp430.h> 
#include <stdio.h>
#include <stdint.h>
#include "msp430f5xx_6xxgeneric.h"


/**
 * main.c
 */
typedef void (*myCallback)(void);
#define HWREG8(x)                                                             \
    (*((volatile uint8_t *)((uint16_t)x)))

/*******************************************************************************
 * Function declaration
 ******************************************************************************/
void spib_init(void);
void spib_setTx_buf(const uint8_t spibTxBuf[], uint16_t spibTxLen,
                    myCallback spibLocalCallback);
void transmit_data(void);
void deselect_CS(void);
/******************************************************************************/
uint8_t tx_testdata[6] = {'A','B','C','D','E','\n'};

void main(void)
{
	WDTCTL = WDTPW | WDTHOLD;	// stop watchdog timer
	// config CS pin
	P9SEL &= ~0x40;
	P9DIR |= 0x40;
	P9OUT |= 0x40;

	spib_init();
	__bis_SR_register(GIE);       // CPU off, enable interrupts
    transmit_data();
	while (1)
	{
	}
//	return 0;
}

void transmit_data(void)
{
    P9OUT &= ~0x40;
    spib_setTx_buf(tx_testdata, 6, &deselect_CS);
}

void deselect_CS(void)
{
    P9OUT |= 0x40;
    __delay_cycles(1000000);
    transmit_data();
}

//******************************************************************************
//
//SPI
//
//******************************************************************************
myCallback spibCallback;
volatile uint8_t *spibPTxData;                     // Pointer to TX data
volatile uint16_t spibTXByteCtr;

void spib_init(void)
{
    // config UCA1 MOSI and MISO pin
    P5SEL |= 0xC0;
    P5DIR |= 0x40;
    // config UCA1 clk pin
    P3SEL |= 0x40;
    P3DIR |= 0x40;

    UCA1CTL1 |= UCSWRST;
    UCA1CTL0 |= UCMST+UCSYNC+UCMODE_0+UCCKPH+UCCKPL+UCMSB;
    UCA1CTL1 |= UCSSEL__SMCLK;
    UCA1BR0 = 0x64;                           // 100
    UCA1BR1 = 0;
    UCA1CTL1 &= ~UCSWRST;
}

void spib_setTx_buf(const uint8_t spibTxBuf[], uint16_t spibTxLen,
                    myCallback spibLocalCallback)
{
    spibCallback = spibLocalCallback;
    spibPTxData = (unsigned char *)spibTxBuf;      // TX array start address
                                            // Place breakpoint here to see each
                                            // transmit operation.
    spibTXByteCtr = spibTxLen;              // Load TX byte counter

    HWREG8(USCI_A1_BASE + OFS_UCAxIFG) &= ~UCTXIE;
    HWREG8(USCI_A1_BASE + OFS_UCAxIE) |= UCTXIE;
    if (spibTXByteCtr > 0)
    {
        if (spibPTxData == NULL)
        {
            UCA1TXBUF = 0xFF;
        }
        else
        {
            UCA1TXBUF = *spibPTxData++;
        }
        spibTXByteCtr--;
    }
//    UCA1TXBUF = spitxbufdata;
}

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=USCI_A1_VECTOR
__interrupt
#elif defined(__GNUC__)
__attribute__((interrupt(USCI_A1_VECTOR)))
#endif
void USCI_A1_ISR (void)
{
    switch (__even_in_range(UCA1IV,4)){
        //Vector 2 - RXIFG
        case 2:
            break;
        case 4:
            if(0 < spibTXByteCtr)
            {
//                UCA1TXBUF = spitxbufdata;
                if (spibPTxData == NULL)
                {
                    UCA1TXBUF = 0xFF;
                }
                else
                {
                    UCA1TXBUF = *spibPTxData++;
                }
                spibTXByteCtr--;
            }
            else if(0 == spibTXByteCtr)
            {
                HWREG8(USCI_A1_BASE + OFS_UCAxIFG) &= ~UCTXIE;
                HWREG8(USCI_A1_BASE + OFS_UCAxIE) &= ~UCTXIE;
                if(NULL != spibCallback)
                    (*spibCallback)();
            }
            break;
        default:
            break;
    }
}

这是我用于生成上述结果的简化代码。

我想问,如果在最后一个字节传输后立即将 CS 置于 ISR 内部,为什么在 CS 被拉高后会出现最后几个周期?
此外,   为何在 SPI 时钟频率发生变化时,CS 被拉高后剩下的周期数会有所不同?
除 SPI 时钟频率外,是否还有其他可能的因素会导致 CS HIGH 后剩余的周期数不同(因此,在 CS 被拉高之前需要不同的延迟量)?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    更改 CS 前,您必须等待数据完成移出。 没有任何中断可以帮助您解决这一问题,就像其他部件一样。

    您将在每个字节的末尾获得一个接收中断,因此更改 CS 的最佳位置就在那里。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    出于某种原因,我收到了一封电子邮件,其中包含一封从未出现在这里的回复。

    我认为您不理解串行硬件的操作。 TXIFG 在 TXBUF 为空时设置,而不是在移位寄存器为空时设置。

    因此,在 TXBUF 中放置最后一个字节后,有一个字节正在移出过程中,另一个字节在 TXBUF 中等待。 当 TXBUF 传输到移位寄存器时,将发生下一个传输中断。 哪一项当然需要一些时间才能清空。

    假设您从未延迟向 TXBUF 提供数据,则可以在发生接收中断时检查 UCBUSY 状态标志。 如果已设置,则有更多的数据移出。 如果清除,则变速器完成。

    我看到的另一种方法是完全不使用传输中断。 而是使用接收中断。 中断总是在所有数据移出后发生。 缺点是,数据将会有轻微的暂停。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    嗨,大卫,谢谢你的详细解释,这有助于我的理解。

    很抱歉收到电子邮件通知,我发布了一条回复,但在测试中发现了一些错误,因此删除了帖子。 希望在重新更新之前进行更彻底的测试。

    在尝试使用接收中断后,我意识到我的代码似乎偶尔会失败一次计时要求。 这可能是由于您提到的数据出现轻微暂停,或者我的项目的其他部分。 我会尝试做更多测试,然后再更新。 但现在,CS 被拉得太早的问题似乎已经得到解决。 感谢你的帮助

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    现在是更新。

    我尝试使用接收中断,并计算它触发以跟踪接收到的最后一个字节的次数。 收到最后一个字节后,我将把 CS 拉高。 这解决了 CS 太早被拉高的问题。

    但是,我注意到有时会错过接收中断的问题,特别是当我的项目的其他部分触发了更高优先级的中断时。 因此,我触发的接收中断次数计数与预期数量不符,CS 也不会被拉高。 我尝试在接收中断开始时检查 UCBUSY 状态标志,以确定它是否可以用来表示接收的字节是最后一个字节,但它没有帮助。 除了计算接收中断已触发的次数之外,是否有更可靠的方法来判断最后一个字节发生了这种特定的接收中断?

    否则,我可能需要考虑 设置计时器中断,以便在最后一个传输中断期间的特定延迟后触发。 计时器中断触发时,CS 将被拉高。 在这种情况下,除 SPI 时钟频率外,是否还有其他因素可能导致所需的延迟发生变化?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    如果您使用 TXIFG 来保持 Tx“管道”处于满状态,则不难超限 Rx 端,因为 Tx 端有(8+8)位缓冲,但 Rx 端只有(8+7)位。

    大卫建议根本不使用 TXIFG,而是根据 RXIFG 设置 TXBUF。 这样,当您加载 TXBUF 时,“管道”始终为空。 您需要处理事务的第一个字节,但看起来您已经在这样做了。

    另一种方法是使用 TXIFG,忽略 RXIFG,但当 ISR 发现它没有更多的可用于 Tx 的字节时,只需在取消 CS 断言之前选择“While (UCA1STAT & UCBUSY)”。 对于快速 SPI,这不是很长(8*br0,减去您已经在 ISR 中花费的任何时间),但您应该判断这一点。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    大家好,布鲁斯,

    感谢您提出的替代建议。

    撤消 CS 前的 While (UCA1STAT 和 UCBUSY)检查也有助于解决问题,但由于我的 SPI 时钟频率将仅为128kHz,因此在 while 循环中花费的时间量对我来说是一个问题。

    关于戴维关于根本不使用 TXIFG 并基于 RXIFG 设置 TXBUF 的建议,我也尝试了该实施。 但是,也许我应该澄清一下,我正在与一名 SBAND 收款人合作。 每一字节传输后的轻微暂停 会影响 RF 传输,并导致难以对 RF 传输接收端捕获的数据进行解码。

    我计划在触发最后一个字节 TXIFG 后设置计时器中断。 计时器中断将在100us 后触发,在此期间我将检查(UCA1STAT 和 UCBUSY)。 在预期的情况下,届时不应设置 UCBUSY 位,我将取消对 CS 的断言。 但是,如果 UCBUSY 仍处于设置状态,我必须执行一些例外处理。 目前,我计划在发生这种情况时重置 SPI 总线。