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.

[参考译文] MSP430FR2532:试图建立一个带有 AIS2IH 加速计的 SPI 总线接口、但是无法获得所需的输出。

Guru**** 664280 points
Other Parts Discussed in Thread: MSP430FR2532, MSP430G2553
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1308047/msp430fr2532-trying-to-establish-a-spi-bus-interface-with-an-ais2ih-accelerometer-but-unable-to-get-the-send-the-required-output

器件型号:MSP430FR2532
主题中讨论的其他器件: MSP430G2553

我正在进行一个项目、即将 AIS2IH 加速器与用于运动检测产品的 MSP430FR2532电路板连接起来。 我已经决定使用 SPI 总线接口来连接两个器件。 硬件连接如下所示:

 

MSP430 AIS2IH SPI 含义
P2.2 CS (特定于 AIS2IH?)
P1.1/USB0CLK SPC SCLK
P1.2/UCB0SIMO sdi 西莫
P1.3/UCB0SOMI SDO SOMI

针对 MSP430电路板上的 SPI 设置、已完成以下操作:

1.根据 AIS2IH SPI 要求、将 UCB0CTL0设置为首先发送最高有效位
2。由于未激活时的时钟必须为高电平、因此时钟极性设置为高电平
3。同样、当没有 SPI 操作时、P2.2设置为高电平;对于 SPI 操作、P2.2设置为低电平
4.在将 P2.2设定为低电平之前、时钟被关闭。 我这样做是因为根据我的理解、无论 MSP430的时钟状态或电源模式如何、时钟都被设置为用于外设操作的有效状态(从 MSP430FR2xx 系列用户指南的第3.2.12节)

下面是我尝试创建的信号:

1) CS 是串行端口使能且由 MSP430控制。 它在传输开始时变为低电平、并在传输结束时恢复为高电平、

2) SPC 是串行端口时钟并且由 MSP430控制。 当 CS 为高电平(无传输)时、停止为高电平。  

3) SDI 和 SDO 在 SPC 的下降沿被驱动、且应在 SPC 的上升沿被捕获。

4) 4) 读取寄存器和写入寄存器命令都以16个时钟脉冲或8的倍数(如果是多个读取/写入字节)完成。

5)第一个位(位0)从 CS 下降沿之后 SPC 的第一个下降沿开始、而最后 一个位在 CS 上升沿之前 SPC 的最后一个下降沿开始。

6) 位0:rw 位。 0表示写入、1表示读取

7) 位1-7:地址 AD (6:0)

8) 8)位8-15:在 SDI 线路中进行写入、在 SDO 线路中进行 读取

9) 9)所有数据均以 MSB 格式写入

在尝试使用 ais2ih 驱动程序来确定器件是否已连接时、我得到的消息是未获得预期输出。 我怀疑我可能没有正确发送信号。  ais2ih_device_id_get (&dev_ctx、&whoami);将 whoi 值指定为0x00

有人能帮助我解决此问题并向我指出正确的方向来连接这两个设备吗? 我在下面附上了我的代码:

#include <msp430.h>
#include "ais2ih_reg.h"

int main() {
    GPIO_Init();
    SPI_Init();
    __delay_ms(BOOT_TIME); //Say 20 ms
    Ais2ih_device();
}

void GPIO_Init(void){
    P2OUT |= BIT2;
    P2DIR |= BIT2;
    P2SEL0 &= ~BIT2;
    P2SEL1 &= ~BIT2;
}

void SPI_Init(void) {
    // Selecting the P1.1 and P1.3 and disabling the interrupt
    P1SEL1 = BIT1 + BIT2 + BIT3;

    UCB0CTL1 |= UCSWRST;                            // Holding USCI in reset state
    UCB0CTL0 |= UCSYNC;                             // Setting to SPI mode
    UCB0CTL0 &= ~UC7BIT >> 8;                       // Setting to 8-bit data
    UCB0CTL0 |= UCMODE_0 >> 8;                      // 3-pin SPI
    UCB0CTL0 |= UCMST >> 8;                         // Master Mode selected
    UCB0CTL0 |= UCMSB >> 8;                         // AIS2IH uses MSB
    UCB0CTL0 &= ~ UCCKPL >> 8;                      // Inactive state is low
    UCB0CTL1 |= UCSSEL__SMCLK;                    
    UCB0BR0 = 0x02;                                 
    UCB0CTL1 &= ~UCSWRST;                           // Finishing Setup
    PM5CTL0 &= ~LOCKLPM5;                     
}


int32_t msp430_spi_TX(uint8_t data) {
    while (!(UCTXIFG)); // Waiting for sensor getting ready to transfer
    UCB0TXBUF = data;
    while ((UCB0STAT & UCBUSY));
    return 0;
}

