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.
Hello all,
I'm trying to implement a I2C multimaster protocol between two MSP430f249 and I'm having trouble when two masters try to start a communication at the same time. According to the user guide, when that happens the arbitration process determines who controls the bus, and the master that lost arbitration is automatically configured as a slave and the UCALIFG flag is set, but that is not happening. To debbug this, i adapted my code to do the following:
- Configure everything that is needed in the MSP (clock source, USCI mode, I2C mode, adresses, interrupts, etc)
- Ensure stop condition got sent
- Send start condition
- check UCALIFG, if it is set, then set P2.0.
- Trap the code
I'm checking the P2.0 pin in both msps using a oscilloscope, but they are never set.
To ensure that both masters are starting communication at the same time, I soldered the supply and GND pins of both msps together so when i reset the boards they start running code at the same time. Also, both codes are exactly the same except for the own adress and slave adress registers, which have to be different so the arbitration process can decide who controls the bus.
Another option I have tried is using the USCIB0RX interrupt to detected arbitration lost and create my own routine to configure as a slave receiver, but it never enters the interrupt. (which is somewhat expected since the UCALIFG flag is never set)
Am I missing some configuration that is needed for the arbitration process to happen? Or maybe there is some hardware problem? (the exactly same hardware works fine for a single master/slave I2C communication though)
The codes for both masters are in attachments.
I aprreciate any help.
#include <msp430.h> int other_master = 0; // flag is set if another master is controlling the bus int we_are_master = 0; volatile int master_data = 0x01; volatile int received_data = 0; void arbitration_lost(void); void config_slave_mode(void); void config_master_mode(void); void main(void){ WDTCTL = WDTPW + WDTHOLD; // Stop WDT P3SEL |= 0x06; // Assign I2C pins to USCI_B0 ************************ P2DIR = 0xff; P2OUT = 0x00; config_master_mode(); __bis_SR_register(GIE); while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent UCB0CTL1 |= UCTXSTT; // start condition // __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts if (UCB0STAT && UCALIFG) P2OUT |= BIT0; while(1); } #pragma vector = USCIAB0TX_VECTOR __interrupt void USCIABOTX_ISR(void){ if(we_are_master){ UCB0TXBUF = master_data; UCB0CTL1 |= UCTXSTP; // I2C stop condition IFG2 &= ~UCB0TXIFG; // Clear USCI_B0 TX int flag __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } else{ received_data = UCB0RXBUF; __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } } #pragma vector = USCIAB0RX_VECTOR __interrupt void USCIAB0RX_ISR (void){ switch(UCB0STAT & (UCSTPIFG + UCSTTIFG + UCALIFG)){ // checks for start and stop conditions, as well as arbitration lost case UCALIFG : arbitration_lost(); UCB0STAT &= ~UCALIFG; // clear arbitration lost flag break; /* case UCSTTIFG : / start_condition_detected(); / break; case UCSTPIFG : stop_condition_detected(); break; */ } } void arbitration_lost(void){ config_slave_mode(); we_are_master = 0; } void config_slave_mode(void){ UCB0CTL1 |= UCSWRST; UCB0CTL0 &= ~UCMST + ~UCMM; UCB0CTL0 |= UCMODE_3 + UCSYNC; UCB0CTL1 &= ~UCTR; UCB0CTL1 |= UCSSEL_3; UCB0I2COA = 0x50; UCB0I2CIE |= UCALIE; IE2 |= UCB0TXIE + UCB0RXIE; UCB0CTL1 &= ~UCSWRST; } void config_master_mode(void){ we_are_master = 1; UCB0CTL1 |= UCSWRST; UCB0CTL0 |= UCMM + UCMST + UCMODE_3 + UCSYNC; UCB0CTL1 |= UCSSEL_3 + UCTR; UCB0BR0 = 11; // fSCL = SMCLK/12 = ~100kHz UCB0BR1 = 0; UCB0CTL1 &= ~UCSWRST; UCB0I2CSA = 0x70; UCB0I2CIE |= UCALIE; IE2 |= UCB0TXIE; }
#include <msp430.h> int other_master = 0; // flag is set if another master is controlling the bus int we_are_master = 0; volatile int master_data = 0x01; volatile int received_data = 0; void arbitration_lost(void); void config_slave_mode(void); void config_master_mode(void); void main(void){ WDTCTL = WDTPW + WDTHOLD; // Stop WDT P3SEL |= 0x06; // Assign I2C pins to USCI_B0 ************************ P2DIR = 0xff; P2OUT = 0x00; __bis_SR_register(GIE); config_master_mode(); while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent UCB0CTL1 |= UCTXSTT; // start condition // __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts if (UCB0STAT & UCALIFG) P2OUT |= BIT0; // __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts while(1); } #pragma vector = USCIAB0TX_VECTOR __interrupt void USCIABOTX_ISR(void){ if(we_are_master){ UCB0TXBUF = master_data; UCB0CTL1 |= UCTXSTP; // I2C stop condition IFG2 &= ~UCB0TXIFG; // Clear USCI_B0 TX int flag __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } else{ received_data = UCB0RXBUF; __bic_SR_register_on_exit(CPUOFF); // Exit LPM0 } } #pragma vector = USCIAB0RX_VECTOR __interrupt void USCIAB0RX_ISR (void){ switch(UCB0STAT & (UCSTPIFG + UCSTTIFG + UCALIFG)){ // checks for start and stop conditions, as well as arbitration lost case UCALIFG : arbitration_lost(); UCB0STAT &= ~UCALIFG; // clear arbitration lost flag break; /* case UCSTTIFG : / start_condition_detected(); / break; case UCSTPIFG : stop_condition_detected(); break; */ } } void arbitration_lost(void){ config_slave_mode(); we_are_master = 0; } void config_slave_mode(void){ UCB0CTL1 |= UCSWRST; UCB0CTL0 &= ~UCMST + ~UCMM; UCB0CTL0 |= UCMODE_3 + UCSYNC; UCB0CTL1 &= ~UCTR; UCB0CTL1 |= UCSSEL_3; UCB0I2COA = 0x70; UCB0I2CIE |= UCALIE + UCSTTIE; IE2 |= UCB0TXIE + UCB0RXIE; UCB0CTL1 &= ~UCSWRST; } void config_master_mode(void){ we_are_master = 1; UCB0CTL1 |= UCSWRST; UCB0CTL0 |= UCMM + UCMST + UCMODE_3 + UCSYNC; UCB0CTL1 |= UCSSEL_3 + UCTR; UCB0BR0 = 11; // fSCL = SMCLK/12 = ~100kHz UCB0BR1 = 0; UCB0CTL1 &= ~UCSWRST; UCB0I2CSA = 0x50; UCB0I2CIE |= UCALIE + UCSTTIE; IE2 |= UCB0TXIE; }
There are lots of errors. && is logical and, not bitwise and. Checking for UCALIFG in the main loop will not work because the interrupt handler will be run first. You must check TXIFG and RXIFG before handling them. The check for UCALIFG in the interrupt handler does not work if any of the other bits are set; do the checks separately. When you lose arbitration, you should not reinitialize the USCI module because that would throw away any data being received; see figure 17-12 in the User's Guide for how the configuration is automatically changed by the hardware in this case.
Hello Clemens,
sorry for the late response, I got caught up with some other work. I tried using a separated signal as you suggested, but the microcontrollers are still not synchronized. Running two code composer instances and using break points I can actually see one microcontroller start running code before the other. Do you have some other suggestion on how to synchronize the two? Also, i'm sending my revised code.
Thank you.
/* FloripaSat I2C multi-mater Author: Bruno Eiterer Descripition: this code implements I2C multi-master protocol between a EPS Directly Coupled prototype board and a OBC prototype board. It checks if the bus is busy before attempting to start an i2c communication, if the bus is busy it reconfigures the microcontroller as a slave receiver and waits for another master to initiate communication. If the bus is not busy, it keeps sending start conditions and the slave andress until the slave send an ACK signal, this way it waits for a possible other master reconfiguring as a slave. To do: validade arbitration procedure when two masters start communication at the same time (implemented in hardware) */ #include <msp430.h> //------------- GLOBAL VARIABLES -------------// unsigned char RXByteCtr; volatile unsigned char RxBuffer[5]; // Variable to save received data volatile unsigned int i = 0; // Counter to determine which RxBuffer position is to be used unsigned char *PTxData; // Pointer to TX data unsigned char TXByteCtr; // Counter to determine which TxData position is to be sent const unsigned char TxData[] = // Table of data to transmit { 0x11, 0x22, 0x33, 0x44, 0x55 }; //------------- FUNCTION PROTOTYPES -------------// void config_master_mode(void); void config_slave_mode(void); //------------- MAIN -------------// int main(void) { DCOCTL = 0; // Select lowest DCOx and MODx settings BCSCTL1 = CALBC1_1MHZ; // Set DCO DCOCTL = CALDCO_1MHZ; WDTCTL = WDTPW + WDTHOLD; // Stop WDT config_master_mode(); // call function to configure I2C master moder while (1) { if(UCB0STAT & UCBBUSY) // Checks if bus is busy { config_slave_mode(); // If bus is busy config as slave to receive data } else { while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent while(P5IN == 1); // used to debug the case which two masters start communication at the same time UCB0CTL1 |= UCTXSTT; // send start condition __delay_cycles(150); // ???waits for NACK flag to be set??? while(1){ // keep sending start condition and adress while NACK is received. This waits for another master possibly reconfiguring as slave UCB0CTL1 |= UCTXSTT; // send start condition __delay_cycles(150); // ???waits for NACK flag to be set??? if(UCB0STAT & UCALIFG){ while(1); } } __bis_SR_register(GIE); // enable interrupts while(1); // traps CPU after communication is done } } } //------------- INTERRUPTS -------------// #pragma vector = USCIAB0TX_VECTOR __interrupt void USCIAB0TX_ISR(void) { if(UCB0CTL1 & UCTR){ // check if interrupt source is Tx or Rx if (TXByteCtr) // check TX byte counter { UCB0TXBUF = *PTxData++; // load TX buffer TXByteCtr--; // decrement TX byte counter } else { UCB0CTL1 |= UCTXSTP; // send I2C stop condition IFG2 &= ~UCB0TXIFG; // clear USCI_B0 TX int flag } } else { RxBuffer[i] = UCB0RXBUF; // move RX data to RxBuffer[i] i++; // incremets RxBuffer counter } } //------------- CONFIGURATION FUNCTIONS -------------// void config_slave_mode(void) { i = 0; // reset RxBuffer counter P3SEL |= 0x06; // assign I2C pins to USCI_B0 UCB0CTL1 |= UCSWRST; // enable SW reset UCB0CTL1 &= ~UCTR; // receiver mode UCB0CTL0 = UCMODE_3 + UCSYNC; // I2C Slave, synchronous mode UCB0I2COA = 0x50; // own andress value (example 0x50h) UCB0CTL1 &= ~UCSWRST; // clear SW reset, resume operation IE2 |= UCB0RXIE; // enable RX interrupt } void config_master_mode(void) { P3SEL |= 0x06; // Assign I2C pins to USCI_B0 UCB0CTL1 |= UCSWRST; // Enable SW reset UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC + UCMM; // I2C Master, synchronous mode, multi-master environment UCB0CTL1 = UCSSEL_2 + UCTR + UCSWRST; // Use SMCLK, keep SW reset, trasmitter mode UCB0BR0 = 12; // fSCL = SMCLK/12 = ~100kHz UCB0BR1 = 0; UCB0I2CSA = 0x50; // Slave Address Register UCB0I2CIE |= UCALIE; UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation PTxData = (unsigned char *)TxData; // TX array start address TXByteCtr = sizeof TxData; // Load TX byte counter IE2 |= UCB0TXIE; // Enable TX interrupt }
This line is what I'm using to wait for the signal:
while(P5IN == 1); // used to debug the case which two masters start communication at the same time
On the two MSP boards the P5.0 pins are connected to a 3.3V source. So when I turn off the source I expect the code to pass this test. Is this not what you meant by using a separated signal?
Thank you
One code executes a loop on P5 and the other on P6, i'm assuming that makes no difference (the boards with the mcus are different so not the same pins are available). I put a breakpoint on the next line of code and the time difference I notice is in reaching that breakpoint so I think it's safe to assume that they don't break out at the same time.
Thank you
Hello Clemens,
I tested the code with leds and they both blink normally. Also, I can see that one of the leds stop before the other, so they are definetely not breaking out of the while loop at the same time. Do you think this could be a hardware problem? I'll see if I can get two equal boards and run this test again.
Thank you
**Attention** This is a public forum