MSP430 I2C从器件保持时钟线低

时间:2016-11-09 12:12:36

标签: c i2c msp430

我更像是一个高级软件人,但最近一直在研究一些嵌入式项目,所以我确信这里有一些显而易见的东西,虽然我花了一个多星期的时间试图调试这个和每个'此时谷歌的MSP相关链接是紫色的......

我目前将MSP430F5529设置为I2C从设备,其当前唯一的责任是从主设备接收数据包。主机使用工业级I2C,并经过严格测试,并排除了我的问题来源。我使用TI v15.12.3.LTS编译器将Code composer用作我的IDE。

当前正在发生的事情是主设备查询从设备可以容纳多少数据包(大小为62字节),然后通过MSP当前正在丢弃的几个数据包发送。这在主机侧每100ms发生一次,对于MSP下面的最小示例,当被问及它可以容纳多少数据包时,它总是会发回63。我已经使用Total Phase Aardvark对主人进行了测试,一切都运行良好,所以我确信这是MSP方面的一个问题。问题如下:

该程序将工作15-20分钟,发送数万个数据包。在某些时刻,从器件开始将时钟线保持为低电平,并且在调试模式下暂停时,显示卡在启动中断中。每次发生相同的事件序列都会导致这种情况发生。

1)Master查询MSP可以容纳多少个数据包 2)成功发送数据包
3)尝试了另一个包但是< MSP接收62个字节(通过记录我收到的Rx中断数来计算)。没有发送停止条件,因此主人超时。
4)尝试另一个数据包。在发送停止条件之前发送一个字节 5)尝试发送另一个数据包。启动中断,然后发生Tx中断,设备挂起。

忽略我没有处理主方面的超时错误这一事实,会发生一些非常奇怪的事情导致事件序列,但这就是每次都会发生的事情。

以下是重现问题的最小工作示例。我特别关注的是SetUpRx和SetUpTx函数。 Code Composer Resource Explorer提供的示例仅包含Rx或Tx的示例,我不确定我是否以正确的方式组合它们。我还尝试完全删除SetUpRx,将设备置​​于发送模式,并用mode = TX_MODE / RX_MODE替换所有对SetUpTx / Rx的调用,这样做确实有效,但最终仍将时钟线保持为低电平。最终,我不能100%确定如何设置它以接收Rx和Tx请求。

#include "driverlib.h"

#define SLAVE_ADDRESS           (0x48)

// During main loop, set mode to either RX_MODE or TX_MODE
// When I2C is finished, OR mode with I2C_DONE, hence upon exit mdoe will be one of I2C_RX_DONE or I2C_TX_DONE
#define RX_MODE                 (0x01)
#define TX_MODE                 (0x02)
#define I2C_DONE                (0x04)
#define I2C_RX_DONE             (RX_MODE | I2C_DONE)
#define I2C_TX_DONE             (TX_MODE | I2C_DONE)


/**
 * I2C message ids
 */
#define MESSAGE_ADD_PACKET      (3)
#define MESSAGE_GET_NUM_SLOTS   (5)

static volatile uint8_t mode = RX_MODE;     // current mode, TX or RX

static volatile uint8_t rx_buff[64] = {0};  // where to write rx data
static volatile uint8_t* rx_data = rx_buff; // used in rx interrupt

static volatile uint8_t tx_len = 0;         // number of bytes to reply with

static inline void SetUpRx(void) {
    // Specify receive mode
    USCI_B_I2C_setMode(USCI_B0_BASE, USCI_B_I2C_RECEIVE_MODE);

    // Enable I2C Module to start operations
    USCI_B_I2C_enable(USCI_B0_BASE);

    // Enable interrupts
    USCI_B_I2C_clearInterrupt(USCI_B0_BASE, USCI_B_I2C_TRANSMIT_INTERRUPT);
    USCI_B_I2C_enableInterrupt(USCI_B0_BASE, USCI_B_I2C_START_INTERRUPT + USCI_B_I2C_RECEIVE_INTERRUPT + USCI_B_I2C_STOP_INTERRUPT);

    mode = RX_MODE;
}


static inline void SetUpTx(void) {
    //Set in transmit mode
    USCI_B_I2C_setMode(USCI_B0_BASE, USCI_B_I2C_TRANSMIT_MODE);

    //Enable I2C Module to start operations
    USCI_B_I2C_enable(USCI_B0_BASE);

    //Enable master trasmit interrupt
    USCI_B_I2C_clearInterrupt(USCI_B0_BASE, USCI_B_I2C_RECEIVE_INTERRUPT);
    USCI_B_I2C_enableInterrupt(USCI_B0_BASE, USCI_B_I2C_START_INTERRUPT + USCI_B_I2C_TRANSMIT_INTERRUPT + USCI_B_I2C_STOP_INTERRUPT);

    mode = TX_MODE;
}


/**
 * Parse the incoming message and set up the tx_data pointer and tx_len for I2C reply
 *
 * In most cases, tx_buff is filled with data as the replies that require it either aren't used frequently or use few bytes.
 * Straight pointer assignment is likely better but that means everything will have to be volatile which seems overkill for this
 */
static void DecodeRx(void) {
    static uint8_t message_id = 0;

    message_id = (*rx_buff);
    rx_data = rx_buff;

    switch (message_id) {
    case MESSAGE_ADD_PACKET:        // Add some data...
        // do nothing for now
        tx_len = 0;

        break;

    case MESSAGE_GET_NUM_SLOTS:     // How many packets can we send to device
        tx_len = 1;

        break;

    default:
        tx_len = 0;

        break;
    }
}

void main(void) {
    //Stop WDT
    WDT_A_hold(WDT_A_BASE);

    //Assign I2C pins to USCI_B0
    GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P3, GPIO_PIN0 + GPIO_PIN1);

    //Initialize I2C as a slave device
    USCI_B_I2C_initSlave(USCI_B0_BASE, SLAVE_ADDRESS);

    // go into listening mode
    SetUpRx();

    while(1) {
        __bis_SR_register(LPM4_bits + GIE);

        // Message received over I2C, check if we have anything to transmit
        switch (mode) {
        case I2C_RX_DONE:
            DecodeRx();
            if (tx_len > 0) {
                // start a reply
                SetUpTx();
            } else {
                // nothing to do, back to listening
                mode = RX_MODE;
            }
            break;

        case I2C_TX_DONE:
            // go back to listening
            SetUpRx();

            break;

        default:
            break;
        }
    }
}

/**
 * I2C interrupt routine
 */
#pragma vector=USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void) {
    switch(__even_in_range(UCB0IV,12)) {
    case USCI_I2C_UCSTTIFG:
        break;

    case USCI_I2C_UCRXIFG:
        *rx_data = USCI_B_I2C_slaveGetData(USCI_B0_BASE);
        ++rx_data;

        break;

    case USCI_I2C_UCTXIFG:
        if (tx_len > 0) {
            USCI_B_I2C_slavePutData(USCI_B0_BASE, 63);
            --tx_len;
        }

        break;

    case USCI_I2C_UCSTPIFG:
        // OR'ing mode will let it be flagged in the main loop
        mode |= I2C_DONE;
        __bic_SR_register_on_exit(LPM4_bits);

        break;
    }
}

对此的任何帮助将不胜感激! 谢谢!

0 个答案:

没有答案