int32_t msp430_spi_RX(uint8_t *data) {
    while (!(UCRXIFG)); // Waiting for sensor getting ready to receive
    *data = UCB0RXBUF;
    while ((UCB0STAT & UCBUSY));
    return 0;
}

int32_t platform_write(void *handle, uint8_t Reg, const uint8_t *Bufp, uint16_t len) {
    int32_t ret;
    CSCTL5 |= SMCLKOFF;
    P2OUT &= ~BIT2;
    while(P2OUT&BIT2 != BIT2);
    uint8_t ad_w = Reg & ~BIT7;
    ret = msp430_spi_TX(ad_w);
    uint16_t i=0;
    for(;i < len; i++){
        if(ret == 0){
            ret = msp430_spi_TX(Bufp[i]);
        }
    }
    P2OUT |= BIT2;
    CSCTL5 &= ~SMCLKOFF;
    return ret;
}

int32_t platform_read(void *handle, uint8_t Reg, uint8_t *Bufp, uint16_t len) {
    int32_t ret;
    CSCTL5 |= SMCLKOFF;
    P2OUT &= ~BIT2;
    while(P2OUT&BIT2 != BIT2);
    uint8_t ad_r = Reg | BIT7;
    ret = msp430_spi_TX(ad_r);
    uint16_t i=0;
    for(;i < len; i++){
        if(ret == 0){
            ret = msp430_spi_RX(Bufp + i);
        }
    }
    P2OUT |= BIT2;
    return ret;
}

void Ais2ih_device(void) {
    uint8_t whoamI;
    stmdev_ctx_t dev_ctx;
    dev_ctx.write_reg = platform_write;
    dev_ctx.read_reg = platform_read;
    ais2ih_device_id_get(&dev_ctx, &whoamI);
    if (whoamI != AIS2IH_ID)
      while (1) {
        /* manage here device not found */
      }
}


读取发生在 platform_read 函数内:

1) 1)我根据 MSP430FR2xxx 系列用户指南中的第3.2.12节关闭 SMCLK。  

2) 2) P2.2是控制位并设置为低电平以启动操作

3) 3)因为 SMCLK 已关闭、所以直到写入缓冲区时才会出现时钟下降沿

4)等待使用 while 循环、以确保 GPIO 有时间将 P2.2设置为低电平

