一、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屏的通信相关信息。本文是在一定基础上直接操作
假设已经做好了时钟配置、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 */
- }
复制代码
————————————————
版权声明:py_free-物联智能
如有侵权请联系删除
|