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.

[参考译文] MSP430FR2311:I2C 从机重复开始接收字传输

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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/758971/msp430fr2311-i2c-slave-repeat-start-for-receive-word-transactions

器件型号:MSP430FR2311

我正在尝试在 MSP430上实现 i2c 从器件。  我找到了一个名为"msp430fr231x_eusci_I2C_standard_slave.c"的寄存器示例。  只要命令代码不同、我就能够使用该代码执行读取操作和写入操作。  该代码基本上假定寄存器是只读或写的。  无法读取和写入、因为当您启动包含重复启动的读取序列时、器件会失败。  解决此问题的一种方法是创建指向同一寄存器的两条命令、但一条用于处理读取、另一条用于处理写入。  这似乎效率低下、但由于我控制链路的两端、我想这是可能的。

我现在要挂机的是如何处理这种重复启动情况。  我是否需要在中断例程中检测新的启动条件?  我注意到的一个问题是、当命令是 CMD_TYPE_X_MASTER 时、它会运行并禁用 RX 中断、这在很大程度上会使整个情况处于不良状态、因为 MSP430认为它将开始传输数据、 实际上、主器件将再次向其发送一个地址。  此时、一旦发生这种情况、通信几乎就完成了。  我在互联网上查看了有关如何执行此操作的任何线索、但似乎并不经常执行此操作。  如果有任何帮助,将不胜感激。

//
// MSP430FR231x 演示- eUSCI_B0、SPI 3线主器件递增数据
//
//说明:SPI 主器件使用3线模式与 SPI 从器件通信。 递增
//数据由从0x01开始的主器件发送。 接收到的数据应该
//与之前的传输一样 TXData = RXData-1。
// USCI RX ISR 用于处理与 CPU 的通信、通常在 LPM0中。
// ACLK = 32.768kHz、MCLK = SMCLK = DCO ~1MHz。 BRCLK = ACLK/2
//
// /|\/|\
// MSP430FR2311 4.7k |
// -------- |4.7K
// /|\| P1.3|-+-|- I2C 时钟(UCB.S.)
// || | |
// ---|RST P1.2|--- +/- I2C 数据(UCB0SDA)
// | |
// | |
// | |
// | |
// | |
// | |////

Nima Eskandari 和 Ryan Meredith
// Texas Instruments Inc.
// 2018年2月
//使用 CCS v7.3构建
/*********

#include 
#include 

//
//引脚配置
//

#define LED0_OUT P1OUT
#define LED0_DIR P1DIR
#define LED0_PIN BIT0

#define LED1_OUT P2OUT
#define LED1_DIR P2DIR
#define LED1_PIN BIT0

//*********
//示例命令(Example Commands Commands
//

#define SLAVE_ADDR 0x48

// CMD_TYPE_X_SLAVE 是主设备发送到从设备的示例命令。
*从属方将发送示例 SlaveTypeX 缓冲区进行响应。
*
* CMD_TYPE_X_MASTER 是主器件发送到从器件的示例命令。
*从器件将初始化自身以接收 MasterTypeX 示例缓冲区。
**/

#define CMD_TYPE_0_SLAVE 0
#define CMD_TYPE_1_SLAVE 1
#define CMD_TYPE_2_SLAVE 2

#define CMD_TYPE_0_MASTER 3
#define CMD_TYPE_1_MASTER 4
#define CMD_TYPE_2_MASTER 5

#define TYPE_0_LENGTH 1
#define TYPE_1_LENGTH 2
#define TYPE_2_LENGTH 6

#define MAX_BUFFER_SIZE 20

/* MasterTypeX 是在主设备中初始化的示例缓冲
区,主设备将*将它们发送给从设备。
* SlaveTypeX 是在从器件中初始化的示例缓冲
区,它们将由从器件*发送到主器件。
*/

uint8_t MasterType2 [type_2_length]={0};
uint8_t MasterType1 [type_1_length]={0、0};
uint8_t MasterType0 [type_0_length]={0};

