TI distributes example code for various MSP430 parts. There's a specific example file which isn't making sense to me: msp430x22x4_uscib0_i2c_12. It transmits and then receives, all from within an ISR for USCIAB0TX. It enables RX interrupts at some point (UCB0RXIE) but there's no ISR for RX, and the RX flag (UCB0RXIFG) is never checked for. So, my question is, how does it work without these features, and why does the code enable an interrupt it appears never to use?
Here's the code:
//******************************************************************************// MSP430F22x4 Demo - USCI_B0 I2C Master TX/RX multiple bytes from MSP430 Slave// with a repeated start in between TX and RX operations.//// Description: This demo connects two MSP430's via the I2C bus. The master// transmits to the slave, then a repeated start is generated followed by a // receive operation. This is the master code. This code demonstrates how to // implement an I2C repeated start with the USCI module using the USCI_B0 TX // interrupt.// ACLK = n/a, MCLK = SMCLK = BRCLK = default DCO = ~1.2MHz//// ***to be used with msp430x22x4_uscib0_i2c_13.c***//// /|\ /|\// MSP430F24x 10k 10k MSP430F22x4// slave | | master// ----------------- | | -----------------// -|XIN P3.1/UCB0SDA|<-|---+->|P3.1/UCB0SDA XIN|-// | | | | |// -|XOUT | | | XOUT|-// | P3.2/UCB0SCL|<-+----->|P3.2/UCB0SCL |// | | | |//// R. B. Elliott / H. Grewal// Texas Instruments Inc.// April 2008// Built with IAR Embedded Workbench Version: 3.42A//******************************************************************************#include "msp430x22x4.h"#define NUM_BYTES_TX 3 // How many bytes?#define NUM_BYTES_RX 2int RXByteCtr, RPT_Flag = 0; // enables repeated start when 1volatile unsigned char RxBuffer[128]; // Allocate 128 byte of RAMunsigned char *PTxData; // Pointer to TX dataunsigned char *PRxData; // Pointer to RX dataunsigned char TXByteCtr, RX = 0;unsigned char MSData = 0x55;void Setup_TX(void);void Setup_RX(void);void Transmit(void);void Receive(void);void main(void){ WDTCTL = WDTPW + WDTHOLD; // Stop WDT P3SEL |= 0x06; // Assign I2C pins to USCI_B0 while(1){ //Transmit process Setup_TX(); RPT_Flag = 1; Transmit(); while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent //Receive process Setup_RX(); Receive(); while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent }}//-------------------------------------------------------------------------------// The USCI_B0 data ISR is used to move received data from the I2C slave// to the MSP430 memory. It is structured such that it can be used to receive// any 2+ number of bytes by pre-loading RXByteCtr with the byte count.//-------------------------------------------------------------------------------#pragma vector = USCIAB0TX_VECTOR__interrupt void USCIAB0TX_ISR(void){ if(RX == 1){ // Master Receive? RXByteCtr--; // Decrement RX byte counter if (RXByteCtr) { *PRxData++ = UCB0RXBUF; // Move RX data to address PRxData } else { if(RPT_Flag == 0) UCB0CTL1 |= UCTXSTP; // No Repeated Start: stop condition if(RPT_Flag == 1){ // if Repeated Start: do nothing RPT_Flag = 0; } *PRxData = UCB0RXBUF; // Move final RX data to PRxData __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 }} else{ // Master Transmit if (TXByteCtr) // Check TX byte counter { UCB0TXBUF = MSData++; // Load TX buffer TXByteCtr--; // Decrement TX byte counter } else { if(RPT_Flag == 1){ RPT_Flag = 0; PTxData = &MSData; // TX array start address TXByteCtr = NUM_BYTES_TX; // Load TX byte counter __bic_SR_register_on_exit(CPUOFF); } else{ UCB0CTL1 |= UCTXSTP; // I2C stop condition IFG2 &= ~UCB0TXIFG; // Clear USCI_B0 TX int flag __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } } } }void Setup_TX(void){ _DINT(); RX = 0; IE2 &= ~UCB0RXIE; while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent// Disable RX interrupt UCB0CTL1 |= UCSWRST; // Enable SW reset UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset UCB0BR0 = 12; // fSCL = SMCLK/12 = ~100kHz UCB0BR1 = 0; UCB0I2CSA = 0x48; // Slave Address is 048h UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation IE2 |= UCB0TXIE; // Enable TX interrupt}void Setup_RX(void){ _DINT(); RX = 1; IE2 &= ~UCB0TXIE; UCB0CTL1 |= UCSWRST; // Enable SW reset UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset UCB0BR0 = 12; // fSCL = SMCLK/12 = ~100kHz UCB0BR1 = 0; UCB0I2CSA = 0x48; // Slave Address is 048h UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation IE2 |= UCB0RXIE; // Enable RX interrupt}void Transmit(void){ PTxData = &MSData; // TX array start address TXByteCtr = NUM_BYTES_TX; // Load TX byte counter while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts}void Receive(void){ PRxData = (unsigned char *)RxBuffer; // Start of RX buffer RXByteCtr = NUM_BYTES_RX-1; // Load RX byte counter while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent UCB0CTL1 |= UCTXSTT; // I2C start condition __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts}
Scott BrennemanSo, my question is, how does it work without these features, and why does the code enable an interrupt it appears never to use?
In I2C mode, RX and TX interrupts are triggering the TX ISR, while the status interrupts trigger the RX ISR. The reason is that I2C is half-duplex, so during one transmission, you only either receive or transmit, but not both.This is described in the users guide, bt you can easily miss this info. And it's nothing you would expect. But after some thinking it makes sense and allows more efficient programming.
On the 5x devices, the (else identical) USART has the interrupts distributed differently: Here ther i s only one USCIARXTX and one USCIBRXTX ISR. So RX and TX (and in case of I2C the status interrupts) arrive in the same ISR, but there are different ISRs for each USCI sub-module (and for each USCI). With this setup, you won't tap into the trap you stepped in for the 2x series USCI.
_____________________________________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.
Wow, that's certainly counter-intuitive. Where in the Guide does it explain this, so I can read more about it?
Thank you so much for clarifying!
Scott
Scott BrennemanWow, that's certainly counter-intuitive.
Scott BrennemanWhere in the Guide does it explain this, so I can read more about it?
slau144e.pdf chapter 17.3.7 (page 543): One interruptvector is associated with the transmit and receive interrupt flags. The otherinterrupt vector is associated with the four state change interrupt flags.
THe problem is that I cannot locate wnywhere which USART interrupt is used for what. The interrupt vector table in the datasheet does not list this I2C special handling and associates one vector with RX and one with TX (which is correct for UART and SPI). You can only see it in the provided code example where the two ISRs are namedUSCIA0_RX_USCIB0_I2C_STATE_ISR and USCIA0_TX_USCIB0_I2C_DATA_ISR.
Thank you Jens-Michael. I guess documentation isn't what it used to be...!
Scott Brenneman I guess documentation isn't what it used to be...!
It's jsut that the whole stuff is so complex that not everything can be written everywhere, so you need to carefully read all of it so you do not miss something.It's not bug-free, however, and sometimes it really isn't exhausting enough or explanation is so short that that you just might miss the point on first (or second) reading.