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.
您好!
何时是在 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)、但不能同时写入这两个字节。