uint8_t SlaveType2 [type_2_length]={'B、'D'、'1' "2"};
uint8_t SlaveType1 [TYP_1_LENGTH]={15、16};
uint8_t SlaveType0 [TYP_0_LENGTH]={12};

//********
//通用 I2C 状态机
//

typedef 枚举 I2C_ModeEnum{
IDLE_MODE、
NACK_MODE、
TX_REG_ADDRESS_MODE、
RX_REG_ADDRESS_MODE、
TX_DATA_MODE、
RX_DATA_MODE、
切换至 RX_MODE、
SWITHC_TO_TX_MODE、
TIMEOUT_MODE
}I2C_Mode;

//用于跟踪软件状态机的状态*/
I2C_Mode SlaveMode = RX_REG_ADDRESS_MODE;

//要使用的寄存器地址/命令*/
uint8_t ReceiveRegAddr = 0;

// ReceiveBuffer:用于接收 ISR 中数据的缓冲
区* RXByteCtr:要接收的字节数
*索引: ReceiveBuffer
* TransmitBuffer:用于在 ISR 中传输数据的缓冲区
* TXByteCtr:剩余要传输的字节数
* TransmitIndex:要在 TransmitBuffer 中传输的下一个字节的索引
*/
uint8_t ReceiveBuffer[MAX_buffer_size]={0};
uint8_t RXByteCtr = 0;
uint8_t ProcesseIndex = 0;
uint8_t TransmitBuffer[MAX_buffer_size]={0};
uint8_t TXByteCtr = 0;
uint8_t TransmitIndex = 0;


//根据接收到的 cmd
*
命令初始化软件状态机* cmd:uint8_t

接收到的 CMD_void *命令*

/*从设备和主设备之间的传输已完成。 使用 cmd
*执行事务后操作。 (将数据从 ProcesseBuffer
*放置到基于上次接收的相应缓冲区 cmd)
*
cmd:与已完成
的*事务对应的命令/寄存器地址
*
/ void I2C_Slave_TransactionDone (uint8_t cmd);
void CopyArray (uint8_t * source、uint8_t * dest、uint8_t cmd);

void I2C_Slave (uint8_t cmd)

ReceiveIndex = 0;
TransmitIndex = 0;
RXByteCtr = 0;
TXByteCtr = 0;

开关(cmd)
{
情况(CMD_TYPE_0_SLAVE): //发送从属设备 ID (此设备的 ID)
SlaveMode = TX_DATA_MODE;
TXByteCtr = TYPE_0_LENGTH;
//填充 TransmitBuffer
CopyArray (SlaveType0、TransmitBuffer、TYPE_0_LENGTH);
UCB0IE &=~UCRXIE; //禁用 RX 中断
UCB0IE |= UCTXIE; //启用 TX 中断
中断;
情况(CMD_TYPE_1_SLAVE): //发送从设备时间(该设备的时间)
SlaveMode = TX_DATA_MODE;
TXByteCtr = TYPE_1_LENGTH;
//填充 TransmitBuffer
CopyArray (SlaveType1、TransmitBuffer、type_1_length);
UCB0IE &=~UCRXIE; //禁用 RX 中断
UCB0IE |= UCTXIE; //启用 TX 中断
中断;
情况(CMD_TYPE_2_SLAVE): //发送从属设备位置(该设备的位置)
SlaveMode = TX_DATA_MODE;
TXByteCtr = TYPE_2_LENGTH;
//填充 TransmitBuffer
CopyArray (SlaveType2、TransmitBuffer、TYPE_2_LENGTH);
UCB0IE &=~UCRXIE; //禁用 RX 中断
UCB0IE |= UCTXIE; //启用 TX 中断
中断;
情况(CMD_TYPE_0_MASTER):
SlaveMode = RX_DATA_MODE;
RXByteCtr = TYPE_0_LENGTH;
UCB0IE &=~UCTXIE; //禁用 RX 中断
UCB0IE |= UCRXIE; //启用 TX 中断
中断;
情况(CMD_TYPE_1_MASTER):
SlaveMode = RX_DATA_MODE;
RXByteCtr = TYPE_1_LENGTH;
UCB0IE &=~UCTXIE; //禁用 RX 中断
UCB0IE |= UCRXIE; //启用 TX 中断
中断;
情况(CMD_TYPE_2_MASTER):
SlaveMode = RX_DATA_MODE;
RXByteCtr = TYPE_2_LENGTH;
UCB0IE &=~UCTXIE; //禁用 RX 中断
UCB0IE |= UCRXIE; //启用 TX 中断
中断;
默认值:
__no_operation();
中断;
}
}


void I2C_Slave_TransactionDone (uint8_t cmd)
{
开关(cmd)
{
情况(CMD_TYPE_0_SLAVE): //从设备 ID 已发送(此设备的 ID)
中断;
情况(CMD_TYPE_1_SLAVE): //从器件时间已发送(此器件的时间)
中断;
情况(CMD_TYPE_2_SLAVE): //发送从属设备位置(该设备的位置)
中断;
情况(CMD_TYPE_0_MASTER):
CopyArray (ReceiveBuffer、MasterType0、TYPE_0_LENGTH);
中断;
情况(CMD_TYPE_1_MASTER):
CopyArray (ReceiveBuffer、MasterType1、Type_1_length);
中断;
情况(CMD_TYPE_2_MASTER):
CopyArray (ReceiveBuffer、MasterType2、TYPE_2_LENGTH);
中断;
默认值:
__no_operation();
中断;
}
}

void CopyArray (uint8_t *源、uint8_t * dest、uint8_t count)
{
uint8_t copyIndex = 0;
for (copyIndex = 0;copyIndex < count;copyIndex++)
{
dest[copyIndex]= source[copyIndex];
}
}


//*********
//设备初始化
//

void initGPIO()
{
//LED
LED0_OUT &=~LED0_PIN;
LED0_DIR |= LED0_PIN;

LED1_OUT &=~LED1_PIN;
LED1_DIR |= LED1_PIN;

// I2C 引脚
P1SEL0 |= BIT2 | BIT3;
P1SEL1 &=~(BIT2 | BIT3);

//禁用 GPIO 上电默认高阻抗模式以激活
//先前配置的端口设置
PM5CTL0 &=~LOCKLPM5;
}

void initI2C()
{
UCB0CTLW0 = UCSWRST; //软件复位被启用
UCB0CTLW0 |= UCMODE_3 | UCSYNC; // I2C 模式、同步模式
UCB0I2COA0 = SLAVE_ADDR | UCOAEN; //自有地址和使能
UCB0CTLW0 &=~UCSWRST; //清除复位寄存器
UCB0IE |= UCRXIE + UCSTPIE;
}

void initClockTo16MHz ()
{
//根据 MCLK 的器件数据表的要求配置一个 FRAM 等待状态
//在配置时钟系统之前在8MHz 以上运行。
FRCTL0 = FRCTLPW | NWAITS_1;

_bis_SR_register (SCG0);//禁用 FLL
CSCTL3 |= SELREF_REFOCLK;//将 REFO 设置为 FLL 基准源
CSCTL0 = 0; //清除 DCO 和 MOD 寄存器
CSCTL1 &=~(DCORSEL_7); //首先清除 DCO 频率选择位
CSCTL1 |= DCORSEL_5; //设置 DCO = 16MHz
CSCTL2 = FLLD_0 + 487; //设置为 fDCOCLKDIV =(FLLN + 1)*(fFLLREFCLK/n)
// =(487 + 1)*(32.768 kHz/1)
// = 16MHz

_DELAY_CYCLES (3);
_BIC_SR_register (SCG0); //启用 FLL
while (CSCTL7 &(FLLUNLOCK0 | FLLUNLOCK1)); // FLL 锁定

CSCTL4 = SELMS_DCOCLKDIV | SELA_REFOCLK;
}


//*********
//主要内容
//进入 LPM0并等待 I2C 中断。 从主器件发送的数据是*
//然后被解释、器件将相应地做出响应 *
//*********


int main (void){
WDTCTL = WDTPW | WDTHOLD;//停止看门狗计时器

initClockTo16MHz();
initGPIO();
initi2C();

_bis_SR_register (LPM0_bits + GIE);
return 0;
}

//*********
// I2C 中断
//

#if defined (__TI_Compiler_version__)|| defined (__IAR_systems_icc_)
#pragma vector = USCI_B0_vector
__interrupt void USCI_B0_ISR (void)
#Elif defined (__GNU__)
void __attribute__(interrupt (USCI_B0_vector)#USCI_ISR vector (void

)(void)#USCI_ISR vector 0!
#endif
{
//必须从 UCB0RXBUF
uint8_t Rx_val 读取;
switch (_even_in_range (UCB0IV、USCI_I2C_UCBIT9IFG)
){
USCI_NONE 案例: 中断; //向量0:无中断
USCI_I2C_UCALIFG 案例:中断; //向量2:ALIFG
USCI_I2C_UCNACKIFG 案例: //向量4:NACKIFG
中断;
案例 USCI_I2C_UCSTTIFG:中断; //向量6:STTIFG
USCI_I2C_UCSTPIFG 案例:
UCB0IFG &=~(UCTXIFG0);
中断; //向量8:STPIFG
USCI_I2C_UCRXIFG3案例:中断; //向量10:RXIFG3
USCI_I2C_UCTXIFG3案例:中断; //向量12:TXIFG3
USCI_I2C_UCRXIFG2案例:中断; //向量14:RXIFG2
USCI_I2C_UCTXIFG2案例:中断; //向量16:TXIFG2
USCI_I2C_UCRXIFG1案例:中断; //向量18:RXIFG1
USCI_I2C_UCTXIFG1案例:中断; //向量20:TXIFG1
USCI_I2C_UCRXIFG0案例: //向量22:RXIFG0
RX_val = UCB0RXBUF;
切换(SlaveMode)
{
情况(RX_REG_ADDRESS_MODE):
ReceiveRegAddr = Rx_val;
I2C_Slave_ProcessCMD (ReceiveRegAddr);
中断;
情况(RX_DATA_MODE):
ReceiveBuffer[ReceiveIndex++]= Rx_val;
RXByteCtr---;
IF (RXByteCtr = 0)
{
//完成接收 MSG
SlaveMode = RX_REG_ADDRESS_MODE;
UCB0IE &=~(UCTXIE);
UCB0IE |= UCRXIE; //启用 RX 中断
I2C_Slave_TransactionDone (ReceiveRegAddr);
}
中断;
默认值:
__no_operation();
中断;
}
中断;
USCI_I2C_UCTXIFG0案例: //向量24:TXIFG0
切换(SlaveMode)
{
情况(TX_DATA_MODE):
UCB0TXBUF =传输缓冲器[TransmitIndex++];
TXByteCtr --;
IF (TXByteCtr = 0)
{
//完成发送 MSG
SlaveMode = RX_REG_ADDRESS_MODE;
UCB0IE &=~(UCTXIE);
UCB0IE |= UCRXIE; //启用 RX 中断
I2C_Slave_TransactionDone (ReceiveRegAddr);
}
中断;
默认值:
__no_operation();
中断;
}
中断; //中断矢量:I2C 模式:UCTXIFG
默认值:中断;
}
}

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

    从器件无法控制主器件请求的内容。 它可以写入比预期更多的数据、或在您不希望写入的情况下读取数据。

    您必须始终使 TXIE 和 RXIE 处于启用状态、并且当您获得 TXIFG 时、您必须传输一个字节。

    每当您访问数组时、都会检查缓冲区溢出。

    通常情况下、当您获得启动条件时、您会重置状态机(至少部分)。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    是的、您回答正确。 这个执行非常严格地禁用 TXIF 和 RXIF。 这是主要问题。