1. 前言
基于RVB2601的GUI程序是利用Lvgl开源组件实现在OLED屏幕上的字符和图形显示。开发者可以利用Lvgl组件在OLED屏幕上实现Label控件显示功能。
建议在看本文之前,先详细看下RVB2601资源。本例程名为ch2601_gui_demo,可以通过CDK直接从OCC拉取。
2. 硬件配置
2.1 显示屏
RVB2601开发板采用的是OLED显示屏, 位于开发板正面。
2.2 屏幕物理接口
CH2601开发板采用单彩色图形显示面板,屏幕分辨率128x64 pixel,屏幕背景颜色可选,该程序中采用的是一块黄色背景的屏幕。屏幕控制器采用SSD1309,通过4 wire SPI接口与主芯片连接, 原理图如下所示, 对应的pin引脚分别为PA27、PA28、PA29、PA30。 原理图如下:
软件通过对SPI进行读写操作来实现对OLED屏上的像素进行点操作,从而实现整个屏的点亮操作。
3. GUI软件开发
3.1 LVGL介绍
LVGL全称Light and Versatile Graphics Library,是一个*的,开源的GUI库,具有界面精美,资源消耗小,可移植度高, 响应式布局等特点, 全库采用纯 c 语言开发.
主要特性如下.
- 具有非常丰富的内置控件,像 buttons, charts, lists, sliders, images 等
- 高级图形效果:动画,反锯齿,透明度,平滑滚动
- 支持多种输入设备,像 touchpad, mouse, keyboard, encoder 等
- 支持多语言的 UTF-8 编码
- 支持多个和多种显示设备,例如同步显示在多个彩色屏或单色屏上
- 完全自定制的图形元素
- 硬件独立于任何微控制器或显示器
- 可以缩小到最小内存 (64 kB Flash, 16 kB RAM)
- 支持操作系统、外部储存和 GPU(非必须)
- 仅仅单个帧缓冲设备就可以呈现高级视觉特效
- 使用 C 编写以获得最大兼容性(兼容 C++)
- 支持 PC 模拟器
- 为加速 GUI 设计,提供教程,案例和主题,支持响应式布局
- 提供了在线和离线文档
- 基于*和开源的 MIT 协议
- 支持MicroPython
3.2 例程下载
打开CDK,点击HOME图标,查找ch2601_gui_demo后,打开工程可以看到以下目录:
3.3 LVGL移植接口
Lvgl移植代码位于app/src/lvgl_porting文件夹内,其包含oled.c和oled.h。
- 以下功能接口位于app/src/lvgl_porting/oled.c, 实现SPI管脚的初始化,主要针对CS, DATA, CLOCK, DATAIN管脚,同时实现了对不同管脚的读写操作。
1. static void oled_gpio_init() 2. { 3. // 4. csi_gpio_pin_init(&pin_clk, PA28); 5. csi_gpio_pin_dir(&pin_clk, GPIO_DIRECTION_OUTPUT); 6. csi_gpio_pin_init(&pin_mosi, PA29); 7. csi_gpio_pin_dir(&pin_mosi, GPIO_DIRECTION_OUTPUT); 8. csi_gpio_pin_init(&pin_cs, PA27); 9. csi_gpio_pin_dir(&pin_cs, GPIO_DIRECTION_OUTPUT); 10. csi_gpio_pin_init(&pin_miso, PA30); //dc 11. csi_gpio_pin_dir(&pin_miso, GPIO_DIRECTION_OUTPUT); 12. } 13. 14. static void lcd_cs(uint8_t d) 15. { 16. if (d == 1) { 17. csi_gpio_pin_write(&pin_cs, GPIO_PIN_HIGH); 18. } else { 19. csi_gpio_pin_write(&pin_cs, GPIO_PIN_LOW); 20. } 21. } 22. 23. static void lcd_dc(uint8_t d) 24. { 25. if (d == 1) { 26. csi_gpio_pin_write(&pin_miso, GPIO_PIN_HIGH); 27. } else { 28. csi_gpio_pin_write(&pin_miso, GPIO_PIN_LOW); 29. } 30. } 31. 32. static void lcd_sclk(uint8_t d) 33. { 34. if (d == 1) { 35. csi_gpio_pin_write(&pin_clk, GPIO_PIN_HIGH); 36. } else { 37. csi_gpio_pin_write(&pin_clk, GPIO_PIN_LOW); 38. } 39. } 40. 41. static void lcd_sdin(uint8_t d) 42. { 43. if (d == 1) { 44. csi_gpio_pin_write(&pin_mosi, GPIO_PIN_HIGH); 45. } else { 46. csi_gpio_pin_write(&pin_mosi, GPIO_PIN_LOW); 47. } 48. } c
- 以下功能函数位于app/src/lvgl_porting/oled.c,通过SPI实现对屏幕的命令和数据写操作。
1. void Write_Command(unsigned char Data) 2. { 3. unsigned char i; 4. 5. lcd_cs(0); 6. lcd_dc(0); 7. for (i = 0; i < 8; i++) { 8. lcd_sclk(0); 9. lcd_sdin((Data & 0x80) >> 7); 10. Data = Data << 1; 11. lcd_sclk(1); 12. } 13. lcd_dc(1); 14. lcd_cs(1); 15. } 16. 17. void Write_Data(unsigned char Data) 18. { 19. unsigned char i; 20. 21. lcd_cs(0); 22. lcd_dc(1); 23. for (i = 0; i < 8; i++) { 24. lcd_sclk(0); 25. lcd_sdin((Data & 0x80) >> 7); 26. Data = Data << 1; 27. lcd_sclk(1); 28. } 29. lcd_dc(1); 30. lcd_cs(1); 31. } c
- 以下功能函数位于app/src/lvgl_porting/oled.c文件中,实现对屏幕的基本命令操作,例如设置屏幕行列地址,屏幕的亮度控制等。
1. void Set_Start_Column(unsigned char d) 2. { 3. Write_Command(0x00 + d % 16); // Set Lower Column Start Address for Page Addressing Mode 4. // Default => 0x00 5. Write_Command(0x10 + d / 16); // Set Higher Column Start Address for Page Addressing Mode 6. // Default => 0x10 7. } 8. 9. void Set_Addressing_Mode(unsigned char d) 10. { 11. Write_Command(0x20); // Set Memory Addressing Mode 12. Write_Command(d); // Default => 0x02 13. // 0x00 => Horizontal Addressing Mode 14. // 0x01 => Vertical Addressing Mode 15. // 0x02 => Page Addressing Mode 16. } 17. 18. void Set_Column_Address(unsigned char a, unsigned char b) 19. { 20. Write_Command(0x21); // Set Column Address 21. Write_Command(a); // Default => 0x00 (Column Start Address) 22. Write_Command(b); // Default => 0x7F (Column End Address) 23. } 24. 25. void Set_Page_Address(unsigned char a, unsigned char b) 26. { 27. Write_Command(0x22); // Set Page Address 28. Write_Command(a); // Default => 0x00 (Page Start Address) 29. Write_Command(b); // Default => 0x07 (Page End Address) 30. } 31. 32. void Set_Start_Line(unsigned char d) 33. { 34. Write_Command(0x40 | d); // Set Display Start Line 35. // Default => 0x40 (0x00) 36. } 37. 38. void Set_Contrast_Control(unsigned char d) 39. { 40. Write_Command(0x81); // Set Contrast Control for Bank 0 41. Write_Command(d); // Default => 0x7F 42. } 43. 44. void Set_Segment_Remap(unsigned char d) 45. { 46. Write_Command(d); // Set Segment Re-Map 47. // Default => 0xA0 48. // 0xA0 => Column Address 0 Mapped to SEG0 49. // 0xA1 => Column Address 0 Mapped to SEG127 50. } 51. 52. void Set_Entire_Display(unsigned char d) 53. { 54. Write_Command(d); // Set Entire Display On / Off 55. // Default => 0xA4 56. // 0xA4 => Normal Display 57. // 0xA5 => Entire Display On 58. } 59. 60. void Set_Inverse_Display(unsigned char d) 61. { 62. Write_Command(d); // Set Inverse Display On/Off 63. // Default => 0xA6 64. // 0xA6 => Normal Display 65. // 0xA7 => Inverse Display On 66. } 67. 68. void Set_Multiplex_Ratio(unsigned char d) 69. { 70. Write_Command(0xA8); // Set Multiplex Ratio 71. Write_Command(d); // Default => 0x3F (1/64 Duty) 72. } 73. 74. void Set_Display_On_Off(unsigned char d) 75. { 76. Write_Command(d); // Set Display On/Off 77. // Default => 0xAE 78. // 0xAE => Display Off 79. // 0xAF => Display On 80. } 81. 82. void Set_Start_Page(unsigned char d) 83. { 84. Write_Command(0xB0 | d); // Set Page Start Address for Page Addressing Mode 85. // Default => 0xB0 (0x00) 86. } 87. 88. void Set_Common_Remap(unsigned char d) 89. { 90. Write_Command(d); // Set COM Output Scan Direction 91. // Default => 0xC0 92. // 0xC0 => Scan from COM0 to 63 93. // 0xC8 => Scan from COM63 to 0 94. } 95. 96. void Set_Display_Offset(unsigned char d) 97. { 98. Write_Command(0xD3); // Set Display Offset 99. Write_Command(d); // Default => 0x00 100. } 101. 102. void Set_Display_Clock(unsigned char d) 103. { 104. Write_Command(0xD5); // Set Display Clock Divide Ratio / Oscillator Frequency 105. Write_Command(d); // Default => 0x70 106. // D[3:0] => Display Clock Divider 107. // D[7:4] => Oscillator Frequency 108. } 109. 110. void Set_Low_Power(unsigned char d) 111. { 112. Write_Command(0xD8); // Set Low Power Display Mode 113. Write_Command(d); // Default => 0x04 (Normal Power Mode) 114. } 115. 116. void Set_Precharge_Period(unsigned char d) 117. { 118. Write_Command(0xD9); // Set Pre-Charge Period 119. Write_Command(d); // Default => 0x22 (2 Display Clocks [Phase 2] / 2 Display Clocks [Phase 1]) 120. // D[3:0] => Phase 1 Period in 1~15 Display Clocks 121. // D[7:4] => Phase 2 Period in 1~15 Display Clocks 122. } 123. 124. void Set_Common_Config(unsigned char d) 125. { 126. Write_Command(0xDA); // Set COM Pins Hardware Configuration 127. Write_Command(d); // Default => 0x12 128. // Alternative COM Pin Configuration 129. // Disable COM Left/Right Re-Map 130. } 131. 132. void Set_NOP() 133. { 134. Write_Command(0xE3); // Command for No Operation 135. } 136. 137. void Set_Command_Lock(unsigned char d) 138. { 139. Write_Command(0xFD); // Set Command Lock 140. Write_Command(d); // Default => 0x12 141. // 0x12 => Driver IC interface is unlocked from entering command. 142. // 0x16 => All Commands are locked except 0xFD. 143. } c
- 该功能函数位于app/src/lvgl_porting/oled.c文件中,实现对屏幕的初始化。
1. static void oled_initialize() 2. { 3. Set_Command_Lock(0x12); // Unlock Driver IC (0x12/0x16) 4. Set_Display_On_Off(0xAE); // Display Off (0xAE/0xAF) 5. Set_Display_Clock(0xA0); // Set Clock as 116 Frames/Sec 6. Set_Multiplex_Ratio(0x3F); // 1/64 Duty (0x0F~0x3F) 7. Set_Display_Offset(0x00); // Shift Mapping RAM Counter (0x00~0x3F) 8. Set_Start_Line(0x00); // Set Mapping RAM Display Start Line (0x00~0x3F) 9. Set_Low_Power(0x04); // Set Normal Power Mode (0x04/0x05) 10. Set_Addressing_Mode(0x02); // Set Page Addressing Mode (0x00/0x01/0x02) 11. Set_Segment_Remap(0xA1); // Set SEG/Column Mapping (0xA0/0xA1) 12. Set_Common_Remap(0xC8); // Set COM/Row Scan Direction (0xC0/0xC8) 13. Set_Common_Config(0x12); // Set Alternative Configuration (0x02/0x12) 14. Set_Contrast_Control(Brightness); // Set SEG Output Current 15. Set_Precharge_Period(0x82); // Set Pre-Charge as 8 Clocks & Discharge as 2 Clocks 16. Set_VCOMH(0x34); // Set VCOM Deselect Level 17. Set_Entire_Display(0xA4); // Disable Entire Display On (0xA4/0xA5) 18. Set_Inverse_Display(0xA6); // Disable Inverse Display On (0xA6/0xA7) 19. 20. Fill_RAM(0x00); // Clear Screen 21. 22. Set_Display_On_Off(0xAF); // Display On (0xAE/0xAF) 23. } c
- 该功能函数位于app/src/main.c文件中,实现在屏幕固定处画一个label, 显示一串字符串。
1. static void gui_label_create(void) 2. { 3. lv_obj_t *p = lv_label_create(lv_scr_act(), NULL); 4. lv_label_set_long_mode(p, LV_LABEL_LONG_BREAK); 5. lv_label_set_align(p, LV_LABEL_ALIGN_CENTER); 6. lv_obj_set_pos(p, 0, 4); 7. lv_obj_set_size(p, 128, 60); 8. lv_label_set_text(p, "THEAD RISC-V\nGUI\nDEMO"); 9. } c
3.4. 编译运行
编译通过后,点击下载,下载成功后复位运行。可看屏上显示"THEAD RISC-V\nGUI\nDEMO" 字符串。
4. 总结
本例程介绍了如何通过SPI接口来实现对OLED屏幕的图形显示。后续还有更多的开发例程,敬请期待!