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.

[参考译文] MSP430FR5969:用于重复启动的 USCIB0 I2C 写入命令序列

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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/565491/msp430fr5969-uscib0-i2c-write-command-sequence-for-repeated-start

器件型号:MSP430FR5969

您好!

何时是在 I2C 模式下为 USCIB0设置重复 START 命令的正确时间? 当我在 TX ISR 期间设置 UCTXSTT=1时、系统会在传输数据之前发出一个重复起始、即使状态图显示之后应该发出起始。 请参阅图。

我在 TX ISR 中尝试执行的操作是:

将数据放入 UCB0TXBUF 中

2.更改从机地址

3.根据我对下一条命令的需要、设置 UCTR = 1或 UCTR = 0

4.设置 UCTXSTT=1

但是、这会在发送数据之前产生重复起始、并且还会跳过大约一半的数据(请参阅 下面的图像1)。

我正在尝试发送此消息:

0x0040>>1|IO::USCI_I2C::ADDR、//设置地址
0x0003| IO:USCI_I2C::写入、//设置 ptr
0x00FF| IO::USCI_I2C::写入、//写入寄存器
0x0040>>>1| IO:USCI_I2C:::ADDR、//设置地址
0x0001| IO::USCI_I2C::写入、//设置 ptr
0x00AA| IO::USCI_I2C::写入、//写入寄存器

0x0046>>1|IO::USCI_I2C::ADDR、//设置地址
0x0003| IO:USCI_I2C::写入、//设置 ptr
0x00FF| IO::USCI_I2C::写入、//写入寄存器
0x0046>>>1| IO:USCI_I2C:::ADDR、//设置地址
0x0001| IO::USCI_I2C::写入、//设置 ptr
0x00AA| IO::USCI_I2C::写入//写入寄存器 

图1:以下是无延迟捕获(不正确):

我发现、如果我在 ISR 中添加一个延迟、数据实际上被传输、然后发出 START 信号。  

// 3. 如果
(isAddr (nextCmd))
{
UCB0I2CSA =(uint8_t) nextCmd;
/*"设置 UCTXSTT 会生成重复的启动条件、则使用新地址重新启动。 在这种情况


下、可以通过置位或清零 UCTR 来配置发送器或接收器、如果需要、可以将不同的从器件地址写入 UCBxI2CSA。" //
要完成此操作,请发送 START WRITE 或 START READ 命令
//这将适当设置 UCTR 标志,并设置 START 位

//递增计数器超过地址 cmd 以进入下一个 rd / wr 命令
// (我们需要进行透视以查看它是读取还是写入)。
_delay_cycles (2000);//使用此选项,代码将按预期执行,在数据传输
nextCmd = seq[+seqCtrl];
if (isWrite (nextCmd)) startWr ();
否则 if (isRead (nextCmd)) startRd ();
} 

GitHub 上的代码:   github.com/.../cribbage_LED

这是具有延迟的捕获(正确) 

这是我的 ISR 随附的代码。

/*USCII2C.h
*创建日期:2016年12月26日
* 作者:Benny*/
#ifndef USCII2C_H_
#define USCII2C_H_

// include 以使用标准类型
#include 

命名空间 IO
{
CLASS USCI_I2C
{
public:
//用作位掩码来检查地址
enum transaction_type{
ADDR = 1<8、
read = 1<9、
write = 1<10
};
//可以稍后尝试替换 transaction_type
//union I2C_transaction
//{
//uint16_t packet;
//uint8_uint8;
//adintuintdr:1
//uint8_t isrd:1;
//uint8_t isWr:1;
//};
USCI_I2C ();
//初始化 I2C 硬件和成员变量
//当前只使用 MCLK 作为时钟源
//如果总线永久繁忙
,繁忙计数将防止无限循环//设置为-1以禁用。
void init (double F_MCLK、double F_I2C、uint8_t defaultAddress、
unsigned busyCnts = 0、volatile unsigned char * SEL_port = 0、
uint8_t pins = 0);
//
void transaction (uint16_t * seq、uint16_t seq seqlen、
uint8_t * upt *)
先前
已完全处理过此序列/ uint16位/ uintuintuintuintu/uintuintuintu/t 数据)。
内联 bWrdone (){return (state == IDLE);};
//如果接收到给定地址的确认则返回 true
//从 TI_USCI_I2C_SLAVE_Present 移植
//-使 eUSCI 正常
工作//-替换了主从术语,替换了协调器-客户端
bool checkAddr (uint8_t adISR);
//
空 Inline bxInt r (handleIntr);//用 tinline bxInt br (inline)
内联空 startSeq();
private:
//检查数据包
中设置的标志内联 bool isAddr (uint16_t Seq){返回 Seq&ADDR;};
内联 bool isWrite (uint16_t Seq){返回 Seq&write;};
内联 bool isRead (uint16_t Seq){返回 Seq&read;};
//启动写入命令(协调器-发送器)
内联 void startWr ();
//启动读取命令(协调器-接收器)
内联 void startRd ();
内联 void waitForBusFree ();

//由状态机使用
//将偶数值设置为每个状态以允许快速处理
//在 ISR 中使用__even_in_range 内在函数
//执行以下操作: 我需要这个吗、还是它只是用于 USI?
枚举状态{
IDLE = 0、
START = 2、
PREPARE_ACKNACK = 4、
Handle_RxTx = 6、
Received_data = 8、
prepared_stop = 10、
stop = 12
}状态;
uint8_t defAddr;//默认 I2C 地址
uint16_t *序列;
uint16_t seqLen、seqCtr;
uint8_t * recvData;
uint16_t wakeupSRBits;
unsigned busyCnts;
};
//需要在中断
外部使用的外部定义对象 USCI_I2C i2c;
}//命名空间 IO */

