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.
Tool/software: Code Composer Studio
Hi all,
I'm trying to communicate with a SSD1306 OLED screen via I2C. I'm having a problem with the driverlib EUSCI_B_I2C_masterSendMultiByteStart function, it is ignoring the first transmission byte. The only case it actually works is with the default MSP clock (1MHz) and 400kbps for I2C.
I'm configuring the peripheral as follows:
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P4, GPIO_PIN6 | GPIO_PIN7, GPIO_PRIMARY_MODULE_FUNCTION);
EUSCI_B_I2C_initMasterParam i2cParam = {0}; i2cParam.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK; i2cParam.i2cClk = CS_getSMCLK(); i2cParam.dataRate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS; i2cParam.byteCounterThreshold = 1; i2cParam.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP; EUSCI_B_I2C_initMaster(EUSCI_B1_BASE, &i2cParam); EUSCI_B_I2C_setSlaveAddress(EUSCI_B1_BASE, SSD1306_ADDRESS); EUSCI_B_I2C_setMode(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_MODE); EUSCI_B_I2C_enable(EUSCI_B1_BASE);
And I'm using this function to send commands:
void OLED_sendCommand(uint8_t cmd) { EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B1_BASE, 0x00); EUSCI_B_I2C_masterSendMultiByteNext(EUSCI_B1_BASE, cmd); EUSCI_B_I2C_masterSendMultiByteStop(EUSCI_B1_BASE); }
Now for the outputs, this is using the default clock (1MHz) and 400kbps I2C:
And this is using the default clock (1MHz) with 100kbps I2C:
If I change the I2C clock to 100kbps, or change the system clock to anything but the default 1MHz, EUSCI_B_I2C_masterSendMultiByteStart ignores the first byte. Why is this happening?
Best regards,
Helder Sales
I've also tried to enable the interrupt mode by configuring this way:
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P4, GPIO_PIN6 | GPIO_PIN7, GPIO_PRIMARY_MODULE_FUNCTION);
EUSCI_B_I2C_initMasterParam i2cParam = {0}; i2cParam.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK; i2cParam.i2cClk = CS_getSMCLK(); i2cParam.dataRate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS; i2cParam.byteCounterThreshold = 1; i2cParam.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP; EUSCI_B_I2C_initMaster(EUSCI_B1_BASE, &i2cParam); EUSCI_B_I2C_setSlaveAddress(EUSCI_B1_BASE, SSD1306_ADDRESS); EUSCI_B_I2C_setMode(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_MODE); EUSCI_B_I2C_enable(EUSCI_B1_BASE); EUSCI_B_I2C_clearInterrupt(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT1 | EUSCI_B_I2C_NAK_INTERRUPT); EUSCI_B_I2C_enableInterrupt(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT1 | EUSCI_B_I2C_NAK_INTERRUPT); __bis_SR_register(GIE);
But after the first call to EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B1_BASE, 0x00), it get stuck at
//Poll for transmit interrupt flag. while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;
Hi,
About the use of EUSCI_B_I2C_masterSendMultiByte function, you can refer to this code example:
/* --COPYRIGHT--,BSD * Copyright (c) 2017, Texas Instruments Incorporated * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * --/COPYRIGHT--*/ //****************************************************************************** // //! This example shows how to configure the I2C module as a master for //! multi byte transmission in interrupt driven mode. The address of the slave //! module is set in this example. //! //! Demo - EUSCI_B0 I2C Master TX multiple bytes to MSP430 Slave //! //! Description: This demo connects two MSP430's via the I2C bus. The master //! transmits to the slave. This is the MASTER CODE. It cntinuously //! transmits an array of data and demonstrates how to implement an I2C //! master transmitter sending multiple bytes using the USCI_B0 TX interrupt. //! ACLK = n/a, MCLK = SMCLK = BRCLK = default DCO = ~1MHz //! //! /|\ /|\ //! MSP430FR2xx_4xx Board 10k 10k MSP430FR2xx_4xx Board //! slave | | master //! ----------------- | | ----------------- //! | UCB0SDA|<-|----+->|UCB0SDA | //! | | | | | //! | | | | | //! | UCB0SCL|<-+------>|UCB0SCL | //! | | | | //! //! This example uses the following peripherals and I/O signals. You must //! review these and change as needed for your own board: //! - I2C peripheral //! - GPIO Port peripheral (for I2C pins) //! - SCL //! - SDA //! //! This example uses the following interrupt handlers. To use this example //! in your own application you must add these interrupt handlers to your //! vector table. //! - USCI_B0_VECTOR. // //****************************************************************************** #include "driverlib.h" #include "Board.h" //***************************************************************************** // //Set the address for slave module. This is a 7-bit address sent in the //following format: //[A6:A5:A4:A3:A2:A1:A0:RS] // //A zero in the "RS" position of the first byte means that the master //transmits (sends) data to the selected slave, and a one in this position //means that the master receives data from the slave. // //***************************************************************************** #define SLAVE_ADDRESS 0x48 //***************************************************************************** // //Target frequency for SMCLK in kHz // //***************************************************************************** #define CS_SMCLK_DESIRED_FREQUENCY_IN_KHZ 1000 //***************************************************************************** // //SMCLK/FLLRef Ratio // //***************************************************************************** #define CS_SMCLK_FLLREF_RATIO 30 // Pointer to TX data uint8_t TXData = 0; uint8_t TXByteCtr; void main(void) { WDT_A_hold(WDT_A_BASE); //Set DCO FLL reference = REFO CS_initClockSignal( CS_FLLREF, CS_REFOCLK_SELECT, CS_CLOCK_DIVIDER_1 ); //Set Ratio and Desired MCLK Frequency and initialize DCO CS_initFLLSettle( CS_SMCLK_DESIRED_FREQUENCY_IN_KHZ, CS_SMCLK_FLLREF_RATIO ); //Set ACLK = VLO with frequency divider of 1 CS_initClockSignal( CS_ACLK, CS_VLOCLK_SELECT, CS_CLOCK_DIVIDER_1 ); //Set SMCLK = DCO with frequency divider of 1 CS_initClockSignal( CS_SMCLK, CS_DCOCLKDIV_SELECT, CS_CLOCK_DIVIDER_1 ); //Set MCLK = DCO with frequency divider of 1 CS_initClockSignal( CS_MCLK, CS_DCOCLKDIV_SELECT, CS_CLOCK_DIVIDER_1 ); // Configure Pins for I2C GPIO_setAsPeripheralModuleFunctionInputPin( GPIO_PORT_UCB0SCL, GPIO_PIN_UCB0SCL, GPIO_FUNCTION_UCB0SCL ); GPIO_setAsPeripheralModuleFunctionInputPin( GPIO_PORT_UCB0SDA, GPIO_PIN_UCB0SDA, GPIO_FUNCTION_UCB0SDA ); /* * Disable the GPIO power-on default high-impedance mode to activate * previously configured port settings */ PMM_unlockLPM5(); EUSCI_B_I2C_initMasterParam param = {0}; param.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK; param.i2cClk = CS_getSMCLK(); param.dataRate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS; param.byteCounterThreshold = 0; param.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP; EUSCI_B_I2C_initMaster(EUSCI_B0_BASE, ¶m); //Specify slave address EUSCI_B_I2C_setSlaveAddress(EUSCI_B0_BASE, SLAVE_ADDRESS ); //Set Master in receive mode EUSCI_B_I2C_setMode(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_MODE ); //Enable I2C Module to start operations EUSCI_B_I2C_enable(EUSCI_B0_BASE); EUSCI_B_I2C_clearInterrupt(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_NAK_INTERRUPT ); //Enable master Receive interrupt EUSCI_B_I2C_enableInterrupt(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_NAK_INTERRUPT ); while(1) { __delay_cycles(1000); // Delay between transmissions TXByteCtr = 4; // Load TX byte counter TXData = 0; while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE)); EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B0_BASE, TXData++); __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts // Remain in LPM0 until all data // is TX'd // Increment data byte } } //------------------------------------------------------------------------------ // The USCIAB0TX_ISR is structured such that it can be used to transmit any // number of bytes by pre-loading TXByteCtr with the byte count. Also, TXData // points to the next byte to transmit. //------------------------------------------------------------------------------ #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=USCI_B0_VECTOR __interrupt #elif defined(__GNUC__) __attribute__((interrupt(USCI_B0_VECTOR))) #endif void USCIB0_ISR(void) { switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG)) { case USCI_NONE: // No interrupts break; break; case USCI_I2C_UCALIFG: // Arbitration lost break; case USCI_I2C_UCNACKIFG: // NAK received (master only) //resend start if NACK EUSCI_B_I2C_masterSendStart(EUSCI_B0_BASE); break; case USCI_I2C_UCTXIFG0: // TXIFG0 // Check TX byte counter if (TXByteCtr) { EUSCI_B_I2C_masterSendMultiByteNext(EUSCI_B0_BASE, TXData++); // Decrement TX byte counter TXByteCtr--; } else { EUSCI_B_I2C_masterSendMultiByteStop(EUSCI_B0_BASE); // Exit LPM0 __bic_SR_register_on_exit(CPUOFF); } break; default: break; } }
Or I think if you add some delay after EUSCI_B_I2C_masterSendMultiByteNext(EUSCI_B1_BASE, cmd), it will be OK.
Eason
Hi,
Adding the delay didn't help. I've also copied the example (modifying to EUSCI_B1), but still stuck at
//Poll for transmit interrupt flag. while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;
The complete code I'm using, (heavily) based on the example is shown below:
/* --COPYRIGHT--,BSD * Copyright (c) 2017, Texas Instruments Incorporated * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * --/COPYRIGHT--*/ //****************************************************************************** // //! This example shows how to configure the I2C module as a master for //! multi byte transmission in interrupt driven mode. The address of the slave //! module is set in this example. //! //! Demo - EUSCI_B0 I2C Master TX multiple bytes to MSP430 Slave //! //! Description: This demo connects two MSP430's via the I2C bus. The master //! transmits to the slave. This is the MASTER CODE. It cntinuously //! transmits an array of data and demonstrates how to implement an I2C //! master transmitter sending multiple bytes using the USCI_B0 TX interrupt. //! ACLK = n/a, MCLK = SMCLK = BRCLK = default DCO = ~1MHz //! //! /|\ /|\ //! MSP430FR2xx_4xx Board 10k 10k MSP430FR2xx_4xx Board //! slave | | master //! ----------------- | | ----------------- //! | UCB0SDA|<-|----+->|UCB0SDA | //! | | | | | //! | | | | | //! | UCB0SCL|<-+------>|UCB0SCL | //! | | | | //! //! This example uses the following peripherals and I/O signals. You must //! review these and change as needed for your own board: //! - I2C peripheral //! - GPIO Port peripheral (for I2C pins) //! - SCL //! - SDA //! //! This example uses the following interrupt handlers. To use this example //! in your own application you must add these interrupt handlers to your //! vector table. //! - USCI_B0_VECTOR. // //****************************************************************************** #include "driverlib.h" #include "Board.h" //***************************************************************************** // //Set the address for slave module. This is a 7-bit address sent in the //following format: //[A6:A5:A4:A3:A2:A1:A0:RS] // //A zero in the "RS" position of the first byte means that the master //transmits (sends) data to the selected slave, and a one in this position //means that the master receives data from the slave. // //***************************************************************************** #define SLAVE_ADDRESS 0x38 //***************************************************************************** // //Target frequency for SMCLK in kHz // //***************************************************************************** #define CS_SMCLK_DESIRED_FREQUENCY_IN_KHZ 1000 //***************************************************************************** // //SMCLK/FLLRef Ratio // //***************************************************************************** #define CS_SMCLK_FLLREF_RATIO 30 // Pointer to TX data uint8_t TXData = 0; uint8_t TXByteCtr; void main(void) { WDT_A_hold(WDT_A_BASE); //Set DCO FLL reference = REFO CS_initClockSignal( CS_FLLREF, CS_REFOCLK_SELECT, CS_CLOCK_DIVIDER_1 ); //Set Ratio and Desired MCLK Frequency and initialize DCO CS_initFLLSettle( CS_SMCLK_DESIRED_FREQUENCY_IN_KHZ, CS_SMCLK_FLLREF_RATIO ); //Set ACLK = VLO with frequency divider of 1 CS_initClockSignal( CS_ACLK, CS_VLOCLK_SELECT, CS_CLOCK_DIVIDER_1 ); //Set SMCLK = DCO with frequency divider of 1 CS_initClockSignal( CS_SMCLK, CS_DCOCLKDIV_SELECT, CS_CLOCK_DIVIDER_1 ); //Set MCLK = DCO with frequency divider of 1 CS_initClockSignal( CS_MCLK, CS_DCOCLKDIV_SELECT, CS_CLOCK_DIVIDER_1 ); // Configure Pins for I2C GPIO_setAsPeripheralModuleFunctionInputPin( GPIO_PORT_P4, GPIO_PIN6, GPIO_PRIMARY_MODULE_FUNCTION ); GPIO_setAsPeripheralModuleFunctionInputPin( GPIO_PORT_P4, GPIO_PIN7, GPIO_PRIMARY_MODULE_FUNCTION ); /* * Disable the GPIO power-on default high-impedance mode to activate * previously configured port settings */ PMM_unlockLPM5(); EUSCI_B_I2C_initMasterParam param = {0}; param.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK; param.i2cClk = CS_getSMCLK(); param.dataRate = EUSCI_B_I2C_SET_DATA_RATE_100KBPS; param.byteCounterThreshold = 0; param.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP; EUSCI_B_I2C_initMaster(EUSCI_B1_BASE, ¶m); //Specify slave address EUSCI_B_I2C_setSlaveAddress(EUSCI_B1_BASE, SLAVE_ADDRESS ); //Set Master in receive mode EUSCI_B_I2C_setMode(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_MODE ); //Enable I2C Module to start operations EUSCI_B_I2C_enable(EUSCI_B1_BASE); EUSCI_B_I2C_clearInterrupt(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT1 + EUSCI_B_I2C_NAK_INTERRUPT ); //Enable master Receive interrupt EUSCI_B_I2C_enableInterrupt(EUSCI_B1_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT1 + EUSCI_B_I2C_NAK_INTERRUPT ); while(1) { __delay_cycles(1000); // Delay between transmissions TXByteCtr = 4; // Load TX byte counter TXData = 0; while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B1_BASE)); EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B1_BASE, TXData++); __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts // Remain in LPM0 until all data // is TX'd // Increment data byte } } #pragma vector=USCI_B1_VECTOR __interrupt void USCIB1_ISR(void) { switch(__even_in_range(UCB1IV, USCI_I2C_UCBIT9IFG)) { case USCI_NONE: // No interrupts break; break; case USCI_I2C_UCALIFG: // Arbitration lost break; case USCI_I2C_UCNACKIFG: // NAK received (master only) //resend start if NACK EUSCI_B_I2C_masterSendStart(EUSCI_B1_BASE); break; case USCI_I2C_UCTXIFG0: // TXIFG0 // Check TX byte counter if (TXByteCtr) { EUSCI_B_I2C_masterSendMultiByteNext(EUSCI_B1_BASE, TXData++); // Decrement TX byte counter TXByteCtr--; } else { EUSCI_B_I2C_masterSendMultiByteStop(EUSCI_B1_BASE); // Exit LPM0 __bic_SR_register_on_exit(CPUOFF); } break; default: break; } }
I did try some other approaches. To summarise:
I also did try to use a PCF8574A, just to be sure it wasn't the I2C device that was causing the problems. At least I know that the slaves aren't a problem.
Best regards,
Helder
Hi,
The modified example code I've mentioned above isn't working. I believe that the code above could reproduce the problem.
One additional thing, I've hooked up my oscilloscope to see the bus lines and discovered that when the microcontroller pulls down the SDA line, the SDA remains low forever (even if I restart the microcontroller) until I remove and connect power to the PCF8574 again.
The OLED has almost the same behavior except I can't make the SDA line high even power cycling the OLED.
Best regards,
Helder
Hi,
I am back, it really takes me a lot of time. A tiny mistake.
The code you offered has a problem.
EUSCI_B_I2C_clearInterrupt(EUSCI_B1_BASE,
EUSCI_B_I2C_TRANSMIT_INTERRUPT1 +
EUSCI_B_I2C_NAK_INTERRUPT
);
//Enable master Receive interrupt
EUSCI_B_I2C_enableInterrupt(EUSCI_B1_BASE,
EUSCI_B_I2C_TRANSMIT_INTERRUPT1 +
EUSCI_B_I2C_NAK_INTERRUPT
);
Hi,
That's it, it was really a tiny mistake that went unnoticed, sorry. I'm really grateful for your help! About the I2C skipping bytes, at least in the interrupt mode, I solved putting a
while(EUSCI_B_I2C_isBusBusy(EUSCI_B1_BASE) == EUSCI_B_I2C_BUS_BUSY);
After the EUSCI_B_I2C_masterSendMultiByteStart. Adding the delay without the bit check solved too.
The only thing is that the delay (or flag check) only works after the start bit. Not even checking for EUSCI_B_I2C_SENDING_START or EUSCI_B_I2C_SENDING_STOP worked. And that is for CPU clock above 1MHz (at least in my case).
Thanks again,
Helder
**Attention** This is a public forum