/* OLED_SSD1306 via I2C * * This program communicate with the OLED_SSD1306 via I2C. * It writes "ABC" on the display and fills the rest of the * display with a checker board pattern. * * I2C1_SCL - PB08 * I2C1_SDA - PB09 * */ #include "stm32f4xx.h" #define SLAVE_ADDR 0x3C /* 0111 100. */ #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define SCREEN_PAGE_NUM (SCREEN_HEIGHT / 8) void delayMs(int n); void I2C1_init(void); int I2C1_byteWrite(char saddr, char maddr, char data); void SSD1306_init(void); void SSD1306_data(char data); void SSD1306_command(char command); char imagebuf[SCREEN_PAGE_NUM][SCREEN_WIDTH]; const static char font_table[][6] = { /* sample font table */ {0x7e, 0x11, 0x11, 0x11, 0x7e, 0}, /* A */ {0x7f, 0x49, 0x49, 0x49, 0x36, 0}, /* B */ {0x3e, 0x41, 0x41, 0x41, 0x22, 0}}; /* C */ int main(void) { int i, j, k; I2C1_init(); SSD1306_init(); SSD1306_command(0x22); /* set page */ SSD1306_command(0x00); /* starting page */ SSD1306_command(0x00); /* end page */ SSD1306_command(0x21); /* set column */ SSD1306_command(0); /* starting column */ SSD1306_command(SCREEN_WIDTH-1); /* end column */ /* write ABC at the beginning of first line */ for (j = 0; j < 3; j++) for (i = 0; i < 6; i++) SSD1306_data(font_table[j][i]); /* clear the rest of the line */ for (i = 0; i < SCREEN_WIDTH-3*6; i++) SSD1306_data(0); for (i = 1; i < SCREEN_PAGE_NUM; i++) { SSD1306_command(0x22); /* set page */ SSD1306_command(i); /* starting page */ SSD1306_command(i); /* end page */ SSD1306_command(0x21); /* set column */ SSD1306_command(0); /* starting column */ SSD1306_command(SCREEN_WIDTH-1); /* end column */ /* make checker board pattern */ for (j = 0; j < SCREEN_WIDTH/8; j++) { for (k = 0; k < 4; k++) SSD1306_data(0xF0); for (k = 0; k < 4; k++) SSD1306_data(0x0F); } } for (;;) { /* infinite loop */ } } /* system clock at 16 MHz */ void delayMs(int n) { int i; for (; n > 0; n--) for (i = 0; i < 3195; i++) ; } /* configure UCB1 as I2C */ void I2C1_init(void) { RCC->AHB1ENR |= 2; /* Enable GPIOB clock */ RCC->APB1ENR |= 0x00200000; /* Enable I2C1 clock */ /* configure PB8, PB9 pins for I2C1 */ GPIOB->AFR[1] &= ~0x000000FF; /* PB8, PB9 I2C1 SCL, SDA */ GPIOB->AFR[1] |= 0x00000044; GPIOB->MODER &= ~0x000F0000; /* PB8, PB9 use alternate function */ GPIOB->MODER |= 0x000A0000; GPIOB->OTYPER |= 0x00000300; /* output open-drain */ GPIOB->PUPDR &= ~0x000F0000; /* with pull-ups */ GPIOB->PUPDR |= 0x00050000; I2C1->CR1 = 0x8000; /* software reset I2C1 */ I2C1->CR1 &= ~0x8000; /* out of reset */ I2C1->CR2 = 0x0010; /* peripheral clock is 16 MHz */ I2C1->CCR = 80; /* standard mode, 100kHz clock */ I2C1->TRISE = 17; /* maximum rise time */ I2C1->CR1 |= 0x0001; /* enable I2C1 module */ } /* Write a single byte at memAddr through I2C * write: S-(slaveAddr+w)-ACK-memAddr-ACK-data-ACK-P */ int I2C1_byteWrite(char saddr, char maddr, char data) { volatile int tmp; while (I2C1->SR2 & 2); /* wait until bus not busy */ I2C1->CR1 |= 0x100; /* generate start */ while (!(I2C1->SR1 & 1)); /* wait until start flag is set */ I2C1->DR = saddr << 1; /* transmit slave address */ while (!(I2C1->SR1 & 2)); /* wait until addr flag is set */ tmp = I2C1->SR2; /* clear addr flag */ while (!(I2C1->SR1 & 0x80)); /* wait until data register empty */ I2C1->DR = maddr; /* send memory address */ while (!(I2C1->SR1 & 0x80)); /* wait until data register empty */ I2C1->DR = data; /* transmit data */ while (!(I2C1->SR1 & 4)); /* wait until transfer finished */ I2C1->CR1 |= 0x200; /* generate stop */ return 0; } /* send the initialization sequence to the SSD1306 controller */ void SSD1306_init(void) { const static char init_seq[] = { 0xae, // turn off oled panel 0x00, // set low column address 0x10, // set high column address 0x40, // set start line address 0x20, 0x02, // page addressing mode 0xc8, // top-down segment (4th quadrant) 0x81, // set contrast control register 0xcf, 0xa1, // set segment re-map 95 to 0 0xa6, // set normal display 0xa8, // set multiplex ratio(1 to 64) 0x3f, // 1/64 duty 0xd3, // set display offset 0x00, // not offset 0xd5, // set display clock divide ratio/oscillator frequency 0x80, // set divide ratio 0xd9, // set pre-charge period 0xf1, 0xda, // set com pins hardware configuration 0x12, 0xdb, // set vcomh 0x40, 0x8d, // set Charge Pump enable/disable 0x14, // set(0x10) disable 0xaf // turn on oled panel }; int i; delayMs(100); for (i = 0; i < sizeof(init_seq); i++) SSD1306_command(init_seq[i]); } /* send a byte of data to SSD1306 */ void SSD1306_data(char data) { I2C1_byteWrite(SLAVE_ADDR, 0xC0, data); } /* send a command to SSD1306 */ void SSD1306_command(char command) { I2C1_byteWrite(SLAVE_ADDR, 0x80, command); }