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.

MSP430FR4133: MSP430FR4133 I2C help

Part Number: MSP430FR4133

Hi there, I'm hoping someone could please help me configure the MSP430Fr4133 for use with the SSD1306 I2C display?

// I2C.c

#include "i2c.h"
#include <msp430fr4133.h>
#include <stdint.h>

#define SDA BIT2                                                        // i2c sda P5.2
#define SCL BIT3                                                        // i2c scl P5.3

unsigned char* PTxData;                                                       // Pointer to TX data
unsigned char TxByteCtr;                                                      // number of bytes to TX


void i2c_init(void) {
    P5SEL0   |= SCL + SDA;                                              // Assign I2C pins to USCI_B0
    UCB0CTL1 |= UCSWRST;                                                // Enable SW reset
    UCB0CTLW0  = UCMST + UCMODE_3 + UCSYNC;                              // I2C Master, synchronous mode
    UCB0CTL1  = UCSSEL_2 + UCSWRST;                                     // Use SMCLK, keep SW reset
    UCB0BRW   = 10;                                                     // fSCL = SMCLK/10 = ~100kHz with SMCLK 1MHz
    UCB0BRW   = 0;
    UCB0CTL1 &= ~UCSWRST;                                               // Clear SW reset, resume operation
    UCB0IE |= UCTXIE0;                                                  // Enable TX interrupt

} // end i2c_init

void i2c_write(unsigned char slave_address, unsigned char *DataBuffer, unsigned char ByteCtr) {
    UCB0I2CSA = slave_address;

    PTxData = DataBuffer;
    TxByteCtr = ByteCtr;

    while (UCB0CTL1 & UCTXSTP);                                         // Ensure stop condition got sent
    UCB0CTL1 |= UCTR + UCTXSTT;                                         // I2C TX, start condition
    while (UCB0CTL1 & UCTXSTP);                                         // Ensure stop condition got sent
    __bis_SR_register(CPUOFF + GIE);                                    // Enter LPM0 w/ interrupts
                                                                        // Remain in LPM0 until all data is TX'd
}


#pragma vector = USCI_B0_VECTOR
__interrupt void USCIB0_ISR(void){
switch(__even_in_range(UCB0IV,0x1E))
{
        case USCI_NONE:          break;               // Vector 0: No interrupts break;
        case USCI_I2C_UCALIFG:   break;
        case USCI_I2C_UCNACKIFG: break;
        case USCI_I2C_UCSTTIFG:  break;               // Vector 6: STTIFG break;
        case USCI_I2C_UCSTPIFG:  break;               // Vector 8: STPIFG break;
        case USCI_I2C_UCRXIFG3:  break;               // Vector 10: RXIFG3 break;
        case USCI_I2C_UCTXIFG3:  break;               // Vector 14: TXIFG3 break;
        case USCI_I2C_UCRXIFG2:  break;               // Vector 16: RXIFG2 break;
        case USCI_I2C_UCTXIFG2:  break;               // Vector 18: TXIFG2 break;
        case USCI_I2C_UCRXIFG1:  break;               // Vector 20: RXIFG1 break;
        case USCI_I2C_UCTXIFG1:  break;               // Vector 22: TXIFG1 break;
        case USCI_I2C_UCRXIFG0:  break;               // Vector 24: RXIFG0 break;
        case USCI_I2C_UCTXIFG0:
            if (TxByteCtr)
            {                                         // Check TX byte counter
                    UCB0TXBUF = *PTxData++;           // Load TX buffer
                    TxByteCtr--;                      // Decrement TX byte counter
            }
            else
            {
                    UCB0CTL1 |= UCTXSTP;              // I2C stop condition
                    UCB0IE &= ~UCTXIE0;                // Clear USCI_B0 TX int flag
                    __bic_SR_register_on_exit(CPUOFF);// Exit LPM0
            }
          break;                                      // Vector 26: TXIFG0 break;
        case USCI_I2C_UCBCNTIFG: break;               // Vector 28: BCNTIFG
        case USCI_I2C_UCCLTOIFG: break;               // Vector 30: clock low timeout
        case USCI_I2C_UCBIT9IFG: break;               // Vector 32: 9th bit
        default: break;
  }
}

//I2C.h

#ifndef I2C_H_
#define I2C_H_

#include <msp430fr4133.h>

/* ====================================================================
 * I2C Prototype Definitions
 * ==================================================================== */
void i2c_init(void);
void i2c_write(unsigned char, unsigned char *, unsigned char);

