nowadays, OLED displays are more and more used in embedded systems. For some requirements that display information is not too complex and mainly display information, we generally choose OLED display. In this article, we will discuss the design and implementation of OLED display driver.
1. Function overview
in terms of use, the 0.96 inch OLED128x64 display is commonly used. This OLED screen mostly uses driver chips such as SSD1306, so our operation of OLED screen is actually the operation of control chip.
for the 0.96 inch OLED128x64 display screen, its pixel points are 128x64, corresponding to 128x64 bits in the display RAM. In the video memory, these areas are divided into 8 pages. The division of these pages is shown in the following figure:
each page includes 128x8 bits corresponding to corresponding pixel points. The operation of display pixels is the operation of corresponding bits in the countryside. The arrangement of pixel points in each page is as follows:
there are many interfaces for operating 0.96 inch OLED128x64 display screen, such as 6800 parallel interface, 8080 parallel interface, SPI serial interface and I2C serial interface. There are few applications for parallel interfaces. SPI and I2C serial bus interfaces are widely used now. In SPI interface mode, three control pins need to be operated, namely reset, data command selection and chip selection signal. In I2C interface mode, only the reset pin is controllable, but there will be one more byte of control word when sending command or data.
2. Driver design and Implementation
we have learned the basic situation of 0.96 inch OLED128x64 display. Here, let's consider how to realize the driver design of 0.96 inch OLED128x64 display.
2.1 object definition
before using an object, we need to get an object. Similarly, if we want an OLED display, we need to define the object of the OLED display first.
2.1.1 object abstraction
to get OLED display object, we need to analyze its basic characteristics first. Generally speaking, an object contains at least two characteristics: properties and operations. Next, let's think about the object of OLED display from these two aspects.
let's consider attributes first. As attributes, they must be used to identify or record the characteristics of objects. Let's consider the 0.96 inch OLED128x64 display object properties. We consider SPI and I2C interfaces, so we need to distinguish the currently used interface form to determine the appropriate operation mode, so we set the port type as its attribute to save the current operation interface type. During I2C interface, each I2C slave device needs to have a device address. We need to record the address of the current slave device, so set it as the attribute.
then we need to consider the operation of OLED display objects. In SPI interface mode, we need to control reset, data command selection and chip selection control pins, while in I2C interface mode, we need to control reset pins. The operation of these control pins depends on the specific hardware platform, so we take them as the object operation. We want to send commands and data to OLED, but no matter what interface type, this operation depends on the specific software and hardware platform, so we regard it as an object operation. In order to control the operation timing, we need the delay operation function, and the delay operation also depends on the specific software and hardware platform, so we take it as the object operation.
according to the above analysis of OLED display, we can define the object types of OLED display as follows:
/*Define OLED object types*/ typedef struct OledObject { uint8_t devAddress; OledPortType port; void (*Write)(struct OledObject *oled,uint8_t *wData,uint16_t wSize); void (*ChipSelcet)(OledCSType en); void (*DCSelcet)(OledDCType dc); void (*ChipReset)(OledRSTType rst); void (*Delayms)(volatile uint32_t nTime); }OledObjectType;
2.1.2 object initialization
we know that an object cannot be used only for declaration. We need to initialize it first, so here we consider the initialization function of OLED display object. Generally speaking, initialization functions need to deal with several aspects. First, check whether the input parameters are reasonable; Second, assign initial values to the attributes of the object; Third, make necessary initialization configuration for objects.
moreover, the 0.96 inch OLED128x64 display will realize its initialization configuration after the operation of the reset pin. Therefore, we design the initialization function of OLED display object as follows:
/*OLED Display object initialization*/ void OledInitialization(OledObjectType *oled, //OLED object OledPortType port, //Communication port uint8_t address, //I2C device address OledWrite write, //Write data function OledChipReset rst, //Reset signal operation function pointer OledDCSelcet dc, //DC signal control function pointer OledChipSelcet cs, //SPI chip selection signal function pointer OledDelayms delayms //Millisecond delay function pointer ) { if((oled==NULL)||(write==NULL)||(rst==NULL) ||(delayms==NULL)) { return; } oled->Write=write; oled->ChipReset=rst; oled->Delayms=delayms; oled->port=port; if(port==OLED_I2C) { if((address==0x3C)||(address==0x3D)) { oled->devAddress=(address<<1); } else if((address==0x78)||(address==0x7A)) { oled->devAddress=address; } else { oled->devAddress=0x00; } if(dc==NULL) { return; } oled->DCSelcet=dc; oled->ChipSelcet=cs; } else { oled->devAddress=0xFF; if(cs==NULL) { oled->ChipSelcet=OledChipSelect; } else { oled->ChipSelcet=cs; } oled->DCSelcet=dc; } oled->ChipReset(OLED_WORK); oled->Delayms(100); oled->ChipReset(OLED_RESET); oled->Delayms(100); oled->ChipReset(OLED_WORK); SendToOled(oled,0xAE,OLEDDC_Command); //Turn off display SendToOled(oled,0x20,OLEDDC_Command); //Set Memory Addressing Mode SendToOled(oled,0x10,OLEDDC_Command); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid SendToOled(oled,0xB0,OLEDDC_Command); //Set Page Start Address for Page Addressing Mode,0-7 SendToOled(oled,0xA1,OLEDDC_Command); //0xa0, X-axis displays normally; 0xa1, X-axis mirror display SendToOled(oled,0xC8,OLEDDC_Command); //0xc0, Y-axis displays normally; 0xc8, Y-axis mirror display SendToOled(oled,0x00,OLEDDC_Command); //Set the lower 4 bits of the column address SendToOled(oled,0x10,OLEDDC_Command); //Set the upper 4 bits of the column address SendToOled(oled,0x40,OLEDDC_Command); //Set start line address SendToOled(oled,0x81,OLEDDC_Command); //Set contrast value SendToOled(oled,0x7F,OLEDDC_Command); //------ SendToOled(oled,0xA6,OLEDDC_Command); //0xa6, normal display mode; 0xa7, SendToOled(oled,0xA8,OLEDDC_Command); //--set multiplex ratio(1 to 64) SendToOled(oled,0x3F,OLEDDC_Command); //------ SendToOled(oled,0xA4,OLEDDC_Command); //0xa4, the display changes with the change of ram; 0xa5, display contents ignore RAM contents SendToOled(oled,0xD3,OLEDDC_Command); //Set display offset SendToOled(oled,0x00,OLEDDC_Command); //------ SendToOled(oled,0xD5,OLEDDC_Command); //Set internal display clock frequency SendToOled(oled,0xF0,OLEDDC_Command); //------ SendToOled(oled,0xD9,OLEDDC_Command); //--set pre-charge period SendToOled(oled,0x22,OLEDDC_Command); //------ SendToOled(oled,0xDA,OLEDDC_Command); //--set com pins hardware configuration SendToOled(oled,0x12,OLEDDC_Command); //------ SendToOled(oled,0xDB,OLEDDC_Command); //--set vcomh SendToOled(oled,0x20,OLEDDC_Command); //------ SendToOled(oled,0x8D,OLEDDC_Command); //--set DC-DC enable SendToOled(oled,0x14,OLEDDC_Command); //------ SendToOled(oled,0xAF,OLEDDC_Command); //Open display OledClearScreen(oled); }
2.2 object operation
we have completed the definition of OLED display object type and the design of object initialization function. However, our main goal is to obtain the information of the object. Next, we have to realize various operations for OLED display.
for the 0.96 inch OLED128x64 display, no matter what interface mode is adopted or what content needs to be displayed. For us, although there are some differences in the operation under different interface modes, they are essentially writing data to OLEDs.
in SPI interface mode, when sending data and commands to OLED, we need to operate the chip selection signal and data command selection signal at the same time to indicate the object to be operated and whether the data or command is sent. The specific operation sequence is as follows:
in the I2C interface mode, when we send data and commands to OLED, there are no chip selection and data command selection signals, so we need to send the slave address to distinguish the object to be operated, and we need to send the control byte to distinguish whether it is data or command. The specific operation sequence is as follows:
according to the above description of 0.96 inch OLED128x64 display screen and the above timing diagram, we can write the function of sending data to OLED as follows:
/*Send data to OLED*/ static void SendToOled(OledObjectType *oled,uint8_t sData,OledDCType type) { uint8_t wData[2]; if(oled->port==OLED_SPI) { oled->ChipSelcet(OLEDCS_Enable); if(type==OLEDDC_Command) { oled->DCSelcet(OLEDDC_Command); } else { oled->DCSelcet(OLEDDC_Data); } oled->Write(oled,&sData,1); oled->ChipSelcet(OLEDCS_Disable); } else { if(type==OLEDDC_Command) { wData[0]=0x00; } else { wData[0]=0x40; } wData[1]=sData; oled->Write(oled,wData,2); } }
3. Use of drivers
we have implemented the design and implementation of 0.96 inch OLED128x64 display driver. Now we need to verify this driver. Based on this, we need to design a simple verification application.
3.1. Declare and initialize objects
to use object-based operation, we need to get this object first, so we must first declare an OLED display object variable using the OLED display object type defined above. The specific operation format is as follows:
OledObjectType oled;
the declared object variable cannot be used immediately. We also need to initialize the variable with the initialization function defined in the driver. The input parameters required for this initialization function are as follows:
OledObjectType *oled, //OLED object
OledPortType port, / / communication port
uint8_t address, //I2C device address
OledWrite write, / / write data function
OledChipReset rst, / / reset the signal operation function pointer
OledDCSelcet dc, //DC signal control function pointer
OledChipSelcet cs, //SPI chip selection signal function pointer
OledDelayms delayms / / pointer to millisecond delay function
for these parameters, the object variables have been defined. The communication interface used is enumeration, which can be selected according to the actual situation. The slave address has several options for OLED, which can be entered according to the actual situation. The main thing is that we need to define several functions and take function pointers as parameters. The types of these functions are as follows:
/*Issue instructions to OLED in the format of 1 byte*/ typedef void (*OledWrite)(OledObjectType *oled,uint8_t *wData,uint16_t wSize); /*Reset signal operation function pointer*/ typedef void (*OledChipReset)(OledRSTType rst); /*Data command for SPI interface*/ typedef void (*OledDCSelcet)(OledDCType dc); /*Chip selection signal for SPI interface*/ typedef void (*OledChipSelcet)(OledCSType en); /*Millisecond delay function*/ typedef void (*OledDelayms)(volatile uint32_t nTime);
for these functions, we can define them according to the style. The specific operation may be related to the hardware platform used. The chip selection operation function is used when software operation is required for multiple devices. If hardware chip selection is adopted, NULL can be passed in. The specific functions are defined as follows:
void WriteDataToLED(struct OledObject *oled,uint8_t *wData,uint16_t wSize) { HAL_I2C_Master_Transmit(&oledhi2c,oled->devAddress,wData,wSize,1000); } void OLedChipResetf(OledRSTType rst) { HAL_GPIO_WritePin(GPIOD,GPIO_PIN_8,(GPIO_PinState)rst); }
we can use various methods to implement the delay function. The STM32 platform and HAL library we use can directly use HAL_Delay() function. So we can call the initialization function as follows:
/*OLED Display object initialization*/ OledInitialization(&oled, //OLED object OLED_I2C, //Communication port 0x78, //I2C device address WriteDataToLED, //Write data function OLedChipResetf, //Reset signal operation function pointer NULL, //DC signal control function pointer NULL, //SPI chip selection signal function pointer HAL_Delay //Millisecond delay function pointer );
in I2C interface mode, the chip selection signal and data command selection signal do not need to be controlled, so it can be input with NULL.
3.2. Operation based on object
we defined the object variable and initialized it with the initialization function. Then we will consider operating this object to obtain the data we want. We have set different operation functions for different font sizes in the driver. Next, we use this driver to develop our application example.
/*OLED display information*/ void OledDisplayMessage(void) { /* World (0) (1) you (2) good (3)*/ uint8_t chinChar[4][32]={ {0x20,0x20,0x20,0xFE,0x20,0x20,0xFF,0x20,0x20,0x20,0xFF,0x20,0x20,0x20,0x20,0x00, 0x00,0x00,0x00,0x7F,0x40,0x40,0x47,0x44,0x44,0x44,0x47,0x40,0x40,0x40,0x00,0x00},//"World", 0 {0x00,0x00,0x00,0xFE,0x92,0x92,0x92,0xFE,0x92,0x92,0x92,0xFE,0x00,0x00,0x00,0x00, 0x08,0x08,0x04,0x84,0x62,0x1E,0x01,0x00,0x01,0xFE,0x02,0x04,0x04,0x08,0x08,0x00},//"Boundary", 1 {0x00,0x80,0x60,0xF8,0x07,0x40,0x20,0x18,0x0F,0x08,0xC8,0x08,0x08,0x28,0x18,0x00, 0x01,0x00,0x00,0xFF,0x00,0x10,0x0C,0x03,0x40,0x80,0x7F,0x00,0x01,0x06,0x18,0x00},//"You", 2 {0x10,0x10,0xF0,0x1F,0x10,0xF0,0x00,0x80,0x82,0x82,0xE2,0x92,0x8A,0x86,0x80,0x00, 0x40,0x22,0x15,0x08,0x16,0x61,0x00,0x00,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00}//"OK", 3 }; char pStr[]="Hello, World!"; float x=1.1; float y=2.2; float z=3.3; //Display 16x16 Chinese characters OledShow16x16Char(&oled,0,32,chinChar[0]); OledShow16x16Char(&oled,0,48,chinChar[1]); OledShow16x16Char(&oled,0,64,chinChar[2]); OledShow16x16Char(&oled,0,80,chinChar[3]); //Displays ASCII characters of 8x16 OledShowString(&oled,OLED_FONT_8x16,2,32,pStr); //Displays ASCII characters of 8x16 OledShowString(&oled,OLED_FONT_8x16,4,20,"X%0.1f,Y%0.1f,Z%0.1f",x,y,z); }
4. Application Summary
in this article, we design and implement the driver of 0.96 inch OLED128x64 display screen, and design a simple verification application to verify the driver. In our verification application, OLED is used to display Chinese characters with 16 lower 6-dot matrix and ASCII characters with 8x16 dot matrix. The display effect is consistent with our expectation.
when using the driver, it should be noted that the 0.96 inch OLED128x64 display supports SPI and I2C interfaces, and SPI also supports 3-wire and 4-wire modes. However, we only use the I2C interface in the test application. When using the I2C interface, we do not need to control the chip selection signal and data command selection signal, so we can pass NULL value during initialization.
when using the driver, it should be noted that the chip selection operation should be considered for devices with SPI interface. If the chip selection signal is realized through hardware circuit, we pass NULL value to it during initialization. If it is a software operation chip selection, the chip selection operation function written by us will be passed. When using SPI interface, SPI mode 0 (CPOL=CPHA=0) and mode 3 (CPOL=CPHA=1) are supported.