此外、如果有人可以指向诸如 CLB 之类的任何 HAL 层、  
HAL_SPI_TRANSMIT (Handle、Registered、11000);//对于 STM 器件

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

        UCB0CTL0 &= ~UC7BIT >> 8;                       // Setting to 8-bit data
        UCB0CTL0 |= UCMODE_0 >> 8;                      // 3-pin SPI
        UCB0CTL0 |= UCMST >> 8;                         // Master Mode selected
        UCB0CTL0 |= UCMSB >> 8;                         // AIS2IH uses MSB
        UCB0CTL0 &= ~ UCCKPL >> 8;                      // Inactive state is low

    我不知道您为什么要移动这些预定义的位模式。 控制寄存器为16位宽、这些模式与该模式相匹配。

    无需调整 SMCLK。 SPI 端口仅在发送数据时生成时钟输出。

    基本 SPI 发送和接收函数在许多方面存在问题。 其中一个字节、SPI 同时发送和接收。 如果没有时钟、它就无法接收数据、并且仅在发送时生成时钟。 对于轮询操作、我使用组合函数。 例如、以下是我在具有不同中断标志的 msp430g2553项目中使用的代码:

    char spi_write(char data)
    {
      UCB0TXBUF = data;
      while(!(IFG2 & UCB0RXIFG))
        ;
      return UCB0RXBUF;
    }
    
    此外、测试一个常量(UCRXIFG)不会完成你想要的操作。 (UCB0IFG 和 UCRXIFG)将会产生。

    可能还会出现其他问题。

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

    我移动位模式、因为 UCB0CTL0是8位值、而 UC7BIT 是16位值。

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

    谢谢。 我将尝试在 SPI 发送和接收函数中实现这些更改。

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

    TI 提供的常用定义为:

    sfr_w(UCB0CTLW0);                             /* USCI B0 Control Word Register 0 */
    sfr_b(UCB0CTLW0_L);                           /* USCI B0 Control Word Register 0 */
    sfr_b(UCB0CTLW0_H);                           /* USCI B0 Control Word Register 0 */
    

    我还看到了您正在使用的字节访问的替代定义、但这完全令人困惑。  例如、UCB0CTL1 |= UCSWRST 设置高字节的 lsb、也称为 UCSYNC。

    您想要的是:UCB0CTLW0 = UCSWRST;

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

    我尝试进行以下更改:
    1) 1)未调整 SMCLK
    2) 2)将 SPI 初始化转换为 改用 UCB0CTLW0
    3) 3)修改了如下读取和写入命令


    int32_t MSP430_SPI_WRITE (uint8_t data){
    while (!(UCB0IFG 和 UCTXIFG));
    UCB0TXBUF =数据;
    返回0;

    int32_t MSP430_SPI_READ (uint8_t *数据){
    while (!(UCB0IFG 和 UCRXIFG));
    *data = UCB0RXBUF;
    返回0;


    我选择不使用组合轮询函数、因为有时 AIS2IH 中的 SPI 操作可以一次读取或写入多个寄存器。

    一个示例在我的板上运行此代码、在执行任何 SPI 操作之前、我得到了以下信息:
    1) 1)  UCB0CTLW0 = 0x69C0

    2) P2_OUT = 0x00

    3) 3) UCB0BR0 = 0x02

    在此之后、当我  向 UCB0TXBUF 寄存器写入0x8F (获取器件 ID)时、预期输出是从 AIS2IH 传感器获取 UCB0RXBUF 寄存器中的器件 ID (0x44)。
    然而、实际输出如下、 UCRXIFG 被设定为1意味着 MSP430接收到来自传感器的数据但 UCB0RXBUF = 0x00。

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

    您告诉我您做了什么、而不是显示代码、因此我必须猜测您所做的事情。 我怀疑你调用了具有0x8F 参数的 SPI_WRITE 函数。 然后调用 SPI_READ 函数。

    这是不起作用的。 为什么? 因为每个 SPI 发送都包括一个接收。 在发送0x8F 读取寄存器命令时、SPI 端口也在接收数据。 这就是您看到的情况。 你绝不会实际生成时钟来读取寄存器。

    我的函数发送一个字节并返回一个字节是出于某种原因。 要使用该函数读取寄存器、我要写入:

       spi_write(0x8f);
       read_value = spi_write(0xff);
       

    第一个调用发送读取寄存器命令并忽略读取数据。 第二个调用发送一个虚值以生成时钟并使用读取的数据。

    它在发送或接收多个字节时也能正常工作。 我一直这么做。

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

    我想我现在明白你的意思了。 生成时钟信号仅用于发送。 所以这段时间、SPI 操作在前8位停止、实际上没有从传感器读取数据。 为了解决这个问题、我应该发送虚拟值、如0x00、以便我可以读取。

    谢谢您的建议。 这种行为已经让我回避了一段时间。

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

    我尝试进行您建议的更改。 由于控制器处于主机模式以接收数据、因此必须 对 UCB0TXBUF 进行写入操作。 在这种情况下、 一旦 数据被写入 TX 缓冲区、UCB0RXIFG 就会被置位 、一旦 RX 缓冲区被读取、UCB0RXIFG 就会被清零。 但是、我始终只 从 RX 缓冲区中获得值0x00。 还有其他我可能遗漏的东西吗?

    #include <msp430.h>
    #include "ais2ih_reg.h"
    
    void GPIO_Init(void);
    void SPI_Init(void);
    int32_t msp430_spi_transmit_and_receive(uint8_t data_to_write, uint8_t *data_to_read);
    int32_t msp430_cs_bit_set(uint8_t cs);
    int32_t platform_write(void *handle, uint8_t Reg, const uint8_t *Bufp, uint16_t len);
    int32_t platform_read(void *handle, uint8_t Reg, uint8_t *Bufp, uint16_t len);
    void Ais2ih_device(void);
    
    
    int main() {
        GPIO_Init();
        SPI_Init();
        __delay_ms(BOOT_TIME); //Say 20 ms
        Ais2ih_device();
    }
    
    void GPIO_Init(void){
        P2OUT |= BIT2;
        P2DIR |= BIT2;
        P2SEL0 &= ~BIT2;
        P2SEL1 &= ~BIT2;
    }
    
    /*
     * Set final value of UCBoCTLW0 to 0x69C0
     */
    void SPI_Init(void) {
        // Selecting the P1.1 and P1.3 and disabling the interrupt
        P1SEL1 = BIT1 + BIT2 + BIT3;
    
        P1SEL1 = BIT1 + BIT2 + BIT3;
    
        UCB0CTLW0 |= UCSWRST; 
    
        UCB0CTLW0 |= UCSYNC;   
        UCB0CTLW0 &= ~UC7BIT;  
        UCB0CTLW0 |= UCMODE_0;          
        UCB0CTLW0 |= UCMST;                         
        UCB0CTLW0 |= UCMSB;                             
        UCB0CTLW0 |= UCCKPL;                          
        UCB0CTLW0 |= UCSSEL__SMCLK;                 
        UCB0BR0 = 0x02;
    
        UCB0CTLW0 &= ~UCSWRST;
        PM5CTL0 &= ~LOCKLPM5;
    }
    
    
    int32_t msp430_spi_transmit_and_receive(uint8_t data_to_write, uint8_t *data_to_read) {
        while(!(UCB0IFG & UCTXIFG));
        UCB0TXBUF = data_to_write;
    
        while ((UCB0STAT & UCBUSY));
    
        while(!(UCB0IFG & UCRXIFG));
        *data_to_read =  UCB0RXBUF;
        return 0;
    }
    
    int32_t msp430_cs_bit_set(uint8_t cs) {
        if( cs == 0){
            P2OUT &= ~BIT2;
            while(P2OUT&BIT2 == BIT2);
        }
        else {
            P2OUT |= BIT2;
            while(P2OUT&BIT2 != BIT2);
        }
    }
    
    int32_t platform_write(void *handle, uint8_t Reg, const uint8_t *Bufp, uint16_t len) {
    
        int32_t ret;
        uint8_t read_data[1];
    
        msp430_cs_bit_set(0);
    
        // Send Register in ais2ih to be written to
        uint8_t ad_w = Reg & ~BIT7;
        ret = msp430_spi_transmit_and_receive(ad_w, read_data);
    
        // Send Data to write into register
        uint16_t i=0;
        for(; i < len; i++){
            if(ret == 0){
                ret = msp430_spi_transmit_and_receive(Bufp[i], read_data);
            }
        }
        msp430_cs_bit_set(1);
        return 0;
    }
    
    int32_t platform_read(void *handle, uint8_t Reg, uint8_t *Bufp, uint16_t len) {
        int32_t ret;
        uint8_t read_data[1];
        uint8_t dummy_write = 0;
    
        msp430_cs_bit_set(0);
    
        // Send Register in ais2ih to read from
        uint8_t ad_r = Reg | BIT7;
        ret = msp430_spi_transmit_and_receive(ad_r, read_data);
    
        // Read Data from the register
        uint16_t i=0;
        for(;i < len; i++){
            if(ret == 0){
                ret = msp430_spi_transmit_and_receive(dummy_write, Bufp + 0);
            }
        }
        msp430_cs_bit_set(1);
        return 0;
    }
    
    void Ais2ih_device(void) {
        uint8_t whoamI;
        stmdev_ctx_t dev_ctx;
        dev_ctx.write_reg = platform_write;
        dev_ctx.read_reg = platform_read;
        ais2ih_device_id_get(&dev_ctx, &whoamI);
        if (whoamI != AIS2IH_ID)
          while (1) {
            /* manage here device not found */
          }
    }

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

    您已经向读取/写入函数添加了无用的检查。

    发送缓冲区将始终为空、因此您不必查看 TXIFG 是否置1。 您无需检查 UCBUSY 以等待它完成发送。 这是在等待 RXIFG 被置位时处理的。

    当函数退出时、TXIFG 将被设置、RXIFG 将被清除。 为下一个通话做好准备。

    通过指针存储返回数据然后始终返回值零也没有意义。 调用该函数的代码出于某种原因测试该返回值、但始终为零。

    同样、CS 信号的置位代码包括无意义检查。 如果您在引脚设置为输出的情况下清除 P2OUT 中的某个位、则无需等待它更改状态。 在您可以读取 P2IN 之前就会进行此操作。 当然、您测试 P2OUT 是完全浪费时间的行为。  我定义了几个预处理器符号:

    #define ASSERT_CS  (P2OUT &= ~BIT0)
    #define NEGATE_CS  (P2OUT |= BIT0)
    

    读取寄存器的函数被写入以读取多个寄存器、但无法将索引递增到存储结果的数组中。 因此,当 len>1时,这将失败。

    假设它被正确调用。 不会显示您对 device_id_get()过于复杂的代码。

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

    感谢您指出 UCBUSY 检查。 我已经阅读了文件, UCTXIFG 被设置并不意味着发送/接收完成,但在进一步内省,一个不同的段落确实提到了它的冗余,因为检查 UCRXIFG 确实指示操作结束 .  

    我知道返回0这一无必要的含义。 我打算实现一个成功/失败 状态。

    正如您提到的、检查 CS 信号没有意义。 我在该主题之前进行了该更改、应该很快将其删除。 另外、使用预处理器符号看起来确实是一种更近一步的编程方法。

     Bufp + i 的错误对我来说是一个真正的错误。 当我在系统中移动代码时、我忘记

    下面是  我在 ais2ih 驱动程序代码中找到的 ais2ih_device_id_get 的代码:

    /*
     * The ais2ih_read_reg refers to the platform_read function
     * AIS2IH_WHO_AM_I = 0x0F
     *
     */
     
    int32_t ais2ih_device_id_get(stmdev_ctx_t *ctx, uint8_t *buff)
    {
      int32_t ret;
    
      ret = ais2ih_read_reg(ctx, AIS2IH_WHO_AM_I, buff, 1);
    
      return ret;
    }


    再次感谢您在这个问题上的专业知识和建议。

    谢谢

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

    尊敬的 David:

    感谢您的大力帮助!

    伊森