#endif /* I2C_H_ */


/*
 * ssd1306.h
 */

#ifndef SSD1306_H_
#define SSD1306_H_

#include <msp430fr4133.h>
#include <stdint.h>
#include <string.h>
#include "i2c.h"

/* ====================================================================
 * Horizontal Centering Number Array
 * ==================================================================== */
#define HCENTERUL_OFF   0
#define HCENTERUL_ON    1

/* ====================================================================
 * SSD1306 OLED Settings and Command Definitions
 * ==================================================================== */

#define SSD1306_I2C_ADDRESS             0x3C

#define SSD1306_LCDWIDTH                128
#define SSD1306_LCDHEIGHT               64
#define SSD1306_128_64

#define SSD1306_SETCONTRAST             0x81
#define SSD1306_DISPLAYALLON_RESUME     0xA4
#define SSD1306_DISPLAYALLON            0xA5
#define SSD1306_NORMALDISPLAY           0xA6
#define SSD1306_INVERTDISPLAY           0xA7
#define SSD1306_DISPLAYOFF              0xAE
#define SSD1306_DISPLAYON               0xAF

#define SSD1306_SETDISPLAYOFFSET        0xD3
#define SSD1306_SETCOMPINS              0xDA

#define SSD1306_SETVCOMDETECT           0xDB

#define SSD1306_SETDISPLAYCLOCKDIV      0xD5
#define SSD1306_SETPRECHARGE            0xD9

#define SSD1306_SETMULTIPLEX            0xA8

#define SSD1306_SETLOWCOLUMN            0x00
#define SSD1306_SETHIGHCOLUMN           0x10

#define SSD1306_SETSTARTLINE            0x40

#define SSD1306_MEMORYMODE              0x20
#define SSD1306_COLUMNADDR              0x21
#define SSD1306_PAGEADDR                0x22

#define SSD1306_COMSCANINC              0xC0
#define SSD1306_COMSCANDEC              0xC8

#define SSD1306_SEGREMAP                0xA0

#define SSD1306_CHARGEPUMP              0x8D

#define SSD1306_EXTERNALVCC             0x1
#define SSD1306_SWITCHCAPVCC            0x2

// currently no scroll functionality, left for possible future use
#define SSD1306_ACTIVATE_SCROLL                         0x2F
#define SSD1306_DEACTIVATE_SCROLL                       0x2E
#define SSD1306_SET_VERTICAL_SCROLL_AREA                0xA3
#define SSD1306_RIGHT_HORIZONTAL_SCROLL                 0x26
#define SSD1306_LEFT_HORIZONTAL_SCROLL                  0x27
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL    0x29
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL     0x2A


/* ====================================================================
 * SSD1306 OLED Prototype Definitions
 * ==================================================================== */
void ssd1306_init(void);
void ssd1306_command(unsigned char);
void ssd1306_clearDisplay(void);
void ssd1306_setPosition(uint8_t, uint8_t);
void ssd1306_printText(uint8_t, uint8_t, char *);
void ssd1306_printTextBlock(uint8_t, uint8_t, char *);
void ssd1306_printUI32(uint8_t, uint8_t, uint32_t, uint8_t);

uint8_t digits(uint32_t);
void ultoa(uint32_t, char *);
void reverse(char *);

#endif /* SSD1306_H_ */

/*
 * ssd1306.c
 */

#include "ssd1306.h"
#include <msp430fr4133.h>
#include <stdint.h>
#include "font_5x7.h"
#include "i2c.h"

/* ====================================================================
 * Horizontal Centering Number Array
 * ==================================================================== */
   
unsigned char buffer[17];                                                     // buffer for data transmission to screen

const unsigned char HcenterUL[] = {                                           // Horizontally center number with separators on screen
                               0,                                       // 0 digits, not used but included to size array correctly
                               61,                                      // 1 digit
                               58,                                      // 2 digits
                               55,                                      // 3 digits
                               49,                                      // 4 digits and 1 separator
                               46,                                      // 5 digits and 1 separator
                               43,                                      // 6 digits and 1 separator
                               37,                                      // 7 digits and 2 separators
                               34,                                      // 8 digits and 2 separators
                               31,                                      // 9 digits and 2 separators
                               25                                       // 10 digits and 3 separators
};

