~~呆萌的瓦力平衡機器人~~的顯示UI我希望做得精致一些 所以尋覓了好久,最終尋來了U8g2這款精巧的圖形庫,這款ui圖形庫可以算得上是圖形庫里面的瑞士小軍🔪,深得吾愛,所以該系列的文章既是我篩選圖形庫程序的心路歷程,也是在選中U8g2這款圖形庫后的一些開發記錄;
U8g2圖形庫簡介:
1.U8g2 是用于嵌入式設備的單色圖形庫,
- 支持顯示控制器:SSD1305、SSD1306、SSD1309、SSD1316、SSD1322、SSD1325、SSD1327、SSD1329、SSD1606、 SSD1607、 SH1106、SH1107、SH1108、SH1122、T6963、RA8835、LC7981、PCD8544、PCF8812、HX1230、UC1601、UC1604、UC1608、UC1610、 UC1611, UC1617, UC1701, ST7511, ST7528, ST7565, ST7567, ST7571, ST7586, ST7588, ST75256, ST75320, NT7 534, ST7920, IST3020, IST7920, LD7032, KS0108, KS0713, SED1520, SBN1661, IL3820, MAX7219 等,
2.U8g2 還包括 U8x8 庫,U8g2 和 U8x8 的功能包括:
- U8g2
- 包括所有圖形程式(線/框/圓畫),
- 支持很豐富的字體庫,
- 需要微控制器中的一些記憶體來渲染顯示屏(需要消耗較多的ram空間資源),
- U8x8
- 僅文本輸出(字符)設備,
- 僅允許使用每個字符固定大小(8x8 像素)的字體,
- 直接寫到顯示屏上,無需微控制器中的緩沖(需要消耗較少的ram空間資源),
U8g2圖形庫使用技巧(硬體驅動介面部分的分析和選擇):
U8g2圖形庫的驅動介面主要取決于所選用的lcd螢屏的驅動芯片方案,目前常用的驅動介面多為spi和i2c兩種串行總線,如果需要較高的重繪幀率,spi的驅動方式是比較好的選擇,spi的驅動時鐘頻率一般可以達到8Mbit,而i2c的方式一般只能達到400Kbit,但是使用spi方式驅動的時候,需要比較多的io管腳資源,一般最少需要3個io(三線spi方式),而i2c方式一般只需要2個io就可以滿足,考慮到~~呆萌的瓦力平衡機器人~~包含了兩路對時序要求很高的foc電機控制,所以果斷選擇了比較高效的spi方式(選用的oled模組的驅動方案采用的是SSD1306,該方案既支持i2c方式,也支持spi的方式);

