目录
一、SPI 通信技术
显示屏(LCD、OLED)接口一般有I2C、SPI、UART、RGB、LVDS、MIPI、EDP和DP等。一般3.5寸以下的小尺寸LCD屏,显示数据量比较少,普遍采用低速串口,如I2C、SPI、UART。SPI(Serial Peripheral Interface)是全双工,同步的通信总线,以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时,MISO、MISI取其一),节约了芯片的管脚。
这些数据线是MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)、CS(片选)。
(1)MISO– Master Input Slave Output,主设备数据输入,从设备数据输出;
(2)MOSI– Master Output Slave Input,主设备数据输出,从设备数据输入;
(3)SCLK – Serial Clock,时钟信号,由主设备产生;
(4)CS – Chip Select,从设备使能信号,由主设备控制。
CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效。。SCK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。
二、SPI通信参配
本文采用的开发板是stm32L496VGT3,该开发板的OLED屏是240*240像素,采用SPI协议通讯,MCU采用4-Line SPI 的接口加上电源、重置等接口,实现 LCD 显示屏控制及输出显示,原理图如下:
LCD 显示屏针脚定义:
MCU连接LCD的芯片引脚定义如下:
各个引脚的具体参数:
三、SPI设置(cubeMx)
在cubeIDE基于stm32L496VGT3芯片创建工程,打开界面配置(cubeMx),配置OLED屏的通信相关信息。本文是在一定基础上直接操作,读者可参考本人以下两篇博文:
【1】cubeIDE快速开发流程_py_free的博客-CSDN博客_cubeide汉化
【2】cubeIDE开发, stm32调试信息串口通信输出显示_py_free的博客-CSDN博客
假设已经做好了时钟配置、lpusart调试输出,先直接设置OLED的SPI接口信息:
1)开启SPI1
本文只是纯粹的MCU向OLED屏输出信号,因此选择Transmit Only Master模式。
2)添加SPI1的DMA支持
3)开启spi1的中断功能
4)设置oled屏控制的辅助引脚、设置名称及调整配置参数
5)调整SPI1配置参数
6)完成配置后,点击保存或生成代码的按钮生成代码
四、SPI通信及OLED屏点亮
在ICore目录创建oled文件夹,创建oled.h、oled.c的LCD驱动文件。
1)辅助引脚设值
在oled.h中加入对LCD引脚(GPIO)设值控制函数
//对该位写1或0
#define Data_Cmd_State(__D_C_State__) HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, (GPIO_PinState)(__D_C_State__))
#define OLED_RST_State(__RST_State__) HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, (GPIO_PinState)(__RST_State__))
#define LCD_PWR_State(__PWR_State__) HAL_GPIO_WritePin(LCD_PWR_GPIO_Port, LCD_PWR_Pin, (GPIO_PinState)(__PWR_State__))
2)spi1写入数据实现
通过调用HAL标准库的HAL_SPI_Transmit函数实现对spi1(hspi1)实现写入命令函数或数据写入函数,也提供LcdWriteDataMultiple函数一次写入多个数据
/********************************************************************
*
* LcdWriteReg
*
* Function description:
* Sets display register
*/
void LcdWriteReg(uint8_t Data)
{
Data_Cmd_State(0);
HAL_SPI_Transmit(&hspi1, &Data, 1, 10);
}
/********************************************************************
*
* LcdWriteData
*
* Function description:
* Writes a value to a display register
*/
void LcdWriteData(uint8_t Data)
{
Data_Cmd_State(1);
HAL_SPI_Transmit(&hspi1, &Data, 1, 10);
}
/********************************************************************
*
* LcdWriteDataMultiple
*
* Function description:
* Writes multiple values to a display register.
*/
void LcdWriteDataMultiple(uint8_t * pData, uint32_t NumItems)
{
Data_Cmd_State(1);
HAL_SPI_Transmit(&hspi1, pData, NumItems, 10);
}
3)OLED初始化指令执行
定义OLED屏的初始化指令集及指令设置函数
/* Init script function */
struct OLED_function {
uint8_t cmd;
uint16_t data;
};
/* Init script commands */
enum OLED_cmd {
OLED_START,
OLED_END,
OLED_CMD,
OLED_DATA,
OLED_DELAY
};
//以上可放置头文件中
//以下放置源文件中
//初始化命令集
static struct OLED_function OLED_cfg_script[] = {
{OLED_START, OLED_START},
{OLED_CMD, 0x11},
{OLED_DELAY, 120},
{OLED_CMD, 0x36},
{OLED_DATA, 0x00},
{OLED_CMD, 0x3a},
{OLED_DATA, 0x65},
{OLED_CMD, 0xb2},
{OLED_DATA, 0x0c},
{OLED_DATA, 0x0c},
{OLED_DATA, 0x00},
{OLED_DATA, 0x33},
{OLED_DATA, 0x33},
{OLED_CMD, 0xb7},
{OLED_DATA, 0x72},
{OLED_CMD, 0xbb},
{OLED_DATA, 0x3d},
{OLED_CMD, 0xc0},
{OLED_DATA, 0x2c},
{OLED_CMD, 0xc2},
{OLED_DATA, 0x01},
{OLED_CMD, 0xc3},
{OLED_DATA, 0x19},
{OLED_CMD, 0xc4},
{OLED_DATA, 0x20},
{OLED_CMD, 0xc6},
{OLED_DATA, 0x0f},
{OLED_CMD, 0xd0},
{OLED_DATA, 0xa4},
{OLED_DATA, 0xa1},
{OLED_CMD, 0xe0},
{OLED_DATA, 0x70},
{OLED_DATA, 0x04},
{OLED_DATA, 0x08},
{OLED_DATA, 0x09},
{OLED_DATA, 0x09},
{OLED_DATA, 0x05},
{OLED_DATA, 0x2a},
{OLED_DATA, 0x33},
{OLED_DATA, 0x41},
{OLED_DATA, 0x07},
{OLED_DATA, 0x13},
{OLED_DATA, 0x13},
{OLED_DATA, 0x29},
{OLED_DATA, 0x2f},
{OLED_CMD, 0xe1},
{OLED_DATA, 0x70},
{OLED_DATA, 0x03},
{OLED_DATA, 0x09},
{OLED_DATA, 0x0a},
{OLED_DATA, 0x09},
{OLED_DATA, 0x06},
{OLED_DATA, 0x2b},
{OLED_DATA, 0x34},
{OLED_DATA, 0x41},
{OLED_DATA, 0x07},
{OLED_DATA, 0x12},
{OLED_DATA, 0x14},
{OLED_DATA, 0x28},
{OLED_DATA, 0x2e},
{OLED_CMD, 0x21},
{OLED_CMD, 0x29},
{OLED_CMD, 0x2a},
{OLED_DATA, 0x00},
{OLED_DATA, 0x00},
{OLED_DATA, 0x00},
{OLED_DATA, 0xef},
{OLED_CMD, 0x2b},
{OLED_DATA, 0x00},
{OLED_DATA, 0x00},
{OLED_DATA, 0x00},
{OLED_DATA, 0xef},
{OLED_CMD, 0x2c},
{OLED_END, OLED_END},
};
static void OLED_run_cfg_script()
{
uint8_t data[2] = {0};
int i = 0;
int end_script = 0;
do {
switch (OLED_cfg_script[i].cmd) {
case OLED_START:
break;
case OLED_CMD:
data[0] = OLED_cfg_script[i].data & 0xff;
LcdWriteReg(data[0]);
break;
case OLED_DATA:
data[0] = OLED_cfg_script[i].data & 0xff;
LcdWriteData(data[0]);
break;
case OLED_DELAY:
HAL_Delay(OLED_cfg_script[i].data);
break;
case OLED_END:
end_script = 1;
}
i++;
} while (!end_script);
}
4)oled初始化
进行oled屏的初始化函数设计
//olde屏重置
static void OLED_reset()
{
OLED_RST_State(0);
HAL_Delay(200);
OLED_RST_State(1);
HAL_Delay(150);
}
//oled屏初始化
int OLED_init()
{
LCD_PWR_State(0);//回显关闭
OLED_reset(); //屏重置
OLED_run_cfg_script();//初始化指令集依次执行脚本函数
LCD_PWR_State(1);//回显开启
OLED_display_picture();//条形码屏幕显示测试
return HAL_OK;
}
5)oled屏幕的亮屏(条形码显示)代码
#define LCD_MAX_MEM16_BLOCK (1 << 6)
#define LCD_PIXEL_PER_BLOCK (LCD_MAX_MEM16_BLOCK >> 1)
static void spec_send_fb(uint16_t color, uint16_t pixel_num)
{
uint16_t real_mem[LCD_MAX_MEM16_BLOCK] = {0};
for (uint16_t i = 0; i < LCD_MAX_MEM16_BLOCK; ++i) {
real_mem[i] = color;
}
Data_Cmd_State(GPIO_PIN_SET);
if (pixel_num <= LCD_MAX_MEM16_BLOCK) {
LcdWriteDataMultiple((uint8_t*)real_mem, pixel_num << 1);
} else {
uint16_t count = pixel_num / LCD_MAX_MEM16_BLOCK;
uint16_t remain = pixel_num % LCD_MAX_MEM16_BLOCK;
for (uint16_t i = 0; i < count; ++i) {
LcdWriteDataMultiple((uint8_t*)real_mem, LCD_MAX_MEM16_BLOCK << 1);
}
LcdWriteDataMultiple((uint8_t*)real_mem, remain << 1);
}
}
static void OLED_display_picture(void)
{
LcdWriteReg(OLED_RAMWR);
spec_send_fb(0x0, WIDTH * HEIGHT / 4);
spec_send_fb(0x1111, WIDTH * HEIGHT / 4);
spec_send_fb(0x7777, WIDTH * HEIGHT / 4);
spec_send_fb(0xeeee, WIDTH * HEIGHT / 4);
}
6)驱动调用
基本功能完成后,在main.c文件实现函数调用
/* USER CODE BEGIN Includes */
#include "../../ICore/key/key.h"
#include "../../ICore/led/led.h"
#include "../../ICore/print/print.h"
#include "../../ICore/usart/usart.h"
#include "../../ICore/oled/oled.h"
/* USER CODE END Includes */
..............................................
/* USER CODE BEGIN 2 */
ResetPrintInit(&hlpuart1);
HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
HLPUSART_RX_STA = 0;
OLED_init();
set_led0_val(0);
set_led1_val(get_key0_val());
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
while (1)
{
Toggle_led0();
/* USER CODE END WHILE */
7)编译加载到开发板上
8)屏幕效果点亮如下:
五、屏幕输出字符串设计
【1】字符串编码表
在ICore/oled目下字符串编码表ASCII.h,具体如下:
/*
* ASCII.h
*
* Created on: Jun 11, 2022
* Author: Administrator
*/
#ifndef OLED_ASCII_H_
#define OLED_ASCII_H_
// ------------------ ASCII字模的数据表 ------------------------ //
// 码表从0x20~0x7e //
// 字库: 纵向取模下高位// (调用时要减512)
// -------------------------------------------------------------- //
const uint8_t ASCII_8x16[] = { // ASCII
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // - -
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x38,0xFC,0xFC,0x38,0x00,0x00, // -!-
0x00,0x00,0x00,0x0D,0x0D,0x00,0x00,0x00,
0x00,0x0E,0x1E,0x00,0x00,0x1E,0x0E,0x00, // -"-
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x20,0xF8,0xF8,0x20,0xF8,0xF8,0x20,0x00, // -#-
0x02,0x0F,0x0F,0x02,0x0F,0x0F,0x02,0x00,
0x38,0x7C,0x44,0x47,0x47,0xCC,0x98,0x00, // -$-
0x03,0x06,0x04,0x1C,0x1C,0x07,0x03,0x00,
0x30,0x30,0x00,0x80,0xC0,0x60,0x30,0x00, // -%-
0x0C,0x06,0x03,0x01,0x00,0x0C,0x0C,0x00,
0x80,0xD8,0x7C,0xE4,0xBC,0xD8,0x40,0x00, // -&-
0x07,0x0F,0x08,0x08,0x07,0x0F,0x08,0x00,
0x00,0x10,0x1E,0x0E,0x00,0x00,0x00,0x00, // -'-
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xF0,0xF8,0x0C,0x04,0x00,0x00, // -(-
0x00,0x00,0x03,0x07,0x0C,0x08,0x00,0x00,
0x00,0x00,0x04,0x0C,0xF8,0xF0,0x00,0x00, // -)-
0x00,0x00,0x08,0x0C,0x07,0x03,0x00,0x00,
0x80,0xA0,0xE0,0xC0,0xC0,0xE0,0xA0,0x80, // -*-
0x00,0x02,0x03,0x01,0x01,0x03,0x02,0x00,
0x00,0x80,0x80,0xE0,0xE0,0x80,0x80,0x00, // -+-
0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // -,-
0x00,0x00,0x10,0x1E,0x0E,0x00,0x00,0x00,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00, // ---
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // -.-
0x00,0x00,0x00,0x0C,0x0C,0x00,0x00,0x00,
0x00,0x00,0x00,0x80,0xC0,0x60,0x30,0x00, // -/-
0x0C,0x06,0x03,0x01,0x00,0x00,0x00,0x00,
0xF8,0xFC,0x04,0xC4,0x24,0xFC,0xF8,0x00, // -0-
0x07,0x0F,0x09,0x08,0x08,0x0F,0x07,0x00,
0x00,0x10,0x18,0xFC,0xFC,0x00,0x00,0x00, // -1-
0x00,0x08,0x08,0x0F,0x0F,0x08,0x08,0x00,
0x08,0x0C,0x84,0xC4,0x64,0x3C,0x18,0x00, // -2-
0x0E,0x0F,0x09,0x08,0x08,0x0C,0x0C,0x00,
0x08,0x0C,0x44,0x44,0x44,0xFC,0xB8,0x00, // -3-
0x04,0x0C,0x08,0x08,0x08,0x0F,0x07,0x00,
0xC0,0xE0,0xB0,0x98,0xFC,0xFC,0x80,0x00, // -4-
0x00,0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,
0x7C,0x7C,0x44,0x44,0xC4,0xC4,0x84,0x00, // -5-
0x04,0x0C,0x08,0x08,0x08,0x0F,0x07,0x00,
0xF0,0xF8,0x4C,0x44,0x44,0xC0,0x80,0x00, // -6-
0x07,0x0F,0x08,0x08,0x08,0x0F,0x07,0x00,
0x0C,0x0C,0x04,0x84,0xC4,0x7C,0x3C,0x00, // -7-
0x00,0x00,0x0F,0x0F,0x00,0x00,0x00,0x00,
0xB8,0xFC,0x44,0x44,0x44,0xFC,0xB8,0x00, // -8-
0x07,0x0F,0x08,0x08,0x08,0x0F,0x07,0x00,
0x38,0x7C,0x44,0x44,0x44,0xFC,0xF8,0x00, // -9-
0x00,0x08,0x08,0x08,0x0C,0x07,0x03,0x00,
0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00, // -:-
0x00,0x00,0x00,0x06,0x06,0x00,0x00,0x00,
0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00, // -;-
0x00,0x00,0x08,0x0E,0x06,0x00,0x00,0x00,
0x00,0x80,0xC0,0x60,0x30,0x18,0x08,0x00, // -<-
0x00,0x00,0x01,0x03,0x06,0x0C,0x08,0x00,
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00, // -=-
0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x00,
0x00,0x08,0x18,0x30,0x60,0xC0,0x80,0x00, // ->-
0x00,0x08,0x0C,0x06,0x03,0x01,0x00,0x00,
0x18,0x1C,0x04,0xC4,0xE4,0x3C,0x18,0x00, // -?-
0x00,0x00,0x00,0x0D,0x0D,0x00,0x00,0x00,
0xF0,0xF8,0x08,0xC8,0xC8,0xF8,0xF0,0x00, // -@-
0x07,0x0F,0x08,0x0B,0x0B,0x0B,0x01,0x00,
0xE0,0xF0,0x98,0x8C,0x98,0xF0,0xE0,0x00, // -A-
0x0F,0x0F,0x00,0x00,0x00,0x0F,0x0F,0x00,
0x04,0xFC,0xFC,0x44,0x44,0xFC,0xB8,0x00, // -B-
0x08,0x0F,0x0F,0x08,0x08,0x0F,0x07,0x00,
0xF0,0xF8,0x0C,0x04,0x04,0x0C,0x18,0x00, // -C-
0x03,0x07,0x0C,0x08,0x08,0x0C,0x06,0x00,
0x04,0xFC,0xFC,0x04,0x0C,0xF8,0xF0,0x00, // -D-
0x08,0x0F,0x0F,0x08,0x0C,0x07,0x03,0x00,
0x04,0xFC,0xFC,0x44,0xE4,0x0C,0x1C,0x00, // -E-
0x08,0x0F,0x0F,0x08,0x08,0x0C,0x0E,0x00,
0x04,0xFC,0xFC,0x44,0xE4,0x0C,0x1C,0x00, // -F-
0x08,0x0F,0x0F,0x08,0x00,0x00,0x00,0x00,
0xF0,0xF8,0x0C,0x84,0x84,0x8C,0x98,0x00, // -G-
0x03,0x07,0x0C,0x08,0x08,0x07,0x0F,0x00,
0xFC,0xFC,0x40,0x40,0x40,0xFC,0xFC,0x00, // -H-
0x0F,0x0F,0x00,0x00,0x00,0x0F,0x0F,0x00,
0x00,0x00,0x04,0xFC,0xFC,0x04,0x00,0x00, // -I-
0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,
0x00,0x00,0x00,0x04,0xFC,0xFC,0x04,0x00, // -J-
0x07,0x0F,0x08,0x08,0x0F,0x07,0x00,0x00,
0x04,0xFC,0xFC,0xC0,0xF0,0x3C,0x0C,0x00, // -K-
0x08,0x0F,0x0F,0x00,0x01,0x0F,0x0E,0x00,
0x04,0xFC,0xFC,0x04,0x00,0x00,0x00,0x00, // -L-
0x08,0x0F,0x0F,0x08,0x08,0x0C,0x0E,0x00,
0xFC,0xFC,0x38,0x70,0x38,0xFC,0xFC,0x00, // -M-
0x0F,0x0F,0x00,0x00,0x00,0x0F,0x0F,0x00,
0xFC,0xFC,0x38,0x70,0xE0,0xFC,0xFC,0x00, // -N-
0x0F,0x0F,0x00,0x00,0x00,0x0F,0x0F,0x00,
0xF0,0xF8,0x0C,0x04,0x0C,0xF8,0xF0,0x00, // -O-
0x03,0x07,0x0C,0x08,0x0C,0x07,0x03,0x00,
0x04,0xFC,0xFC,0x44,0x44,0x7C,0x38,0x00, // -P-
0x08,0x0F,0x0F,0x08,0x00,0x00,0x00,0x00,
0xF8,0xFC,0x04,0x04,0x04,0xFC,0xF8,0x00, // -Q-
0x07,0x0F,0x08,0x0E,0x3C,0x3F,0x27,0x00,
0x04,0xFC,0xFC,0x44,0xC4,0xFC,0x38,0x00, // -R-
0x08,0x0F,0x0F,0x00,0x00,0x0F,0x0F,0x00,
0x18,0x3C,0x64,0x44,0xC4,0x9C,0x18,0x00, // -S-
0x06,0x0E,0x08,0x08,0x08,0x0F,0x07,0x00,
0x00,0x1C,0x0C,0xFC,0xFC,0x0C,0x1C,0x00, // -T-
0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,
0xFC,0xFC,0x00,0x00,0x00,0xFC,0xFC,0x00, // -U-
0x07,0x0F,0x08,0x08,0x08,0x0F,0x07,0x00,
0xFC,0xFC,0x00,0x00,0x00,0xFC,0xFC,0x00, // -V-
0x01,0x03,0x06,0x0C,0x06,0x03,0x01,0x00,
0xFC,0xFC,0x00,0x80,0x00,0xFC,0xFC,0x00, // -W-
0x03,0x0F,0x0E,0x03,0x0E,0x0F,0x03,0x00,
0x0C,0x3C,0xF0,0xC0,0xF0,0x3C,0x0C,0x00, // -X-
0x0C,0x0F,0x03,0x00,0x03,0x0F,0x0C,0x00,
0x00,0x3C,0x7C,0xC0,0xC0,0x7C,0x3C,0x00, // -Y-
0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,
0x1C,0x0C,0x84,0xC4,0x64,0x3C,0x1C,0x00, // -Z-
0x0E,0x0F,0x09,0x08,0x08,0x0C,0x0E,0x00,
0x00,0x00,0xFC,0xFC,0x04,0x04,0x00,0x00, // -[-
0x00,0x00,0x0F,0x0F,0x08,0x08,0x00,0x00,
0x38,0x70,0xE0,0xC0,0x80,0x00,0x00,0x00, // -\-
0x00,0x00,0x00,0x01,0x03,0x07,0x0E,0x00,
0x00,0x00,0x04,0x04,0xFC,0xFC,0x00,0x00, // -]-
0x00,0x00,0x08,0x08,0x0F,0x0F,0x00,0x00,
0x08,0x0C,0x06,0x03,0x06,0x0C,0x08,0x00, // -^-
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // -_-
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x00,0x00,0x03,0x07,0x04,0x00,0x00,0x00, // -`-
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0xA0,0xA0,0xA0,0xE0,0xC0,0x00,0x00, // -a-
0x07,0x0F,0x08,0x08,0x07,0x0F,0x08,0x00,
0x04,0xFC,0xFC,0x20,0x60,0xC0,0x80,0x00, // -b-
0x08,0x0F,0x07,0x08,0x08,0x0F,0x07,0x00,
0xC0,0xE0,0x20,0x20,0x20,0x60,0x40,0x00, // -c-
0x07,0x0F,0x08,0x08,0x08,0x0C,0x04,0x00,
0x80,0xC0,0x60,0x24,0xFC,0xFC,0x00,0x00, // -d-
0x07,0x0F,0x08,0x08,0x07,0x0F,0x08,0x00,
0xC0,0xE0,0xA0,0xA0,0xA0,0xE0,0xC0,0x00, // -e-
0x07,0x0F,0x08,0x08,0x08,0x0C,0x04,0x00,
0x40,0xF8,0xFC,0x44,0x0C,0x18,0x00,0x00, // -f-
0x08,0x0F,0x0F,0x08,0x00,0x00,0x00,0x00,
0xC0,0xE0,0x20,0x20,0xC0,0xE0,0x20,0x00, // -g-
0x27,0x6F,0x48,0x48,0x7F,0x3F,0x00,0x00,
0x04,0xFC,0xFC,0x40,0x20,0xE0,0xC0,0x00, // -h-
0x08,0x0F,0x0F,0x00,0x00,0x0F,0x0F,0x00,
0x00,0x00,0x20,0xEC,0xEC,0x00,0x00,0x00, // -i-
0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,
0x00,0x00,0x00,0x00,0x20,0xEC,0xEC,0x00, // -j-
0x00,0x30,0x70,0x40,0x40,0x7F,0x3F,0x00,
0x04,0xFC,0xFC,0x80,0xC0,0x60,0x20,0x00, // -k-
0x08,0x0F,0x0F,0x01,0x03,0x0E,0x0C,0x00,
0x00,0x00,0x04,0xFC,0xFC,0x00,0x00,0x00, // -l-
0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,
0xE0,0xE0,0x60,0xC0,0x60,0xE0,0xC0,0x00, // -m-
0x0F,0x0F,0x00,0x0F,0x00,0x0F,0x0F,0x00,
0x20,0xE0,0xC0,0x20,0x20,0xE0,0xC0,0x00, // -n-
0x00,0x0F,0x0F,0x00,0x00,0x0F,0x0F,0x00,
0xC0,0xE0,0x20,0x20,0x20,0xE0,0xC0,0x00, // -o-
0x07,0x0F,0x08,0x08,0x08,0x0F,0x07,0x00,
0x20,0xE0,0xC0,0x20,0x20,0xE0,0xC0,0x00, // -p-
0x40,0x7F,0x7F,0x48,0x08,0x0F,0x07,0x00,
0xC0,0xE0,0x20,0x20,0xC0,0xE0,0x20,0x00, // -q-
0x07,0x0F,0x08,0x48,0x7F,0x7F,0x40,0x00,
0x20,0xE0,0xC0,0x60,0x20,0x60,0xC0,0x00, // -r-
0x08,0x0F,0x0F,0x08,0x00,0x00,0x00,0x00,
0x40,0xE0,0xA0,0x20,0x20,0x60,0x40,0x00, // -s-
0x04,0x0C,0x09,0x09,0x0B,0x0E,0x04,0x00,
0x20,0x20,0xF8,0xFC,0x20,0x20,0x00,0x00, // -t-
0x00,0x00,0x07,0x0F,0x08,0x0C,0x04,0x00,
0xE0,0xE0,0x00,0x00,0xE0,0xE0,0x00,0x00, // -u-
0x07,0x0F,0x08,0x08,0x07,0x0F,0x08,0x00,
0x00,0xE0,0xE0,0x00,0x00,0xE0,0xE0,0x00, // -v-
0x00,0x03,0x07,0x0C,0x0C,0x07,0x03,0x00,
0xE0,0xE0,0x00,0x00,0x00,0xE0,0xE0,0x00, // -w-
0x07,0x0F,0x0C,0x07,0x0C,0x0F,0x07,0x00,
0x20,0x60,0xC0,0x80,0xC0,0x60,0x20,0x00, // -x-
0x08,0x0C,0x07,0x03,0x07,0x0C,0x08,0x00,
0xE0,0xE0,0x00,0x00,0x00,0xE0,0xE0,0x00, // -y-
0x47,0x4F,0x48,0x48,0x68,0x3F,0x1F,0x00,
0x60,0x60,0x20,0xA0,0xE0,0x60,0x20,0x00, // -z-
0x0C,0x0E,0x0B,0x09,0x08,0x0C,0x0C,0x00,
0x00,0x40,0x40,0xF8,0xBC,0x04,0x04,0x00, // -{-
0x00,0x00,0x00,0x07,0x0F,0x08,0x08,0x00,
0x00,0x00,0x00,0xBC,0xBC,0x00,0x00,0x00, // -|-
0x00,0x00,0x00,0x0F,0x0F,0x00,0x00,0x00,
0x00,0x04,0x04,0xBC,0xF8,0x40,0x40,0x00, // -}-
0x00,0x08,0x08,0x0F,0x07,0x00,0x00,0x00,
0x08,0x0C,0x04,0x0C,0x08,0x0C,0x04,0x00, // -~-
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x80,0xC0,0x60,0x30,0x60,0xC0,0x80,0x00, // --
0x07,0x07,0x04,0x04,0x04,0x07,0x07,0x00,
};
#endif /* OLED_ASCII_H_ */
【2】将创建屏幕打印函数,输出显示字符串能力
(1)首选了解ASCII_8x16字库表如何使用的,例如我们取"!"为例:
!16个8bit的字段的二进制表示如下
0X00 0 0 0 0 0 0 0 0
0X00 0 0 0 0 0 0 0 0
0X38 0 0 1 1 1 0 0 0
0XFC 1 1 1 1 1 1 0 0
0XFC 1 1 1 1 1 1 0 0
0X38 0 0 1 1 1 0 0 0
0X00 0 0 0 0 0 0 0 0
0X00 0 0 0 0 0 0 0 0
0X00 0 0 0 0 0 0 0 0
0X00 0 0 0 0 0 0 0 0
0X00 0 0 0 0 0 0 0 0
0X0D 0 0 0 0 1 1 0 1
0X0D 0 0 0 0 1 1 0 1
0X00 0 0 0 0 0 0 0 0
0X00 0 0 0 0 0 0 0 0
0X00 0 0 0 0 0 0 0 0
(2)将划分为前8*8和后8*8的的二进制矩阵,将8*8矩阵做逆时针(90度)翻转,将翻转后矩阵进行紧凑(去掉空格),这些“1”组成了一个大的“!”字符如下:
//将8*8二进制矩阵翻转
0X000X000X380XFC0XFC0X380X000X00
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 1 1 0 0 0
0 0 1 1 1 1 0 0
0 0 1 1 1 1 0 0
0 0 1 1 1 1 0 0
0 0 0 1 1 0 0 0
0 0 0 1 1 0 0 0
0X000X000X000X0D0X0D0X000X000X00
0 0 0 1 1 0 0 0
0 0 0 0 0 0 0 0
0 0 0 1 1 0 0 0
0 0 0 1 1 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
将上述矩阵进行紧凑(去掉空格),这些“1”组成了一个大的“!”字符
00000000
00000000
00011000
00111100
00111100
00111100
00011000
00011000
00011000
00000000
00011000
00011000
00000000
00000000
00000000
00000000
//显然,在代码实现中,每个字符就是8*16像素的二进制矩阵,我们将“1”的绘制前景色,“0”的绘制背景色,就能显示字符
(3)显然,在代码实现中,每个字符就是8*16像素的二进制矩阵,我们将“1”的绘制前景色,“0”的绘制背景色,就能显示字符
现在给出字符8*16二进制矩阵获取代码:
//addr为字符的地址信息
uint8_t buffer[16][8]={0};
for(uint8_t i=0; i<16; i++)
{
uint16_t buf=ASCII_8x16[(addr*16)+i-512];
printf("0X%02X ",buf);
for(uint8_t j=0;j<8;j++){
printf("%d ",(0x01&(buf>>j))>0?0x01:0x00);//注意这里低字节开始取值,与前面分析的就刚好反至了,但原理是一样的
buffer[8*(i/8)+j][i%8]=(0x01&(buf>>j))>0?1:0;
}
printf("\r\n");
}
for(uint8_t i=0; i<16; i++)
{
for(uint8_t j=0;j<8;j++){
printf("%d",buffer[i][j]);
}
printf("\r\n");
}
(4)测试“!”输出如下,妥妥的一个!,需要注意的是,下图第一部分的打印输出,由于我们打印时从低字节位开始取数,和前面分析(步骤(1)是从高位取数)的呈现出反至效果:
【3】字符串olde输出
知道一个字符如何在屏幕输出,那么一个字符串输出就是逐个8*16的像素格子渲染输出,具体代码设置如下,OLED_printf实现在屏幕指定位置开始,打印输出字符串,字符串如果过大,将更换到下一行(即y方向下移16像素,屏幕左上角位置为0,0,向右为X轴方向递增,向下位Y轴方向递增),因此该函数会将输入的字符串进行切分成多行。OLED_DISPLAY_8x16函数用于在oled屏幕上输出一个字符的8*16像素绘制。OLED_printf_US测试通过调研OLED_DISPLAY_8x16实现一行字符的输出。
//显示英文与数字8*16的ASCII码
void OLED_DISPLAY_8x16(uint8_t x, uint8_t y, uint16_t w){ //要显示汉字的编号
// printf("x=%d,y=%d,w=0X%04X w*16-512=%d\r\n",x,y,w,w*16-512);
uint16_t color = 0;
uint16_t textData[16][8]={0};
// uint8_t buffer[16][8]={0};
for(uint8_t i=0; i<16; i++)
{
uint16_t buf=ASCII_8x16[(w*16)+i-512];
// printf("0X%02X ",buf);
for(uint8_t j=0;j<8;j++){
// printf("%d ",(0x01&(buf>>j))>0?0x01:0x00);
// buffer[8*(i/8)+j][i%8]=(0x01&(buf>>j))>0?1:0;
color = (0x01&(buf>>j))>0?LCD_DISP_BLACK:LCD_DISP_BLUE;
textData[8*(i/8)+j][i%8] = color;
// OLED_WritePixel(x+i%8,y+8*(i/8)+j,color);//单像素渲染
}
// printf("\r\n");
}
for(uint8_t i=0; i<16; i++)
{
OLED_WriteLine(x,y+i,textData[i],8);//列像素渲染
// for(uint8_t j=0;j<8;j++){
// printf("%d",buffer[i][j]);
// }
// printf("\r\n");
}
}
//用于OLED_printf函数专用的显示程序
void OLED_printf_US(uint8_t xpos,uint8_t ypos,uint8_t *str,uint8_t i){
uint8_t r=0;
while(i != r){//i是长度值,当显示到i之后退出
OLED_DISPLAY_8x16(xpos+r*8,ypos,*str++);//显示英文与数字8*16的ASCII码
r++;
}
}
//OLED专用的printf函数
//调用方法:OLED_printf(0,0,"123");
//注意若xpos或ypos过大,并内容较多时,会出现溢出边界
#define line_size 29
#define row_size 16
void OLED_printf (uint8_t xpos,uint8_t ypos,char *fmt, ...)
{
char buff[line_size]; //用于存放转换后的数据 [长度],限定30字符长度,每个字符8像素宽度,屏幕宽240
uint16_t size=0;
uint16_t row = 1+strlen(fmt)/line_size;//暂时没考虑自带换行符号情况
// printf("row=%d\r\n",row);
for(uint16_t i=0;i<row;i++){
va_list arg_ptr;
va_start(arg_ptr,fmt);
vsnprintf(buff, line_size,fmt+i*line_size,arg_ptr);//数据转换
size=strlen(buff); //得出数据长度
if(strlen(buff)>line_size)size=line_size; //如果长度大于最大值,则长度等于最大值(多出部分忽略)
OLED_printf_US(xpos,ypos+i*row_size,(uint8_t *)buff,size);//最终调用OLED专用的printf函数来显示
va_end(arg_ptr);
}
}
【4】SPI的直接输出和DMA输出区别实现
设置两个清屏函数BSP_LCD_Clear和BSP_LCD_Clear_DMA,一个采用HAL_SPI_Transmit写入数据显示,前面所述都是这种方式(通过调用LcdWriteData、LcdWriteDataMultiple,这个函数采用HAL_SPI_Transmit实现的),一个调用HAL_SPI_Transmit_DMA写入数据显示。
BSP_LCD_Clear实现就是调用LcdWriteDataMultiple绘制240行的线,BSP_LCD_Clear_DMA实现就是分为两个半屏绘制(HAL_SPI_Transmit_DMA的接收数据组大小类型是uint16_t,最大支持0xfff,65535)。
static void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
/* 指定X方向操作区域 */
LcdWriteReg(0x2a);
LcdWriteData(x1 >> 8);
LcdWriteData(x1);
LcdWriteData(x2 >> 8);
LcdWriteData(x2);
/* 指定Y方向操作区域 */
LcdWriteReg(0x2b);
LcdWriteData(y1 >> 8);
LcdWriteData(y1);
LcdWriteData(y2 >> 8);
LcdWriteData(y2);
/* 发送该命令,LCD开始等待接收显存数据 */
LcdWriteReg(0x2C);
}
//指定位置,绘制一行像素直到屏幕右侧
void OLED_SetCursor(uint16_t Xpos, uint16_t Ypos)
{
LCD_Address_Set(Xpos,Ypos,WIDTH-1,Ypos);
}
void BSP_LCD_Clear(uint16_t Color)
{
uint8_t black_gui[480] = {0};
memset(black_gui, Color, sizeof(black_gui));
for (uint32_t counter = 0; counter < 240; counter++)
{
/* Set Cursor */
OLED_SetCursor(0, counter);
/* Prepare to write to LCD RAM */
OLED_WriteReg(0x2C, (uint8_t*)NULL, 0);
/* RAM write data command */
LcdWriteDataMultiple(black_gui, 480);
}
}
extern volatile uint8_t one_frame_done;
void BSP_LCD_Clear_DMA(uint16_t Color)
{
uint8_t black_gui_DMA[WIDTH*HEIGHT*2] = {0X00};
for(uint32_t i=0; i<WIDTH*HEIGHT; i++)
{
black_gui_DMA[2*i]=Color>>8;
black_gui_DMA[2*i+1] = Color;
}
printf("0X%02x%02x\r\n",black_gui_DMA[0],black_gui_DMA[1]);
//需要分两次写入HAL_SPI_Transmit_DMA写入缓存大小uint16_t(0xfff,65535)
one_frame_done = 0;
/* 指定显存操作地址为全屏*/
LCD_Address_Set(0, 0, WIDTH - 1, HEIGHT/2 - 1);
Data_Cmd_State(1);/* 指定接下来的数据为数据 */
/*DMA 写前半屏*/
HAL_SPI_Transmit_DMA(&hspi1,black_gui_DMA, WIDTH*HEIGHT);
while(!one_frame_done){/*release cpu and doing something else*/}
one_frame_done = 0;
/* 指定显存操作地址为全屏*/
LCD_Address_Set(0, HEIGHT/2, WIDTH - 1, HEIGHT - 1);
Data_Cmd_State(1);/* 指定接下来的数据为数据 */
/*DMA 写后半屏*/
HAL_SPI_Transmit_DMA(&hspi1,black_gui_DMA+WIDTH*HEIGHT, WIDTH*HEIGHT);
while(!one_frame_done){/*release cpu and doing something else*/}
}
前文3.3小节,已经开启了SPI的外部中断功能,则SPI的回调函数会被调用。one_frame_done是一个全局变量,在spi.c中定义,需要在spi.c加上HAL_SPI_TxCpltCallback的函数,用来替换stm32l4xx_hal_spi.c文件的spi中断回调实现:
//spi.c末加上spi的中断回调实现
/* USER CODE BEGIN 1 */
volatile uint8_t one_frame_done;
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
one_frame_done = 1;
}
/* USER CODE END 1 */
//用来替换调stm32l4xx_hal_spi.c的弱函数
__weak void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hspi);
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_SPI_TxCpltCallback should be implemented in the user file
*/
}
六、程序编译及测试
在main.c中加入按键功能,一个按键直接写入方式清屏(黑屏),一个按键DMA写入方式清屏(蓝屏幕),一个按键实现屏幕输出一段文字,另外通过串口发送字符串,实现在屏幕输出显示。
/* USER CODE BEGIN WHILE */
while (1)
{
if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
HLPUSART_RX_STA=0;//接收错误,重新开始
HAL_Delay(100);//等待
}
if(KEY_0())
{
OLED_printf(10,10,"hello world to stm32!I am an embedded enthusiast.");
OLED_printf(10,48," STM32CubeIDE is an advanced C/C++ development platform "
"with peripheral configuration, code generation, code compilation, "
"and debug features for STM32 micro controllers and microprocessors.");
// OLED_printf(10,10,"!");
}
if(KEY_1())
{
BSP_LCD_Clear(0xF800);
printf("OLED_Clear\r\n");
}
if(KEY_2())
{
BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
printf("OLED_Clear_DMA\r\n");
}
Toggle_led0();
/* USER CODE END WHILE */
编译完成后测试效果如下:
1)按键key1清屏,发送字符串
2)按键key0,输出一段文字
七、附件
oled.h
#ifndef _OLED_H_
#define _OLED_H_
/*
* for st7789 oled 模组
*/
#define WIDTH 240
#define HEIGHT 240
#define BPP 16
/* Init script function */
struct OLED_function {
uint8_t cmd;
uint16_t data;
};
/* Init script commands */
enum OLED_cmd {
OLED_START,
OLED_END,
OLED_CMD,
OLED_DATA,
OLED_DELAY
};
/************** 颜色(RGB 5,6,5) **************/
#define LCD_DISP_RED 0xF800 //先高字节,后低字节
#define LCD_DISP_GREEN 0x07E0
#define LCD_DISP_BLUE 0x001F
#define LCD_DISP_WRITE 0xFFFF
#define LCD_DISP_BLACK 0x0000
#define LCD_DISP_GRAY 0xEF5D
#define LCD_DISP_GRAY75 0x39E7
#define LCD_DISP_GRAY50 0x7BEF
#define LCD_DISP_GRAY25 0xADB5
/* oled Commands */
#define OLED_CASET 0x2A
#define OLED_RASET 0x2B
#define OLED_RAMWR 0x2C
#define OLED_RAMRD 0x2E
//对该位写1或0
#define Data_Cmd_State(__D_C_State__) HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, (GPIO_PinState)(__D_C_State__))
#define OLED_RST_State(__RST_State__) HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, (GPIO_PinState)(__RST_State__))
#define LCD_PWR_State(__PWR_State__) HAL_GPIO_WritePin(LCD_PWR_GPIO_Port, LCD_PWR_Pin, (GPIO_PinState)(__PWR_State__))
int OLED_init();
void LcdWriteReg(uint8_t Data);
void LcdWriteData(uint8_t Data);
void LcdWriteDataMultiple(uint8_t * pData, uint32_t NumItems);
void OLED_WritePixel(uint16_t Xpos, uint16_t Ypos, uint16_t RGBCode);
void OLED_WriteLine(uint16_t Xpos, uint16_t Ypos, uint16_t *RGBCode, uint16_t pointNum);
void BSP_LCD_Clear(uint16_t Color);
void BSP_LCD_Clear_DMA(uint16_t Color);
void OLED_printf (uint8_t xpos,uint8_t ypos,char *fmt, ...);
#endif /* __oled_H */
oled.c
#include "stm32l4xx_hal.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "main.h"
#include "ASCII.h"
#include "oled.h"
extern SPI_HandleTypeDef hspi1;
//初始化命令集
static struct OLED_function OLED_cfg_script[] = {
{OLED_START, OLED_START},
{OLED_CMD, 0x11},
{OLED_DELAY, 120},
{OLED_CMD, 0x36},
{OLED_DATA, 0x00},
{OLED_CMD, 0x3a},
{OLED_DATA, 0x65},
{OLED_CMD, 0xb2},
{OLED_DATA, 0x0c},
{OLED_DATA, 0x0c},
{OLED_DATA, 0x00},
{OLED_DATA, 0x33},
{OLED_DATA, 0x33},
{OLED_CMD, 0xb7},
{OLED_DATA, 0x72},
{OLED_CMD, 0xbb},
{OLED_DATA, 0x3d},
{OLED_CMD, 0xc0},
{OLED_DATA, 0x2c},
{OLED_CMD, 0xc2},
{OLED_DATA, 0x01},
{OLED_CMD, 0xc3},
{OLED_DATA, 0x19},
{OLED_CMD, 0xc4},
{OLED_DATA, 0x20},
{OLED_CMD, 0xc6},
{OLED_DATA, 0x0f},
{OLED_CMD, 0xd0},
{OLED_DATA, 0xa4},
{OLED_DATA, 0xa1},
{OLED_CMD, 0xe0},
{OLED_DATA, 0x70},
{OLED_DATA, 0x04},
{OLED_DATA, 0x08},
{OLED_DATA, 0x09},
{OLED_DATA, 0x09},
{OLED_DATA, 0x05},
{OLED_DATA, 0x2a},
{OLED_DATA, 0x33},
{OLED_DATA, 0x41},
{OLED_DATA, 0x07},
{OLED_DATA, 0x13},
{OLED_DATA, 0x13},
{OLED_DATA, 0x29},
{OLED_DATA, 0x2f},
{OLED_CMD, 0xe1},
{OLED_DATA, 0x70},
{OLED_DATA, 0x03},
{OLED_DATA, 0x09},
{OLED_DATA, 0x0a},
{OLED_DATA, 0x09},
{OLED_DATA, 0x06},
{OLED_DATA, 0x2b},
{OLED_DATA, 0x34},
{OLED_DATA, 0x41},
{OLED_DATA, 0x07},
{OLED_DATA, 0x12},
{OLED_DATA, 0x14},
{OLED_DATA, 0x28},
{OLED_DATA, 0x2e},
{OLED_CMD, 0x21},
{OLED_CMD, 0x29},
{OLED_CMD, 0x2a},
{OLED_DATA, 0x00},
{OLED_DATA, 0x00},
{OLED_DATA, 0x00},
{OLED_DATA, 0xef},
{OLED_CMD, 0x2b},
{OLED_DATA, 0x00},
{OLED_DATA, 0x00},
{OLED_DATA, 0x00},
{OLED_DATA, 0xef},
{OLED_CMD, 0x2c},
{OLED_END, OLED_END},
};
static void OLED_run_cfg_script()
{
uint8_t data[2] = {0};
int i = 0;
int end_script = 0;
do {
switch (OLED_cfg_script[i].cmd) {
case OLED_START:
break;
case OLED_CMD:
data[0] = OLED_cfg_script[i].data & 0xff;
LcdWriteReg(data[0]);
break;
case OLED_DATA:
data[0] = OLED_cfg_script[i].data & 0xff;
LcdWriteData(data[0]);
break;
case OLED_DELAY:
HAL_Delay(OLED_cfg_script[i].data);
break;
case OLED_END:
end_script = 1;
}
i++;
} while (!end_script);
}
static void OLED_reset()
{
OLED_RST_State(0);
HAL_Delay(200);
OLED_RST_State(1);
HAL_Delay(150);
}
#define LCD_MAX_MEM16_BLOCK (1 << 6)
#define LCD_PIXEL_PER_BLOCK (LCD_MAX_MEM16_BLOCK >> 1)
static void spec_send_fb(uint16_t color, uint16_t pixel_num)
{
uint16_t real_mem[LCD_MAX_MEM16_BLOCK] = {0};
for (uint16_t i = 0; i < LCD_MAX_MEM16_BLOCK; ++i) {
real_mem[i] = color;
}
// HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, GPIO_PIN_SET);
Data_Cmd_State(GPIO_PIN_SET);
if (pixel_num <= LCD_MAX_MEM16_BLOCK) {
LcdWriteDataMultiple((uint8_t*)real_mem, pixel_num << 1);
} else {
uint16_t count = pixel_num / LCD_MAX_MEM16_BLOCK;
uint16_t remain = pixel_num % LCD_MAX_MEM16_BLOCK;
for (uint16_t i = 0; i < count; ++i) {
LcdWriteDataMultiple((uint8_t*)real_mem, LCD_MAX_MEM16_BLOCK << 1);
}
LcdWriteDataMultiple((uint8_t*)real_mem, remain << 1);
}
}
static void OLED_display_picture(void)
{
LcdWriteReg(OLED_RAMWR);
spec_send_fb(0x0, WIDTH * HEIGHT / 4);
spec_send_fb(0x1111, WIDTH * HEIGHT / 4);
spec_send_fb(0x7777, WIDTH * HEIGHT / 4);
spec_send_fb(0xeeee, WIDTH * HEIGHT / 4);
}
int OLED_init()
{
LCD_PWR_State(0);//回显关闭
OLED_reset();
OLED_run_cfg_script();
LCD_PWR_State(1);//回显开启
OLED_display_picture();
return HAL_OK;
}
void OLED_WriteReg(uint8_t Command, uint8_t *Parameters, uint8_t NbParameters)
{
/* Send command */
LcdWriteReg(Command);
/* Send command's parameters if any */
for (uint8_t i=0; i<NbParameters; i++)
{
LcdWriteData(Parameters[i]);
}
}
static void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
/* 指定X方向操作区域 */
LcdWriteReg(0x2a);
LcdWriteData(x1 >> 8);
LcdWriteData(x1);
LcdWriteData(x2 >> 8);
LcdWriteData(x2);
/* 指定Y方向操作区域 */
LcdWriteReg(0x2b);
LcdWriteData(y1 >> 8);
LcdWriteData(y1);
LcdWriteData(y2 >> 8);
LcdWriteData(y2);
/* 发送该命令,LCD开始等待接收显存数据 */
LcdWriteReg(0x2C);
}
void OLED_SetCursor(uint16_t Xpos, uint16_t Ypos)
{
LCD_Address_Set(Xpos,Ypos,WIDTH-1,Ypos);
}
void BSP_LCD_Clear(uint16_t Color)
{
uint8_t black_gui[480] = {0};
memset(black_gui, Color, sizeof(black_gui));
for (uint32_t counter = 0; counter < 240; counter++)
{
/* Set Cursor */
OLED_SetCursor(0, counter);
/* Prepare to write to LCD RAM */
OLED_WriteReg(0x2C, (uint8_t*)NULL, 0);
/* RAM write data command */
LcdWriteDataMultiple(black_gui, 480);
}
}
extern volatile uint8_t one_frame_done;
void BSP_LCD_Clear_DMA(uint16_t Color)
{
uint8_t black_gui_DMA[WIDTH*HEIGHT*2] = {0X00};
for(uint32_t i=0; i<WIDTH*HEIGHT; i++)
{
black_gui_DMA[2*i]=Color>>8;
black_gui_DMA[2*i+1] = Color;
}
printf("0X%02x%02x\r\n",black_gui_DMA[0],black_gui_DMA[1]);
//需要分两次写入HAL_SPI_Transmit_DMA写入缓存大小uint16_t(0xfff,65535)
one_frame_done = 0;
/* 指定显存操作地址为全屏*/
LCD_Address_Set(0, 0, WIDTH - 1, HEIGHT/2 - 1);
Data_Cmd_State(1);/* 指定接下来的数据为数据 */
/*DMA 写前半屏*/
HAL_SPI_Transmit_DMA(&hspi1,black_gui_DMA, WIDTH*HEIGHT);
while(!one_frame_done){/*release cpu and doing something else*/}
one_frame_done = 0;
/* 指定显存操作地址为全屏*/
LCD_Address_Set(0, HEIGHT/2, WIDTH - 1, HEIGHT - 1);
Data_Cmd_State(1);/* 指定接下来的数据为数据 */
/*DMA 写后半屏*/
HAL_SPI_Transmit_DMA(&hspi1,black_gui_DMA+WIDTH*HEIGHT, WIDTH*HEIGHT);
while(!one_frame_done){/*release cpu and doing something else*/}
}
//显示英文与数字8*16的ASCII码
void OLED_DISPLAY_8x16(uint8_t x, uint8_t y, uint16_t w){ //要显示汉字的编号
// printf("x=%d,y=%d,w=0X%04X w*16-512=%d\r\n",x,y,w,w*16-512);
uint16_t color = 0;
uint16_t textData[16][8]={0};
// uint8_t buffer[16][8]={0};
for(uint8_t i=0; i<16; i++)
{
uint16_t buf=ASCII_8x16[(w*16)+i-512];
// printf("0X%02X ",buf);
for(uint8_t j=0;j<8;j++){
// printf("%d ",(0x01&(buf>>j))>0?0x01:0x00);
// buffer[8*(i/8)+j][i%8]=(0x01&(buf>>j))>0?1:0;
color = (0x01&(buf>>j))>0?LCD_DISP_BLACK:LCD_DISP_BLUE;
textData[8*(i/8)+j][i%8] = color;
// OLED_WritePixel(x+i%8,y+8*(i/8)+j,color);//单像素渲染
}
// printf("\r\n");
}
for(uint8_t i=0; i<16; i++)
{
OLED_WriteLine(x,y+i,textData[i],8);//列像素渲染
// for(uint8_t j=0;j<8;j++){
// printf("%d",buffer[i][j]);
// }
// printf("\r\n");
}
}
//用于OLED_printf函数专用的显示程序
void OLED_printf_US(uint8_t xpos,uint8_t ypos,uint8_t *str,uint8_t i){
uint8_t r=0;
while(i != r){//i是长度值,当显示到i之后退出
OLED_DISPLAY_8x16(xpos+r*8,ypos,*str++);//显示英文与数字8*16的ASCII码
r++;
}
}
//OLED专用的printf函数
//调用方法:OLED_printf(0,0,"123");
//注意若xpos或ypos过大,并内容较多时,会出现溢出边界
#define line_size 29
#define row_size 16
void OLED_printf (uint8_t xpos,uint8_t ypos,char *fmt, ...)
{
char buff[line_size]; //用于存放转换后的数据 [长度],限定30字符长度,每个字符8像素宽度,屏幕宽240
uint16_t size=0;
uint16_t row = 1+strlen(fmt)/line_size;//暂时没考虑自带换行符号情况
// printf("row=%d\r\n",row);
for(uint16_t i=0;i<row;i++){
va_list arg_ptr;
va_start(arg_ptr,fmt);
vsnprintf(buff, line_size,fmt+i*line_size,arg_ptr);//数据转换
size=strlen(buff); //得出数据长度
if(strlen(buff)>line_size)size=line_size; //如果长度大于最大值,则长度等于最大值(多出部分忽略)
OLED_printf_US(xpos,ypos+i*row_size,(uint8_t *)buff,size);//最终调用OLED专用的printf函数来显示
va_end(arg_ptr);
}
}
void OLED_WritePixel(uint16_t Xpos, uint16_t Ypos, uint16_t data)
{
uint8_t dataB = 0;
/* Set Cursor */
OLED_SetCursor(Xpos, Ypos);
/* Prepare to write to LCD RAM */
OLED_WriteReg(0x2C, (uint8_t*)NULL, 0); /* RAM write data command */
/* Write RAM data */
dataB = (uint8_t)(data >> 8);
LcdWriteData(dataB);
dataB = (uint8_t)data;
LcdWriteData(dataB);
}
uint8_t endian_buffer[480];
void OLED_WriteLine(uint16_t Xpos, uint16_t Ypos, uint16_t *RGBCode, uint16_t pointNum)
{
/* Set Cursor */
OLED_SetCursor(Xpos, Ypos);
/* Prepare to write to LCD RAM */
OLED_WriteReg(0x2C, (uint8_t*)NULL, 0); /* RAM write data command */
for (uint16_t i = 0; i < pointNum; i++) {
endian_buffer[2*i] = (uint8_t)(RGBCode[i] >> 8);
endian_buffer[2*i + 1] = (uint8_t)RGBCode[i];
}
/* Write RAM data */
LcdWriteDataMultiple(endian_buffer, pointNum*2);
}
/********************************************************************
*
* LcdWriteReg
*
* Function description:
* Sets display register
*/
void LcdWriteReg(uint8_t Data)
{
Data_Cmd_State(0);
HAL_SPI_Transmit(&hspi1, &Data, 1, 10);
}
/********************************************************************
*
* LcdWriteData
*
* Function description:
* Writes a value to a display register
*/
void LcdWriteData(uint8_t Data)
{
Data_Cmd_State(1);
HAL_SPI_Transmit(&hspi1, &Data, 1, 10);
}
/********************************************************************
*
* LcdWriteDataMultiple
*
* Function description:
* Writes multiple values to a display register.
*/
void LcdWriteDataMultiple(uint8_t * pData, uint32_t NumItems)
{
Data_Cmd_State(1);
HAL_SPI_Transmit(&hspi1, pData, NumItems, 10);
}
ASCII.h源码前文已给出,mian.c部分源码:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dcmi.h"
#include "dma.h"
#include "i2c.h"
#include "usart.h"
#include "spi.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "../../ICore/key/key.h"
#include "../../ICore/led/led.h"
#include "../../ICore/print/print.h"
#include "../../ICore/usart/usart.h"
#include "../../ICore/oled/oled.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//volatile uint8_t data1[LCD_RAM_SIZE];
//volatile uint8_t data2[LCD_RAM_SIZE];
//extern volatile uint8_t one_frame_done;
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_LPUART1_UART_Init();
MX_SPI1_Init();
/* USER CODE BEGIN 2 */
ResetPrintInit(&hlpuart1);
HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
HLPUSART_RX_STA = 0;
OLED_init();
//
set_led0_val(0);
set_led1_val(get_key0_val());
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
HLPUSART_RX_STA=0;//接收错误,重新开始
HAL_Delay(100);//等待
}
if(KEY_0())
{
OLED_printf(10,10,"hello world to stm32!I am an embedded enthusiast.");
OLED_printf(10,48," STM32CubeIDE is an advanced C/C++ development platform "
"with peripheral configuration, code generation, code compilation, "
"and debug features for STM32 micro controllers and microprocessors.");
// OLED_printf(10,10,"!");
}
if(KEY_1())
{
BSP_LCD_Clear(0xF800);
printf("OLED_Clear\r\n");
}
if(KEY_2())
{
BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
printf("OLED_Clear_DMA\r\n");
}
Toggle_led0();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}