今天通过SPI实现SSD1331显示屏显示,同时也熟悉使用了SPI功能。
第一步:配置相应的管脚,选择SPI1,PA1为SSD1331的RES,PA3为SSD1331的DC,PB5为SSD1331的CS接口。
第二步:选择SPI1的模式Mode为Full-Duplex Master
第三步:配置SPI参数,根据SSD1331手册选择CPOL和CPHA
第四步:程序编写
首先在主程序中添加如下程序,完成用绿色清屏,然后用不同字体和颜色显示“WaveShare”,“01”,”0”。
SSD1331的驱动代码如下:
#include "SSD1331.h"
#include "gpio.h"
#include "spi.h"
#include "Fonts.h"
static uint8_t s_ucGRAM[8][128];
void ssd1331_init(void)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1,GPIO_PIN_SET); /* SET RES */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5,GPIO_PIN_SET); /* SET CS */
ssd1331_Write_Cmd(DISPLAY_OFF);
ssd1331_Write_Cmd(SET_CONTRAST_A); //Set contrast for color A
ssd1331_Write_Cmd(0xFF); //145 0x91 /* 这个是设置对比度(亮度)的值 */
ssd1331_Write_Cmd(SET_CONTRAST_B); //Set contrast for color B
ssd1331_Write_Cmd(0xFF); //80 0x50 /* 这个是设置对比度(亮度)的值 */
ssd1331_Write_Cmd(SET_CONTRAST_C); //Set contrast for color C
ssd1331_Write_Cmd(0xFF); //125 0x7D /* 这个是设置对比度(亮度)的值 */
ssd1331_Write_Cmd(MASTER_CURRENT_CONTROL);//master current control
ssd1331_Write_Cmd(0x06); //6
ssd1331_Write_Cmd(SET_PRECHARGE_SPEED_A);//Set Second Pre-change Speed For ColorA
ssd1331_Write_Cmd(0x64); //100 64
ssd1331_Write_Cmd(SET_PRECHARGE_SPEED_B);//Set Second Pre-change Speed For ColorB
ssd1331_Write_Cmd(0x78); //120 78
ssd1331_Write_Cmd(SET_PRECHARGE_SPEED_C);//Set Second Pre-change Speed For ColorC
ssd1331_Write_Cmd(0x64); //100 64
ssd1331_Write_Cmd(SET_REMAP); //set remap & data format
ssd1331_Write_Cmd(0x72); //0x72
ssd1331_Write_Cmd(SET_DISPLAY_START_LINE);//Set display Start Line
ssd1331_Write_Cmd(0x0);
ssd1331_Write_Cmd(SET_DISPLAY_OFFSET); //Set display offset
ssd1331_Write_Cmd(0x0);
ssd1331_Write_Cmd(NORMAL_DISPLAY); //Set display mode
ssd1331_Write_Cmd(SET_MULTIPLEX_RATIO); //Set multiplex ratio
ssd1331_Write_Cmd(0x3F);
ssd1331_Write_Cmd(SET_MASTER_CONFIGURE); //Set master configuration
ssd1331_Write_Cmd(0x8E);
ssd1331_Write_Cmd(POWER_SAVE_MODE); //Set Power Save Mode
ssd1331_Write_Cmd(0x00); //0x00
ssd1331_Write_Cmd(PHASE_PERIOD_ADJUSTMENT);//phase 1 and 2 period adjustment
ssd1331_Write_Cmd(0x31); //0x31
ssd1331_Write_Cmd(DISPLAY_CLOCK_DIV); //display clock divider/oscillator frequency
ssd1331_Write_Cmd(0xF0);
ssd1331_Write_Cmd(SET_PRECHARGE_VOLTAGE);//Set Pre-Change Level
ssd1331_Write_Cmd(0x3A);
ssd1331_Write_Cmd(SET_V_VOLTAGE); //Set vcomH
ssd1331_Write_Cmd(0x3E);
ssd1331_Write_Cmd(DEACTIVE_SCROLLING); //disable scrolling
ssd1331_Write_Cmd(NORMAL_BRIGHTNESS_DISPLAY_ON);//set display on
ssd1331_clear_screen(GREEN);
}
static void ssd1331_Write_Cmd(uint8_t Cmd)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5,GPIO_PIN_RESET); /* RESET CS */
HAL_SPI_Transmit(&hspi1, &Cmd,1,10);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5,GPIO_PIN_SET); /* SET CS */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3,GPIO_PIN_SET); /* SET DC */
}
static void ssd1331_Write_Data(uint8_t Data)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5,GPIO_PIN_RESET); /* RESET CS */
HAL_SPI_Transmit(&hspi1, &Data,1,10);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5,GPIO_PIN_SET); /* SET CS */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3,GPIO_PIN_SET); /* SET DC */
}
/*
********************************************************************************
* 函 数 名: ssd1331_clear_screen
* 功能说明: 指定颜色清屏
* 形 参:
* Color :像素颜色
* 返 回 值: 无
********************************************************************************
*/
void ssd1331_clear_screen(uint16_t Color)
{
uint16_t i, j;
for(i = 0; i < OLED_HEIGHT; i ++){
for(j = 0; j < OLED_WIDTH; j ++){
ssd1331_draw_point(j, i, Color);
}
}
}
/*
********************************************************************************
* 函 数 名: ssd1331_draw_point
* 功能说明: 在指定位置画1个像素点
* 形 参:
* chXpos,chYpos : 像素坐标
* hwColor :像素颜色
* 返 回 值: 无
********************************************************************************
*/
void ssd1331_draw_point(uint8_t chXpos, uint8_t chYpos, uint16_t hwColor)
{
if (chXpos >= OLED_WIDTH || chYpos >= OLED_HEIGHT) {
return;
}
//set column point
ssd1331_Write_Cmd(SET_COLUMN_ADDRESS); /* 这个指令设置起始和结束COLUMN地址 */
ssd1331_Write_Cmd(chXpos); /* 起始地址 */
ssd1331_Write_Cmd(chXpos); /* 结束地址 */
//set row point
ssd1331_Write_Cmd(SET_ROW_ADDRESS); /* 这个指令设置起始和结束ROW地址 */
ssd1331_Write_Cmd(chYpos);
ssd1331_Write_Cmd(chYpos);
//fill 16bit colour
ssd1331_Write_Data(hwColor >> 8);
ssd1331_Write_Data(hwColor);
}
/*
********************************************************************************
* 函 数 名: ssd1331_DrawLine
* 功能说明: 采用 Bresenham 算法,在2点间画一条直线。
* 形 参:
* _usX1, _usY1 :起始点坐标
* _usX2, _usY2 :终止点Y坐标
* _ucColor :像素颜色
* 返 回 值: 无
********************************************************************************
*/
void ssd1331_DrawLine(uint16_t _usX1 , uint16_t _usY1 , uint16_t _usX2 , uint16_t _usY2 , uint16_t _ucColor)
{
int32_t dx , dy ;
int32_t tx , ty ;
int32_t inc1 , inc2 ;
int32_t d , iTag ;
int32_t x , y ;
/* 采用 Bresenham 算法,在2点间画一条直线 */
ssd1331_draw_point(_usX1 , _usY1 , _ucColor);
/* 如果两点重合,结束后面的动作。*/
if ( _usX1 == _usX2 && _usY1 == _usY2 )
{
return;
}
iTag = 0 ;
/* dx = abs ( _usX2 - _usX1 ); */
if (_usX2 >= _usX1)
{
dx = _usX2 - _usX1;
}
else
{
dx = _usX1 - _usX2;
}
/* dy = abs ( _usY2 - _usY1 ); */
if (_usY2 >= _usY1)
{
dy = _usY2 - _usY1;
}
else
{
dy = _usY1 - _usY2;
}
if ( dx < dy ) /*如果dy为计长方向,则交换纵横坐标。*/
{
uint16_t temp;
iTag = 1 ;
temp = _usX1; _usX1 = _usY1; _usY1 = temp;
temp = _usX2; _usX2 = _usY2; _usY2 = temp;
temp = dx; dx = dy; dy = temp;
}
tx = _usX2 > _usX1 ? 1 : -1 ; /* 确定是增1还是减1 */
ty = _usY2 > _usY1 ? 1 : -1 ;
x = _usX1 ;
y = _usY1 ;
inc1 = 2 * dy ;
inc2 = 2 * ( dy - dx );
d = inc1 - dx ;
while ( x != _usX2 ) /* 循环画点 */
{
if ( d < 0 )
{
d += inc1 ;
}
else
{
y += ty ;
d += inc2 ;
}
if ( iTag )
{
ssd1331_draw_point ( y , x , _ucColor) ;
}
else
{
ssd1331_draw_point ( x , y , _ucColor) ;
}
x += tx ;
}
}
/*
********************************************************************************
* 函 数 名: ssd1331_DrawCircle
* 功能说明: 绘制一个圆。
* 形 参:
* _usX, _usY :圆心坐标
* _usRadius :半径
* _ucColor :像素颜色
* 返 回 值: 无
********************************************************************************
*/
void ssd1331_DrawCircle(uint16_t _usX, uint16_t _usY, uint16_t _usRadius, uint16_t _ucColor)
{
int32_t D; /* Decision Variable */
uint32_t CurX; /* 当前 X 值 */
uint32_t CurY; /* 当前 Y 值 */
D = 3 - (_usRadius << 1);
CurX = 0;
CurY = _usRadius;
while (CurX <= CurY)
{
ssd1331_draw_point(_usX + CurX, _usY + CurY, _ucColor);
ssd1331_draw_point(_usX + CurX, _usY - CurY, _ucColor);
ssd1331_draw_point(_usX - CurX, _usY + CurY, _ucColor);
ssd1331_draw_point(_usX - CurX, _usY - CurY, _ucColor);
ssd1331_draw_point(_usX + CurY, _usY + CurX, _ucColor);
ssd1331_draw_point(_usX + CurY, _usY - CurX, _ucColor);
ssd1331_draw_point(_usX - CurY, _usY + CurX, _ucColor);
ssd1331_draw_point(_usX - CurY, _usY - CurX, _ucColor);
if (D < 0)
{
D += (CurX << 2) + 6;
}
else
{
D += ((CurX - CurY) << 2) + 10;
CurY--;
}
CurX++;
}
}
void ssd1331_display_string(uint8_t chXpos, uint8_t chYpos, const uint8_t *pchString, uint8_t chSize, uint16_t hwColor)
{
if (chXpos >= OLED_WIDTH || chYpos >= OLED_HEIGHT)
{
return;
}
while (*pchString != '\0')
{
if (chXpos > (OLED_WIDTH - chSize / 2))
{
chXpos = 0;
chYpos += chSize;
if (chYpos > (OLED_HEIGHT - chSize))
{
chYpos = chXpos = 0;
ssd1331_clear_screen(0x00);
}
}
ssd1331_display_char(chXpos, chYpos, *pchString, chSize, hwColor);
chXpos += chSize / 2;
pchString ++;
}
}
void ssd1331_display_char(uint8_t chXpos, uint8_t chYpos, uint8_t chChr, uint8_t chSize, uint16_t hwColor)
{
uint8_t i, j, chTemp;
uint8_t chYpos0 = chYpos;
if (chXpos >= OLED_WIDTH || chYpos >= OLED_HEIGHT) {
return;
}
for (i = 0; i < chSize; i ++)
{
if (FONT_1206 == chSize)
{
chTemp = c_chFont1206[chChr - 0x20][i];
}
else if (FONT_1608 == chSize)
{
chTemp = c_chFont1608[chChr - 0x20][i];
}
for (j = 0; j < 8; j ++)
{
if (chTemp & 0x80)
{
ssd1331_draw_point(chXpos, chYpos, hwColor);
}
chTemp <<= 1;
chYpos ++;
if ((chYpos - chYpos0) == chSize)
{
chYpos = chYpos0;
chXpos ++;
break;
}
}
}
}
/* 显示宽度和高度都是32的字符 */
void ssd1331_draw_3216char(uint8_t chXpos, uint8_t chYpos, uint8_t chChar, uint16_t hwColor)
{
uint8_t i, j;
uint8_t chTemp = 0, chYpos0 = chYpos;
for (i = 0; i < 64; i ++) {
chTemp = c_chFont3216[chChar - 0x30][i];
for (j = 0; j < 8; j ++) {
if (chTemp & 0x80) {
ssd1331_draw_point(chXpos, chYpos, hwColor);
}
chTemp <<= 1;
chYpos ++;
if ((chYpos - chYpos0) == 32) {
chYpos = chYpos0;
chXpos ++;
break;
}
}
}
}
/*
********************************************************************************
* 函 数 名: ssd1331_draw_bitmap
* 功能说明: 在指定位置画一个图片
* 形 参:
* chXpos,chYpos : 像素坐标
* pchBmp:像素指针
* chWidth,chHeight:图片大小
* hwColor :像素颜色
* 返 回 值: 无
* 说 明:取模软件采用pctolcd2002,图片大小就是新建图片时图片的宽度和高度
* 取模方式:逐行式,顺向
********************************************************************************
*/
void ssd1331_draw_bitmap(uint8_t chXpos, uint8_t chYpos, const uint8_t *pchBmp, uint8_t chWidth, uint8_t chHeight, uint16_t hwColor)
{
uint16_t i, j, byteWidth = (chWidth + 7) / 8;
for(j = 0; j < chHeight; j ++)
{
for(i = 0; i < chWidth; i ++ )
{
if(*(pchBmp + j * byteWidth + i / 8) & (128 >> (i & 7))) /* 用于判断字模中哪些位是1,就用相应颜色点亮 */
{
ssd1331_draw_point(chXpos + i, chYpos + j, hwColor);
}
}
}
}