本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明.
说明:
一个可穿戴项目中使用到0.96寸OLED屏,这是一种小尺寸的单色屏,不需要复杂的界面。移植了emwin发现小屏幕单色屏很不合适,控件显示不尽如人意,占用的flash又太大。所以我自己编写一个轻量级的GUI。
GUI简介:
目前已有控件:
1.文本框
2.单色bmp图片显示控件
3.进度条
并支持:
1.中文字库
2.基本的2D绘图功能
下载链接:
http://download.csdn.net/detail/jdh99/9376437
源代码:
驱动函数为GUI提供绘点函数以供调用
drv_lcd.h
/**
* Copyright (c), 2015-2025
* @file drv_lcd.h
* @brief lcd驱动层头文件
* @author jdh
* @date 2015/7/28
* @verbatim
* 硬件连接说明
* 电路标号 单片机引脚 LCD引脚
* SDI PB15 D0
* SCL PB13 D1
* CS PB12 CS
* NRST NRST RES
* A0 PB1 DC
* SDI PB13 D0
* SCL PB15 D1
* CS PB12 CS
* NRST PB10 RES
* A0 PB11 DC
* @endverbatim
*/
#ifndef _DRV_LCD_H_
#define _DRV_LCD_H_
/*********************************************************************
* 头文件
**********************************************************************/
#include "stm32f10x.h"
/*********************************************************************
* 函数
**********************************************************************/
/**
* @brief 初始lcd
*/
void drv_lcd_init(void);
/**
* @brief 画点
* @param x:x坐标
* @param y:y坐标
* @param show:0:不显示,1:显示
*/
void drv_lcd_draw_pixel(uint8_t x,uint8_t y,uint8_t show);
/**
* @brief 打开显示
* @param 0:关闭,1:打开
*/
void drv_lcd_display_set(uint8_t state);
#en
drv_lcd.c
/**
* Copyright (c), 2015-2025
* @file drv_lcd.c
* @brief lcd驱动层主文件
* @author jdh
* @date 2015/7/28
*/
/*********************************************************************
* 头文件
**********************************************************************/
#include "drv_lcd.h"
/*********************************************************************
* 宏定义
**********************************************************************/
/**
* @brief 写入模式
*/
//写命令
#define OLED_CMD 0
//写数据
#define OLED_DATA 1
/*********************************************************************
* 静态变量
**********************************************************************/
/**
* @brief OLED的显存
* @verbatim
* 存放格式如下:
* [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
* @endverbatim
*/
static uint8_t OLED_Gram[128][8];
/*********************************************************************
* 静态函数
**********************************************************************/
/**
* @brief 初始io
*/
static void init_io(void);
/**
* @brief 初始化spi
*/
static void init_spi(void);
/**
* @brief 初始化SSD1306
*/
static void init_oled(void);
/**
* @brief 向SSD1306写入一个字节
* @param dat:要写入的数据/命令
* @param cmd:数据/命令标志 0,表示命令;1,表示数据
*/
static void oled_write_byte(uint8_t dat,uint8_t cmd);
/**
* @brief 切换数据或者命令模式
* @param state:0:命令,1:数据
*/
static void oled_dc_set(uint8_t state);
/**
* @brief 片选
* @param mode:0:片选,1:未选
*/
static void oled_cs_set(uint8_t state);
/**
* @brief 更新显存到LCD
*/
static void oled_refresh_gram(void);
/**
* @brief 清屏
*/
static void oled_clear(void);
/**
* @brief 设置坐标点
* @param x:x坐标
* @param y:y坐标
*/
static void oled_set_pos(uint8_t x,uint8_t y);
/**
* @brief spi发送一个字节
* @param byte:发送的字节
* @retval 读取的字节
*/
static uint8_t spi_send_byte(uint8_t byte);
/*********************************************************************
* 函数
**********************************************************************/
/**
* @brief 初始lcd
*/
void drv_lcd_init(void)
{
init_io();
init_spi();
init_oled();
}
/**
* @brief 画点
* @param x:x坐标
* @param y:y坐标
* @param show:0:不显示,1:显示
*/
void drv_lcd_draw_pixel(uint8_t x,uint8_t y,uint8_t show)
{
uint8_t pos = 0;
uint8_t bx = 0;
uint8_t temp = 0;
//设置坐标点
oled_set_pos(x,y);
//p岸段是否超出范围
if (x > 127 || y > 64)
{
return;
}
pos = 7 - y / 8;
bx = y % 8;
temp = 1 << (7 - bx);
if (show)
{
OLED_Gram[x][pos] |= temp;
}
else
{
OLED_Gram[x][pos] &= ~temp;
}
//设置显示位置—列高地址
oled_write_byte(OLED_Gram[x][pos],OLED_DATA);
}
/**
* @brief 打开显示
* @param 0:关闭,1:打开
*/
void drv_lcd_display_set(uint8_t state)
{
if (state)
{
oled_write_byte(0xAF,OLED_CMD);
}
else
{
oled_write_byte(0xAE,OLED_CMD);
}
}
/**
* @brief 初始io
*/
static void init_io(void)
{
//定义IO初始化结构体
GPIO_InitTypeDef GPIO_InitStructure;
//初始化时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//管脚初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
//选择GPIO响应速度
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//设置为输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
//初始化
GPIO_Init(GPIOB, &GPIO_InitStructure);
//复位脚
//初始化时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//管脚初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
//选择GPIO响应速度
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//设置为输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
//初始化
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_10);
}
/*********************************************************************
* 初始化spi
**********************************************************************/
static void init_spi(void)
{
//定义IO初始化结构体
GPIO_InitTypeDef GPIO_InitStructure ;
//定义SPI初始化结构体
SPI_InitTypeDef SPI_InitStructure ;
//配置其他引脚
//配置CSN
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE ) ;
//配置SPI引脚CSN
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;
GPIO_Init( GPIOB, &GPIO_InitStructure );
//初始化SPI
//关闭SPI
SPI_Cmd(SPI2,DISABLE);
//初始化SPI时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
//设置IO口时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
//配置SPI1引脚:SCK, MISO and MOSI
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;
GPIO_Init( GPIOB, &GPIO_InitStructure );
// SPI配置
//SPI_I2S_DeInit(SPI2);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex ;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master ;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b ;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High ;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge ;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft ;
//SPI波特率分频设置
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2 ;
//SPI设置成LSB模式
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB ;
SPI_InitStructure.SPI_CRCPolynomial = 7 ;
SPI_Init( SPI2, &SPI_InitStructure ) ;
//启动SPI
SPI_Cmd(SPI2,ENABLE);
}
/**
* @brief 初始化SSD1306
*/
static void init_oled(void)
{
oled_write_byte(0xAE,OLED_CMD); //关闭显示
oled_write_byte(0xD5,OLED_CMD); //设置时钟分频因子,震荡频率
oled_write_byte(80,OLED_CMD); //[3:0],分频因子;[7:4],震荡频率
oled_write_byte(0xA8,OLED_CMD); //设置驱动路数
oled_write_byte(0X3F,OLED_CMD); //默认0X3F(1/64)
oled_write_byte(0xD3,OLED_CMD); //设置显示偏移
oled_write_byte(0X00,OLED_CMD); //默认为0
oled_write_byte(0x40,OLED_CMD); //设置显示开始行 [5:0],行数.
oled_write_byte(0x8D,OLED_CMD); //电荷泵设置
oled_write_byte(0x14,OLED_CMD); //bit2,开启/关闭
oled_write_byte(0x20,OLED_CMD); //设置内存地址模式
oled_write_byte(0x02,OLED_CMD); //[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10;
oled_write_byte(0xA1,OLED_CMD); //段重定义设置,bit0:0,0->0;1,0->127;
oled_write_byte(0xC0,OLED_CMD); //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数
oled_write_byte(0xDA,OLED_CMD); //设置COM硬件引脚配置
oled_write_byte(0x12,OLED_CMD); //[5:4]配置
oled_write_byte(0x81,OLED_CMD); //对比度设置
oled_write_byte(0xEF,OLED_CMD); //1~255;默认0X7F (亮度设置,越大越亮)
oled_write_byte(0xD9,OLED_CMD); //设置预充电周期
oled_write_byte(0xf1,OLED_CMD); //[3:0],PHASE 1;[7:4],PHASE 2;
oled_write_byte(0xDB,OLED_CMD); //设置VCOMH 电压倍率
oled_write_byte(0x30,OLED_CMD); //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;
oled_write_byte(0xA4,OLED_CMD); //全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)
oled_write_byte(0xA6,OLED_CMD); //设置显示方式;bit0:1,反相显示;0,正常显示
oled_write_byte(0xAF,OLED_CMD); //开启显示
oled_clear();
}
/**
* @brief 向SSD1306写入一个字节
* @param dat:要写入的数据/命令
* @param cmd:数据/命令标志 0,表示命令;1,表示数据
*/
static void oled_write_byte(uint8_t dat,uint8_t cmd)
{
//设置传输模式
oled_dc_set(cmd);
//片选
oled_cs_set(0);
//发送数据
spi_send_byte(dat);
//关闭片选
oled_cs_set(1);
//设置传输模式
oled_dc_set(cmd);
//设置传输模式:数据
oled_dc_set(OLED_DATA);
}
/**
* @brief 切换数据或者命令模式
* @param state:0:命令,1:数据
*/
static void oled_dc_set(uint8_t state)
{
if (state)
{
GPIO_SetBits(GPIOB,GPIO_Pin_11);
}
else
{
GPIO_ResetBits(GPIOB,GPIO_Pin_11);
}
}
/**
* @brief 片选
* @param mode:0:片选,1:未选
*/
static void oled_cs_set(uint8_t state)
{
if (state)
{
GPIO_SetBits(GPIOB,GPIO_Pin_12);
}
else
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}
}
/**
* @brief 更新显存到LCD
*/
static void oled_refresh_gram(void)
{
uint8_t i = 0;
uint8_t n = 0;
for (i = 0;i < 8;i++)
{
//设置页地址(0~7)
oled_write_byte(0xb0 + i,OLED_CMD);
//设置显示位置—列低地址
oled_write_byte(0x02,OLED_CMD);
//设置显示位置—列高地址
oled_write_byte(0x10,OLED_CMD);
//更新数据
for (n = 0;n < 128;n++)
{
oled_write_byte(OLED_Gram[n][i],OLED_DATA);
}
}
}
/**
* @brief 清屏
*/
static void oled_clear(void)
{
uint8_t i = 0;
uint8_t n = 0;
for (i = 0;i < 8;i++)
{
for(n=0;n<128;n++)
{
OLED_Gram[n][i] = 0;
}
}
//更新显示
oled_refresh_gram();
}
/**
* @brief 设置坐标点
* @param x:x坐标
* @param y:y坐标
*/
static void oled_set_pos(uint8_t x,uint8_t y)
{
oled_write_byte(0xb0 + (7 - y / 8),OLED_CMD);
oled_write_byte(((x & 0xf0) >> 4) | 0x10,OLED_CMD);
oled_write_byte((x & 0x0f),OLED_CMD);
}
/**
* @brief spi发送一个字节
* @param byte:发送的字节
* @retval 读取的字节
*/
static uint8_t spi_send_byte(uint8_t byte)
{
while (SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI2,byte);
while (SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET);
return SPI_I2S_ReceiveData(SPI2);
}
inf_lcd.h
/**
* Copyright (c), 2015-2025
* @file inf_lcd.c
* @brief lcd接口层头文件
* @author jdh
* @date 2015/7/28
*/
#ifndef _INF_LCD_H_
#define _INF_LCD_H_
/*********************************************************************
* 头文件
**********************************************************************/
#include "drv_lcd.h"
/*********************************************************************
* 函数
**********************************************************************/
/**
* @brief 初始lcd
*/
void inf_lcd_init(void);
/**
* @brief 画点
* @param x:x坐标
* @param y:y坐标
* @param show:0:不显示,1:显示
*/
void inf_lcd_draw_pixel(uint8_t x,uint8_t y,uint8_t show);
/**
* @brief 打开显示
* @param 0:关闭,1:打开
*/
void inf_lcd_display_set(uint8_t state);
#endif
inf_lcd.c
/**
* Copyright (c), 2015-2025
* @file inf_lcd.c
* @brief lcd接口层主文件
* @author jdh
* @date 2015/7/28
* @update 2015/9/2
* @update 2015/9/3
*/
/*********************************************************************
* 头文件
**********************************************************************/
#include "inf_lcd.h"
#include "string.h"
/*********************************************************************
* 函数
**********************************************************************/
/**
* @brief 初始lcd
*/
void inf_lcd_init(void)
{
drv_lcd_init();
}
/**
* @brief 画点
* @param x:x坐标
* @param y:y坐标
* @param show:0:不显示,1:显示
*/
void inf_lcd_draw_pixel(uint8_t x,uint8_t y,uint8_t show)
{
drv_lcd_draw_pixel(x,y,show);
}
/**
* @brief 打开显示
* @param 0:关闭,1:打开
*/
void inf_lcd_display_set(uint8_t state)
{
drv_lcd_display_set(state);
}