#endif // USCII2C_H_*

/*USCII2C.cpp
*创建日期:2016年12月26日
* 作者:Benny
*/
//包含 MSP430头文件以访问 USCI 寄存器
#include "msp430.h"
#include "USCII2C.h"
#include 
#include 

io::USCI_I2C:::USCI_I2C ()
{
this->state = IDLE;
}
void io:::USCI_I2C:::init (double F_MCLK、double F_I2C、uint8_t defaultAddress、
unsigned busyCnts、volatile unsigned char *SEL_port、uint8_t pins)
{this->busines=>isfaults=



1、s1、s1、s2cnts= bs1、s1、s1、s1、s1、s1、s1、s1、s1、s1、s1、sel = busyCnts1
、s1、s1、s1、s1、s1、s1、s1
//在我们配置 eUSCI_B 时将其置于复位状态
UCB0CTLW0 = UCSWRST;
//使用 SMCLK 作为时钟源、I2C 模式、发送 I2C 停止
UCB0CTLW0 |= UCMODE_3 | UCMST | UCSSEL_SMCLK;
断言(F_MCLK/F_I2C > UCB0CTLW0

);/ UCB0CSRW0 = UCB0CSRW0
~UCB0CLK;断言 UCB0CSRW0 = UCB0CST_SMCLK 地址/ UCB0CSRWCLK;/ UCB0CST_I2C = UCB0CST_CCESI//将 eUSCI_B 置于运行状态
//启用 TX 中断和 NACK 中断
UCB0IE |= UCTXIE0 | UCNACKIE;
}

void IO::::USCI_I2C:::waitForBusFree (){
unsigned cnt = 0;
while (UCB0STAT 和 UCBBUSY)
{
// counter busynts ='/'ning+

!if counter cnts= numps (if)


I2C 总线被冻结\n");
assert (0);
}



}void io:::USCI_I2C:::transaction (uint16_t * seq、uint16_t seqlen、
uint8_t * recvData、uint16_t wakeupSRBits)
{
//在当前序列完成之前,我们不能启动另一个序列,
如果
UCB0STP/
UCB0 (UCB0STP/ UCB0)|将 UCB0CTFUST

(UCB0)置位到库
中;//确保我们有一个序列
,此序列->Seq = Seq;
assert (seqLen);//确保我们有 seqLength
此->seqLen = seqLen;
//无 assert,只能是空 PTR,只写
此-> recvData = recvData;
//无
位,无法唤醒此-> wakeupstatus = SRqCtr

;//升级
STATE = START;
//启动序列传输,触发,但尚未设置数据
startSeq();
//从中断中退出并处理事务
}
内联 void IO::::USCI_I2C:



