Hi,
I am using MSP430f249, and I2C in USCI-B1 to connect to TMP102.I can read the temperatures on power on reset using msp430x24x_uscib0_i2c_01.c.
However, I want to use the SD + OC mode too, hence I am trying to use the MSP as a Master Transmitter followed by Master Receiver. I have to write 3 bytes to TMP102 (Priority Register, Config. register byte 1, config register byte 2).(Ideally, then I would send a stop signal, and then one more write operation with updated pointer register. Followed by multiple Read operations to Temp. Register.
I am using the following code. I am getting error after the 1st byte is transmitted to TMP102. The control returns to main routine from ISR instead of waiting in ISR for transmitting 3 bytes. I tried to use two additional __bis_SR_register(CPUOFF + GIE); with the hope to send the control back to ISR. But the code exitted with error: Cannot single step at the 2nd __bis_SR_register(CPUOFF + GIE);
Can someone please point out where am I going wrong?
# include <msp430f249.h>unsigned char RxByteCtr, TxByteCtr;unsigned int RxWord;unsigned char DataToBeTx[3]={0x01, 0xE1, 0xA0}; void main(void){ --code to init USC1_B1 -- (i have used it, but not posted the init_code here) TxByteCtr = 3; // Load TX byte counter UCB1CTL1 |= UCTR + UCTXSTT; // I2C start condition //here when UCB1TXIFG=1 indiacates that data should be placed in TXBUF. //code to send data in TXBUF and send stop signal is in ISR while ( UCB1CTL1 & UCTXSTT); /* while (UCB1STAT & UCNACKIFG){ // send STOP if slave sends NACK UCB1CTL1 |= UCTXSTP; UCB1STAT &= ~UCNACKIFG; while (UCB1CTL1 & UCTXSTP); UCB1CTL1 |= UCTXSTT; while ( UCB1CTL1 & UCTXSTT); } */ __bis_SR_register(CPUOFF + GIE);
__bis_SR_register(CPUOFF + GIE); __bis_SR_register(CPUOFF + GIE);
__disable_interrupt(); //if you dont disable here, then you lose 1st data in RXBUF --code to read 2 bytes from TMP102, to ensure correct write operation. Ultimately, I will use 1 more write operation to update pointer register, followed by code to read as many sets of 2 bytes of Temp. Register as required. Code modified from msp430x24x_uscib0_i2c_01.c.
--this code was same as the example code, hence not posted here--
}--ISR for TA0
#pragma vector = USCIAB1TX_VECTOR__interrupt void USCIAB1TX_ISR(void){ / if (UC1IFG & UCB1RXIFG) { ISR as from example code msp430x24x_uscib0_i2c_01.c } if (UC1IFG & UCB1TXIFG) { TxByteCtr--; // Decrement RX byte counter if (TxByteCtr) { UCB1TXBUF = DataToBeTx[TxByteCtr]; // Get received byte } else { UCB1TXBUF = DataToBeTx[TxByteCtr]; // Get final received byte, UCB1CTL1 |= UCTXSTP; UC1IFG &= ~UCB1TXIFG; __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } }}
Thanks,
Deepika
Theoretically, I think, even if the control exits the ISR, until I specify to disable the interrupts, it should still be able to access the ISR. Hence I think this could be something that single stepping wont catch but actual hardware will.
I tried this approach too.. but only 1 byte was sent, and code stopped at __disable_interrupts();.
If I comment out the __disable_interrupts() parts, still only 1 byte is sent, the code still gets stuck at the 1st iteration of while loop. (Error: MSP430: Can't Single Step Target Program: Could not single step device, at RxByteCtr=2; )
Please let me know where am I going wrong.
I cant use a for or a while loop in the ISR. It will simulate well, but in real hardware scenario, I need to wait for 8 SCL cycles before the TXIFG is set again, unlike the one-step-transfer-complete -TXIFG-set that i see in single stepping.
I also tried to use the TI's sample code from slaa382.zip. I cant read any values from that code either.
Thanks in anticipation,
Deepika Ranade86842 UC1IFG &= ~UCB1TXIFG;
However, you should not set UCTXSTP after writing to TXBUF, you should set it instead. When TXIFG is set, the previous byte is stills ending and your writ eto TXBUF is jsu tbuffered. Immediately settign UCSTP then will finishe the transfer of teh byte that's jsut sending, but will not send the byte that's waiting in TXBUF.
So do it this way:
if (UC1IFG & UCB1TXIFG) { if (TxByteCtr) { UCB1TXBUF = DataToBeTx[TxByteCtr--]; // send byte and decrement byte counter } else { UCB1CTL1 |= UCTXSTP; __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } }
However, you should also handle the NACKIFG case, or else you might enter a deadlock if the slave doesn't respond.TXIFG is set as soon as you set UCTXSTT (and in case of a NACK, the content of TXBUF is discarded). And if you don't write to TXBUF, the USCI will stall the bus while reading the slaves ACK, until you either do or set UCTXSTP.
_____________________________________Before posting bug reports or ask for help, do at least quick scan over this article. It applies to any kind of problem reporting. On any forum. And/or look here.If you cannot discuss your problem in the public, feel free to start a private conversation: click on my name and then 'start conversation'. But please do so only if you really cannot do it in a public thread, as I usually read all threads. And I prefer to answer where others can profit from it (or contribute to it) too.
As soon as I start to Transmit, I get a NACK, and am stuck in the NACK loop. But otherwise I could read the temperatures (Master in Receive mode, works fine).
Tried a slave_present test too. I am stuck in NACK. I do not have a CRO/ Logic analyzer to check the waveforms.
I cross checked that ADD0 is connected to Ground. So I am using 0x48 as slave address. (1001000 for Ao connected to Ground).
The other thing is USSCLLOW is set too (Because it is expecting data in TXBUF?). (I have 10K pull-up resistors connected, There is no other slave connected. )
The only other connection apart from these is that the ALERT pin of TMP102 is connected to P5.0/ UCB1STE/UCA1CLK + a pull-up resistor.
slave_present test:
// from http://e2e.ti.com/support/microcontrollers/msp43016-bit_ultra-low_power_mcus/f/166/t/19585.aspx#138376# include <msp430f249.h>void main(void){ WDTCTL = WDTPW + WDTHOLD; // Stop WDT P5SEL |= 0x06; // Assign I2C pins to USCI_B1 UCB1CTL1 |= UCSWRST; // Enable SW reset UCB1CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode UCB1CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset UCB1BR0 = 12; // fSCL = SMCLK/12 = ~100kHz UCB1BR1 = 0; UCB1I2CSA = 0x48; // Remember to change the slave device address here if neededUCB1CTL1 &= ~UCSWRST; // Clear SW reset, resume operation UC1IE &= ~(UCB1TXIE + UCB1RXIE); // no RX or TX interrupts __disable_interrupt(); UCB1CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition while (UCB1CTL1 & UCTXSTT); UCB1CTL1 |= UCTXSTP; while (UCB1CTL1 & UCTXSTP); // wait for STOP conditionwhile(1);}
Deepika Ranade86842The other thing is USSCLLOW is set too (Because it is expecting data in TXBUF?).
Actually, I didn't ever use I2C on the USCI in a productivity project. The 1611 we use in most of our projects did have an USART. And after a dew experiments, I found the USART I2C to be much too complex for practical use. A single, long background transfer is done nicely, but for short, busy-waiting transfers with changing direction, the handling was causing too much overhead, so I wrote my I2C in plain software.And the hardware development based on new 5438 with USCI has been stopped over a year ago in favor of vast improvements of out PC frontend software.So while I did some successful starting experiments with I2C on the USCI, my practical experience, especially with possible error ocnditions) is rather limited.
However, I'd expect NACKIFG not being set before the end of the ACK cycle. And with USCLLOW, the ACK cycle of the address send is not finished (until you write to TXBUF or set UCTXSTP).
But for your original code, I have an idea what might be wrong: TxByteCtr. You should add the volatile addtribute to it.You set it to 3, but since it is just a global variable that is set and never used again in main, the compiler may place the actual assignment of this value somewhere else in the code for size or speed optimization reasons. Maybe it is still 0 when you start the transfer, and the ISR ends the transfer before it has begun. Just an idea.Adding the volatile attribute tells the compiler to do the job where it is written, even if it doesn't seem to make sense (the compiler does not know about interrupts and ISRs)
Is there any way to see cycle-by-cyle (cycle with respect to SCL) values? I am using stepping in CCS v4, I can only see the end result due to a line of code.
I could manage to see waveforms on a Logic Analyzer though. The D0 is SDA and D1 is SCL.Marker A= start, marker B= end of address sending. After address is set, SCL is not toggled at all. I see now what you meant by ACK cycle never being finished.
Ok, so as I understand: data has to be in TXBUF before end of START sequence, so that SCL will keep toggling.
So, should I start the code with __bis_SR_register(GIE) ? Tried to single step through this version, I still get a NACK and UCSCLLOW=1. But I could read the POR values of the Config. Register (Not the values that I wrote.) I think I am nearly there now. Not sure now exactly whats wrong.
# include <msp430f249.h>volatile unsigned char RxByteCtr, TxByteCtr, array_index;unsigned int RxWord;unsigned char DataToBeTx[3]={0x01, 0xE1, 0xA0}; void main(void){
--code to initialize USCB1 Master mode , SMCLK, with prescale= 12. (code not pasted here) /// ################### Write Pointer Register =0x01, Config Register byte 1 = E1 for OS + SD, byte 2=A0 ################ TxByteCtr = 3; // Load TX byte counter array_index=0; __bis_SR_register(GIE); UCB1CTL1 |= UCTR + UCTXSTT; // I2C start condition //here when UCB1TXIFG=1 indicates that data should be placed in TXBUF, (In ISR) while ( UCB1CTL1 & UCTXSTT); while (UCB1STAT & UCNACKIFG){ // send STOP if slave sends NACK. Restart. UCB1CTL1 |= UCTXSTP; UCB1STAT &= ~UCNACKIFG; while (UCB1CTL1 & UCTXSTP); UCB1CTL1 |= UCTXSTT; while ( UCB1CTL1 & UCTXSTT); } // ################### Read config. Register to check if write was successful ############### while (1) { --code from msp430x24x_uscib0_i2c_01.c }}#pragma vector = TIMERA0_VECTOR__interrupt void TA0_ISR(void){ __bic_SR_register_on_exit(CPUOFF); // Exit LPM0}#pragma vector = USCIAB1TX_VECTOR__interrupt void USCIAB1TX_ISR(void){ if (UC1IFG & UCB1RXIFG) { RxByteCtr--; // Decrement RX byte counter if (RxByteCtr) { RxWord = (unsigned int)UCB1RXBUF << 8; // Get received byte if (RxByteCtr == 1) // Only one byte left? UCB1CTL1 |= UCTXSTP; // Generate I2C stop condition } else { RxWord |= UCB1RXBUF; // Get final received byte, // Combine MSB and LSB __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } } if (UC1IFG & UCB1TXIFG) { if (TxByteCtr) { UCB1TXBUF = DataToBeTx[array_index]; // send byte and decrement byte counter
array_index++; TxByteCtr--; } else { UCB1CTL1 |= UCTXSTP; __disable_interrupt(); } }}
Sorry for the repeated questions. I can read off the TMP102, but really have to write to it too to send it to Low Power mode.
Thanks
As soon as you set UCTXSTT, the TX ISR is called. If a NACK is received then, teh byte already in TXBUF is discarded. So on a NACK, you'll have to reset the byte counter before you start again. Else after the 3rd unsuccessful attempt, the TX function will send a stop immediately after you set UCTXSTT.However, __disable_interrupt() inside an ISR is a NOP. Interrupts are disabled during ISR execution anyway (or else the ISR would interrupt itself infinitely), and on ISR exit, th eold state is restored.There are special intrinsics such as __bic_SR_register_on_exit() to clear the GIE bit (or the LPM bits) from the saved content of the status register that is restored on ISR exit. You already use it in the RX ISR code.
Looking at the analyzer output I see, it seems that there are two problems.After SCL goes low the 9th time, the MSP should release SDA (and most liekly does). Since it stays low, the slave is pullling it low. So the slave obviously issues an ACK. However, the ACK cycle doesn't complete. Most likely because nothing is written to TXBUF.
This should have been done by the ISR at this point. If not, it means that the TX ISR already has written all the three bytes. In three previous attempts to address the slave.
So the quesiton is: were there three unsuccessful attempts to address the slave? If so, why? If not, there should be 'bytes left' even with the code that does nto reset the counter on a NCKK (as there were no NACK). And then the question is: why did the ISR not write to TXBUF?