U8g2圖形庫使用技巧(軟體驅動程式分析和記錄):
U8g2圖形庫的代碼框架寫得非常的完善,只需要實作底層幾個讀寫介面函式,就可以把整個圖形庫驅動起來,它的幾乎所有的初始化函式介面都定義在u8g2_d_setup.c源檔案中,該源檔案實作了u8g2圖形庫所有支持lcd驅動方案的初始化方法,通過函式名來區分,初始化函式的名字包含了很多硬體相關的資訊,就拿我在使用的oled模組的初始化函式來作為例子,模組驅動方案為SSD1306,解析度為128x64,驅動介面為三線spi方式,所以可選的初始化方法如下:
u8g2_Setup_ssd1306_128x64_noname_1 /*芯片SSD1306,解析度128x64,128位元組頁大小*/
u8g2_Setup_ssd1306_128x64_noname_2 /*芯片SSD1306,解析度128x64,256位元組頁大小*/
u8g2_Setup_ssd1306_128x64_noname_f /*芯片SSD1306,解析度128x64,1024位元組頁大小*/
考慮到esp32的ram資源比較充足的,果斷選擇了u8g2_Setup_ssd1306_128x64_noname_f方法,到這里了就結束了!spi的驅動方式體現在哪里呢?
接著往里扒--->
void u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
該函式需要傳入四個引數--->
u8g2_t *u8g2; /*u8g2圖形庫結構體指標*/
const u8g2_cb_t *rotation; /*u8g2圖形庫ui旋轉:U8G2_R0(無旋轉),U8G2_R1(順時針90°),U8G2_R2(順時針180°)......*/
u8x8_msg_cb byte_cb; /*u8g2圖形庫位元組互動回呼函式*/
u8x8_msg_cb gpio_and_delay_cb; /*u8g2圖形庫gpio和延時回呼函式*/
所以和spi部分相關的就是--->
u8x8_msg_cb byte_cb; /*u8g2圖形庫位元組互動回呼函式*/
這個callback函式需要我們自己實作如下的方法--->
uint8_t u8x8_byte_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
uint8_t max_transfer_sz = 0;
switch(msg)
{
case U8X8_MSG_BYTE_SEND:
//ESP_LOGI(TAG, "U8X8_MSG_BYTE_SEND:%d,%d", arg_int ,spi_device_max_transfer_sz(u8g2_spi));
max_transfer_sz = spi_device_max_transfer_sz(u8g2_spi);
if (arg_int <= max_transfer_sz) {
u8g2_tran.length = arg_int*8,
u8g2_tran.tx_buffer = (const void *)arg_ptr,
u8g2_tran.rxlength = 0,
u8g2_tran.rx_buffer = NULL,
spi_device_polling_transmit(u8g2_spi, &u8g2_tran);
} else {
int packet_num = arg_int/max_transfer_sz;
int packet_remain = arg_int%max_transfer_sz;
uint8_t *data_prt = (uint8_t *)arg_ptr;
if (packet_num > 0) {
for (int i=0;i<packet_num;i++) {
u8g2_tran.length = max_transfer_sz*8,
u8g2_tran.tx_buffer = (const void *)data_prt,
u8g2_tran.rxlength = 0,
u8g2_tran.rx_buffer = NULL,
spi_device_polling_transmit(u8g2_spi, &u8g2_tran);
data_prt += max_transfer_sz;
}
}
if (packet_remain > 0) {
u8g2_tran.length = packet_remain*8,
u8g2_tran.tx_buffer = (const void *)data_prt,
u8g2_tran.rxlength = 0,
u8g2_tran.rx_buffer = NULL,
spi_device_polling_transmit(u8g2_spi, &u8g2_tran);
}
}
break;
case U8X8_MSG_BYTE_SET_DC:
u8g2_ssd1306_set_dc(arg_int);
break;
default:
return 0;
}
return 1;
}
而剩下的u8x8_msg_cb gpio_and_delay_cb這個callback主要是用來做輸入相關功能的回呼介面(瓦力ui配合rc遙控器實作的輸入互動就是通過該介面實作的),u8g2庫可以通過該介面來讀取硬體按鍵輸入的狀態資訊然后進行處理--->
uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, U8X8_UNUSED void *arg_ptr)
{
case U8X8_MSG_GPIO_MENU_SELECT:
u8x8->gpio_result = u8g2_gpio_menu_select();
vTaskDelay(3);
break;
case U8X8_MSG_GPIO_MENU_NEXT:
u8x8->gpio_result = u8g2_gpio_menu_next();
vTaskDelay(3);
break;
case U8X8_MSG_GPIO_MENU_PREV:
u8x8->gpio_result = u8g2_gpio_menu_prev();
vTaskDelay(3);
break;
case U8X8_MSG_GPIO_MENU_HOME:
u8x8->gpio_result = u8g2_gpio_menu_home();
vTaskDelay(3);
break;
case U8X8_MSG_GPIO_MENU_UP:
u8x8->gpio_result = u8g2_gpio_menu_up();
vTaskDelay(3);
break;
case U8X8_MSG_GPIO_MENU_DOWN:
u8x8->gpio_result = u8g2_gpio_menu_down();
vTaskDelay(3);
break;
case U8X8_MSG_GPIO_RESET:
u8g2_ssd1306_set_rst(arg_int);
break;
default:
break;
}
return 1;
}
到此底層相關的作業基本做完,接下來就是一些基于上層API介面的花活了--->
void u8g2_ssd1306_init(spi_device_handle_t handle)
{
u8g2_spi = handle;
//Initialize non-SPI GPIOs
gpio_set_direction(PIN_NUM_DC, GPIO_MODE_OUTPUT);
gpio_set_direction(PIN_NUM_RST, GPIO_MODE_OUTPUT);
//Reset the display
gpio_set_level(PIN_NUM_RST, 0);
vTaskDelay(100 / portTICK_RATE_MS);
gpio_set_level(PIN_NUM_RST, 1);
vTaskDelay(100 / portTICK_RATE_MS);
u8g2_Setup_ssd1306_128x64_noname_f(
&u8g2,
U8G2_R0,
u8x8_byte_hw_spi,
u8x8_gpio_and_delay); // init u8g2 structure
ESP_LOGI(TAG, "u8g2_InitDisplay");
u8g2_InitDisplay(&u8g2); // send init sequence to the display, display is in sleep mode after this,
ESP_LOGI(TAG, "u8g2_SetPowerSave");
u8g2_SetPowerSave(&u8g2, 0); // wake up display
ESP_LOGI(TAG, "u8g2_ClearBuffer");
u8g2_ClearBuffer(&u8g2);
ESP_LOGI(TAG, "u8g2_SetFont");
u8g2_SetFont(&u8g2, u8g2_font_6x10_mf);
ESP_LOGI(TAG, "u8g2_DrawStr");
u8g2_DrawStr(&u8g2, 20, 14, "esp32 foc +-x%/");
ESP_LOGI(TAG, "u8g2 init all done!");
}
雖然是花活,其中也有一些需要注意的地方,例如在設定字庫的時候,如果選擇了一些精簡字庫,會導致字符重繪出現字符像素殘留的現象,特別是在動態重繪一些數字的情景下,效果很差,十分影響顯示效果;
小結:
這次主要記錄了為~~呆萌的瓦力平衡機器人~~選擇ui影像庫的心路歷程以及在開始使用該圖形庫時所需要做的一些前期分析和實操程序中需要注意的技術要點;
以下是一些和本文相關的文章鏈接和U8g2的wiki鏈接:
一、~~呆萌的瓦力平衡機器人~~鏈接:
1.基于ESP32雙無刷FOC電機的瓦力平衡機器人(1);
2.基于ESP32雙無刷FOC電機的瓦力平衡機器人(2);
二、U8g2 wiki鏈接:
1.U8g2_wiki;
后續的計劃:
下一期準備記錄在做U8g2界面重繪和兩路foc電機驅動遇到的沖突問題,以及我的解決思路;
大家如果也感興趣,可以加qun交流學習(群里提供了豐富的esp32資料):

燥起來吧,sao年!!!!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/297924.html
標籤:其他
上一篇:第一節·導讀與通用開發歷程
下一篇:第一篇文章