void ssd1306_init(void) {
    // SSD1306 init sequence
    ssd1306_command(SSD1306_DISPLAYOFF);                                // 0xAE
    ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV);                        // 0xD5
    ssd1306_command(0x80);                                              // the suggested ratio 0x80

    ssd1306_command(SSD1306_SETMULTIPLEX);                              // 0xA8
    ssd1306_command(SSD1306_LCDHEIGHT - 1);

    ssd1306_command(SSD1306_SETDISPLAYOFFSET);                          // 0xD3
    ssd1306_command(0x0);                                               // no offset
    ssd1306_command(SSD1306_SETSTARTLINE | 0x0);                        // line #0
    ssd1306_command(SSD1306_CHARGEPUMP);                                // 0x8D
    ssd1306_command(0x14);                                              // generate high voltage from 3.3v line internally
    ssd1306_command(SSD1306_MEMORYMODE);                                // 0x20
    ssd1306_command(0x00);                                              // 0x0 act like ks0108
    ssd1306_command(SSD1306_SEGREMAP | 0x1);
    ssd1306_command(SSD1306_COMSCANDEC);

    ssd1306_command(SSD1306_SETCOMPINS);                                // 0xDA
    ssd1306_command(0x12);
    ssd1306_command(SSD1306_SETCONTRAST);                               // 0x81
    ssd1306_command(0xCF);

    ssd1306_command(SSD1306_SETPRECHARGE);                              // 0xd9
    ssd1306_command(0xF1);
    ssd1306_command(SSD1306_SETVCOMDETECT);                             // 0xDB
    ssd1306_command(0x40);
    ssd1306_command(SSD1306_DISPLAYALLON_RESUME);                       // 0xA4
    ssd1306_command(SSD1306_NORMALDISPLAY);                             // 0xA6

    ssd1306_command(SSD1306_DEACTIVATE_SCROLL);

    ssd1306_command(SSD1306_DISPLAYON);                                 //--turn on oled panel
} // end ssd1306_init

void ssd1306_command(unsigned char command) {
    buffer[0] = 0x80;
    buffer[1] = command;

    i2c_write(SSD1306_I2C_ADDRESS, buffer, 2);
} // end ssd1306_command

void ssd1306_clearDisplay(void) {

    ssd1306_setPosition(0, 0);
    uint8_t i;
    for (i = 64; i > 0; i--) {                                          // count down for loops when possible for ULP
        uint8_t x;
        for(x = 16; x > 0; x--) {
            if (x == 1) {
                buffer[x-1] = 0x40;
            } else {
                buffer[x-1] = 0x0;
            }
        }

        i2c_write(SSD1306_I2C_ADDRESS, buffer, 17);
    }
} // end ssd1306_clearDisplay

void ssd1306_setPosition(uint8_t column, uint8_t page) {
    if (column > 128) {
        column = 0;                                                     // constrain column to upper limit
    }

    if (page > 8) {
        page = 0;                                                       // constrain page to upper limit
    }

    ssd1306_command(SSD1306_COLUMNADDR);
    ssd1306_command(column);                                            // Column start address (0 = reset)
    ssd1306_command(SSD1306_LCDWIDTH-1);                                // Column end address (127 = reset)

    ssd1306_command(SSD1306_PAGEADDR);
    ssd1306_command(page);                                              // Page start address (0 = reset)
    ssd1306_command(7);                                                 // Page end address
} // end ssd1306_setPosition

void ssd1306_printText(uint8_t x, uint8_t y, char *ptString) {
    ssd1306_setPosition(x, y);

    while (*ptString != '\0') {
        buffer[0] = 0x40;

        if ((x + 5) >= 127) {                                           // char will run off screen
            x = 0;                                                      // set column to 0
            y++;                                                        // jump to next page
            ssd1306_setPosition(x, y);                                  // send position change to oled
        }

        uint8_t i;
        for(i = 0; i< 5; i++) {
            buffer[i+1] = font_5x7[*ptString - ' '][i];
        }

        buffer[6] = 0x0;

        i2c_write(SSD1306_I2C_ADDRESS, buffer, 7);
        ptString++;
        x+=6;
    }
} // end ssd1306_printText

void ssd1306_printTextBlock(uint8_t x, uint8_t y, char *ptString) {
    char word[12];
    uint8_t i;
    uint8_t endX = x;
    while (*ptString != '\0'){
        i = 0;
        while ((*ptString != ' ') && (*ptString != '\0')) {
            word[i] = *ptString;
            ptString++;
            i++;
            endX += 6;
        }

        word[i++] = '\0';

        if (endX >= 127) {
            x = 0;
            y++;
            ssd1306_printText(x, y, word);
            endX = (i * 6);
            x = endX;
        } else {
            ssd1306_printText(x, y, word);
            endX += 6;
            x = endX;
        }
        ptString++;
    }

}


