STM32F103+OLED曲線繪制
從本文的標題便能看出,此次分享的內容是以STM32F103C8T6芯片為控制核心,OLED則為0.96寸I2C通信的4針類型。其實OLED的類型不是重點,各種類型的操作基本大同小異,無非是讀寫通信方式的不同而已,接下來為大家詳細介紹OLED屏除顯示漢字、字符、數字外,繪圖功能的實現,大體分為硬件連接與軟件代碼編寫兩大部分。
硬件連接部分
0.96寸OLED屏如下圖所示:

根據上圖我們可以看到,這個型號的OLED屏只有4個引腳,真正與MCU通信的信號線才2根,比較適合於通用I/O口資源較少的控制芯片。在本文中OLED與STM32F103C8T6芯片的硬件I2C相連接: PB6 – SCL;PB7 – SDA 。
軟件程序部分
1. OLED I2C通信代碼
void I2C_Configuration(void)
{
I2C_InitTypeDef I2C_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*STM32F103C8T6芯片的硬件I2C: PB6 -- SCL; PB7 -- SDA */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;//I2C必須開漏輸出
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_DeInit(I2C1);//使用I2C1
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0x30;//主機的I2C地址,隨便寫的
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 400000;//400K
I2C_Cmd(I2C1, ENABLE);
I2C_Init(I2C1, &I2C_InitStructure);
}
/**
* @brief I2C_WriteByte,向OLED寄存器地址寫一個byte的數據
* @param addr:寄存器地址
* data:要寫入的數據
* @retval 無
*/
void I2C_WriteByte(uint8_t addr,uint8_t data)
{
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
I2C_GenerateSTART(I2C1, ENABLE);//開啟I2C1
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));/*EV5,主模式*/
I2C_Send7bitAddress(I2C1, OLED_ADDRESS, I2C_Direction_Transmitter);//器件地址 -- 默認0x78
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2C1, addr);//寄存器地址
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2C1, data);//發送數據
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTOP(I2C1, ENABLE);//關閉I2C1總線
}
/**
* @brief WriteCmd,向OLED寫入命令
* @param I2C_Command:命令代碼
* @retval 無
*/
void WriteCmd(unsigned char I2C_Command)//寫命令
{
I2C_WriteByte(0x00, I2C_Command);
}
/**
* @brief WriteDat,向OLED寫入數據
* @param I2C_Data:數據
* @retval 無
*/
void WriteDat(unsigned char I2C_Data)//寫數據
{
I2C_WriteByte(0x40, I2C_Data);
}
/**
* @brief OLED_Init,初始化OLED
* @param 無
* @retval 無
*/
void OLED_Init(void)
{
DelayMs(100); //這里的延時很重要
WriteCmd(0xae);//--turn off oled panel"關閉led面板"
WriteCmd(0x00);//---set low column address設置低列地址
WriteCmd(0x10);//---set high column address設置高列地址
WriteCmd(0x40);//--set start line address設置起始地址線 Set Mapping RAM Display Start Line (0x00~0x3F)
WriteCmd(0x81);//--set contrast control register設置對比度控制寄存器
WriteCmd(0xcf); // Set SEG Output Current Brightness設置亮度控制寄存器
WriteCmd(0xa1);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常
WriteCmd(0xc8);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常
WriteCmd(0xa6);//--set normal display
WriteCmd(0xa8);//--set multiplex ratio(1 to 64)設置多路復用
WriteCmd(0x3f);//--1/64 duty
WriteCmd(0xd3);//-set display offset Shift Mapping RAM Counter設置顯示的偏移映射內存計數器 (0x00~0x3F)
WriteCmd(0x00);//-not offset取消偏移補償
WriteCmd(0xd5);//--set display clock divide ratio/oscillator frequency設置顯示時鍾分頻比/振盪器頻率
WriteCmd(0x80);//--set divide ratio, Set Clock as 100 Frames/Sec設置分離比例,時鍾設置為100幀/秒
WriteCmd(0xd9);//--set pre-charge period預充電時間
WriteCmd(0xf1);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock預充電15鍾及放電1時鍾
WriteCmd(0xda);//--set com pins hardware configurationCOM引腳設置硬件配置
WriteCmd(0x12);
WriteCmd(0xdb);//--set vcomh設置VCOM電平
WriteCmd(0x40);//Set VCOM Deselect Level取消設置VCOM電平
WriteCmd(0x20);//-Set Page Addressing Mode (0x00/0x01/0x02)設置頁面尋址模式(0x00 /頭/ 0x02)
WriteCmd(0x02);//
WriteCmd(0x8d);//--set Charge Pump enable/disable設置電荷泵啟用/禁用
WriteCmd(0x14);//--set(0x10) 設為0x10失能
WriteCmd(0xa4);// Disable Entire Display On (0xa4/0xa5)禁用整個顯示
WriteCmd(0xa6);// Disable Inverse Display On (0xa6/a7) 禁用反顯示
WriteCmd(0xaf);//--turn on oled panel打開led面板
}
2.OLED的相關操作函數
2.1.基礎函數
/**
* @brief OLED_SetPos,設置光標
* @param x,光標x位置
* y,光標y位置
* @retval 無
*/
void OLED_SetPos(unsigned char x, unsigned char y) //設置起始點坐標
{
WriteCmd(0xb0+y);
WriteCmd(((x&0xf0)>>4)|0x10);
WriteCmd((x&0x0f)|0x01);
}
/**
* @brief OLED_Fill,填充整個屏幕
* @param fill_Data:要填充的數據
* @retval 無
*/
void OLED_Fill(unsigned char fill_Data)//全屏填充
{
unsigned char m,n;
for(m=0;m<8;m++)
{
WriteCmd(0xb0+m); //page0-page1
WriteCmd(0x00); //low column start address
WriteCmd(0x10); //high column start address
for(n=0;n<128;n++)
{
WriteDat(fill_Data);
}
}
}
/**
* @brief OLED_CLS,清屏
* @param 無
* @retval 無
*/
void OLED_CLS(void)//清屏
{
OLED_Fill(0x00);
}
/**
* @brief OLED_ON,將OLED從休眠中喚醒
* @param 無
* @retval 無
*/
void OLED_ON(void)
{
WriteCmd(0X8D); //設置電荷泵
WriteCmd(0X14); //開啟電荷泵
WriteCmd(0XAF); //OLED喚醒
}
/**
* @brief OLED_OFF,讓OLED休眠 -- 休眠模式下,OLED功耗不到10uA
* @param 無
* @retval 無
*/
void OLED_OFF(void)
{
WriteCmd(0X8D); //設置電荷泵
WriteCmd(0X10); //關閉電荷泵
WriteCmd(0XAE); //OLED休眠
}
/**
* @brief OLED_ShowStr,顯示codetab.h中的ASCII字符,有6*8和8*16可選擇
* @param x,y : 起始點坐標(x:0~127, y:0~7);
* ch[] :- 要顯示的字符串;
* TextSize : 字符大小(1:6*8 ; 2:8*16)
* @retval 無
*/
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
{
unsigned char c = 0,i = 0,j = 0;
switch(TextSize)
{
case 1:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 126)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<6;i++)
WriteDat(F6x8[c][i]);
x += 6;
j++;
}
}break;
case 2:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 120)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<8;i++)
WriteDat(F8X16[c*16+i]);
OLED_SetPos(x,y+1);
for(i=0;i<8;i++)
WriteDat(F8X16[c*16+i+8]);
x += 8;
j++;
}
}break;
}
}
/**
* @brief OLED_ShowCN,顯示codetab.h中的漢字,16*16點陣
* @param x,y: 起始點坐標(x:0~127, y:0~7);
* N:漢字在codetab.h中的索引
* @retval 無
*/
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
{
unsigned char wm=0;
unsigned int adder=32*N;
OLED_SetPos(x , y);
for(wm = 0;wm < 16;wm++)
{
WriteDat(F16x16[adder]);
adder += 1;
}
OLED_SetPos(x,y + 1);
for(wm = 0;wm < 16;wm++)
{
WriteDat(F16x16[adder]);
adder += 1;
}
}
/**
* @brief OLED_DrawBMP,顯示BMP位圖
* @param x0,y0 :起始點坐標(x0:0~127, y0:0~7);
* x1,y1 : 起點對角線(結束點)的坐標(x1:1~128,y1:1~8)
* @retval 無
*/
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{
unsigned int j=0;
unsigned char x,y;
if(y1%8==0)
y = y1/8;
else
y = y1/8 + 1;
for(y=y0;y<y1;y++)
{
OLED_SetPos(x0,y);
for(x=x0;x<x1;x++)
{
WriteDat(BMP[j++]);
}
}
}
2.2.描點函數
入口參數:
x:點的x坐標;
y:點的y坐標;
t:點的狀態;“0”代表暗,“1”代表亮
void OLED_DrawDot(unsigned char x,unsigned char y,unsigned char t)
{
unsigned char pos,bx,temp=0;
if(x>127||y>63) return;
pos=(y)/8;
bx=y%8;
temp=1<<(bx);
if(t) OLED_GRAM[x][pos]|=temp;
else OLED_GRAM[x][pos]&=~temp;
OLED_Refresh_Gram();
}
2.3.連線函數
入口參數:
x1:起點的x坐標;
y1:起點的y坐標;
x2:終點的x坐標;
y2:終點的y坐標;
void LCD_DrawLine(unsigned int x1, unsigned int y1, unsigned int x2,unsigned int y2)
{
unsigned int t;
int xerr=0,yerr=0,delta_x,delta_y,distance;
int incx,incy,uRow,uCol;
delta_x=x2-x1; //計算坐標增量
delta_y=y2-y1;
uRow=x1;
uCol=y1;
if(delta_x>0)incx=1; //設置單步方向
else if(delta_x==0)incx=0;//垂直線
else {incx=-1;delta_x=-delta_x;}
if(delta_y>0)incy=1;
else if(delta_y==0)incy=0;//水平線
else{incy=-1;delta_y=-delta_y;}
if( delta_x>delta_y)distance=delta_x; //選取基本增量坐標軸
else distance=delta_y;
for(t=0;t<=distance+1;t++ )//畫線輸出
{
OLED_DrawDot(uRow,uCol,1);//畫點
xerr+=delta_x ;
yerr+=delta_y ;
if(xerr>distance)
{
xerr-=distance;
uRow+=incx;
}
if(yerr>distance)
{
yerr-=distance;
uCol+=incy;
}
}
}
最后實際運行效果圖如下:

詳細程序如下
1 OLED I2C通信代碼




2.OLED的相關操作函數
2.1.基礎函數






2.2.描點函數
入口參數:
x:點的x坐標;
y:點的y坐標;
t:點的狀態;“0”代表暗,“1”代表亮

2.3.連線函數
入口參數:
x1:起點的x坐標;
y1:起點的y坐標;
x2:終點的x坐標;
y2:終點的y坐標;

