前言
中文字库已经有了不过是TFTLCD驱动里的, 专门再给OLED写字库有些浪费内存, 所以要复用下. 开发板SPI2总线空着正好用
硬件连接和字段定义, OLED是只写硬件MISO线用不到, 注意电压匹配和正负极尽量被插错
#define OLED_CS PGout(7)//CS 片选
#define OLED_DC PGout(8) //DC 命令数据控制
#define OLED_RST PGout(6) //RES 复位
#define OLED_DATA PBout(15) //D1 数据 SPI2_MOSI
#define OLED_CLK PBout(13) //D0 时钟 SPI2_SCK
GPIO 引脚初始化
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
/* GPIOB打开时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOG, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_7|GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_Init(GPIOG, &GPIO_InitStructure);
GPIO_SetBits(GPIOG,GPIO_Pin_8|GPIO_Pin_7|GPIO_Pin_6); //拉高电平
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_12); //拉高电平 GPIO_Pin_12 是 flash片选
/* SPI2外设打开时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
/* SPI2的IO口设置 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* SPI2的模式设置 */
SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; //设置SPI2单向或者双向的数据模式:SPI2设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI2工作模式:设置为主SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI2的数据大小:SPI2发送接收8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步时钟的空闲状态为高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32; //定义波特率预分频的值:波特率预分频值为256
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_CalculateCRC(SPI2, DISABLE); //关闭crc
SPI_Cmd(SPI2, ENABLE); //使能SPI外设
OLED 硬件初始化, 这段比较繁琐还好由供应商提供
/* 重置硬件 */
OLED_RST=1;
delay_ms(100);
OLED_RST=0;
delay_ms(100);
OLED_RST=1;
select(); // 片选
OLED_WR_Cmd(0xAE);//关闭显示
OLED_WR_Cmd(0x00);//X轴低位,起始X轴为0
OLED_WR_Cmd(0x10);//X轴高位
OLED_WR_Cmd(0x40);//Y轴,可设区间[0x40,0x7F],设置为0了
OLED_WR_Cmd(0xA1);//设置X轴扫描方向,0xa0左右反置 ,0xa1正常(左边为0列)
OLED_WR_Cmd(0xC8);//设置Y轴扫描方向,0xc0上下反置 ,0xc8正常(上边为0行)
OLED_WR_Cmd(0xA6);//位值表示的意义,0xa6表示正常,1为点亮,0为关闭,0xa7显示效果相反
OLED_WR_Cmd(0x81);//命令头,调节亮度,对比度,变化很小,但是仔细可以观察出来
OLED_WR_Cmd(0xFF);//可设置区间[0x00,0xFF]
OLED_WR_Cmd(0xA8);//命令头,设置多路复用率(1 to 64)
OLED_WR_Cmd(0x3f);//--1/64 duty
OLED_WR_Cmd(0xD3);//命令头,设置显示偏移移位映射RAM计数器(0x00~0x3F)
OLED_WR_Cmd(0x00);//不偏移
OLED_WR_Cmd(0xd5);//命令头,设置显示时钟分频比/振荡器频率
OLED_WR_Cmd(0x80);//设置分割比率,设置时钟为100帧/秒
OLED_WR_Cmd(0xD9);//命令头,--set pre-charge period
OLED_WR_Cmd(0xF1);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
OLED_WR_Cmd(0xDA);//命令头,--set com pins hardware configuration
OLED_WR_Cmd(0x12);
OLED_WR_Cmd(0xDB);//命令头,--set vcomh
OLED_WR_Cmd(0x40);//Set VCOM Deselect Level
OLED_WR_Cmd(0x20);//命令头,设置寻址模式
OLED_WR_Cmd(0x10);//页面寻址模式(重置) (0x00/0x01/0x02)
OLED_WR_Cmd(0x8D);//命令头,--set Charge Pump enable/disable
OLED_WR_Cmd(0x14);//--set(0x10) disable
OLED_WR_Cmd(0xA4);//恢复到RAM内容显示(重置)
OLED_WR_Cmd(0xAF);//开启显示
OLED_Clearby_Gram(0x00);
unselect(); //反片选
OLED 核心写入逻辑
/* SPI2 硬件写入 */
void SPI2_WriteOLEDByte(u8 TxData)
{
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET); // 等待可发送
SPI_I2S_SendData(SPI2, TxData); //通过外设SPI2发送一个byte数据
}
/* 写命令 */
void OLED_WR_Cmd(u8 data)
{
OLED_DC=OLED_CMD_F; //命令状态
select();
SPI2_WriteOLEDByte(data); //写入一个字节
unselect();
}
/* 写数据 */
void OLED_WR_Data(u8 data)
{
OLED_DC=OLED_DATA_F; //数据状态
select();
SPI2_WriteOLEDByte(data); //写入一个字节
unselect();
}
画汉字核心逻辑
//OLED的显存
//[0]0 1 2 3 ... 127
//[1]0 1 2 3 ... 127
//[2]0 1 2 3 ... 127
//[3]0 1 2 3 ... 127
//[4]0 1 2 3 ... 127
//[5]0 1 2 3 ... 127
//[6]0 1 2 3 ... 127
//[7]0 1 2 3 ... 127
u8 OLED_GRAM[128][8]; // 静态声明, 也可动态
//在显存里画点
void OLED_DrawGramPoint(u8 x,u8 y,u8 t)
{
u8 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; //清空
}
// 在显存里画文字
void OLCD_GramFont(u16 x,u16 y,u8 *font,u8 size,u8 mode)
{
u8 temp,t,t1;
u16 y0=y;
u8 dzk[128];
u8 csize=(size/8+((size%8)?1:0))*(size); //得到字体一个字符对应点阵集所占的字节数
if(size!=12&&size!=16&&size!=24&&size!=32)return; //不支持的size跳过
Get_HzMatrix(font,dzk,size); //得到相应大小的点阵数据, TFTLCD驱动提供
for(t=0;t<csize;t++)
{
temp=dzk[t]; //得到点阵数据
for(t1=0;t1<8;t1++)
{
if(temp&0x80)OLED_DrawGramPoint(x,y,1);
else if(mode==0)OLED_DrawGramPoint(x,y,0); // 背景色是否画
temp<<=1;
y++;
if(y>=64)return; //Y 超区域了
if((y-y0)==size)
{
y=y0;
x++;
if(x>=128)return; //X 超区域了
break;
}
}
}
}
//更新显存到 OLED
void OLED_Refresh_Gram(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
OLED_WR_Cmd (0xb0+i); //设置页地址(0~7)
OLED_WR_Cmd (0x00); //设置显示位置—列低地址
OLED_WR_Cmd (0x10); //设置显示位置—列高地址
for(n=0;n<128;n++)OLED_WR_Data(OLED_GRAM[n][i]);
}
}
//写字符串
void OLED_Draw_FontString(u8 x, u8 y, u8 *str) {
u16 x0=x;
u8 bHz=0; // 字符或者中文
u8 size = 16;
while(*str!=0) // 数据未结束
{
if(!bHz)
{
if(*str>0x80) bHz=1; // 中文
else // 字符
{
if(*str==13)// 换行
{
// 先跳过
y+=size;
x=x0;
str++;
}
else if(*str==10) {
y+=size;
x=x0;
}
else OLED_GramChar(x,y,*str,size,0);// 使用 ascii 点阵写英文和数字
str++; // 下一个字符或跳过\n
x+=size/2; // 字符, 为全字的一半
}
}
else //中文
{
bHz=0;
OLCD_GramFont(x,y,str,size,0); // 显示汉字背景色也输出
str+=2;
x+=size; // 下一个汉字偏移
}
}
OLED_Refresh_Gram();
}
显示温度和感光度
// 显示温度
void OLED1_Show_TEMP(float temp, u8 *title) {
u8 s_str[20];
sprintf((char *)s_str, "湿:----, 温:%.1f", temp);
OLED_Draw_FontString(0, 0, title);
OLED_Draw_FontString(0, 16, s_str);
}
// 显示感光
void OLED1_Show_LSENS(u8 lsens) {
u8 s_str[20];
sprintf((char *)s_str, "感光值:%hhu", lsens);
OLED_Draw_FontString(0, 32, s_str);
}
sprintf(tempstr, "温度: %.2f \r\n", temp);
LCD_ShowFontString(10,460,200,16, 16,(u8*)tempstr, 0);
sprintf((char *)s_str, "感光值:%hhu", lsens);
OLED_Draw_FontString(0, 32, s_str);