void ssd1306_printUI32( uint8_t x, uint8_t y, uint32_t val, uint8_t Hcenter ) {
    char text[14];

    ultoa(val, text);
    if (Hcenter) {
        ssd1306_printText(HcenterUL[digits(val)], y, text);
    } else {
        ssd1306_printText(x, y, text);
    }
} // end ssd1306_printUI32

uint8_t digits(uint32_t n) {
    if (n < 10) {
        return 1;
    } else if (n < 100) {
        return 2;
    } else if (n < 1000) {
        return 3;
    } else if (n < 10000) {
        return 4;
    } else if (n < 100000) {
        return 5;
    } else if (n < 1000000) {
        return 6;
    } else if (n < 10000000) {
        return 7;
    } else if (n < 100000000) {
        return 8;
    } else if (n < 1000000000) {
        return 9;
    } else {
        return 10;
    }
} // end digits

void ultoa(uint32_t val, char *string) {
    uint8_t i = 0;
    uint8_t j = 0;
                                                                        // use do loop to convert val to string
    do {
        if (j==3) {                                                     // we have reached a separator position
            string[i++] = ',';                                          // add a separator to the number string
            j=0;                                                        // reset separator indexer thingy
        }
            string[i++] = val%10 + '0';                                 // add the ith digit to the number string
            j++;                                                        // increment counter to keep track of separator placement
    } while ((val/=10) > 0);

    string[i++] = '\0';                                                 // add termination to string
    reverse(string);                                                    // string was built in reverse, fix that
} // end ultoa

void reverse(char *s)
{
    uint8_t i, j;
    uint8_t c;

    for (i = 0, j = strlen(s)-1; i<j; i++, j--) {
        c = s[i];
        s[i] = s[j];
        s[j] = c;
    }
} // end reverse

/*
 * font_5x7.h
 */

#ifndef FONT_5X7_H_
#define FONT_5X7_H_

