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

news/2024/5/2 19:46:40/文章来源:https://blog.csdn.net/py8105/article/details/128017976

一、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[] =  {            // ASCII0x00,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 00X00     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像素宽度,屏幕宽240uint16_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像素宽度,屏幕宽240uint16_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://www.luyixian.cn/news_show_36498.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

css-实现卡牌的发牌和翻转动画

场景描述&#xff1a; 打开抽卡界面&#xff0c;卡牌出现并发牌至固定的位置&#xff0c;此时展示的是卡牌的背面&#xff1b;用户点击卡牌时&#xff0c;卡牌进行翻转&#xff0c;并展示卡牌内容&#xff0c;或者发牌后自动进行翻转和展示。 本实例在页面挂载后自动播放动画&…

systemd 252 如预期的锁定了 Linux 引导过程

导读今天给大家介绍一下systemd 252锁定 Linux 引导过程systemd 252 如预期的锁定了 Linux 引导过程 之前&#xff0c;我们 报道 过&#xff0c;systemd 创始人发文指出 Linux 引导过程不安全&#xff0c;并提出采用加密签名的统一内核镜像&#xff08;UKI&#xff09;&#x…

linux搭建git服务器,windows客户端配置git

Linux服务器配置之Git服务器搭建步骤&#xff1a; 一、配置环境 1、服务器&#xff1a;CentOS 8.2&#xff08;64位&#xff09; Git &#xff08;version 2.27.0&#xff09; 2、客户端&#xff1a;Windows 10 &#xff08;64位&#xff09; Git&#xff08;version 2.38.…

Actipro Windows Forms Controls 22.1.3 注册版

Actipro Windows Forms Controls 窗体控件 一组用于构建漂亮的 Windows 窗体桌面应用程序的 UI 控件 语法编辑器 语法高亮代码编辑器控件和解析套件。 为您自己的应用程序带来类似于 Visual Studio 的强大代码编辑体验&#xff0c;以及流行代码编辑器中的所有高级功能。大多数流…

C#程序采用AOT发布,真的可以避免被反编译?

上次跟大家分享过&#xff0c;C#程序反编译与篡改代码的教程《C#程序发布时&#xff0c;一定要好好的保护&#xff0c;不然你会后悔的&#xff01;》&#xff0c;根据这个教程&#xff0c;我们都知道C#程序&#xff0c;发布后必须进行加密混淆&#xff0c;不然就是相当于源码直…

Redis基础命令(set类型)交集并集差集

目录 概述: 特征&#xff1a; Set常见命令&#xff1a; 1.Sadd key number..&#xff1a;向set中添加一个或多个元素 2.Srem key number...&#xff1a;移除set中指定的元素 3.Scard key&#xff1a;返回set中元素的个数 4.Sismember key member&#xff1a;判断一个元素…

html实现好看的导航主页(附源码)

文章目录1.设计来源1.1 主界面1.2 底部导航1.3 屏幕保护2.效果和源码2.1 动态效果2.2 源代码源码下载作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/128028326 html实现好看的导航主页(附源码) html实现好看的导航主页&…

小小王总,如何变成任正非、化腾、强东这样的巨人!

原创&#xff1a;小姐姐味道&#xff08;微信公众号ID&#xff1a;xjjdog&#xff09;&#xff0c;欢迎分享&#xff0c;非公众号转载保留此声明。王总特别迷信外面的企业培训。当遇到问题时&#xff0c;他喜欢去取经。这个经不像唐僧取经一样&#xff0c;需要历经九九八十一难…

谱本征正交分解 (SPOD)附matlab代码

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

python下载安装教程

1、下载python 下载地址&#xff1a;https://www.python.org/&#xff0c;点击Downloads,选择对应电脑系统&#xff0c;进行下载。 &#xff08;文末送读者福利&#xff09; 2、安装python&#xff0c;以python3.10.7安装为例。 &#xff08;1&#xff09;双击安装包&#x…

[ Linux ] 进程间通信之共享内存

在上篇博文我们了解了通过管道完成进程间通信&#xff0c;我们了解匿名管道和命名管道&#xff0c;并且通过编码模拟实现使用了匿名管道和命名管道。我们知道要让进程间完成通信必须让这两个进程首先看到同一份资源&#xff0c;因此给予这个前提&#xff0c;本篇博文我们了解另…

Java 内存溢出(二)使用 MAT 分析 .hprof 内存映像文件

目录一、内存溢出时自动导出 .hprof 文件二、下载安装 MAT三、启动 MAT四、MAT 分析 hprof 文件1.Overview 概览2.Leak Suspects 溢出原因猜测3.Histogram 对象实例数量排序4.Dominator Tree 支配树.hprof 文件&#xff1a; 是 java 项目的 Heap Dump 文件&#xff0c;也叫内存…

基于JAVA的新闻发布管理系统开发参考【数据库设计、源码、开题报告】

数据库脚本下载地址&#xff1a; https://download.csdn.net/download/itrjxxs_com/86427655 目的 本系统的目的是实现新闻发布系统的基本功能。新闻发布系统提供了不同类型新闻&#xff08;如社会新闻、娱乐新闻和技术前沿新闻等&#xff09; 满足不同用户需求&#xff1b;系…

简述供应商管理SRM系统

简道云SRM管理系统供应商关系管理(SRM系统)&#xff0c;是企业可以用来对供应商的优势和能力进行系统的、全行业范围的评估&#xff0c;涉及企业整体的商业战略&#xff0c;供应商寻源、采购审批、比价、招投标管理、订单执行、库存可视化管理、财务支付审批对账、供应商绩效评…

Steam项目推进 (一) ——项目情况简述

一、前言 之前跟一个策划朋友一起做过一个小项目Demo&#xff0c;然后中止了大半年&#xff0c;现在想继续把这个项目推进下去。又怕自制力不够&#xff0c;所以建立一个栏目来记录这个项目的推进情况&#xff0c;记忆在项目中学习到的东西。 二、目前的项目情况 1、项目定位…

Flutter高仿微信-第32篇-单聊-语音

Flutter高仿微信系列共59篇&#xff0c;从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。 详情请查看 效果图&#xff1a; 详情请参考 Flutter高仿微信-第29篇-单聊 &#xff0c; 这里只是提取语音聊天实…

基于遥感和GIS技术的生态承载力评价的解决方案

生态承载力的概念最早来自于生态学。1921年&#xff0c;Park和Burgess在生态学领域中首次应用了生态承载力的概念&#xff0c;即在某一特定环境条件下&#xff08;主要指生存空间、营养物质、阳光等生态因子的组合&#xff09;&#xff0c;某种个体存在数量的最高极限。生态承载…

APP到底有没有权限-恶意拷贝删除照片

作者&#xff1a;黑蛋 近期发生了一件比较恶劣的事情&#xff0c;某客户在某物上面买了一件东西&#xff0c;但是这个东西是假货&#xff0c;所以客户致电某物人工客服&#xff0c;并进行了录音&#xff0c;这时候某物试图通过自身的客户端软件&#xff0c;去删除客户手机上的…

Python字符串及正则表达式

一&#xff1a; 字符串可以用单引号或双引号来创建&#xff0c;也可以用三引号来创建多行字符串。 String_1 加快发展 String_2 "促进生产" String_3 """吾家吾国&#xff0c; 吾家吾国&#xff0c; 吾家吾国&#xff0c; 吾家吾国。""…

Principal branch

In mathematics, a principal branch is a function which selects one branch (“slice”) of a multi-valued function. Most often, this applies to functions defined on the complex plane. Contents1 Examples1.1 Trigonometric inverses1.2 Exponentiation to fraction…