Simple line-by-line way to use SSD1306 screens.
SSD1306_simple.cpp
- Committer:
- austinbrown124
- Date:
- 2020-05-25
- Revision:
- 3:b9855e428053
- Parent:
- 2:574d1b329593
File content as of revision 3:b9855e428053:
#include "SSD1306_simple.h" // I2C is stupid. We can only transfer upt to 255 bytes at a time, but we need header shit // so we will transfer the data to the screen one row at a time // luckily there are only 8 rows // this is OK because it keeps the frame buf short anyways // and makes less code per interrupt SSD1306::SSD1306() { // init screen RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; GPIOB->MODER |= GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1; GPIOB->AFR[0] |= 0x44000000; // PB6, PB7 to alternate function 6 I2C1->TIMINGR |= 0<<I2C_TIMINGR_PRESC_Pos; // presacler = 1 I2C1->TIMINGR |= 5 << I2C_TIMINGR_SCLH_Pos; // I2C1->TIMINGR |= 5 << I2C_TIMINGR_SCLL_Pos; // I2C1->CR2 |= I2C_CR2_AUTOEND; I2C1->CR1 |= I2C_CR1_PE; I2C1->CR2 &= ~0xFF; // clear prev address I2C1->CR2 |= 0x3C<<1; // write SSD1306 address int _vccstate = 0; _vccstate = SSD1306_SWITCHCAPVCC; //GPIOA->ODR |= GPIO_ODR_8; // Init sequence if (ssd1306_command(SSD1306_DISPLAYOFF) ) { // 0xAE // if first command doesn't work, give up 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 if (_vccstate == SSD1306_EXTERNALVCC) { ssd1306_command(0x10); } else { ssd1306_command(0x14); } 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 if (_vccstate == SSD1306_EXTERNALVCC) { ssd1306_command(0x9F); } else { ssd1306_command(0xCF); } ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9 if (_vccstate == SSD1306_EXTERNALVCC) { ssd1306_command(0x22); } else { 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 wait_ms(10); clearDisplay(); } SetupFrameBuf(); // write initial conditions RCC->AHBENR |= RCC_AHBENR_DMA1EN; RCC->AHBENR |= RCC_AHBENR_DMA1EN; DMA1_Channel6->CPAR = (uint32_t)(&(I2C1->TXDR)); //set peripheral register address- the UART transmit data register DMA1_Channel6->CMAR = (uint32_t)framebuf; //set memory address- the thing to transfer DMA1_Channel6->CCR |= DMA_CCR_PL_1; //set channel priority to high DMA1_Channel6->CCR |= DMA_CCR_MINC; //memory increment for array transfer DMA1_Channel6->CCR |= DMA_CCR_DIR; //configure data direction: send data to UART _char_width = FONT8x8_WIDTH; _font = FONT_8x8; timeout_cnt = TIMEOUT_INIT; } // I2C stuff bool SSD1306::i2cWrite( uint8_t *buf, uint8_t num_bytes) { I2C1->CR2 &= ~(I2C_CR2_RD_WRN); // set to write mode I2C1->CR2 &= ~(0xff << I2C_CR2_NBYTES_Pos); I2C1->CR2 |= I2C_CR2_START | (num_bytes << I2C_CR2_NBYTES_Pos); // transfer X bits and start int timeout = 10000; while(I2C1->CR2 & I2C_CR2_START) {if(--timeout <= 0) { return false; } } timeout = 10000; for (int i = 0; i < num_bytes; i++) { I2C1->TXDR = buf[i]; while (!(I2C1->ISR & I2C_ISR_TXE)){ if(--timeout <= 0) { return false; } } } return true; } void SSD1306::DMAi2cWrite( uint8_t *buf, uint8_t num_bytes) { I2C1->CR1 |= I2C_CR1_TXDMAEN; I2C1->CR2 &= ~(I2C_CR2_RD_WRN); // set to write mode I2C1->CR2 &= ~(0xff << I2C_CR2_NBYTES_Pos); // clear previous number of bytes I2C1->CR2 |= num_bytes << I2C_CR2_NBYTES_Pos; // transfer X bits DMA1_Channel6->CCR &= ~DMA_CCR_EN; DMA1_Channel6->CNDTR = num_bytes; DMA1_Channel6->CCR |= DMA_CCR_EN; I2C1->CR2 |= I2C_CR2_START; // start } bool SSD1306::IsReady() { timeout_cnt--; if (timeout_cnt == 0) {ResetI2C(); timeout_cnt = TIMEOUT_INIT; return false;} if (DMA1_Channel6->CNDTR > 0) {return false;} // needed because I2C does not start immidiately if ( (((I2C1->ISR)&I2C_ISR_BUSY) == 0) && (((I2C1->ISR)&I2C_ISR_TXE) != 0) ) { timeout_cnt = TIMEOUT_INIT; return true; } else { return false; } } void SSD1306::ResetI2C() { I2C1->CR1 &= ~I2C_CR1_PE; I2C1->CR1 &= ~I2C_CR1_PE; I2C1->CR1 &= ~I2C_CR1_PE; I2C1->CR1 &= ~I2C_CR1_PE; I2C1->CR1 &= ~I2C_CR1_PE; I2C1->CR1 &= ~I2C_CR1_PE; I2C1->CR1 &= ~I2C_CR1_PE; DMA1_Channel6->CCR &= ~DMA_CCR_EN; DMA1_Channel6->CNDTR = 0; I2C1->CR1 |= I2C_CR1_PE; I2C1->ISR |= I2C_ISR_TXE; I2C1->ICR |= I2C_ICR_OVRCF | I2C_ICR_ARLOCF | I2C_ICR_BERRCF; I2C1->ICR |= I2C_ICR_STOPCF | I2C_ICR_NACKCF | I2C_ICR_ADDRCF; } bool SSD1306::ssd1306_command(uint8_t c) { // I2C uint8_t control = 0x00; // Co = 0, D/C = 0 uint8_t cmd[2]; cmd[0] = control; cmd[1] = c; return i2cWrite(cmd, 2); } void SSD1306::_sendCommand(uint8_t command, uint8_t param1, uint8_t param2) { // Note continuationbit is set, so COMMAND_MODE must be // repeated before each databyte that serves as parameter! uint8_t databytes[6]; databytes[0] = COMMAND_MODE; databytes[1] = command; databytes[2] = COMMAND_MODE; databytes[3] = param1; databytes[4] = COMMAND_MODE; databytes[5] = param2; i2cWrite(databytes, 6); // Write command } void SSD1306::_sendData(uint8_t data){ uint8_t databytes[2]; databytes[0] = DATA_MODE; databytes[1] = data; i2cWrite(databytes, 2); } void SSD1306::setPageAddress(uint8_t start, uint8_t end) { _sendCommand(SET_PAGE_ADDRESS, start, end); } void SSD1306::setColumnAddress(uint8_t start, uint8_t end) { _sendCommand(SET_COLUMN_ADDRESS, start, end); } /*void SSD1306::writeChar(char chr) { const uint8_t char_index = chr - 0x20; if (font == 8x8) { for (uint8_t i = 0; i < _char_width; i++) { _sendData( font_8x8[char_index][i] ); } } else if (font == 16x12_0) { for (uint8_t i = 0; i < 8; i++) { _sendData( font_16x12_0[char_index][i] ); } } else if (font == 16x12_1) { for (uint8_t i = 0; i < 8; i++) { _sendData( font_16x12_1[char_index][i] ); } }*/ void SSD1306::writeString( uint8_t col, const char * text) { // screenbuffered version of above function // row is 0 thru 7, col is 0 thr 127 uint16_t index = 0; uint16_t len = strlen(text); while ((col < MAX_COL) && (index < len)) { // write line, starting at given position // dont write if its too long writeCharToBuf(col, text[index++]); col+=_char_width; } } void SSD1306::writeInt( uint8_t col, int32_t num) { //GPIOA->ODR |= GPIO_ODR_8; if(num<0) { num = -num; writeCharToBuf(col, '-'); col+=_char_width; } char chr = 0; static char Representation[]= "0123456789ABCDEF"; int base = 10; int mult = 10000; // can print maximum of 5 digits num = num%100000; while (mult > num) { mult /= base; } int num2print = 0; do { num2print = (num - num%mult)/mult; chr = Representation[num2print]; num -= num2print*mult; mult /= base; writeCharToBuf(col, chr); col+=_char_width; }while(mult != 0); } void SSD1306::setFont( uint8_t font ) { _font = font; if (font == FONT_8x8) { _char_width = FONT8x8_WIDTH; } if (font == FONT_16x12_0) { _char_width = FONT16x12_WIDTH; } if (font == FONT_16x12_1) { _char_width = FONT16x12_WIDTH; } } void SSD1306::writeCharToBuf( uint8_t col, char chr ) { //GPIOA->ODR |= GPIO_ODR_8; uint8_t char_index = chr - 0x20; uint16_t k = FRAME_BUF_OFFSET + col; if (col > MAX_COL) {col = MAX_COL;} //will overwrite but whatever /*for (uint8_t j = 0; j < 8; j++) { framebuf[k] = font_8x8[char_index][j]; k++; }*/ if (_font == FONT_8x8) { for (uint8_t i = 0; i < FONT8x8_WIDTH; i++) { if (k >= (COLUMNS+FRAME_BUF_OFFSET)) { break; } framebuf[k] = font_8x8[char_index][i]; k++; } } else if (_font == FONT_16x12_0) { for (uint8_t i = 0; i < FONT16x12_WIDTH; i++) { if (k >= (COLUMNS+FRAME_BUF_OFFSET)) { break; } framebuf[k] = font_16x12_0[char_index][i]; k++; } } else if (_font == FONT_16x12_1) { for (uint8_t i = 0; i < FONT16x12_WIDTH; i++) { if (k >= (COLUMNS+FRAME_BUF_OFFSET)) { break; } framebuf[k] = font_16x12_1[char_index][i]; k++; } } // lets try above but with DMA. It is 11us with no DMA //DMA1_Channel2->CNDTR = 8; // send 8 bytes //DMA1_Channel2->CMAR = (uint32_t)&font_8x8[char_index]; //set memory address- charachters. Will need to be moved on the fly //DMA1_Channel2->CPAR = (uint32_t)&framebuf[k]; //set peripheral register address- frame buffer //DMA1_Channel2->CCR |= DMA_CCR_EN; //while( //while (!(DMA1->ISR & DMA_ISR_TCIF2)); //DMA1->IFCR |= DMA_IFCR_CTCIF2; // //for (uint8_t j = 0; j < 8; j++) { // framebuf[k] = font_8x8[char_index][j]; // k++; //} //GPIOA->ODR &= ~GPIO_ODR_8; } void SSD1306::writeFrameBufRow( uint8_t page ) { framebuf[9] = page; //i2cWrite(framebuf, COLUMNS+FRAME_BUF_OFFSET); // takes about 3.2ms to write whole thing DMAi2cWrite(framebuf, COLUMNS+FRAME_BUF_OFFSET); // takes 18 MICROSECONDS!!! sheeeeeet } void SSD1306::WriteRow( uint8_t page ) { writeFrameBufRow( page ); } void SSD1306::ClearBuf() { for (uint8_t j = FRAME_BUF_OFFSET; j < COLUMNS+FRAME_BUF_OFFSET; j++) { framebuf[j] = 0; } } // Standard version void SSD1306::clearDisplay() { //setDisplayOff(); setPageAddress(0, MAX_PAGE); // all pages setColumnAddress(0, MAX_COL); // all columns for (uint8_t page = 0; page < PAGES; page++) { for (uint8_t col = 0; col < COLUMNS; col++) { _sendData(0x00); } } } void SSD1306::SetupFrameBuf() { for (uint8_t i = 0; i < PAGES; i++) { framebuf[0] = COMMAND_MODE; framebuf[1] = SET_COLUMN_ADDRESS; framebuf[2] = COMMAND_MODE; framebuf[3] = 0; framebuf[4] = COMMAND_MODE; framebuf[5] = MAX_COL; framebuf[6] = COMMAND_MODE; framebuf[7] = SET_PAGE_ADDRESS; framebuf[8] = COMMAND_MODE; framebuf[9] = 0; framebuf[10] = COMMAND_MODE; framebuf[11] = MAX_PAGE; framebuf[12] = DATA_MODE; } }