:startWr (){UCB0CTLW0 |= UCTR | UCTXSTT;}内联 void IO::::::USCI_I2C:::::startWr (

= UCB0CTLWD;= UCTX0 ~UCTLW= UCTR | UCTLW0;}内联 void = UCTLW0UCTL

bool IO::USCI_I2C::checkAddr (uint8_t addr)
{
uint8_t clientAddrBak、UCB0IEBak;
bool Present;
UCB0IEBak = UCB0IE; //恢复旧的 UCB0I2CIE
clientAddrBAK = UCB0I2CSA; //存储旧从地址
UCB0IE &=~ UCNACKIE; //无 NACK 中断
UCB0I2CSA = addr; //设置从地址
UCB0IE &=~(UCTXIE0 | UCRXIE0); //没有 RX 或 TX 中断
//禁用中断,以便我们可以处理这里的所有中断标志
//并且不运行任何 ISR 代码
__disable_interrupt ();
UCB0CTLW0 |= UCTR | UCTXSTT | UCTXSTP; // I2C TX,启动条件
while (UCB0CTLW0 & UCTXSTP); //等待停止条件
UCB0CTLW0 |= UCTXSTP;
存在=!(UCB0IFG & UCNACKIFG);
UCB0IFG = 0x00;//清除中断
__ENABLE_INTERRUPT ();
UCB0I2CSA = clientAddrBAK; //恢复从地址
UCB0IE = UCB0IEBAK; //恢复中断
返回存在;
}
内联空 IO:::USCI_I2C::startSeq()
{
uint16_t curSeq = seqCtr;
// 1. 如果
(isAddr (curSeq)
){
UCB0I2CSA =(uint8_t) curSeq;
curSeq = seq[+seqCtr ];//递增并处理下一个序列条
目}
// 2a、则检查地址字节。 检查数据读取字节
if (isRead (curSeq)) startRd ();
// 2b。 如果
(isWrite (curSeq) startWr ();
}
inline void IO:::USCI_I2C:::handleTxRxInt (bool isWrInt)
{
// TODO:make class private 变量?
uint16_t curCmd = SEQ[seqCTR];
//使用此选项准备下一条命令。
//尚未设置,因为我们可能在序列的末尾。
uint16_t nextCmd;
////////////////////////////////////////
//处理当前命令:
////////////////////////////////////////////////////////////////
//检查数据写入字节
if (isWrite (curCmd))
{
//将数据从序列条目写入发送器缓冲
区//UCB0TXBUF =(uint8_t) curSeq;//导致间歇性数据丢失:o cmds 被截断为8位!
UCB0TXBUF = curCmd;
}
//检查数据读取字节
、否则(isRead (curCmd))
{
// TODO:从寄存器
中抓取无符号 dataRead = UCB0RXBUF;
}

////////////////////////////////////
//准备下一个命令
//////////////////////////////////////////////////////////////////////////////////////////////////////////
//检查即将停止或启动
// 1. 停止:遇到序列结束-如果
(seqCtr == seqLen)
{
//发送停止
UCB0CTLW0 |= UCTXSTP;
//将状态设置为空闲,以便用户知道我们已准备好执行新序列
,则检查序列结束。->状态=空闲;
返回
;}
//尚未停止,请在序列
seqCTR++中加载下一个命令;
nextCmd = seq[seqCtr ];
// 2. 使用当前地址重新启动:
// 2.a. read->write
if (isWrite (nextCmd)&!isWrInt)
{
startWr();
}
// 2.b. write->read
else if (isRead (nextCmd)& isWrInt)
{
startRd();
}
// 3. 如果
(isAddr (nextCmd))
{
UCB0I2CSA =(uint8_t) nextCmd;
/*"设置 UCTXSTT 会生成重复的启动条件、则使用新地址重新启动。 在这种情况


下、可以通过置位或清零 UCTR 来配置发送器或接收器、如果需要、可以将不同的从器件地址写入 UCBxI2CSA。" //
要完成此操作,请发送 START WRITE 或 START READ 命令
//这将适当设置 UCTR 标志,并设置 START 位

//递增计数器超过地址 cmd 以进入下一个 rd / wr 命令
// (我们需要进行透视以查看它是读取还是写入)。
_delay_cycles (2000);//使用此选项,代码将按预期执行,在数据传输
nextCmd = seq[+seqCtrl];
if (isWrite (nextCmd)) startWr ();
否则 if (isRead (nextCmd)) startRd ();
}
}

// I2C ISR
//在此处解决 I2C 中断、包括使用
下一个字节更新//传输缓冲寄存器
//发送
#pragma vector=USCI_B0_vector
__interrupt void eUSCI_B0 (void)
{
switch (__even_in_range (UCB0IV、USCI_I2C_UCBIT9IFG))))
{
案例 USCI_none: 中断; //向量0:无中断
情况 USCI_I2C_UCALIFG:中断; //矢量2:ALIFG
案例 USCI_I2C_UCNACKIFG: //矢量4:NACKIFG -客户端 NACK
UCB0CTLW0 |= UCTXSTT; //重新发送开始和地址
中断;
案例 USCI_I2C_UCSTTIFG:中断; //矢量6:STTIFG
case USCI_I2C_UCSTPIFG:break; //向量8:STPIFG
案例 USCI_I2C_UCRXIFG3:中断; //向量10:RXIFG3
案例 USCI_I2C_UCTXIFG3:中断; //向量12:TXIFG3
案例 USCI_I2C_UCRXIFG2:中断; //向量14:RXIFG2
大小写 USCI_I2C_UCTXIFG2:break; //向量16:TXIFG2
大小写 USCI_I2C_UCRXIFG1:break; //向量18:RXIFG1
case USCI_I2C_UCTXIFG1:break; //向量20:TXIFG1
用例 USCI_I2C_UCRXIFG0://向量22:RXIFG0 -接收到的数据就绪
IO::i2c.handleTxRxInt (false);
break;
case USCI_I2C_UCTXIFG0: //矢量24:TXIFG0
//我们完成了一个事务,检查下一个 cmd
io:::i2c.handleTxRxInt (true);
break;
默认值:break;
}

__BIC_SR_REGISTER_ON_EXIT (LPM0_BITS);//退出 LPM0
}
#include 
#include 
#include "InputHandler.h"
#include "cribbbage_LED.h"
//待办事项:删除、这仅用于调试 I2C
#include "USCII2C.h"
// include 以使用标准类型
#include 
//切换以关闭 launchpad 上功能的使用
//例如 launchpad 上的 LED 和按钮
#define Launchpad

IO:::InputPin
Up(1、1、0、IO:::PULLUP:)、
Down(4、 5、0、IO::上拉::上拉)、
右侧(1、2、 0、IO::上拉::上拉)、
左侧(1、3、0、 IO::上拉::上拉)、
后退(1、4、0、IO:上拉:上拉)、
ENTER(1、5、0、IO::上拉::上拉);

//本地函数声明
void setUpTimers (const double F_CLK、const double F_PIN_interrupt);
void setUpPins (const double F_PIN_interrupt);

int main (void)
{
clbbage::::::::控制器游戏;
const unsigned F_PIN_interrupt

= 10Hz;const 双精度 F_中断= eMCLK = 3Hz;unsigned eLK = eLK = eLK = eLK = eLK//所需的 MCLK 频率[Hz]

WDTCTL = WDTPW | WDTHOLD;//停止看门狗计时器

//解锁时钟系统(CS)
CSCTL0 = CSKEY;
//将 MCLK 频率设置为8MHz (DCOFSEL ="110b =如果 DCORSEL = 0")
CSCTL1 |= DCOFSEL_DCOSEL1| DCOFL1
// VLO -> ACLK、DCO -> MCLK、
CSCTL2 |= SELA_VLOCLK | SELM_DCOCLK;

//设置用于输入去抖的计时器
setUpTimers (F_ACLK、F_PIN_INTERRUPT);
//初始化输入
setUpPins (F_PIN_INTERRUPT);
//清除端口设置锁定
PM5CTL0 &=~LOCKLPM5;

//设置和检查硬件:
gon.sysinit (F_MCLK);

// TODO:删除、这只是用于调试 I2C
uint16_t dummyTransaction[]=
{
0x0040>>1|IO::USCI_I2C::ADDR、//设置地址
0x0003| IO:USCI_I2C::写入、//设置 ptr
0x00FF| IO::USCI_I2C::写入、//写入寄存器
0x0040>>>1| IO:USCI_I2C:::ADDR、//设置地址
0x0001| IO::USCI_I2C::写入、//设置 ptr
0x00AA| IO::USCI_I2C::写入、//写入寄存器

0x0046>>1|IO::USCI_I2C::ADDR、//设置地址
0x0003| IO:USCI_I2C::写入、//设置 ptr
0x00FF| IO::USCI_I2C::写入、//写入寄存器
0x0046>>>1| IO:USCI_I2C:::ADDR、//设置地址
0x0001| IO::USCI_I2C::写入、//设置 ptr
0x00AA| IO::USCI_I2C::写入//写入寄存器
};
__ENABLE_INTERRUPT ();
while (1)
{
_DELAY_CYCLES (1000);
if (up.Read())
{
P1OUT ^= BIT0;
IO:::i2c.transaction (dummyTransaction、sizeof (dummyTransaction)/sizeof (dummyTransaction[0]))、0、0);
}
if (down.read ()
){
P4OUT ^= BIT6;
}
_BIS_SR (LPM0_Bits);//输入 LPM0
}

//game.run();
返回0;
}

//////////////////////////////////////////////////////////////////////////
//主要支助职能和 ISR
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setUpTimers (double F_CLK、double F_PIN_interrupt)
//void setUpTimers (const double F_CLK)
{
TA0CCR0 = F_CLK/8/F_PIN_INTERRUPT;
TA0CCR1 = 0xFFFF;
TA0CCR2 = 0xFFFF;
// SMCLK、/8、 计数到 CCR0、启用中断
TA0CTL = tassel__ACLK | ID_3 | MC_UP | TAIE;
//为 TA0 CCR0
TA0CCTL0启用中断= CCIE;
}
void setUpPins (const double F_PIN_interrupt)
//void setUpPins (

){double t_int_100ms = CCIE;}void setUpPins (const double f_PIN_interrupt = 100ms)*= 100ms;/void setUpPin =
//将此处定义的引脚链接到 bbage 库,
//这允许 bbage 板使用这些引脚
//进行游戏控制:)
bbbage::up =&up;
bbage::down =&down;
bbage::right =&right;
bbbbbbage:::left=&left;
bbbbbbbbbbbbage::&back =&back;bbbbbbbbbbbbbb
bchbage:::enter =&enter;

//初始化输入引脚
UP.init (25.0、3.0、100.0、t_int_ms);
DOWN.init (25.0、3.0、100.0、t_int_ms);
RIGHT.init (25.0、3.0、100.0、t_int_ms);
LEFT.init (25.0、3.0、100.0、T_int_ms);
BACK.init (25.0、3.0、100.0、t_int_ms);
ENTER.init (25.0、3.0、100.0、t_int_ms);
#ifdef Launchpad
//这些引脚仅用于 Launchpad 上的调试
P1DIR |= BIT0;
P4DIR |= BIT6;
#endif
}

//在进行任何初始化之前禁用 WDT 以防止
//在内存初始化期间重置 WDT int
_system_pre_init (void)
{
WDTCTL = WDTPW | WDTHOLD;
返回1;
}

// ISR
//计时器 A0中断服务例程
#pragma vector=TIMER0_A0_vector
#pragma vector=TIMER0_A1_vector//为什么需要此功能????
_interrupt void Timer_A (void)
{
//检查去抖中断 TA0IV;
if (TA0IV & TA0IV_TAIFG)
{
UP.debounce();
DOWN.debounce();
RIGHT.debounce();
LEFT.debounce();
BACK.debounce();
ENTER.debounce();
}
_BIC_SR_REGISTER_ON_EXIT (LPM0_BITS);//退出 LPM0
}

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

    有两个发送寄存器、TXBUF 和内部移位寄存器。 如用户指南的图26-12所示、TXIFG 中断在一个字节仍在发送时发生。

    中断处理程序负责执行该字节之后的任何操作。 它必须写入下一个字节、或者置位 UCTXSTT (/UCTXSTP)、但不能同时写入这两个字节。

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

    克莱明、您好、感谢您的建议。 我要通过设置开始位、然后将下一个字节加载到 TX 缓冲区中来使其正常工作。 之前我加载了下一个字节、然后设置起始位。 这是不按顺序进行的。
    此致、