const unsigned char font_5x7[96][5] = {{0x00, 0x00, 0x00, 0x00, 0x00},  // space
                                       {0x00, 0x00, 0x4F, 0x00, 0x00},  // !
                                       {0x00, 0x07, 0x00, 0x07, 0x00},  // "
                                       {0x14, 0x7F, 0x14, 0x7F, 0x14},  // #
                                       {0x24, 0x2A, 0x7F, 0x2A, 0x12},  // $
                                       {0x23, 0x13, 0x08, 0x64, 0x62},  // %
                                       {0x36, 0x49, 0x55, 0x22, 0x50},  // &
                                       {0x00, 0x05, 0x03, 0x00, 0x00},  // '
                                       {0x00, 0x1C, 0x22, 0x41, 0x00},  // (
                                       {0x00, 0x41, 0x22, 0x1C, 0x00},  // )
                                       {0x14, 0x08, 0x3E, 0x08, 0x14},  // *
                                       {0x08, 0x08, 0x3E, 0x08, 0x08},  // +
                                       {0x00, 0x50, 0x30, 0x00, 0x00},  // ,
                                       {0x08, 0x08, 0x08, 0x08, 0x08},  // -
                                       {0x00, 0x60, 0x60, 0x00, 0x00},  // .
                                       {0x20, 0x10, 0x08, 0x04, 0x02},  // /
                                       {0x3E, 0x51, 0x49, 0x45, 0x3E},  // 0
                                       {0x00, 0x42, 0x7F, 0x40, 0x00},  // 1
                                       {0x42, 0x61, 0x51, 0x49, 0x46},  // 2
                                       {0x21, 0x41, 0x45, 0x4B, 0x31},  // 3
                                       {0x18, 0x14, 0x12, 0x7F, 0x10},  // 4
                                       {0x27, 0x49, 0x49, 0x49, 0x31},  // 5
                                       {0x3C, 0x4A, 0x49, 0x49, 0x30},  // 6
                                       {0x01, 0x71, 0x09, 0x05, 0x03},  // 7
                                       {0x36, 0x49, 0x49, 0x49, 0x36},  // 8
                                       {0x06, 0x49, 0x49, 0x29, 0x1E},  // 9
                                       {0x00, 0x36, 0x36, 0x00, 0x00},  // :
                                       {0x00, 0x56, 0x36, 0x00, 0x00},  // ;
                                       {0x08, 0x14, 0x22, 0x41, 0x00},  // <
                                       {0x14, 0x14, 0x14, 0x14, 0x14},  // =
                                       {0x00, 0x41, 0x22, 0x14, 0x08},  // >
                                       {0x02, 0x01, 0x51, 0x09, 0x06},  // ?
                                       {0x32, 0x49, 0x79, 0x41, 0x3E},  // @
                                       {0x7E, 0x11, 0x11, 0x11, 0x7E},  // A
                                       {0x7F, 0x49, 0x49, 0x49, 0x36},  // B
                                       {0x3E, 0x41, 0x41, 0x41, 0x22},  // C
                                       {0x7F, 0x41, 0x41, 0x22, 0x1C},  // D
                                       {0x7F, 0x49, 0x49, 0x49, 0x41},  // E
                                       {0x7F, 0x09, 0x09, 0x09, 0x01},  // F
                                       {0x3E, 0x41, 0x49, 0x49, 0x7A},  // G
                                       {0x7F, 0x08, 0x08, 0x08, 0x7F},  // H
                                       {0x00, 0x41, 0x7F, 0x41, 0x00},  // I
                                       {0x20, 0x40, 0x41, 0x3F, 0x01},  // J
                                       {0x7F, 0x08, 0x14, 0x22, 0x41},  // K
                                       {0x7F, 0x40, 0x40, 0x40, 0x40},  // L
                                       {0x7F, 0x02, 0x0C, 0x02, 0x7F},  // M
                                       {0x7F, 0x04, 0x08, 0x10, 0x7F},  // N
                                       {0x3E, 0x41, 0x41, 0x41, 0x3E},  // O
                                       {0x7F, 0x09, 0x09, 0x09, 0x06},  // P
                                       {0x3E, 0x41, 0x51, 0x21, 0x5E},  // Q
                                       {0x7F, 0x09, 0x19, 0x29, 0x46},  // R
                                       {0x46, 0x49, 0x49, 0x49, 0x31},  // S
                                       {0x01, 0x01, 0x7F, 0x01, 0x01},  // T
                                       {0x3F, 0x40, 0x40, 0x40, 0x3F},  // U
                                       {0x1F, 0x20, 0x40, 0x20, 0x1F},  // V
                                       {0x3F, 0x40, 0x38, 0x40, 0x3F},  // W
                                       {0x63, 0x14, 0x08, 0x14, 0x63},  // X
                                       {0x07, 0x08, 0x70, 0x08, 0x07},  // Y
                                       {0x61, 0x51, 0x49, 0x45, 0x43},  // Z
                                       {0x7F, 0x41, 0x41, 0x00, 0x00},  // [
                                       {0x02, 0x04, 0x08, 0x10, 0x20},  // forward slash
                                       {0x00, 0x41, 0x41, 0x7F, 0x00},  // ]
                                       {0x04, 0x02, 0x01, 0x02, 0x04},  // ^
                                       {0x40, 0x40, 0x40, 0x40, 0x40},  // _
                                       {0x00, 0x01, 0x02, 0x04, 0x00},  // `
                                       {0x20, 0x54, 0x54, 0x54, 0x78},  // a
                                       {0x7F, 0x48, 0x44, 0x44, 0x38},  // b
                                       {0x38, 0x44, 0x44, 0x44, 0x20},  // c
                                       {0x38, 0x44, 0x44, 0x48, 0x7F},  // d
                                       {0x38, 0x54, 0x54, 0x54, 0x18},  // e
                                       {0x08, 0x7E, 0x09, 0x01, 0x02},  // f
                                       {0x0C, 0x52, 0x52, 0x52, 0x3E},  // g
                                       {0x7F, 0x08, 0x04, 0x04, 0x78},  // h
                                       {0x00, 0x44, 0x7D, 0x40, 0x00},  // i
                                       {0x20, 0x40, 0x44, 0x3D, 0x00},  // j
                                       {0x7F, 0x10, 0x28, 0x44, 0x00},  // k
                                       {0x00, 0x41, 0x7F, 0x40, 0x00},  // l
                                       {0x7C, 0x04, 0x18, 0x04, 0x78},  // m
                                       {0x7C, 0x08, 0x04, 0x04, 0x78},  // n
                                       {0x38, 0x44, 0x44, 0x44, 0x38},  // o
                                       {0x7C, 0x14, 0x14, 0x14, 0x08},  // p
                                       {0x08, 0x14, 0x14, 0x18, 0x7C},  // q
                                       {0x7C, 0x08, 0x04, 0x04, 0x08},  // r
                                       {0x48, 0x54, 0x54, 0x54, 0x20},  // s
                                       {0x04, 0x3F, 0x44, 0x40, 0x20},  // t
                                       {0x3C, 0x40, 0x40, 0x20, 0x7C},  // u
                                       {0x1C, 0x20, 0x40, 0x20, 0x1C},  // v
                                       {0x3C, 0x40, 0x30, 0x40, 0x3C},  // w
                                       {0x44, 0x28, 0x10, 0x28, 0x44},  // x
                                       {0x0C, 0x50, 0x50, 0x50, 0x3C},  // y
                                       {0x44, 0x64, 0x54, 0x4C, 0x44},  // z
};

