我更像是一个高级软件人,但最近一直在研究一些嵌入式项目,所以我确信这里有一些显而易见的东西,虽然我花了一个多星期的时间试图调试这个和每个'此时谷歌的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;
}
}
对此的任何帮助将不胜感激! 谢谢!