cubeIDE开发, stm32的OLED点亮及字符显示设计(基于SPI通信)

news/2024/7/7 20:40:20

一、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】将创建屏幕打印函数,输出显示字符串能力

        首选了解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

        将划分为前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”的绘制背景色,就能显示字符

        显然,在代码实现中,每个字符就是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");
	}

        测试“!”输出如下,妥妥的一个!:

 【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 */
}


http://lihuaxi.xjx100.cn/news/176009.html

相关文章

Linux——进程间通信(共享内存)

一、共享内存 1、定义 共享内存为多个进程之间共享和传递数据提供了一种有效的方式。共享内存是先在物理内存上申请一块空间&#xff0c;多个进程可以将其映射到自己的虚拟地址空间中。所有进程都可以访问共享内存中的地址&#xff0c;就好像它们是由malloc分配的一样。如果某…

vue3 框架学习概念笔记

文章目录前情提要框架设计概览命令式声明式小结虚拟dom性能运行时和编译时框架设计核心要素声明式描述UI渲染器组件的本质vue.js模板响应式系统概念完善的响应式系统响应式系统的调度计算属性Computedwatch 原理竞态问题非原始值的响应式方案javaScript 对象原始值的响应式方案…

双软认证需要什么条件

双软认证需要什么条件 双软认证一般指的是软件产品登记和软件企业认证。如果要申请软件企业认证则首先要进行软件产品检测和软件产品登记。成为“软件企业”应当包含四个程序&#xff1a;“软件著作权登记”、“软件产品测试”、“软件产品评估”、“软件企业评估”。 软件产…

《机械工程基础》复习题

一、填空题&#xff1a; 1. 构件由于受力不同&#xff0c;会产生不同的变形。基本形式有以下五种&#xff1a;1. 弯曲 &#xff1b;2. 扭转 &#xff1b; 3. 剪切 &#xff1b;4. 轴向拉伸 &#xff1b;5. 轴向压缩 。 2. 在机器中&#xff0c;运动的基本单元称之为__机构_ ___…

Caffeine《一》

《Caffeine&#xff08;Java顶级缓存组件&#xff09;一》 提示: 本材料只做个人学习参考,不作为系统的学习流程,请注意识别!!! 《Caffeine&#xff08;Java顶级缓存组件&#xff09;》《Caffeine&#xff08;Java顶级缓存组件&#xff09;一》1. Caffeine缓存概念1.1 缓存的分…

Red Eye Camera开发日记之API 移植I2C 和关键接口函数

API 说明文件里面有官方的移植指导&#xff0c;但我觉得可以把重点放在与 MLX90640 具体操作有关的几个函数上&#xff0c;而与标准 I2C 相关的函数和文件结构还是按照自己习惯的套路实现。这样更符合我们开发人员的可控性的习惯。步骤如下&#xff1a; 建立标准 I2C 文件 IIC…

【电力系统】CJAYA算法优化光伏模型SDM参数附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

java毕业生设计医院住院综合服务管理系统计算机源码+系统+mysql+调试部署+lw

java毕业生设计医院住院综合服务管理系统计算机源码系统mysql调试部署lw java毕业生设计医院住院综合服务管理系统计算机源码系统mysql调试部署lw本源码技术栈&#xff1a; 项目架构&#xff1a;B/S架构 开发语言&#xff1a;Java语言 开发软件&#xff1a;idea eclipse 前端技…