#endif /* FONT_5X7_H_ */

#include <msp430fr4133.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "SSD1306.h"
#include "i2c.h"

#define ANUMBER     19052019
#define MAX_COUNT   4.2e9
#define LONG_DELAY  __delay_cycles(3000000)
#define SHORT_DELAY __delay_cycles(50000)

int main(void)
{
    WDTCTL = WDTPW | WDTHOLD;	                // stop watchdog timer

    i2c_init();                                 // initialize I2C to use with OLED
  
    ssd1306_init();                             // Initialize SSD1306 OLED
    ssd1306_clearDisplay();                     // Clear OLED display
    
    
    __enable_interrupt();
    PMM_unlockLPM5();

    while(1) {
        // =========================================
        // Display a Long Text Block
        // =========================================
        // There seems to be a bug in the ssd1306_printTextBlock code,
        //  long text blocks like below need 1 extra character in their array
        //  so the code does not read outside of the array. The below 91 character
        //  text block should only need a array of 92 but to fix the bug use an array of 93.
        //  !!!! MUST FIX BUG IN THE FUTURE !!!!
        char txtBlock[93] = "This is a long multiline text block. The code will automatically add extra lines as needed.";
        ssd1306_printTextBlock(0, 1, txtBlock);
        LONG_DELAY;                             // Avoid delay loops in real code, use timer
        ssd1306_clearDisplay();

        // =========================================
        // Print Single Lines of Text at each row
        // =========================================
        ssd1306_printText(0, 1, "Line 1");
        ssd1306_printText(0, 2, "Line 2");
        ssd1306_printText(0, 3, "Line 3");
        ssd1306_printText(0, 4, "Line 4");
        ssd1306_printText(0, 5, "Line 5");
        ssd1306_printText(0, 6, "Line 6");
        ssd1306_printText(0, 7, "Line 7");

        // Print these at a column other then zero
        ssd1306_printText(40, 1, "40");
        ssd1306_printText(50, 2, "50");
        ssd1306_printText(60, 3, "60");
        ssd1306_printText(70, 4, "70");
        ssd1306_printText(80, 5, "80");
        ssd1306_printText(90, 6, "90");
        ssd1306_printText(100, 7, "100");

        // Hold screen for some time before displaying the next sample
        LONG_DELAY;                              // Avoid delay loops in real code, use timer
        ssd1306_clearDisplay();

        // =========================================
        // Print Unsigned 32 bit number
        // =========================================
        ssd1306_printUI32(0, 1, ANUMBER, HCENTERUL_OFF);  // Print number on line 1
        ssd1306_printUI32(0, 2, ANUMBER, HCENTERUL_ON);   // Print number on line 2, horizontally centered

        // Print counting on line 6 horizontally centered
        uint32_t i = 1;
        uint32_t j;
        uint32_t val = 0;
        while (i<1e9) {
            for(j=0; j<10; j++) {
                val = i * j;
                ssd1306_printUI32(0, 6 , val, HCENTERUL_ON);
                SHORT_DELAY;
            }
            i *= 10;
        }

        for (j=0; j<5; j++) {
            val = i * j;
            ssd1306_printUI32(0, 6 , val, HCENTERUL_ON);
            SHORT_DELAY;
        }

        LONG_DELAY;
        ssd1306_clearDisplay();
    }
}

