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


    目录

    一、SPI 通信技术

    二、SPI通信参配

     三、SPI设置(cubeMx)

            1)开启SPI1

             2)添加SPI1的DMA支持

             3)开启spi1的中断功能

             4)设置oled屏控制的辅助引脚、设置名称及调整配置参数

             5)调整SPI1配置参数

             6)完成配置后,点击保存或生成代码的按钮生成代码

    四、SPI通信及OLED屏点亮

            1)辅助引脚设值

            2)spi1写入数据实现

            3)OLED初始化指令执行

            4)oled初始化

            5)oled屏幕的亮屏(条形码显示)代码

            6)驱动调用

            7)编译加载到开发板上

             8)屏幕效果点亮如下:

     五、屏幕输出字符串设计

            【1】字符串编码表

    【2】将创建屏幕打印函数,输出显示字符串能力

     【3】字符串olde输出

    【4】SPI的直接输出和DMA输出区别实现

    六、程序编译及测试

     七、附件


    一、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. //对该位写1或0
    2. #define Data_Cmd_State(__D_C_State__) HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, (GPIO_PinState)(__D_C_State__))
    3. #define OLED_RST_State(__RST_State__) HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, (GPIO_PinState)(__RST_State__))
    4. #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函数一次写入多个数据

    1. /********************************************************************
    2. *
    3. * LcdWriteReg
    4. *
    5. * Function description:
    6. * Sets display register
    7. */
    8. void LcdWriteReg(uint8_t Data)
    9. {
    10. Data_Cmd_State(0);
    11. HAL_SPI_Transmit(&hspi1, &Data, 1, 10);
    12. }
    13. /********************************************************************
    14. *
    15. * LcdWriteData
    16. *
    17. * Function description:
    18. * Writes a value to a display register
    19. */
    20. void LcdWriteData(uint8_t Data)
    21. {
    22. Data_Cmd_State(1);
    23. HAL_SPI_Transmit(&hspi1, &Data, 1, 10);
    24. }
    25. /********************************************************************
    26. *
    27. * LcdWriteDataMultiple
    28. *
    29. * Function description:
    30. * Writes multiple values to a display register.
    31. */
    32. void LcdWriteDataMultiple(uint8_t * pData, uint32_t NumItems)
    33. {
    34. Data_Cmd_State(1);
    35. HAL_SPI_Transmit(&hspi1, pData, NumItems, 10);
    36. }

            3)OLED初始化指令执行

            定义OLED屏的初始化指令集及指令设置函数

    1. /* Init script function */
    2. struct OLED_function {
    3. uint8_t cmd;
    4. uint16_t data;
    5. };
    6. /* Init script commands */
    7. enum OLED_cmd {
    8. OLED_START,
    9. OLED_END,
    10. OLED_CMD,
    11. OLED_DATA,
    12. OLED_DELAY
    13. };
    14. //以上可放置头文件中
    15. //以下放置源文件中
    16. //初始化命令集
    17. static struct OLED_function OLED_cfg_script[] = {
    18. {OLED_START, OLED_START},
    19. {OLED_CMD, 0x11},
    20. {OLED_DELAY, 120},
    21. {OLED_CMD, 0x36},
    22. {OLED_DATA, 0x00},
    23. {OLED_CMD, 0x3a},
    24. {OLED_DATA, 0x65},
    25. {OLED_CMD, 0xb2},
    26. {OLED_DATA, 0x0c},
    27. {OLED_DATA, 0x0c},
    28. {OLED_DATA, 0x00},
    29. {OLED_DATA, 0x33},
    30. {OLED_DATA, 0x33},
    31. {OLED_CMD, 0xb7},
    32. {OLED_DATA, 0x72},
    33. {OLED_CMD, 0xbb},
    34. {OLED_DATA, 0x3d},
    35. {OLED_CMD, 0xc0},
    36. {OLED_DATA, 0x2c},
    37. {OLED_CMD, 0xc2},
    38. {OLED_DATA, 0x01},
    39. {OLED_CMD, 0xc3},
    40. {OLED_DATA, 0x19},
    41. {OLED_CMD, 0xc4},
    42. {OLED_DATA, 0x20},
    43. {OLED_CMD, 0xc6},
    44. {OLED_DATA, 0x0f},
    45. {OLED_CMD, 0xd0},
    46. {OLED_DATA, 0xa4},
    47. {OLED_DATA, 0xa1},
    48. {OLED_CMD, 0xe0},
    49. {OLED_DATA, 0x70},
    50. {OLED_DATA, 0x04},
    51. {OLED_DATA, 0x08},
    52. {OLED_DATA, 0x09},
    53. {OLED_DATA, 0x09},
    54. {OLED_DATA, 0x05},
    55. {OLED_DATA, 0x2a},
    56. {OLED_DATA, 0x33},
    57. {OLED_DATA, 0x41},
    58. {OLED_DATA, 0x07},
    59. {OLED_DATA, 0x13},
    60. {OLED_DATA, 0x13},
    61. {OLED_DATA, 0x29},
    62. {OLED_DATA, 0x2f},
    63. {OLED_CMD, 0xe1},
    64. {OLED_DATA, 0x70},
    65. {OLED_DATA, 0x03},
    66. {OLED_DATA, 0x09},
    67. {OLED_DATA, 0x0a},
    68. {OLED_DATA, 0x09},
    69. {OLED_DATA, 0x06},
    70. {OLED_DATA, 0x2b},
    71. {OLED_DATA, 0x34},
    72. {OLED_DATA, 0x41},
    73. {OLED_DATA, 0x07},
    74. {OLED_DATA, 0x12},
    75. {OLED_DATA, 0x14},
    76. {OLED_DATA, 0x28},
    77. {OLED_DATA, 0x2e},
    78. {OLED_CMD, 0x21},
    79. {OLED_CMD, 0x29},
    80. {OLED_CMD, 0x2a},
    81. {OLED_DATA, 0x00},
    82. {OLED_DATA, 0x00},
    83. {OLED_DATA, 0x00},
    84. {OLED_DATA, 0xef},
    85. {OLED_CMD, 0x2b},
    86. {OLED_DATA, 0x00},
    87. {OLED_DATA, 0x00},
    88. {OLED_DATA, 0x00},
    89. {OLED_DATA, 0xef},
    90. {OLED_CMD, 0x2c},
    91. {OLED_END, OLED_END},
    92. };
    93. static void OLED_run_cfg_script()
    94. {
    95. uint8_t data[2] = {0};
    96. int i = 0;
    97. int end_script = 0;
    98. do {
    99. switch (OLED_cfg_script[i].cmd) {
    100. case OLED_START:
    101. break;
    102. case OLED_CMD:
    103. data[0] = OLED_cfg_script[i].data & 0xff;
    104. LcdWriteReg(data[0]);
    105. break;
    106. case OLED_DATA:
    107. data[0] = OLED_cfg_script[i].data & 0xff;
    108. LcdWriteData(data[0]);
    109. break;
    110. case OLED_DELAY:
    111. HAL_Delay(OLED_cfg_script[i].data);
    112. break;
    113. case OLED_END:
    114. end_script = 1;
    115. }
    116. i++;
    117. } while (!end_script);
    118. }

            4)oled初始化

            进行oled屏的初始化函数设计

    1. //olde屏重置
    2. static void OLED_reset()
    3. {
    4. OLED_RST_State(0);
    5. HAL_Delay(200);
    6. OLED_RST_State(1);
    7. HAL_Delay(150);
    8. }
    9. //oled屏初始化
    10. int OLED_init()
    11. {
    12. LCD_PWR_State(0);//回显关闭
    13. OLED_reset(); //屏重置
    14. OLED_run_cfg_script();//初始化指令集依次执行脚本函数
    15. LCD_PWR_State(1);//回显开启
    16. OLED_display_picture();//条形码屏幕显示测试
    17. return HAL_OK;
    18. }

            5)oled屏幕的亮屏(条形码显示)代码

    1. #define LCD_MAX_MEM16_BLOCK (1 << 6)
    2. #define LCD_PIXEL_PER_BLOCK (LCD_MAX_MEM16_BLOCK >> 1)
    3. static void spec_send_fb(uint16_t color, uint16_t pixel_num)
    4. {
    5. uint16_t real_mem[LCD_MAX_MEM16_BLOCK] = {0};
    6. for (uint16_t i = 0; i < LCD_MAX_MEM16_BLOCK; ++i) {
    7. real_mem[i] = color;
    8. }
    9. Data_Cmd_State(GPIO_PIN_SET);
    10. if (pixel_num <= LCD_MAX_MEM16_BLOCK) {
    11. LcdWriteDataMultiple((uint8_t*)real_mem, pixel_num << 1);
    12. } else {
    13. uint16_t count = pixel_num / LCD_MAX_MEM16_BLOCK;
    14. uint16_t remain = pixel_num % LCD_MAX_MEM16_BLOCK;
    15. for (uint16_t i = 0; i < count; ++i) {
    16. LcdWriteDataMultiple((uint8_t*)real_mem, LCD_MAX_MEM16_BLOCK << 1);
    17. }
    18. LcdWriteDataMultiple((uint8_t*)real_mem, remain << 1);
    19. }
    20. }
    21. static void OLED_display_picture(void)
    22. {
    23. LcdWriteReg(OLED_RAMWR);
    24. spec_send_fb(0x0, WIDTH * HEIGHT / 4);
    25. spec_send_fb(0x1111, WIDTH * HEIGHT / 4);
    26. spec_send_fb(0x7777, WIDTH * HEIGHT / 4);
    27. spec_send_fb(0xeeee, WIDTH * HEIGHT / 4);
    28. }

            6)驱动调用

            基本功能完成后,在main.c文件实现函数调用

    1. /* USER CODE BEGIN Includes */
    2. #include "../../ICore/key/key.h"
    3. #include "../../ICore/led/led.h"
    4. #include "../../ICore/print/print.h"
    5. #include "../../ICore/usart/usart.h"
    6. #include "../../ICore/oled/oled.h"
    7. /* USER CODE END Includes */
    8. ..............................................
    9. /* USER CODE BEGIN 2 */
    10. ResetPrintInit(&hlpuart1);
    11. HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
    12. HLPUSART_RX_STA = 0;
    13. OLED_init();
    14. set_led0_val(0);
    15. set_led1_val(get_key0_val());
    16. /* USER CODE END 2 */
    17. /* USER CODE BEGIN WHILE */
    18. while (1)
    19. {
    20. Toggle_led0();
    21. /* USER CODE END WHILE */

            7)编译加载到开发板上

             8)屏幕效果点亮如下:

     五、屏幕输出字符串设计

            【1】字符串编码表

            在ICore/oled目下字符串编码表ASCII.h,具体如下:

    1. /*
    2. * ASCII.h
    3. *
    4. * Created on: Jun 11, 2022
    5. * Author: Administrator
    6. */
    7. #ifndef OLED_ASCII_H_
    8. #define OLED_ASCII_H_
    9. // ------------------ ASCII字模的数据表 ------------------------ //
    10. // 码表从0x20~0x7e //
    11. // 字库: 纵向取模下高位// (调用时要减512)
    12. // -------------------------------------------------------------- //
    13. const uint8_t ASCII_8x16[] = { // ASCII
    14. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // - -
    15. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    16. 0x00,0x00,0x38,0xFC,0xFC,0x38,0x00,0x00, // -!-
    17. 0x00,0x00,0x00,0x0D,0x0D,0x00,0x00,0x00,
    18. 0x00,0x0E,0x1E,0x00,0x00,0x1E,0x0E,0x00, // -"-
    19. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    20. 0x20,0xF8,0xF8,0x20,0xF8,0xF8,0x20,0x00, // -#-
    21. 0x02,0x0F,0x0F,0x02,0x0F,0x0F,0x02,0x00,
    22. 0x38,0x7C,0x44,0x47,0x47,0xCC,0x98,0x00, // -$-
    23. 0x03,0x06,0x04,0x1C,0x1C,0x07,0x03,0x00,
    24. 0x30,0x30,0x00,0x80,0xC0,0x60,0x30,0x00, // -%-
    25. 0x0C,0x06,0x03,0x01,0x00,0x0C,0x0C,0x00,
    26. 0x80,0xD8,0x7C,0xE4,0xBC,0xD8,0x40,0x00, // -&-
    27. 0x07,0x0F,0x08,0x08,0x07,0x0F,0x08,0x00,
    28. 0x00,0x10,0x1E,0x0E,0x00,0x00,0x00,0x00, // -'-
    29. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    30. 0x00,0x00,0xF0,0xF8,0x0C,0x04,0x00,0x00, // -(-
    31. 0x00,0x00,0x03,0x07,0x0C,0x08,0x00,0x00,
    32. 0x00,0x00,0x04,0x0C,0xF8,0xF0,0x00,0x00, // -)-
    33. 0x00,0x00,0x08,0x0C,0x07,0x03,0x00,0x00,
    34. 0x80,0xA0,0xE0,0xC0,0xC0,0xE0,0xA0,0x80, // -*-
    35. 0x00,0x02,0x03,0x01,0x01,0x03,0x02,0x00,
    36. 0x00,0x80,0x80,0xE0,0xE0,0x80,0x80,0x00, // -+-
    37. 0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00,
    38. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // -,-
    39. 0x00,0x00,0x10,0x1E,0x0E,0x00,0x00,0x00,
    40. 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00, // ---
    41. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    42. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // -.-
    43. 0x00,0x00,0x00,0x0C,0x0C,0x00,0x00,0x00,
    44. 0x00,0x00,0x00,0x80,0xC0,0x60,0x30,0x00, // -/-
    45. 0x0C,0x06,0x03,0x01,0x00,0x00,0x00,0x00,
    46. 0xF8,0xFC,0x04,0xC4,0x24,0xFC,0xF8,0x00, // -0-
    47. 0x07,0x0F,0x09,0x08,0x08,0x0F,0x07,0x00,
    48. 0x00,0x10,0x18,0xFC,0xFC,0x00,0x00,0x00, // -1-
    49. 0x00,0x08,0x08,0x0F,0x0F,0x08,0x08,0x00,
    50. 0x08,0x0C,0x84,0xC4,0x64,0x3C,0x18,0x00, // -2-
    51. 0x0E,0x0F,0x09,0x08,0x08,0x0C,0x0C,0x00,
    52. 0x08,0x0C,0x44,0x44,0x44,0xFC,0xB8,0x00, // -3-
    53. 0x04,0x0C,0x08,0x08,0x08,0x0F,0x07,0x00,
    54. 0xC0,0xE0,0xB0,0x98,0xFC,0xFC,0x80,0x00, // -4-
    55. 0x00,0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,
    56. 0x7C,0x7C,0x44,0x44,0xC4,0xC4,0x84,0x00, // -5-
    57. 0x04,0x0C,0x08,0x08,0x08,0x0F,0x07,0x00,
    58. 0xF0,0xF8,0x4C,0x44,0x44,0xC0,0x80,0x00, // -6-
    59. 0x07,0x0F,0x08,0x08,0x08,0x0F,0x07,0x00,
    60. 0x0C,0x0C,0x04,0x84,0xC4,0x7C,0x3C,0x00, // -7-
    61. 0x00,0x00,0x0F,0x0F,0x00,0x00,0x00,0x00,
    62. 0xB8,0xFC,0x44,0x44,0x44,0xFC,0xB8,0x00, // -8-
    63. 0x07,0x0F,0x08,0x08,0x08,0x0F,0x07,0x00,
    64. 0x38,0x7C,0x44,0x44,0x44,0xFC,0xF8,0x00, // -9-
    65. 0x00,0x08,0x08,0x08,0x0C,0x07,0x03,0x00,
    66. 0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00, // -:-
    67. 0x00,0x00,0x00,0x06,0x06,0x00,0x00,0x00,
    68. 0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00, // -;-
    69. 0x00,0x00,0x08,0x0E,0x06,0x00,0x00,0x00,
    70. 0x00,0x80,0xC0,0x60,0x30,0x18,0x08,0x00, // -<-
    71. 0x00,0x00,0x01,0x03,0x06,0x0C,0x08,0x00,
    72. 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00, // -=-
    73. 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x00,
    74. 0x00,0x08,0x18,0x30,0x60,0xC0,0x80,0x00, // ->-
    75. 0x00,0x08,0x0C,0x06,0x03,0x01,0x00,0x00,
    76. 0x18,0x1C,0x04,0xC4,0xE4,0x3C,0x18,0x00, // -?-
    77. 0x00,0x00,0x00,0x0D,0x0D,0x00,0x00,0x00,
    78. 0xF0,0xF8,0x08,0xC8,0xC8,0xF8,0xF0,0x00, // -@-
    79. 0x07,0x0F,0x08,0x0B,0x0B,0x0B,0x01,0x00,
    80. 0xE0,0xF0,0x98,0x8C,0x98,0xF0,0xE0,0x00, // -A-
    81. 0x0F,0x0F,0x00,0x00,0x00,0x0F,0x0F,0x00,
    82. 0x04,0xFC,0xFC,0x44,0x44,0xFC,0xB8,0x00, // -B-
    83. 0x08,0x0F,0x0F,0x08,0x08,0x0F,0x07,0x00,
    84. 0xF0,0xF8,0x0C,0x04,0x04,0x0C,0x18,0x00, // -C-
    85. 0x03,0x07,0x0C,0x08,0x08,0x0C,0x06,0x00,
    86. 0x04,0xFC,0xFC,0x04,0x0C,0xF8,0xF0,0x00, // -D-
    87. 0x08,0x0F,0x0F,0x08,0x0C,0x07,0x03,0x00,
    88. 0x04,0xFC,0xFC,0x44,0xE4,0x0C,0x1C,0x00, // -E-
    89. 0x08,0x0F,0x0F,0x08,0x08,0x0C,0x0E,0x00,
    90. 0x04,0xFC,0xFC,0x44,0xE4,0x0C,0x1C,0x00, // -F-
    91. 0x08,0x0F,0x0F,0x08,0x00,0x00,0x00,0x00,
    92. 0xF0,0xF8,0x0C,0x84,0x84,0x8C,0x98,0x00, // -G-
    93. 0x03,0x07,0x0C,0x08,0x08,0x07,0x0F,0x00,
    94. 0xFC,0xFC,0x40,0x40,0x40,0xFC,0xFC,0x00, // -H-
    95. 0x0F,0x0F,0x00,0x00,0x00,0x0F,0x0F,0x00,
    96. 0x00,0x00,0x04,0xFC,0xFC,0x04,0x00,0x00, // -I-
    97. 0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,
    98. 0x00,0x00,0x00,0x04,0xFC,0xFC,0x04,0x00, // -J-
    99. 0x07,0x0F,0x08,0x08,0x0F,0x07,0x00,0x00,
    100. 0x04,0xFC,0xFC,0xC0,0xF0,0x3C,0x0C,0x00, // -K-
    101. 0x08,0x0F,0x0F,0x00,0x01,0x0F,0x0E,0x00,
    102. 0x04,0xFC,0xFC,0x04,0x00,0x00,0x00,0x00, // -L-
    103. 0x08,0x0F,0x0F,0x08,0x08,0x0C,0x0E,0x00,
    104. 0xFC,0xFC,0x38,0x70,0x38,0xFC,0xFC,0x00, // -M-
    105. 0x0F,0x0F,0x00,0x00,0x00,0x0F,0x0F,0x00,
    106. 0xFC,0xFC,0x38,0x70,0xE0,0xFC,0xFC,0x00, // -N-
    107. 0x0F,0x0F,0x00,0x00,0x00,0x0F,0x0F,0x00,
    108. 0xF0,0xF8,0x0C,0x04,0x0C,0xF8,0xF0,0x00, // -O-
    109. 0x03,0x07,0x0C,0x08,0x0C,0x07,0x03,0x00,
    110. 0x04,0xFC,0xFC,0x44,0x44,0x7C,0x38,0x00, // -P-
    111. 0x08,0x0F,0x0F,0x08,0x00,0x00,0x00,0x00,
    112. 0xF8,0xFC,0x04,0x04,0x04,0xFC,0xF8,0x00, // -Q-
    113. 0x07,0x0F,0x08,0x0E,0x3C,0x3F,0x27,0x00,
    114. 0x04,0xFC,0xFC,0x44,0xC4,0xFC,0x38,0x00, // -R-
    115. 0x08,0x0F,0x0F,0x00,0x00,0x0F,0x0F,0x00,
    116. 0x18,0x3C,0x64,0x44,0xC4,0x9C,0x18,0x00, // -S-
    117. 0x06,0x0E,0x08,0x08,0x08,0x0F,0x07,0x00,
    118. 0x00,0x1C,0x0C,0xFC,0xFC,0x0C,0x1C,0x00, // -T-
    119. 0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,
    120. 0xFC,0xFC,0x00,0x00,0x00,0xFC,0xFC,0x00, // -U-
    121. 0x07,0x0F,0x08,0x08,0x08,0x0F,0x07,0x00,
    122. 0xFC,0xFC,0x00,0x00,0x00,0xFC,0xFC,0x00, // -V-
    123. 0x01,0x03,0x06,0x0C,0x06,0x03,0x01,0x00,
    124. 0xFC,0xFC,0x00,0x80,0x00,0xFC,0xFC,0x00, // -W-
    125. 0x03,0x0F,0x0E,0x03,0x0E,0x0F,0x03,0x00,
    126. 0x0C,0x3C,0xF0,0xC0,0xF0,0x3C,0x0C,0x00, // -X-
    127. 0x0C,0x0F,0x03,0x00,0x03,0x0F,0x0C,0x00,
    128. 0x00,0x3C,0x7C,0xC0,0xC0,0x7C,0x3C,0x00, // -Y-
    129. 0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,
    130. 0x1C,0x0C,0x84,0xC4,0x64,0x3C,0x1C,0x00, // -Z-
    131. 0x0E,0x0F,0x09,0x08,0x08,0x0C,0x0E,0x00,
    132. 0x00,0x00,0xFC,0xFC,0x04,0x04,0x00,0x00, // -[-
    133. 0x00,0x00,0x0F,0x0F,0x08,0x08,0x00,0x00,
    134. 0x38,0x70,0xE0,0xC0,0x80,0x00,0x00,0x00, // -\-
    135. 0x00,0x00,0x00,0x01,0x03,0x07,0x0E,0x00,
    136. 0x00,0x00,0x04,0x04,0xFC,0xFC,0x00,0x00, // -]-
    137. 0x00,0x00,0x08,0x08,0x0F,0x0F,0x00,0x00,
    138. 0x08,0x0C,0x06,0x03,0x06,0x0C,0x08,0x00, // -^-
    139. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    140. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // -_-
    141. 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
    142. 0x00,0x00,0x03,0x07,0x04,0x00,0x00,0x00, // -`-
    143. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    144. 0x00,0xA0,0xA0,0xA0,0xE0,0xC0,0x00,0x00, // -a-
    145. 0x07,0x0F,0x08,0x08,0x07,0x0F,0x08,0x00,
    146. 0x04,0xFC,0xFC,0x20,0x60,0xC0,0x80,0x00, // -b-
    147. 0x08,0x0F,0x07,0x08,0x08,0x0F,0x07,0x00,
    148. 0xC0,0xE0,0x20,0x20,0x20,0x60,0x40,0x00, // -c-
    149. 0x07,0x0F,0x08,0x08,0x08,0x0C,0x04,0x00,
    150. 0x80,0xC0,0x60,0x24,0xFC,0xFC,0x00,0x00, // -d-
    151. 0x07,0x0F,0x08,0x08,0x07,0x0F,0x08,0x00,
    152. 0xC0,0xE0,0xA0,0xA0,0xA0,0xE0,0xC0,0x00, // -e-
    153. 0x07,0x0F,0x08,0x08,0x08,0x0C,0x04,0x00,
    154. 0x40,0xF8,0xFC,0x44,0x0C,0x18,0x00,0x00, // -f-
    155. 0x08,0x0F,0x0F,0x08,0x00,0x00,0x00,0x00,
    156. 0xC0,0xE0,0x20,0x20,0xC0,0xE0,0x20,0x00, // -g-
    157. 0x27,0x6F,0x48,0x48,0x7F,0x3F,0x00,0x00,
    158. 0x04,0xFC,0xFC,0x40,0x20,0xE0,0xC0,0x00, // -h-
    159. 0x08,0x0F,0x0F,0x00,0x00,0x0F,0x0F,0x00,
    160. 0x00,0x00,0x20,0xEC,0xEC,0x00,0x00,0x00, // -i-
    161. 0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,
    162. 0x00,0x00,0x00,0x00,0x20,0xEC,0xEC,0x00, // -j-
    163. 0x00,0x30,0x70,0x40,0x40,0x7F,0x3F,0x00,
    164. 0x04,0xFC,0xFC,0x80,0xC0,0x60,0x20,0x00, // -k-
    165. 0x08,0x0F,0x0F,0x01,0x03,0x0E,0x0C,0x00,
    166. 0x00,0x00,0x04,0xFC,0xFC,0x00,0x00,0x00, // -l-
    167. 0x00,0x00,0x08,0x0F,0x0F,0x08,0x00,0x00,
    168. 0xE0,0xE0,0x60,0xC0,0x60,0xE0,0xC0,0x00, // -m-
    169. 0x0F,0x0F,0x00,0x0F,0x00,0x0F,0x0F,0x00,
    170. 0x20,0xE0,0xC0,0x20,0x20,0xE0,0xC0,0x00, // -n-
    171. 0x00,0x0F,0x0F,0x00,0x00,0x0F,0x0F,0x00,
    172. 0xC0,0xE0,0x20,0x20,0x20,0xE0,0xC0,0x00, // -o-
    173. 0x07,0x0F,0x08,0x08,0x08,0x0F,0x07,0x00,
    174. 0x20,0xE0,0xC0,0x20,0x20,0xE0,0xC0,0x00, // -p-
    175. 0x40,0x7F,0x7F,0x48,0x08,0x0F,0x07,0x00,
    176. 0xC0,0xE0,0x20,0x20,0xC0,0xE0,0x20,0x00, // -q-
    177. 0x07,0x0F,0x08,0x48,0x7F,0x7F,0x40,0x00,
    178. 0x20,0xE0,0xC0,0x60,0x20,0x60,0xC0,0x00, // -r-
    179. 0x08,0x0F,0x0F,0x08,0x00,0x00,0x00,0x00,
    180. 0x40,0xE0,0xA0,0x20,0x20,0x60,0x40,0x00, // -s-
    181. 0x04,0x0C,0x09,0x09,0x0B,0x0E,0x04,0x00,
    182. 0x20,0x20,0xF8,0xFC,0x20,0x20,0x00,0x00, // -t-
    183. 0x00,0x00,0x07,0x0F,0x08,0x0C,0x04,0x00,
    184. 0xE0,0xE0,0x00,0x00,0xE0,0xE0,0x00,0x00, // -u-
    185. 0x07,0x0F,0x08,0x08,0x07,0x0F,0x08,0x00,
    186. 0x00,0xE0,0xE0,0x00,0x00,0xE0,0xE0,0x00, // -v-
    187. 0x00,0x03,0x07,0x0C,0x0C,0x07,0x03,0x00,
    188. 0xE0,0xE0,0x00,0x00,0x00,0xE0,0xE0,0x00, // -w-
    189. 0x07,0x0F,0x0C,0x07,0x0C,0x0F,0x07,0x00,
    190. 0x20,0x60,0xC0,0x80,0xC0,0x60,0x20,0x00, // -x-
    191. 0x08,0x0C,0x07,0x03,0x07,0x0C,0x08,0x00,
    192. 0xE0,0xE0,0x00,0x00,0x00,0xE0,0xE0,0x00, // -y-
    193. 0x47,0x4F,0x48,0x48,0x68,0x3F,0x1F,0x00,
    194. 0x60,0x60,0x20,0xA0,0xE0,0x60,0x20,0x00, // -z-
    195. 0x0C,0x0E,0x0B,0x09,0x08,0x0C,0x0C,0x00,
    196. 0x00,0x40,0x40,0xF8,0xBC,0x04,0x04,0x00, // -{-
    197. 0x00,0x00,0x00,0x07,0x0F,0x08,0x08,0x00,
    198. 0x00,0x00,0x00,0xBC,0xBC,0x00,0x00,0x00, // -|-
    199. 0x00,0x00,0x00,0x0F,0x0F,0x00,0x00,0x00,
    200. 0x00,0x04,0x04,0xBC,0xF8,0x40,0x40,0x00, // -}-
    201. 0x00,0x08,0x08,0x0F,0x07,0x00,0x00,0x00,
    202. 0x08,0x0C,0x04,0x0C,0x08,0x0C,0x04,0x00, // -~-
    203. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    204. 0x80,0xC0,0x60,0x30,0x60,0xC0,0x80,0x00, // --
    205. 0x07,0x07,0x04,0x04,0x04,0x07,0x07,0x00,
    206. };
    207. #endif /* OLED_ASCII_H_ */

    【2】将创建屏幕打印函数,输出显示字符串能力

            (1)首选了解ASCII_8x16字库表如何使用的,例如我们取"!"为例:

    1. !16个8bit的字段的二进制表示如下
    2. 0X00 0 0 0 0 0 0 0 0
    3. 0X00 0 0 0 0 0 0 0 0
    4. 0X38 0 0 1 1 1 0 0 0
    5. 0XFC 1 1 1 1 1 1 0 0
    6. 0XFC 1 1 1 1 1 1 0 0
    7. 0X38 0 0 1 1 1 0 0 0
    8. 0X00 0 0 0 0 0 0 0 0
    9. 0X00 0 0 0 0 0 0 0 0
    10. 0X00 0 0 0 0 0 0 0 0
    11. 0X00 0 0 0 0 0 0 0 0
    12. 0X00 0 0 0 0 0 0 0 0
    13. 0X0D 0 0 0 0 1 1 0 1
    14. 0X0D 0 0 0 0 1 1 0 1
    15. 0X00 0 0 0 0 0 0 0 0
    16. 0X00 0 0 0 0 0 0 0 0
    17. 0X00 0 0 0 0 0 0 0 0

            (2)将划分为前8*8和后8*8的的二进制矩阵,将8*8矩阵做逆时针(90度)翻转,将翻转后矩阵进行紧凑(去掉空格),这些“1”组成了一个大的“!”字符如下:

    1. //将8*8二进制矩阵翻转
    2. 0X000X000X380XFC0XFC0X380X000X00
    3. 0 0 0 0 0 0 0 0
    4. 0 0 0 0 0 0 0 0
    5. 0 0 0 1 1 0 0 0
    6. 0 0 1 1 1 1 0 0
    7. 0 0 1 1 1 1 0 0
    8. 0 0 1 1 1 1 0 0
    9. 0 0 0 1 1 0 0 0
    10. 0 0 0 1 1 0 0 0
    11. 0X000X000X000X0D0X0D0X000X000X00
    12. 0 0 0 1 1 0 0 0
    13. 0 0 0 0 0 0 0 0
    14. 0 0 0 1 1 0 0 0
    15. 0 0 0 1 1 0 0 0
    16. 0 0 0 0 0 0 0 0
    17. 0 0 0 0 0 0 0 0
    18. 0 0 0 0 0 0 0 0
    19. 0 0 0 0 0 0 0 0
    20. 将上述矩阵进行紧凑(去掉空格),这些“1”组成了一个大的“!”字符
    21. 00000000
    22. 00000000
    23. 00011000
    24. 00111100
    25. 00111100
    26. 00111100
    27. 00011000
    28. 00011000
    29. 00011000
    30. 00000000
    31. 00011000
    32. 00011000
    33. 00000000
    34. 00000000
    35. 00000000
    36. 00000000
    37. //显然,在代码实现中,每个字符就是8*16像素的二进制矩阵,我们将“1”的绘制前景色,“0”的绘制背景色,就能显示字符

            (3)显然,在代码实现中,每个字符就是8*16像素的二进制矩阵,我们将“1”的绘制前景色,“0”的绘制背景色,就能显示字符

            现在给出字符8*16二进制矩阵获取代码:

    1. //addr为字符的地址信息
    2. uint8_t buffer[16][8]={0};
    3. for(uint8_t i=0; i<16; i++)
    4. {
    5. uint16_t buf=ASCII_8x16[(addr*16)+i-512];
    6. printf("0X%02X ",buf);
    7. for(uint8_t j=0;j<8;j++){
    8. printf("%d ",(0x01&(buf>>j))>0?0x01:0x00);//注意这里低字节开始取值,与前面分析的就刚好反至了,但原理是一样的
    9. buffer[8*(i/8)+j][i%8]=(0x01&(buf>>j))>0?1:0;
    10. }
    11. printf("\r\n");
    12. }
    13. for(uint8_t i=0; i<16; i++)
    14. {
    15. for(uint8_t j=0;j<8;j++){
    16. printf("%d",buffer[i][j]);
    17. }
    18. printf("\r\n");
    19. }

            (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实现一行字符的输出。

    1. //显示英文与数字8*16的ASCII码
    2. void OLED_DISPLAY_8x16(uint8_t x, uint8_t y, uint16_t w){ //要显示汉字的编号
    3. // printf("x=%d,y=%d,w=0X%04X w*16-512=%d\r\n",x,y,w,w*16-512);
    4. uint16_t color = 0;
    5. uint16_t textData[16][8]={0};
    6. // uint8_t buffer[16][8]={0};
    7. for(uint8_t i=0; i<16; i++)
    8. {
    9. uint16_t buf=ASCII_8x16[(w*16)+i-512];
    10. // printf("0X%02X ",buf);
    11. for(uint8_t j=0;j<8;j++){
    12. // printf("%d ",(0x01&(buf>>j))>0?0x01:0x00);
    13. // buffer[8*(i/8)+j][i%8]=(0x01&(buf>>j))>0?1:0;
    14. color = (0x01&(buf>>j))>0?LCD_DISP_BLACK:LCD_DISP_BLUE;
    15. textData[8*(i/8)+j][i%8] = color;
    16. // OLED_WritePixel(x+i%8,y+8*(i/8)+j,color);//单像素渲染
    17. }
    18. // printf("\r\n");
    19. }
    20. for(uint8_t i=0; i<16; i++)
    21. {
    22. OLED_WriteLine(x,y+i,textData[i],8);//列像素渲染
    23. // for(uint8_t j=0;j<8;j++){
    24. // printf("%d",buffer[i][j]);
    25. // }
    26. // printf("\r\n");
    27. }
    28. }
    29. //用于OLED_printf函数专用的显示程序
    30. void OLED_printf_US(uint8_t xpos,uint8_t ypos,uint8_t *str,uint8_t i){
    31. uint8_t r=0;
    32. while(i != r){//i是长度值,当显示到i之后退出
    33. OLED_DISPLAY_8x16(xpos+r*8,ypos,*str++);//显示英文与数字8*16的ASCII码
    34. r++;
    35. }
    36. }
    37. //OLED专用的printf函数
    38. //调用方法:OLED_printf(0,0,"123");
    39. //注意若xpos或ypos过大,并内容较多时,会出现溢出边界
    40. #define line_size 29
    41. #define row_size 16
    42. void OLED_printf (uint8_t xpos,uint8_t ypos,char *fmt, ...)
    43. {
    44. char buff[line_size]; //用于存放转换后的数据 [长度],限定30字符长度,每个字符8像素宽度,屏幕宽240
    45. uint16_t size=0;
    46. uint16_t row = 1+strlen(fmt)/line_size;//暂时没考虑自带换行符号情况
    47. // printf("row=%d\r\n",row);
    48. for(uint16_t i=0;i
    49. va_list arg_ptr;
    50. va_start(arg_ptr,fmt);
    51. vsnprintf(buff, line_size,fmt+i*line_size,arg_ptr);//数据转换
    52. size=strlen(buff); //得出数据长度
    53. if(strlen(buff)>line_size)size=line_size; //如果长度大于最大值,则长度等于最大值(多出部分忽略)
    54. OLED_printf_US(xpos,ypos+i*row_size,(uint8_t *)buff,size);//最终调用OLED专用的printf函数来显示
    55. va_end(arg_ptr);
    56. }
    57. }

    【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)。

    1. static void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
    2. {
    3. /* 指定X方向操作区域 */
    4. LcdWriteReg(0x2a);
    5. LcdWriteData(x1 >> 8);
    6. LcdWriteData(x1);
    7. LcdWriteData(x2 >> 8);
    8. LcdWriteData(x2);
    9. /* 指定Y方向操作区域 */
    10. LcdWriteReg(0x2b);
    11. LcdWriteData(y1 >> 8);
    12. LcdWriteData(y1);
    13. LcdWriteData(y2 >> 8);
    14. LcdWriteData(y2);
    15. /* 发送该命令,LCD开始等待接收显存数据 */
    16. LcdWriteReg(0x2C);
    17. }
    18. //指定位置,绘制一行像素直到屏幕右侧
    19. void OLED_SetCursor(uint16_t Xpos, uint16_t Ypos)
    20. {
    21. LCD_Address_Set(Xpos,Ypos,WIDTH-1,Ypos);
    22. }
    23. void BSP_LCD_Clear(uint16_t Color)
    24. {
    25. uint8_t black_gui[480] = {0};
    26. memset(black_gui, Color, sizeof(black_gui));
    27. for (uint32_t counter = 0; counter < 240; counter++)
    28. {
    29. /* Set Cursor */
    30. OLED_SetCursor(0, counter);
    31. /* Prepare to write to LCD RAM */
    32. OLED_WriteReg(0x2C, (uint8_t*)NULL, 0);
    33. /* RAM write data command */
    34. LcdWriteDataMultiple(black_gui, 480);
    35. }
    36. }
    37. extern volatile uint8_t one_frame_done;
    38. void BSP_LCD_Clear_DMA(uint16_t Color)
    39. {
    40. uint8_t black_gui_DMA[WIDTH*HEIGHT*2] = {0X00};
    41. for(uint32_t i=0; i
    42. {
    43. black_gui_DMA[2*i]=Color>>8;
    44. black_gui_DMA[2*i+1] = Color;
    45. }
    46. printf("0X%02x%02x\r\n",black_gui_DMA[0],black_gui_DMA[1]);
    47. //需要分两次写入HAL_SPI_Transmit_DMA写入缓存大小uint16_t(0xfff,65535)
    48. one_frame_done = 0;
    49. /* 指定显存操作地址为全屏*/
    50. LCD_Address_Set(0, 0, WIDTH - 1, HEIGHT/2 - 1);
    51. Data_Cmd_State(1);/* 指定接下来的数据为数据 */
    52. /*DMA 写前半屏*/
    53. HAL_SPI_Transmit_DMA(&hspi1,black_gui_DMA, WIDTH*HEIGHT);
    54. while(!one_frame_done){/*release cpu and doing something else*/}
    55. one_frame_done = 0;
    56. /* 指定显存操作地址为全屏*/
    57. LCD_Address_Set(0, HEIGHT/2, WIDTH - 1, HEIGHT - 1);
    58. Data_Cmd_State(1);/* 指定接下来的数据为数据 */
    59. /*DMA 写后半屏*/
    60. HAL_SPI_Transmit_DMA(&hspi1,black_gui_DMA+WIDTH*HEIGHT, WIDTH*HEIGHT);
    61. while(!one_frame_done){/*release cpu and doing something else*/}
    62. }

            前文3.3小节,已经开启了SPI的外部中断功能,则SPI的回调函数会被调用。one_frame_done是一个全局变量,在spi.c中定义,需要在spi.c加上HAL_SPI_TxCpltCallback的函数,用来替换stm32l4xx_hal_spi.c文件的spi中断回调实现:

    1. //spi.c末加上spi的中断回调实现
    2. /* USER CODE BEGIN 1 */
    3. volatile uint8_t one_frame_done;
    4. void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
    5. {
    6. one_frame_done = 1;
    7. }
    8. /* USER CODE END 1 */
    9. //用来替换调stm32l4xx_hal_spi.c的弱函数
    10. __weak void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
    11. {
    12. /* Prevent unused argument(s) compilation warning */
    13. UNUSED(hspi);
    14. /* NOTE : This function should not be modified, when the callback is needed,
    15. the HAL_SPI_TxCpltCallback should be implemented in the user file
    16. */
    17. }

    六、程序编译及测试

            在main.c中加入按键功能,一个按键直接写入方式清屏(黑屏),一个按键DMA写入方式清屏(蓝屏幕),一个按键实现屏幕输出一段文字,另外通过串口发送字符串,实现在屏幕输出显示。

    1. /* USER CODE BEGIN WHILE */
    2. while (1)
    3. {
    4. if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
    5. OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
    6. HLPUSART_RX_STA=0;//接收错误,重新开始
    7. HAL_Delay(100);//等待
    8. }
    9. if(KEY_0())
    10. {
    11. OLED_printf(10,10,"hello world to stm32!I am an embedded enthusiast.");
    12. OLED_printf(10,48," STM32CubeIDE is an advanced C/C++ development platform "
    13. "with peripheral configuration, code generation, code compilation, "
    14. "and debug features for STM32 micro controllers and microprocessors.");
    15. // OLED_printf(10,10,"!");
    16. }
    17. if(KEY_1())
    18. {
    19. BSP_LCD_Clear(0xF800);
    20. printf("OLED_Clear\r\n");
    21. }
    22. if(KEY_2())
    23. {
    24. BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
    25. printf("OLED_Clear_DMA\r\n");
    26. }
    27. Toggle_led0();
    28. /* USER CODE END WHILE */

            编译完成后测试效果如下:

            1)按键key1清屏,发送字符串

            2)按键key0,输出一段文字

     七、附件

            oled.h

    1. #ifndef _OLED_H_
    2. #define _OLED_H_
    3. /*
    4. * for st7789 oled 模组
    5. */
    6. #define WIDTH 240
    7. #define HEIGHT 240
    8. #define BPP 16
    9. /* Init script function */
    10. struct OLED_function {
    11. uint8_t cmd;
    12. uint16_t data;
    13. };
    14. /* Init script commands */
    15. enum OLED_cmd {
    16. OLED_START,
    17. OLED_END,
    18. OLED_CMD,
    19. OLED_DATA,
    20. OLED_DELAY
    21. };
    22. /************** 颜色(RGB 5,6,5) **************/
    23. #define LCD_DISP_RED 0xF800 //先高字节,后低字节
    24. #define LCD_DISP_GREEN 0x07E0
    25. #define LCD_DISP_BLUE 0x001F
    26. #define LCD_DISP_WRITE 0xFFFF
    27. #define LCD_DISP_BLACK 0x0000
    28. #define LCD_DISP_GRAY 0xEF5D
    29. #define LCD_DISP_GRAY75 0x39E7
    30. #define LCD_DISP_GRAY50 0x7BEF
    31. #define LCD_DISP_GRAY25 0xADB5
    32. /* oled Commands */
    33. #define OLED_CASET 0x2A
    34. #define OLED_RASET 0x2B
    35. #define OLED_RAMWR 0x2C
    36. #define OLED_RAMRD 0x2E
    37. //对该位写1或0
    38. #define Data_Cmd_State(__D_C_State__) HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, (GPIO_PinState)(__D_C_State__))
    39. #define OLED_RST_State(__RST_State__) HAL_GPIO_WritePin(LCD_RST_GPIO_Port, LCD_RST_Pin, (GPIO_PinState)(__RST_State__))
    40. #define LCD_PWR_State(__PWR_State__) HAL_GPIO_WritePin(LCD_PWR_GPIO_Port, LCD_PWR_Pin, (GPIO_PinState)(__PWR_State__))
    41. int OLED_init();
    42. void LcdWriteReg(uint8_t Data);
    43. void LcdWriteData(uint8_t Data);
    44. void LcdWriteDataMultiple(uint8_t * pData, uint32_t NumItems);
    45. void OLED_WritePixel(uint16_t Xpos, uint16_t Ypos, uint16_t RGBCode);
    46. void OLED_WriteLine(uint16_t Xpos, uint16_t Ypos, uint16_t *RGBCode, uint16_t pointNum);
    47. void BSP_LCD_Clear(uint16_t Color);
    48. void BSP_LCD_Clear_DMA(uint16_t Color);
    49. void OLED_printf (uint8_t xpos,uint8_t ypos,char *fmt, ...);
    50. #endif /* __oled_H */

    oled.c

    1. #include "stm32l4xx_hal.h"
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include "main.h"
    7. #include "ASCII.h"
    8. #include "oled.h"
    9. extern SPI_HandleTypeDef hspi1;
    10. //初始化命令集
    11. static struct OLED_function OLED_cfg_script[] = {
    12. {OLED_START, OLED_START},
    13. {OLED_CMD, 0x11},
    14. {OLED_DELAY, 120},
    15. {OLED_CMD, 0x36},
    16. {OLED_DATA, 0x00},
    17. {OLED_CMD, 0x3a},
    18. {OLED_DATA, 0x65},
    19. {OLED_CMD, 0xb2},
    20. {OLED_DATA, 0x0c},
    21. {OLED_DATA, 0x0c},
    22. {OLED_DATA, 0x00},
    23. {OLED_DATA, 0x33},
    24. {OLED_DATA, 0x33},
    25. {OLED_CMD, 0xb7},
    26. {OLED_DATA, 0x72},
    27. {OLED_CMD, 0xbb},
    28. {OLED_DATA, 0x3d},
    29. {OLED_CMD, 0xc0},
    30. {OLED_DATA, 0x2c},
    31. {OLED_CMD, 0xc2},
    32. {OLED_DATA, 0x01},
    33. {OLED_CMD, 0xc3},
    34. {OLED_DATA, 0x19},
    35. {OLED_CMD, 0xc4},
    36. {OLED_DATA, 0x20},
    37. {OLED_CMD, 0xc6},
    38. {OLED_DATA, 0x0f},
    39. {OLED_CMD, 0xd0},
    40. {OLED_DATA, 0xa4},
    41. {OLED_DATA, 0xa1},
    42. {OLED_CMD, 0xe0},
    43. {OLED_DATA, 0x70},
    44. {OLED_DATA, 0x04},
    45. {OLED_DATA, 0x08},
    46. {OLED_DATA, 0x09},
    47. {OLED_DATA, 0x09},
    48. {OLED_DATA, 0x05},
    49. {OLED_DATA, 0x2a},
    50. {OLED_DATA, 0x33},
    51. {OLED_DATA, 0x41},
    52. {OLED_DATA, 0x07},
    53. {OLED_DATA, 0x13},
    54. {OLED_DATA, 0x13},
    55. {OLED_DATA, 0x29},
    56. {OLED_DATA, 0x2f},
    57. {OLED_CMD, 0xe1},
    58. {OLED_DATA, 0x70},
    59. {OLED_DATA, 0x03},
    60. {OLED_DATA, 0x09},
    61. {OLED_DATA, 0x0a},
    62. {OLED_DATA, 0x09},
    63. {OLED_DATA, 0x06},
    64. {OLED_DATA, 0x2b},
    65. {OLED_DATA, 0x34},
    66. {OLED_DATA, 0x41},
    67. {OLED_DATA, 0x07},
    68. {OLED_DATA, 0x12},
    69. {OLED_DATA, 0x14},
    70. {OLED_DATA, 0x28},
    71. {OLED_DATA, 0x2e},
    72. {OLED_CMD, 0x21},
    73. {OLED_CMD, 0x29},
    74. {OLED_CMD, 0x2a},
    75. {OLED_DATA, 0x00},
    76. {OLED_DATA, 0x00},
    77. {OLED_DATA, 0x00},
    78. {OLED_DATA, 0xef},
    79. {OLED_CMD, 0x2b},
    80. {OLED_DATA, 0x00},
    81. {OLED_DATA, 0x00},
    82. {OLED_DATA, 0x00},
    83. {OLED_DATA, 0xef},
    84. {OLED_CMD, 0x2c},
    85. {OLED_END, OLED_END},
    86. };
    87. static void OLED_run_cfg_script()
    88. {
    89. uint8_t data[2] = {0};
    90. int i = 0;
    91. int end_script = 0;
    92. do {
    93. switch (OLED_cfg_script[i].cmd) {
    94. case OLED_START:
    95. break;
    96. case OLED_CMD:
    97. data[0] = OLED_cfg_script[i].data & 0xff;
    98. LcdWriteReg(data[0]);
    99. break;
    100. case OLED_DATA:
    101. data[0] = OLED_cfg_script[i].data & 0xff;
    102. LcdWriteData(data[0]);
    103. break;
    104. case OLED_DELAY:
    105. HAL_Delay(OLED_cfg_script[i].data);
    106. break;
    107. case OLED_END:
    108. end_script = 1;
    109. }
    110. i++;
    111. } while (!end_script);
    112. }
    113. static void OLED_reset()
    114. {
    115. OLED_RST_State(0);
    116. HAL_Delay(200);
    117. OLED_RST_State(1);
    118. HAL_Delay(150);
    119. }
    120. #define LCD_MAX_MEM16_BLOCK (1 << 6)
    121. #define LCD_PIXEL_PER_BLOCK (LCD_MAX_MEM16_BLOCK >> 1)
    122. static void spec_send_fb(uint16_t color, uint16_t pixel_num)
    123. {
    124. uint16_t real_mem[LCD_MAX_MEM16_BLOCK] = {0};
    125. for (uint16_t i = 0; i < LCD_MAX_MEM16_BLOCK; ++i) {
    126. real_mem[i] = color;
    127. }
    128. // HAL_GPIO_WritePin(LCD_DCX_GPIO_Port, LCD_DCX_Pin, GPIO_PIN_SET);
    129. Data_Cmd_State(GPIO_PIN_SET);
    130. if (pixel_num <= LCD_MAX_MEM16_BLOCK) {
    131. LcdWriteDataMultiple((uint8_t*)real_mem, pixel_num << 1);
    132. } else {
    133. uint16_t count = pixel_num / LCD_MAX_MEM16_BLOCK;
    134. uint16_t remain = pixel_num % LCD_MAX_MEM16_BLOCK;
    135. for (uint16_t i = 0; i < count; ++i) {
    136. LcdWriteDataMultiple((uint8_t*)real_mem, LCD_MAX_MEM16_BLOCK << 1);
    137. }
    138. LcdWriteDataMultiple((uint8_t*)real_mem, remain << 1);
    139. }
    140. }
    141. static void OLED_display_picture(void)
    142. {
    143. LcdWriteReg(OLED_RAMWR);
    144. spec_send_fb(0x0, WIDTH * HEIGHT / 4);
    145. spec_send_fb(0x1111, WIDTH * HEIGHT / 4);
    146. spec_send_fb(0x7777, WIDTH * HEIGHT / 4);
    147. spec_send_fb(0xeeee, WIDTH * HEIGHT / 4);
    148. }
    149. int OLED_init()
    150. {
    151. LCD_PWR_State(0);//回显关闭
    152. OLED_reset();
    153. OLED_run_cfg_script();
    154. LCD_PWR_State(1);//回显开启
    155. OLED_display_picture();
    156. return HAL_OK;
    157. }
    158. void OLED_WriteReg(uint8_t Command, uint8_t *Parameters, uint8_t NbParameters)
    159. {
    160. /* Send command */
    161. LcdWriteReg(Command);
    162. /* Send command's parameters if any */
    163. for (uint8_t i=0; i
    164. {
    165. LcdWriteData(Parameters[i]);
    166. }
    167. }
    168. static void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
    169. {
    170. /* 指定X方向操作区域 */
    171. LcdWriteReg(0x2a);
    172. LcdWriteData(x1 >> 8);
    173. LcdWriteData(x1);
    174. LcdWriteData(x2 >> 8);
    175. LcdWriteData(x2);
    176. /* 指定Y方向操作区域 */
    177. LcdWriteReg(0x2b);
    178. LcdWriteData(y1 >> 8);
    179. LcdWriteData(y1);
    180. LcdWriteData(y2 >> 8);
    181. LcdWriteData(y2);
    182. /* 发送该命令,LCD开始等待接收显存数据 */
    183. LcdWriteReg(0x2C);
    184. }
    185. void OLED_SetCursor(uint16_t Xpos, uint16_t Ypos)
    186. {
    187. LCD_Address_Set(Xpos,Ypos,WIDTH-1,Ypos);
    188. }
    189. void BSP_LCD_Clear(uint16_t Color)
    190. {
    191. uint8_t black_gui[480] = {0};
    192. memset(black_gui, Color, sizeof(black_gui));
    193. for (uint32_t counter = 0; counter < 240; counter++)
    194. {
    195. /* Set Cursor */
    196. OLED_SetCursor(0, counter);
    197. /* Prepare to write to LCD RAM */
    198. OLED_WriteReg(0x2C, (uint8_t*)NULL, 0);
    199. /* RAM write data command */
    200. LcdWriteDataMultiple(black_gui, 480);
    201. }
    202. }
    203. extern volatile uint8_t one_frame_done;
    204. void BSP_LCD_Clear_DMA(uint16_t Color)
    205. {
    206. uint8_t black_gui_DMA[WIDTH*HEIGHT*2] = {0X00};
    207. for(uint32_t i=0; i
    208. {
    209. black_gui_DMA[2*i]=Color>>8;
    210. black_gui_DMA[2*i+1] = Color;
    211. }
    212. printf("0X%02x%02x\r\n",black_gui_DMA[0],black_gui_DMA[1]);
    213. //需要分两次写入HAL_SPI_Transmit_DMA写入缓存大小uint16_t(0xfff,65535)
    214. one_frame_done = 0;
    215. /* 指定显存操作地址为全屏*/
    216. LCD_Address_Set(0, 0, WIDTH - 1, HEIGHT/2 - 1);
    217. Data_Cmd_State(1);/* 指定接下来的数据为数据 */
    218. /*DMA 写前半屏*/
    219. HAL_SPI_Transmit_DMA(&hspi1,black_gui_DMA, WIDTH*HEIGHT);
    220. while(!one_frame_done){/*release cpu and doing something else*/}
    221. one_frame_done = 0;
    222. /* 指定显存操作地址为全屏*/
    223. LCD_Address_Set(0, HEIGHT/2, WIDTH - 1, HEIGHT - 1);
    224. Data_Cmd_State(1);/* 指定接下来的数据为数据 */
    225. /*DMA 写后半屏*/
    226. HAL_SPI_Transmit_DMA(&hspi1,black_gui_DMA+WIDTH*HEIGHT, WIDTH*HEIGHT);
    227. while(!one_frame_done){/*release cpu and doing something else*/}
    228. }
    229. //显示英文与数字8*16的ASCII码
    230. void OLED_DISPLAY_8x16(uint8_t x, uint8_t y, uint16_t w){ //要显示汉字的编号
    231. // printf("x=%d,y=%d,w=0X%04X w*16-512=%d\r\n",x,y,w,w*16-512);
    232. uint16_t color = 0;
    233. uint16_t textData[16][8]={0};
    234. // uint8_t buffer[16][8]={0};
    235. for(uint8_t i=0; i<16; i++)
    236. {
    237. uint16_t buf=ASCII_8x16[(w*16)+i-512];
    238. // printf("0X%02X ",buf);
    239. for(uint8_t j=0;j<8;j++){
    240. // printf("%d ",(0x01&(buf>>j))>0?0x01:0x00);
    241. // buffer[8*(i/8)+j][i%8]=(0x01&(buf>>j))>0?1:0;
    242. color = (0x01&(buf>>j))>0?LCD_DISP_BLACK:LCD_DISP_BLUE;
    243. textData[8*(i/8)+j][i%8] = color;
    244. // OLED_WritePixel(x+i%8,y+8*(i/8)+j,color);//单像素渲染
    245. }
    246. // printf("\r\n");
    247. }
    248. for(uint8_t i=0; i<16; i++)
    249. {
    250. OLED_WriteLine(x,y+i,textData[i],8);//列像素渲染
    251. // for(uint8_t j=0;j<8;j++){
    252. // printf("%d",buffer[i][j]);
    253. // }
    254. // printf("\r\n");
    255. }
    256. }
    257. //用于OLED_printf函数专用的显示程序
    258. void OLED_printf_US(uint8_t xpos,uint8_t ypos,uint8_t *str,uint8_t i){
    259. uint8_t r=0;
    260. while(i != r){//i是长度值,当显示到i之后退出
    261. OLED_DISPLAY_8x16(xpos+r*8,ypos,*str++);//显示英文与数字8*16的ASCII码
    262. r++;
    263. }
    264. }
    265. //OLED专用的printf函数
    266. //调用方法:OLED_printf(0,0,"123");
    267. //注意若xpos或ypos过大,并内容较多时,会出现溢出边界
    268. #define line_size 29
    269. #define row_size 16
    270. void OLED_printf (uint8_t xpos,uint8_t ypos,char *fmt, ...)
    271. {
    272. char buff[line_size]; //用于存放转换后的数据 [长度],限定30字符长度,每个字符8像素宽度,屏幕宽240
    273. uint16_t size=0;
    274. uint16_t row = 1+strlen(fmt)/line_size;//暂时没考虑自带换行符号情况
    275. // printf("row=%d\r\n",row);
    276. for(uint16_t i=0;i
    277. va_list arg_ptr;
    278. va_start(arg_ptr,fmt);
    279. vsnprintf(buff, line_size,fmt+i*line_size,arg_ptr);//数据转换
    280. size=strlen(buff); //得出数据长度
    281. if(strlen(buff)>line_size)size=line_size; //如果长度大于最大值,则长度等于最大值(多出部分忽略)
    282. OLED_printf_US(xpos,ypos+i*row_size,(uint8_t *)buff,size);//最终调用OLED专用的printf函数来显示
    283. va_end(arg_ptr);
    284. }
    285. }
    286. void OLED_WritePixel(uint16_t Xpos, uint16_t Ypos, uint16_t data)
    287. {
    288. uint8_t dataB = 0;
    289. /* Set Cursor */
    290. OLED_SetCursor(Xpos, Ypos);
    291. /* Prepare to write to LCD RAM */
    292. OLED_WriteReg(0x2C, (uint8_t*)NULL, 0); /* RAM write data command */
    293. /* Write RAM data */
    294. dataB = (uint8_t)(data >> 8);
    295. LcdWriteData(dataB);
    296. dataB = (uint8_t)data;
    297. LcdWriteData(dataB);
    298. }
    299. uint8_t endian_buffer[480];
    300. void OLED_WriteLine(uint16_t Xpos, uint16_t Ypos, uint16_t *RGBCode, uint16_t pointNum)
    301. {
    302. /* Set Cursor */
    303. OLED_SetCursor(Xpos, Ypos);
    304. /* Prepare to write to LCD RAM */
    305. OLED_WriteReg(0x2C, (uint8_t*)NULL, 0); /* RAM write data command */
    306. for (uint16_t i = 0; i < pointNum; i++) {
    307. endian_buffer[2*i] = (uint8_t)(RGBCode[i] >> 8);
    308. endian_buffer[2*i + 1] = (uint8_t)RGBCode[i];
    309. }
    310. /* Write RAM data */
    311. LcdWriteDataMultiple(endian_buffer, pointNum*2);
    312. }
    313. /********************************************************************
    314. *
    315. * LcdWriteReg
    316. *
    317. * Function description:
    318. * Sets display register
    319. */
    320. void LcdWriteReg(uint8_t Data)
    321. {
    322. Data_Cmd_State(0);
    323. HAL_SPI_Transmit(&hspi1, &Data, 1, 10);
    324. }
    325. /********************************************************************
    326. *
    327. * LcdWriteData
    328. *
    329. * Function description:
    330. * Writes a value to a display register
    331. */
    332. void LcdWriteData(uint8_t Data)
    333. {
    334. Data_Cmd_State(1);
    335. HAL_SPI_Transmit(&hspi1, &Data, 1, 10);
    336. }
    337. /********************************************************************
    338. *
    339. * LcdWriteDataMultiple
    340. *
    341. * Function description:
    342. * Writes multiple values to a display register.
    343. */
    344. void LcdWriteDataMultiple(uint8_t * pData, uint32_t NumItems)
    345. {
    346. Data_Cmd_State(1);
    347. HAL_SPI_Transmit(&hspi1, pData, NumItems, 10);
    348. }

     ASCII.h源码前文已给出,mian.c部分源码:

    1. /* USER CODE END Header */
    2. /* Includes ------------------------------------------------------------------*/
    3. #include "main.h"
    4. #include "dcmi.h"
    5. #include "dma.h"
    6. #include "i2c.h"
    7. #include "usart.h"
    8. #include "spi.h"
    9. #include "gpio.h"
    10. /* Private includes ----------------------------------------------------------*/
    11. /* USER CODE BEGIN Includes */
    12. #include "../../ICore/key/key.h"
    13. #include "../../ICore/led/led.h"
    14. #include "../../ICore/print/print.h"
    15. #include "../../ICore/usart/usart.h"
    16. #include "../../ICore/oled/oled.h"
    17. /* USER CODE END Includes */
    18. /* Private typedef -----------------------------------------------------------*/
    19. /* USER CODE BEGIN PTD */
    20. /* USER CODE END PTD */
    21. /* Private define ------------------------------------------------------------*/
    22. /* USER CODE BEGIN PD */
    23. /* USER CODE END PD */
    24. /* Private macro -------------------------------------------------------------*/
    25. /* USER CODE BEGIN PM */
    26. /* USER CODE END PM */
    27. /* Private variables ---------------------------------------------------------*/
    28. /* USER CODE BEGIN PV */
    29. /* USER CODE END PV */
    30. /* Private function prototypes -----------------------------------------------*/
    31. void SystemClock_Config(void);
    32. /* USER CODE BEGIN PFP */
    33. /* USER CODE END PFP */
    34. /* Private user code ---------------------------------------------------------*/
    35. /* USER CODE BEGIN 0 */
    36. //volatile uint8_t data1[LCD_RAM_SIZE];
    37. //volatile uint8_t data2[LCD_RAM_SIZE];
    38. //extern volatile uint8_t one_frame_done;
    39. /* USER CODE END 0 */
    40. /**
    41. * @brief The application entry point.
    42. * @retval int
    43. */
    44. int main(void)
    45. {
    46. /* USER CODE BEGIN 1 */
    47. /* USER CODE END 1 */
    48. /* MCU Configuration--------------------------------------------------------*/
    49. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    50. HAL_Init();
    51. /* USER CODE BEGIN Init */
    52. /* USER CODE END Init */
    53. /* Configure the system clock */
    54. SystemClock_Config();
    55. /* USER CODE BEGIN SysInit */
    56. /* USER CODE END SysInit */
    57. /* Initialize all configured peripherals */
    58. MX_GPIO_Init();
    59. MX_DMA_Init();
    60. MX_LPUART1_UART_Init();
    61. MX_SPI1_Init();
    62. /* USER CODE BEGIN 2 */
    63. ResetPrintInit(&hlpuart1);
    64. HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
    65. HLPUSART_RX_STA = 0;
    66. OLED_init();
    67. //
    68. set_led0_val(0);
    69. set_led1_val(get_key0_val());
    70. /* USER CODE END 2 */
    71. /* Infinite loop */
    72. /* USER CODE BEGIN WHILE */
    73. while (1)
    74. {
    75. if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
    76. OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
    77. HLPUSART_RX_STA=0;//接收错误,重新开始
    78. HAL_Delay(100);//等待
    79. }
    80. if(KEY_0())
    81. {
    82. OLED_printf(10,10,"hello world to stm32!I am an embedded enthusiast.");
    83. OLED_printf(10,48," STM32CubeIDE is an advanced C/C++ development platform "
    84. "with peripheral configuration, code generation, code compilation, "
    85. "and debug features for STM32 micro controllers and microprocessors.");
    86. // OLED_printf(10,10,"!");
    87. }
    88. if(KEY_1())
    89. {
    90. BSP_LCD_Clear(0xF800);
    91. printf("OLED_Clear\r\n");
    92. }
    93. if(KEY_2())
    94. {
    95. BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
    96. printf("OLED_Clear_DMA\r\n");
    97. }
    98. Toggle_led0();
    99. /* USER CODE END WHILE */
    100. /* USER CODE BEGIN 3 */
    101. }
    102. /* USER CODE END 3 */
    103. }
  • 相关阅读:
    C++学习之类和对象
    蓝桥杯day3刷题日记--P9420 [蓝桥杯 2023 国 B] 子 2023 / 双子数
    UE4 Niagara Module Script 初次使用笔记
    2.5k的ChatGPT-Java版SDK升级1.1.2-beta0支持GPT-4V、Dall-e-3模型、ToolCalls、微调Job、TTS...
    郁金香2021年游戏辅助技术中级班(一)
    C. Binary String Reconstruction
    SELinux
    EventBus 一篇文章就够了
    Linux之文件打包和解压缩
    SpringBoot Filter过滤器的使用篇
  • 原文地址:https://blog.csdn.net/py8105/article/details/128017976