I am having challenges driving the SDD1306 OLED, I am using a Launchpad for debugging, it is software implementation as it was a late addition to the system and is not connected to the msp430’s hardware on my custom board, and those ports are used already for other comunication purposes.
According to the waveform it should be working, if I switch the code to hardware i2c it works, as I am using the lauchpad’s P1.6 and P1.7.
Anyways, does anyone of your team has expertise on those little OLEDs?
to switch from software to hardware "#define i2c_bitbang true|false // enable i2c bitbang"
thanks
here is the code
#include <msp430.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "fonts.h"
#define true 0x1
#define false 0x0
//######################################################################
//for Bitbang i2c
//######################################################################
/* Pin Definitions. These should be changed depending on the device that
* you are using.
*/
#define SWI2C_SCL BIT6
#define SWI2C_SDA BIT7
#define SWI2C_PxDIR P1DIR
#define SWI2C_PxOUT P1OUT
#define SWI2C_PxIN P1IN
#define SWI2C_SDA_LOW SWI2C_PxDIR |= SWI2C_SDA
#define SWI2C_SCL_LOW SWI2C_PxDIR |= SWI2C_SCL
#define SWI2C_SCL_HIGH SWI2C_PxDIR &= ~SWI2C_SCL
#define SWI2C_SDA_HIGH SWI2C_PxDIR &= ~SWI2C_SDA
#define I2C_BB_PORT P1 // port to use for I2C pins
#define I2C_BB_SCL BIT6 // pins to use on I2C port
#define I2C_BB_SDA BIT7 // pins to use on I2C port
#define I2C_BB_LED0 BIT0
// tricky macros, needed to get around macro expansion
#define out(r) _out(r)
#define _out(r) (r##OUT)
#define in(r) _in(r)
#define _in(r) (r##IN)
#define dir(r) _dir(r)
#define _dir(r) (r##DIR)
//I2C registers
#define I2C_BB_IN in(I2C_BB_PORT)
#define I2C_BB_OUT out(I2C_BB_PORT)
#define I2C_BB_DIR dir(I2C_BB_PORT)
//Function prototypes
void i2c_bb_setup(void);
void i2c_bb_start(void);
void i2c_bb_stop(void);
void i2c_bb_clear_OLED(void);
void i2c_bb_TransferBuffer(void);
short i2c_bb_tx_byte(unsigned char val);
void i2c_bb_set_OLED_home(void);
unsigned char i2c_bb_rx_byte(unsigned short ack);
short i2c_bb_tx(unsigned char addr,const char *dat,unsigned short len);
short i2c_bb_rx(unsigned char addr,unsigned char *dest,unsigned short len);
//###################### bitbang i2c end ##################################
typedef uint8_t boolean;
typedef uint8_t byte;
#define i2c_bitbang true // enable i2c bitbang
void begin(uint8_t contrast, boolean inverse);
void printS();
void printE();
void printC(const char* Array, unsigned int length);
void printD(const char Data);
void text(const char *s, uint8_t font_size);
uint8_t i, j; // used in: text()
void text__continue_cursor_to_end_of_row(const char *s, uint8_t font_size);
uint8_t width, rows;
uint16_t i2;
#define BBSCL BIT6
#define BBSDA BIT7
void begin(uint8_t contrast, boolean inverse) {
const char Init[] = { // www.adafruit.com/.../SSD1306.pdf
0xAE, // Display off
0x81, // Set Contrast Control for BANK0 (81h)
contrast, // This command sets the Contrast Setting of the display.
// The chip has 256 contrast steps from 00h to FFh.
// The segment output current increases as the contrast step value increases.
0x20, // Set Memory Adress Mode
0x00, // Horizontal Adressing Mode
0x21, // Set Column Adress
0x00, // Start Adress 0
0x7F, // End Adress 127
0x22, // Set Page Adress
0x00, // Start Adress 0
0x07, // End Adress 7
0x40, // Set start line adress 0
0xA1, // Set Segment Re-map (A0h/A1h) --set segment re-map 0 to 127
0xA8, // Set Multiplex Ratio (A8h) (1 to 64)
0x3F, // This command switches the default 63 multiplex mode to any multiplex ratio,
// ranging from 16 to 63.
// The output pads COM0~COM63 will be switched to the corresponding COM signal.
0xC8, // Set COM Output Scan Direction to normal mode
0xD3, // set display offset
0x00, // 0
0x8D, // Set Charge Pump Setting
0x14, // ON
0xDA, // Set COM Pins Hardware Configuration (DAh)
0x12, // alternative + disable remap
0xD5, // Set Display Clock Divide Ratio/ Oscillator Frequency (D5h)
0x80, // divide by 1 and medium freq
0xD9, // Set Pre-charge Period (D9h)
0x22, // medium (reset value)
0xDB, // Set Vcomh regulator output
0x20, // 0.77 x Vcc
(inverse==false?0xA6:0xA7), // Set Normal/Inverse Display (A6h/A7h)
0xA4, // Output follows RAM Content
0xAF // Display on
};
const char Mod[] = {0xA5}; // Display on
printC(Init,31);
//__delay_cycles(5000000);
__delay_cycles(500);
printC(Mod,1);
}
void i2c_bb_set_OLED_home(void){
const char home[] = {
0x20, // Set Memory Adress Mode
0x00, // Horizontal Adressing Mode
0x21, // Set Column Adress
0x00, // Start Adress 0
0x7F, // End Adress 127
0x22, // Set Page Adress
0x00, // Start Adress 0
0x07, // End Adress 7
0x40 // Set start line adress 0
};
printC(home,10);
}
void printC(const char* Array, unsigned int length){
unsigned int c;
if (i2c_bitbang){
short ack;
ack=i2c_bb_tx(0x3C,Array,length);
if (!ack) P1OUT |= I2C_BB_LED0;
// for(c = 0; c < length; c++){
// ack=i2c_bb_tx(0x3C,Array[c],1);
// }
}else{
P1OUT |= BIT0;
UCB0CTL1 = UCSWRST;
UCB0CTL0 = UCMODE_3 + UCMST + UCSYNC; // I2C master mode
UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
UCB0BR0 = 0x15; // < 400 kHz
UCB0I2CSA = 0x3C; // address
UCB0CTL1 &= ~UCSWRST;
UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
while (!(IFG2 & UCB0TXIFG));
for(c = 0; c < length; c++){
UCB0TXBUF = 0x80;
while (!(IFG2 & UCB0TXIFG));
UCB0TXBUF = Array[c];
while (!(IFG2 & UCB0TXIFG));
}
UCB0CTL1 |= UCTXSTP;
P1OUT &= ~BIT0;
}
}
void printS(void){
if (i2c_bitbang){
short ack;
i2c_bb_start(); // Send start
P1OUT |= BIT5;
ack=i2c_bb_tx_byte(0x3C<<1); // Send address with W-bit=0
P1OUT &= ~BIT5;
if (!ack) P1OUT |= I2C_BB_LED0; //
P1OUT |= BIT5;
ack=i2c_bb_tx_byte(0x40); // Control byte D/C=1 next byte is data
if (!ack) P1OUT |= I2C_BB_LED0; //
P1OUT &= ~BIT5;
}else{
UCB0CTL1 = UCSWRST;
UCB0CTL0 = UCMODE_3 + UCMST + UCSYNC; // I2C master mode
UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
UCB0BR0 = 0x15; // < 400 kHz
UCB0I2CSA = 0x3C; // address
UCB0CTL1 &= ~UCSWRST;
P1OUT |= BIT5;
UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
P1OUT &= ~BIT5;
while (!(IFG2 & UCB0TXIFG));
P1OUT |= BIT5;
UCB0TXBUF = 0x40;
while (!(IFG2 & UCB0TXIFG));
P1OUT &= ~BIT5;
}
}
void printE(void){
if (i2c_bitbang){
i2c_bb_stop();
}else{
while (!(IFG2 & UCB0TXIFG));
UCB0CTL1 |= UCTR + UCTXSTP;
}
}
void printD(const char Data){
P1OUT |= BIT5; // debug
if (i2c_bitbang){
short ack;
ack=i2c_bb_tx_byte(Data);
if (!ack) P1OUT |= I2C_BB_LED0; //
}else{
while (!(IFG2 & UCB0TXIFG));
UCB0TXBUF = Data;
}
P1OUT &= ~BIT5; // debug
}
/******************************************************************************
* Usage: One call per row and the font size heights must be equal to 64.
* Example:
* myOLED.text("",2); // 3 rows -- 16x24 font size
* myOLED.text("----Hello World !----",0); // 1 row -- 5x 8 font size
* myOLED.text("",1); // 2 rows -- 11x16 font size
* myOLED.text("---------------------",0); // 1 row -- 5x 8 font size
* myOLED.text("",0); // 1 row -- 5x 8 font size
* //= 8 rows -- = 64 pixels
* s: The string you want to print.
* font_size: Choose a font size {0, 1, 2, 3}, one per row.
* 0 (5x8 pixels), 1 (11x16 pixels), 2 (16x24 pixels) or 3 (24x36 pixels).
*/
void text(const char *s, uint8_t font_size){
if (strlen(s) > 0){
if (font_size==0){
if (strlen(s) < 22){
for (j=0; j<strlen(s); j++){
for (i=0; i<5; i++) printD(Terminal6x8[s[j]-' '][i]);
printD(0x00); // put a distance of one pixel between the characters
}
text__continue_cursor_to_end_of_row(s,font_size);
}else
text("-ERROR:Exceeds row-",font_size);
}else if (font_size==1){
if(strlen(s) < 11){
// print first part/row of the character(s)
for (j=0; j<strlen(s); j++){
for (i=0; i<11; i++) printD(Terminal11x16[s[j]-' '][2*i]);
printD(0x00); // put a distance of one pixel between the characters
}
text__continue_cursor_to_end_of_row(s,font_size);
// print second part/row of the character(s)
for (j=0; j<strlen(s); j++){
for (i=0; i<11; i++) printD(Terminal11x16[s[j]-' '][2*i+1]);
printD(0x00); //put a distance of one pixel between the characters
}
text__continue_cursor_to_end_of_row(s,font_size);
}else
text("-ERR:long-",font_size);
}else if(font_size == 2){
if(strlen(s) < 8){
// print first part/row of the character(s)
for (j=0; j<strlen(s); j++){
for (i=0; i<16; i++) printD(Arial16x24[s[j]-'0'][i*3]);
printD(0x00); // put a distance of one pixel between the characters
}
text__continue_cursor_to_end_of_row(s,font_size);
// print second part/row of the character(s)
for (j=0; j<strlen(s); j++){
for (i=0; i<16; i++) printD(Arial16x24[s[j]-'0'][i*3+1]);
printD(0x00); //put a distance of one pixel between the characters
}
text__continue_cursor_to_end_of_row(s,font_size);
// print third part/row of the character(s)
for (j=0; j<strlen(s); j++){
for (i=0; i<16; i++) printD(Arial16x24[s[j]-'0'][i*3+2]);
printD(0x00); //put a distance of one pixel between the characters
}
text__continue_cursor_to_end_of_row(s,font_size);
}
else
text("",font_size); // since this Arial font size only has numbers included, so instead of displaying an error-text, we display nothing
}else if(font_size == 3){
if(strlen(s) < 6){
// print first part/row of the character(s)
for (j=0; j<strlen(s); j++){
for (i=0; i<24; i++) printD(Arial24x40[s[j]-'0'][i*5]);
printD(0x00); // put a distance of one pixel between the characters
}
text__continue_cursor_to_end_of_row(s,font_size);
// print second part/row of the character(s)
for (j=0; j<strlen(s); j++){
for (i=0; i<24; i++) printD(Arial24x40[s[j]-'0'][i*5+1]);
printD(0x00); //put a distance of one pixel between the characters
}
text__continue_cursor_to_end_of_row(s,font_size);
// print third part/row of the character(s)
for (j=0; j<strlen(s); j++){
for (i=0; i<24; i++)
printD(Arial24x40[s[j]-'0'][i*5+2]);
printD(0x00); //put a distance of one pixel between the characters
}
text__continue_cursor_to_end_of_row(s,font_size);
// print fourth part/row of the character(s)
for (j=0; j<strlen(s); j++){
for (i=0; i<24; i++) printD(Arial24x40[s[j]-'0'][i*5+3]);
printD(0x00); //put a distance of one pixel between the characters
}
text__continue_cursor_to_end_of_row(s,font_size);
// print fifth part/row of the character(s)
for (j=0; j<strlen(s); j++){
for (i=0; i<24; i++) printD(Arial24x40[s[j]-'0'][i*5+4]);
printD(0x00); //put a distance of one pixel between the characters
}
text__continue_cursor_to_end_of_row(s,font_size);
}else
text("",font_size); // since this Arial font size only has numbers included, so instead of displaying an error-text, we display nothing
}
}else
text__continue_cursor_to_end_of_row("",font_size);
}
/************************************************************************
* After printing a string (or if no string is given (text(""))), put the
* cursor at the end of that row so that a next call of text() starts
* at the beginning of the next row.
************************************************************************/
void text__continue_cursor_to_end_of_row(const char *s, uint8_t font_size) {
if(font_size == 3) {
width = 24;
rows = 5;
}else if(font_size == 2) {
width = 16;
rows = 3;
}else if(font_size == 1) {
width = 11;
rows = 2;
}else if(font_size == 0) {
width = 5;
rows = 1;
}
if(strlen(s) > 0)
for(i2=0; i2<128 - width * strlen(s) - strlen(s); ++i2) printD(0x00);
else
for(i2=0; i2<128 * rows; ++i2) printD(0x00);
}
main(void){
WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer
uint32_t i=0;
BCSCTL1=CALBC1_16MHZ;
DCOCTL=CALDCO_16MHZ;
__delay_cycles(5000000);
if (i2c_bitbang){
P1OUT &= ~(I2C_BB_SDA+I2C_BB_SCL); // set output to low
P1DIR &= ~(I2C_BB_SDA+I2C_BB_SCL); // set pins as inputs
P1DIR |= BIT0+BIT5;
P1OUT &= ~(BIT0+BIT5);
}else{
P1SEL |= BBSDA + BBSCL; // Assign I2C pins to USCI_B0
P1SEL2 |= BBSDA + BBSCL; // Assign I2C pins to USCI_B0
P1DIR |= I2C_BB_LED0+BIT5;
P1OUT &= ~(I2C_BB_LED0+BIT5);
}
char str[42];
char str2[50];
begin(0xff, // contrast from 0x00 to 0xff (0~255)
false // inverse display
);
while(1){
P1OUT |= BIT0; // debug
//if (i2c_bitbang) i2c_bb_clear_OLED();
//if (i2c_bitbang) i2c_bb_set_OLED_home();
printS();
// Number of text() calls: The sum of font size heights must be 64.
//sprintf(str, "%lu", ++i);
sprintf(str2, "--- Hello ---");
//myOLED.text(str,1); // 3 rows == x24 font size
//myOLED.text("----Hello World !----",0); // 1 row == x 8 font size
//text(" testing new code ---",0); // 1 row == x 8 font size
// 8 rows == 64 pixels
// text(str,3); // x40 pixels
// text(str,2); // x24 pixels
//= 64 pixels
text("6290 9001",1); // 3 rows == x24 font size
text("Logpoint Operation",0); // 1 row == x 8 font size
text("1234567",2); // 2 rows == x16 font size
text("-----Lot Number------",0); // 1 row == x 8 font size
text("SmartRack WIP mngmnt",0); // 1 row == x 8 font size
// text("1testing new code L1 ",0); // 1 row == x 8 font size
// text("2testing new code L2 ",0); // 1 row == x 8 font size
// text("3testing new code L3 ",0); // 1 row == x 8 font size
// text("4testing new code L4 ",0); // 1 row == x 8 font size
// text("5testing new code L5 ",0); // 1 row == x 8 font size
// text("6testing new code L6 ",0); // 1 row == x 8 font size
// text("7testing new code L7 ",0); // 1 row == x 8 font size
// text("8testing new code L8 ",0); // 1 row == x 8 font size
printE(); //= 64 pixels
P1OUT &= ~BIT0; // debug
__delay_cycles(160000);
//while(1);
// //delay(10); // update OLED every 100 milliseconds
//
// printS();
// // Number of text() calls: The sum of font size heights must be 64.
//
// //sprintf(str, "%lu", ++i);
// sprintf(str2, "luis flores");
// //myOLED.text(str,1); // 3 rows == x24 font size
// //myOLED.text("----Hello World !----",0); // 1 row == x 8 font size
// //text(" testing new code ---",0); // 1 row == x 8 font size
// // 8 rows == 64 pixels
// // text(str,3); // x40 pixels
// // text(str,2); // x24 pixels
// //= 64 pixels
//
//// text("6290 9001",1); // 3 rows == x24 font size
//// text("Logpoint Operation",0); // 1 row == x 8 font size
//// text("1234567",2); // 2 rows == x16 font size
//// text("-----Lot Number------",0); // 1 row == x 8 font size
//// text(" SmartRack WIP mngmnt",0); // 1 row == x 8 font size
//
// text("1testing new code L1 ",0); // 1 row == x 8 font size
// text("2testing new code L2 ",0); // 1 row == x 8 font size
// text("3testing new code L3 ",0); // 1 row == x 8 font size
// text("4testing new code L4 ",0); // 1 row == x 8 font size
// text("5testing new code L5 ",0); // 1 row == x 8 font size
// text("6testing new code L6 ",0); // 1 row == x 8 font size
// text("7testing new code L7 ",0); // 1 row == x 8 font size
// text("8testing new code L8 ",0); // 1 row == x 8 font size
//
// printE(); //= 64 pixels
//
// __delay_cycles(160000);
//delay(10); // update OLED every 100 milliseconds
}
}
//setup bit bang I2C pins
void i2c_bb_setup(void){
SWI2C_PxOUT &= ~(SWI2C_SCL | SWI2C_SDA);
SWI2C_SCL_HIGH;
SWI2C_SDA_HIGH;
P1DIR |= BIT0|BIT5;
P1OUT &= ~(BIT0|BIT5);
}
//wait for 1/2 of I2C clock period
void i2c_bb_hc(void){
//_delay_cycles(800); // wait for 0.05ms
__delay_cycles(1); // wait for 0.05ms
}
void i2c_bb_start(void){
// I2C_BB_DIR |= I2C_BB_SDA; // pull SDA low
// i2c_bb_hc(); // wait for 1/2 clock first
// I2C_BB_DIR |= I2C_BB_SCL; // pull SCL low chly
// while (!(I2C_BB_IN&I2C_BB_SCL)) { // Clock stretching
// // You should add timeout to this loop
// }
SWI2C_SDA_LOW;
i2c_bb_hc(); // wait for 1/2 clock first
SWI2C_SCL_LOW;
}
void i2c_bb_stop(void){
// I2C_BB_DIR |= I2C_BB_SDA; // pull SDA low
// i2c_bb_hc(); // wait for 1/2 clock for end of start
// I2C_BB_DIR &= ~I2C_BB_SCL; // float SCL or SCL high
// i2c_bb_hc(); // wait for 1/2 clock for end of start
// I2C_BB_DIR &= ~I2C_BB_SDA; // float SDA or SDA high
// i2c_bb_hc(); // wait for 1/2 clock
SWI2C_SDA_LOW;
i2c_bb_hc(); // wait for 1/2 clock first
i2c_bb_hc(); // wait for 1/2 clock first
SWI2C_SCL_HIGH;
i2c_bb_hc(); // wait for 1/2 clock first
i2c_bb_hc(); // wait for 1/2 clock first
while(!(SWI2C_PxIN & SWI2C_SCL)); // Waiting for our clock line to go high if the slave is stretching
SWI2C_SCL_LOW;
i2c_bb_hc(); // wait for 1/2 clock first
i2c_bb_hc(); // wait for 1/2 clock first
SWI2C_SCL_HIGH;
i2c_bb_hc(); // wait for 1/2 clock first
i2c_bb_hc(); // wait for 1/2 clock first
while(!(SWI2C_PxIN & SWI2C_SCL)); // Waiting for our clock line to go high if the slave is stretching
SWI2C_SDA_HIGH;
i2c_bb_hc(); // wait for 1/2 clock first
}
//send value over I2C return 1 if slave ACKed
short i2c_bb_tx_byte(unsigned char val){
unsigned char i;
for(i=0;i<8;i++){ // shift out bits
if (val&0x80) SWI2C_SDA_HIGH; // check bit, float SDA
else SWI2C_SDA_LOW; // pull SDA low
i2c_bb_hc(); // wait for 1/2 clock
SWI2C_SCL_HIGH; // float SCL
val<<=1; // shift
i2c_bb_hc(); // wait for 1/2 clock
i2c_bb_hc(); // wait for 1/2 clock
SWI2C_SCL_LOW; // pull SCL low chly
i2c_bb_hc(); // wait for 1/2 clock
}
SWI2C_SDA_HIGH;
i2c_bb_hc(); // wait for 1/2 clock
SWI2C_SCL_HIGH;
while(!(SWI2C_PxIN & SWI2C_SCL)); // Waiting for our clock line to go high if the slave is stretching
val= I2C_BB_IN&I2C_BB_SDA; // sample SDA
i2c_bb_hc(); // wait for 1/2 clock
//SWI2C_SDA_LOW;
SWI2C_SCL_LOW; // pull SCL low
i2c_bb_hc(); // wait for 1/2 clock
return !val; // return sampled value
}
short i2c_bb_tx(unsigned char addr,const char *dat,unsigned short len){
short ack;
unsigned char i;
i2c_bb_start(); // send start
ack=i2c_bb_tx_byte(addr<<1); // send address with W bit
if (!ack) P1OUT |= I2C_BB_LED0; //
for(i=0;i<len && ack;i++){ //
ack|=i2c_bb_tx_byte(0x80); // control byte
ack|=i2c_bb_tx_byte(dat[i]); // transmit next byte
}
i2c_bb_stop(); // transmit stop
if (!ack) P1OUT |= I2C_BB_LED0; //
return ack; // return if slave NACKed
}
//receive value over I2C return 1 if slave ACKed
unsigned char i2c_bb_rx_byte(unsigned short ack){
unsigned char val;
int i;
for(i=0;i<8;i++){ // shift out bits
I2C_BB_DIR |= I2C_BB_SCL; // pull SCL low
i2c_bb_hc(); // wait for 1/2 clock
I2C_BB_DIR &= ~I2C_BB_SCL; // float SCL
while (!(I2C_BB_IN&I2C_BB_SCL)) { // Clock stretching
// You should add timeout to this loop
}
i2c_bb_hc(); // wait for 1/2 clock
val<<=1; // shift value to make room
if(I2C_BB_IN&I2C_BB_SDA) val|=1; // sample data
}
//check ack bit
I2C_BB_DIR |= I2C_BB_SCL; // pull SCL low
if(ack){ // check if we are ACKing this byte
I2C_BB_DIR |= I2C_BB_SDA; // pull SDA low for ACK
}else{
I2C_BB_DIR &= ~I2C_BB_SDA; // float SDA for NACK
}
i2c_bb_hc(); // wait for 1/2 clock
I2C_BB_DIR &= ~I2C_BB_SCL; // float SCL
i2c_bb_hc(); // wait for 1/2 clock
I2C_BB_DIR |= I2C_BB_SCL; // float SDA
//I2C_BB_DIR &=~ I2C_BB_SDA;
return val; // return value
}
short i2c_bb_rx(unsigned char addr,unsigned char *dest,unsigned short len){
int i;
i2c_bb_start(); // send start
if(!i2c_bb_tx_byte((addr<<1)|BIT0)){ // send address with R bit
return 0; // got NACK return error
}
for(i=0;i<len;i++){ // send data bytes
dest[i]=i2c_bb_tx_byte(i==len-1); // transmit next byte
}
i2c_bb_stop(); //transmit stop
return 1; //return if slave NACKed
}
void i2c_bb_clear_OLED(void){
short ack;
int c;
i2c_bb_start(); // send start
ack=i2c_bb_tx_byte( 0x3C<<1 ); // send address with plus W-bit=0, write
ack=i2c_bb_tx_byte(0x40); // Control byte D/C=1 next byte is data
for(c = 0; c>1024; c++){
ack=i2c_bb_tx_byte(0x00); // Data byte
}
i2c_bb_stop();
}