I have attached my code for reference. I think the issue may be related to the configuration of the I2C pins/ interrupt vector since this was what I have changed from the original library the code has been adapted from on GitHub. Any help would be greatly appreciated! Thank you

  • What does it do when you run it?

    ------------

    >  PMM_unlockLPM5();

    Move this up to precede the call to ssd1306_init (or maybe even before i2c_init) since it needs the GPIOs running.

    ------------

    Also I suggest you check for NACK (UCNACKIE/USCI_I2C_UCNACKIFG) just in case.  

    [Edit: Fixed typo.]

  • Hi Bruce, thank you for your reply. I have incorporated your suggestions but still no luck. The screen displays nothing. From doing some debugging (setting a breakpoint at the interrupt vector), the code never seems to enter the interrupt routine at all. I have revised the registers in accordance with the MSP academy example of using I2C, and got the following code. Still no output on the screen though. Thank you for attempting to solve this problem.

    #include "i2c.h"
    #include <msp430fr4133.h>
    #include <stdint.h>
    
    #define SDA BIT2                                                        // i2c sda P5.2
    #define SCL BIT3                                                        // i2c scl P5.3
    
    unsigned char *PTxData;                                                       // Pointer to TX data
    unsigned char TxByteCtr;                                                      // number of bytes to TX
    
    void i2c_init(void) {
        P5SEL0   |= SCL + SDA;                                              // Assign I2C pins to USCI_B0
        UCB0CTLW0 |= UCSWRST;                                                // Enable SW reset
        UCB0CTLW0  = UCMST + UCMODE_3 + UCSYNC;                              // I2C Master, synchronous mode
        UCB0CTLW0  = UCSSEL_2 + UCSWRST;                                     // Use SMCLK, keep SW reset
        UCB0BRW   = 10;                                                     // fSCL = SMCLK/10 = ~100kHz with SMCLK 1MHz
        UCB0BRW   = 0;
        UCB0CTLW0 &= ~UCSWRST;                                               // Clear SW reset, resume operation
        UCB0IE |= UCTXIE0 | UCNACKIE;                                                 // Enable TX interrupt
    
    } // end i2c_init
    
    void i2c_write(unsigned char slave_address, unsigned char *DataBuffer, unsigned char ByteCtr) {
        UCB0I2CSA = slave_address;
    
        PTxData = DataBuffer;
        TxByteCtr = ByteCtr;
    
        while (UCB0CTLW0 & UCTXSTP);                                         // Ensure stop condition got sent
        UCB0CTLW0 |= UCTR + UCTXSTT;                                         // I2C TX, start condition
        while (UCB0CTLW0 & UCTXSTP);                                         // Ensure stop condition got sent                                                                        // Remain in LPM0 until all data is TX'd
    }
    
    
    #pragma vector = USCI_B0_VECTOR
    __interrupt void USCIB0_ISR(void){
    switch(__even_in_range(UCB0IV,0x1E))
    {
            case USCI_NONE:          break;               // Vector 0: No interrupts break;
            case USCI_I2C_UCALIFG:   break;
            case USCI_I2C_UCNACKIFG:
                // do something
                break;
            case USCI_I2C_UCSTTIFG:  break;               // Vector 6: STTIFG break;
            case USCI_I2C_UCSTPIFG:  break;               // Vector 8: STPIFG break;
            case USCI_I2C_UCRXIFG3:  break;               // Vector 10: RXIFG3 break;
            case USCI_I2C_UCTXIFG3:  break;               // Vector 14: TXIFG3 break;
            case USCI_I2C_UCRXIFG2:  break;               // Vector 16: RXIFG2 break;
            case USCI_I2C_UCTXIFG2:  break;               // Vector 18: TXIFG2 break;
            case USCI_I2C_UCRXIFG1:  break;               // Vector 20: RXIFG1 break;
            case USCI_I2C_UCTXIFG1:  break;               // Vector 22: TXIFG1 break;
            case USCI_I2C_UCRXIFG0:  break;               // Vector 24: RXIFG0 break;
            case USCI_I2C_UCTXIFG0:
                if (TxByteCtr)
                {                                         // Check TX byte counter
                        UCB0TXBUF = *PTxData++;           // Load TX buffer
                        TxByteCtr--;                      // Decrement TX byte counter
                }
                else
                {
                        UCB0CTLW0 |= UCTXSTP;               // I2C stop condition
                        UCB0IFG &= ~UCTXIFG;
                }
              break;                                      // Vector 26: TXIFG0 break;
            case USCI_I2C_UCBCNTIFG: break;               // Vector 28: BCNTIFG
            case USCI_I2C_UCCLTOIFG: break;               // Vector 30: clock low timeout
            case USCI_I2C_UCBIT9IFG: break;               // Vector 32: 9th bit
            default: break;
      }
    }
    
    
    // main.c, prior to while loop
    
    WDTCTL = WDTPW | WDTHOLD;	                // stop watchdog timer
    PMM_unlockLPM5();
    __enable_interrupt();
    i2c_init();                                 // initialize I2C to use with OLED
    ssd1306_init();                             // Initialize SSD1306 OLED
    ssd1306_clearDisplay();                     // Clear OLED display

  • This is problematic:

        UCB0CTLW0 |= UCSWRST;                                                // Enable SW reset
        UCB0CTLW0  = UCMST + UCMODE_3 + UCSYNC;                              // I2C Master, synchronous mode
        UCB0CTLW0  = UCSSEL_2 + UCSWRST; 

    At least you changed it from the first version where you mixed word and byte access but this is actually worse.

    Line 2 clears UCSWRST and line 3 clears the settings made in line 2.

    It should be something like:

        UCB0CTLW0 = UCSWRST;                                                // Enable SW reset
        UCB0CTLW0  |= UCMST | UCMODE_3 | UCSYNC | UCSSEL_2;                 // I2C Master, synchronous mode
      

  • Thank you, David. For some reason, the code only enters the ISR if I use the "+" symbol between the macros - not the "|" symbol. This code now triggers the ISR:

    void i2c_init(void) {

        P5SEL0 |= SCL; // Assign I2C pins to USCI_B0

        P5SEL0 |= SDA;

        UCB0CTLW0 |= UCSWRST; // Enable SW reset

        UCB0CTLW0 |= UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode

        UCB0CTLW0 |= UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset

        UCB0BRW = 10; // fSCL = SMCLK/10 = ~100kHz with SMCLK 1MHz

        UCB0BRW = 0;

        UCB0CTLW0 &= ~UCSWRST; // Clear SW reset, resume operation

        UCB0IE |= UCTXIE0; // Enable TX interrupt

    } // end i2c_init

    But using UCB0CTLW0 |= UCSSEL_2 | UCSWRST will not work. Still no output on the screen, but this is a bit of progress. Could you please offer clarification on when it is better to use byte/ word access? I've looked at the user guide for this but don't really follow. Thanks for your help 

  • The byte access is a holdover from really old MSP430 parts which did it that way. TI includes symbols for those that started out there and prefer it that way. I hate it because when I see UCB0CTL1 I think high byte when in fact it is low. Too confusing. It is a word register use word accesses.

    Your code to set the bit rate uses word accesses but is written assuming two byte accesses. Not only do you get too fast a bit rate, but you violate the brclk/4 limit.

    Since all of the fields in those registers are non-overlapping, there should be no difference in the results between  bit-wise OR and addition. I prefer to use bit-wise OR because I really don't want a carry between bits. Even by accident.

    Another problem is that registers are not always zero after reset. (See docs.) UCCSEL for example is 3. Not a problem this time.

  • Thanks very much for the detailed reply, David. Lots of helpful information I will take on board to try and get this working. Cheers 

  • You've removed the "__bis_SR_register(CPUOFF + GIE); " in the last line of i2c_write. This is what maintains the scheduling. As it is now, the code will circle around and try to start a new operation before the previous one is finished. I suggest you put that line back.

  • Thanks Bruce for check and suggestion.

    Hi Ross,

    If it still stucks, please share the new code you are using.

    Meanwhile, I have further debugging suggestion - Please check the signal wavform in IIC bus, whetehr there is no data, or incorrect clock / data.

    B.R.

    Sal

  • Thank you Bruce and Sal for your support. I believe the UCTXIE0 may need to be changed to UCTXIE2, since the original library (written for a different MSP device used the line  IE2 |= UCB0TXIE). I obviously do not have the IE2 register on my device so this may be the root of the problem. I will try out your suggestion with an oscilloscope tomorrow, Sal. Thanks again guys 

  • UCTXIFG2 (UCTXIE2) only triggers on a slave which is using multiple addresses (notably UCB0I2COA2), which is not your case. UCTXIE is the same as UCTXIE0.

    [Edit: Minor clarification.]

  • Thanks Bruce. Hours saved!

  • David already pointed this out:

    >    UCB0BRW = 10; // fSCL = SMCLK/10 = ~100kHz with SMCLK 1MHz

    >    UCB0BRW = 0;

    You should remove that second line, which (re)sets the I2C clock to 1MHz.

**Attention** This is a public forum