上篇文章已经移植rtthread nano,boot,usb。今天开始移植u8g2到oled上,资源足够,完全没必要自己再重复造轮子。
我使用的oled是中景电子的0.96寸屏幕,128X64,显存芯片:sh1106.由于u8g2给我们做了大量的寄存器配置工作,我们不用关心寄存器功能(当然如果是做低功耗之类的,需要了解oled的省电原理),我们只需要注册回调初始化函数即可。
u8g2的库中我们使用csrc中文件,选择所有文件,注意这种u8x8_d_芯片名称.c的文件只要选择一个,我这里选择的是u8x8_d_ssd1306_128x64_noname.c,因为sh1106芯片就是调用这个文件,所以这个。这是我的目录结构
这里不要害怕选了这么多文件编译之后单片机存不下,在keil优化等级选择level 2,编译器会把没用到的直接丢弃。
1.u8g2_d_memory.c 这里我们选择下面这个函数,其他的全部删掉,如果U8G2_USE_DYNAMIC_ALLOC 这个开启了,请注释掉,这里不选择动态内存。
uint8_t *u8g2_m_16_8_f(uint8_t *page_cnt) { #ifdef U8G2_USE_DYNAMIC_ALLOC *page_cnt = 8; return 0; #else static uint8_t buf[1024]; *page_cnt = 8; return buf; #endif }2. 找到u8g2_d_setup.c ,找到下面这个函数,其他的全部删掉。
/* sh1106 f */ void u8g2_Setup_sh1106_i2c_128x64_noname_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb) { uint8_t tile_buf_height; uint8_t *buf; u8g2_SetupDisplay(u8g2, u8x8_d_sh1106_128x64_noname, u8x8_cad_ssd13xx_fast_i2c, byte_cb, gpio_and_delay_cb); buf = u8g2_m_16_8_f(&tile_buf_height); u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation); }3. 接下来新建一个gui_task.c,写入以下代码
static u8g2_t gui_u8g2; volatile char ui_key = 0; //由于用的是硬件IIC,这里啥都不用干。 uint8_t ft32_msg_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { #ifdef USING_SOFT_IIC switch(msg) { case U8X8_MSG_DELAY_MILLI: board_delay(1); // 延迟1ms break; case U8X8_MSG_DELAY_10MICRO: board_udelay(10);延迟10us break; case U8X8_MSG_DELAY_100NANO: __NOP(); // 1tick break; case U8X8_MSG_GPIO_I2C_CLOCK: if(arg_int) OLED_SCL_Set(); // 这里就是clk gpio的初始化。注意是开漏输出,推挽会把总线钳住,会通信异常 else OLED_SCL_Clr(); // break; case U8X8_MSG_GPIO_I2C_DATA: if(arg_int) OLED_SDA_Set(); // 这里就是data gpio的初始化。注意是开漏输出,推挽会把总线钳住,会通信异常 else OLED_SDA_Clr(); break; default: return 0; } return 1; #else switch(msg) { case U8X8_MSG_GPIO_AND_DELAY_INIT: break; case U8X8_MSG_DELAY_MILLI: // board_delay(arg_int); break; case U8X8_MSG_GPIO_I2C_CLOCK: break; case U8X8_MSG_GPIO_I2C_DATA: break; default: return 0; } return 1; // command processed successfully. #endif } //IIC回调函数 uint8_t u8x8_byte_oled_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { static uint8_t buffer[32]; /* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */ static uint8_t buf_idx; uint8_t *data; switch(msg) { case U8X8_MSG_BYTE_SEND: data = (uint8_t *)arg_ptr; while( arg_int > 0 ) { buffer[buf_idx++] = *data; data++; arg_int--; } break; case U8X8_MSG_BYTE_INIT: /* add your custom code to init i2c subsystem */ //oled_IIC_init(); break; case U8X8_MSG_BYTE_START_TRANSFER: buf_idx = 0; break; case U8X8_MSG_BYTE_END_TRANSFER: IIC_Send(buffer, buf_idx); // 这里开始传数据,IIC通信请自己实现。 break; default: return 0; } return 1; } void gui_thread_entry(void *parameter) { rt_thread_delay(100); // 这里延迟是防止上电oled还没稳定。 // 这里U8G2_R2代表旋转角度,请根据自身的设备设置旋转角度。 u8g2_Setup_sh1106_i2c_128x64_noname_f(&gui_u8g2, U8G2_R2, u8x8_byte_oled_hw_i2c, ft32_msg_cb); // 初始化注册 u8g2_InitDisplay(&gui_u8g2); // 初始化屏幕 u8g2_SetPowerSave(&gui_u8g2, 0);// 关闭省电。 extern void my_mui_init(u8g2_t *u8g2); my_mui_init(&gui_u8g2); // 初始化mui 这里后面说明 for(;;) { //gui_process(); extern void draw_menu(u8g2_t *u8g2,char key); draw_menu(&gui_u8g2,ui_key); // 画图,ui_key 表示按键输入,自己实现。 ui_key = 0; // rt_thread_delay(150); } } int gui_task_int() { rt_thread_t tid; #ifdef RT_USING_HEAP tid = rt_thread_create("gui", gui_thread_entry, RT_NULL, RT_GUI_THREAD_STACK_SIZE, 5, 20); RT_ASSERT(tid != RT_NULL); #else rt_err_t result; tid = &gui_thread; result = rt_thread_init(tid, "gui", gui_thread_entry, RT_NULL, rt_thread_gui_stack, sizeof(rt_thread_gui_stack), 5, 20); RT_ASSERT(result == RT_EOK); /* if not define RT_USING_HEAP, using to eliminate the warning */ (void)result; #endif rt_thread_startup(tid); rt_kprintf("%s init\n", __func__); return 0; } INIT_APP_EXPORT(gui_task_int);4. 接下来新建一个menu.c,我们移植官方的例程,将菜单功能做进来。
#include <mui.h> #include <mui_u8g2.h> mui_t mui; /* global variables which form the communication gateway between the user interface and the rest of the code */ uint8_t number_input = 2; // variable where the user can input a number between 0 and 9 uint8_t number_input2 = 100; // variable where the user can input a number between 0 and 9 uint8_t fruit_input = 2; uint8_t fruit_input2 = 2; uint8_t my_value3 = 0; uint8_t color_input = 0; uint8_t food_input = 0; uint8_t checkbox_input = 0; uint8_t direction_input = 0; uint8_t text_input[4] = { ' ',' ',' ',' '} ; uint8_t exit_code = 0; uint16_t list_selection = 0; uint16_t list_selection2 = 0; uint16_t list_selection3 = 0; uint16_t list_selection4 = 0; uint8_t array_pos = 0; uint8_t array_led_off_time[4] = { 10, 5, 3, 1}; uint8_t led_off_time = 0; uint8_t array_led_on_time[4] = { 10, 5, 3, 1}; uint8_t led_on_time = 0; //处理函数 uint8_t muif_array_pos_selection(mui_t *ui, uint8_t msg) { uint8_t return_value = 0; switch(msg) { case MUIF_MSG_FORM_START: led_off_time = array_led_off_time[array_pos]; led_on_time = array_led_on_time[array_pos]; return_value = mui_u8g2_u8_min_max_wm_mse_pi(ui, msg); break; case MUIF_MSG_FORM_END: return_value = mui_u8g2_u8_min_max_wm_mse_pi(ui, msg); break; case MUIF_MSG_CURSOR_SELECT: case MUIF_MSG_EVENT_NEXT: case MUIF_MSG_EVENT_PREV: array_led_off_time[array_pos] = led_off_time; array_led_on_time[array_pos] = led_on_time; return_value = mui_u8g2_u8_min_max_wm_mse_pi(ui, msg); led_off_time = array_led_off_time[array_pos]; led_on_time = array_led_on_time[array_pos]; break; default: return_value = mui_u8g2_u8_min_max_wm_mse_pi(ui, msg); } return return_value; } const char *animals[] = { "Bird", "Bison", "Cat", "Crow", "Dog", "Elephant", "Fish", "Gnu", "Horse", "Koala", "Lion", "Mouse", "Owl", "Rabbit", "Spider", "Turtle", "Zebra" }; uint16_t list_get_cnt(void *data) { return sizeof(animals)/sizeof(*animals); /* number of animals */ } const char *list_get_str(void *data, uint16_t index) { return animals[index]; } uint16_t menu_get_cnt(void *data) { return 10; /* number of menu entries */ } const char *menu_get_str(void *data, uint16_t index) { static const char *menu[] = { MUI_0 "Goto Main Menu", MUI_10 "Enter a number", MUI_11 "Parent/Child Selection", MUI_13 "Checkbox", MUI_14 "Radio Selection", MUI_15 "Text Input", MUI_16 "Single Line Selection", MUI_17 "List Line Selection", MUI_18 "Parent/Child List", MUI_20 "Array Edit", }; return menu[index]; } //菜单结构 fds_t fds_data[] = /* top level main menu */ MUI_FORM(0) MUI_STYLE(1) MUI_LABEL(5,10, "Main Menu") MUI_XY("HR", 0,13) MUI_STYLE(0) MUI_DATA("GP", MUI_10 "Enter a number|" MUI_11 "Parent/Child Selection|" MUI_13 "Checkbox|" MUI_14 "Radio Selection|" MUI_15 "Text Input|" MUI_16 "Single Line Selection|" MUI_17 "List Line Selection|" MUI_18 "Parent/Child List|" MUI_20 "Array Edit|" MUI_3 "Alternative Menu" ) MUI_XYA("GC", 5, 25, 0) MUI_XYA("GC", 5, 37, 1) MUI_XYA("GC", 5, 49, 2) MUI_XYA("GC", 5, 61, 3) MUI_FORM(3) MUI_STYLE(1) MUI_LABEL(5,10, "Alternative Menu") MUI_XY("HR", 0,13) MUI_STYLE(0) MUI_XYA("LG", 5, 25, 0) MUI_XYA("LG", 5, 37, 1) MUI_XYA("LG", 5, 49, 2) MUI_XYA("LG", 5, 61, 3) /* number entry demo */ MUI_FORM(10) MUI_STYLE(1) MUI_LABEL(5,10, "Number Menu") MUI_XY("HR", 0,13) MUI_STYLE(0) MUI_LABEL(5,27, "Number [mse]:") MUI_XY("IN",76, 27) MUI_LABEL(5,41, "Number [mud]:") MUI_XY("IH",76, 41) MUI_XYAT("G1",64, 59, 0, " OK ") /* parent / child selection */ MUI_FORM(11) MUI_STYLE(1) MUI_LABEL(5,10, "Parent/Child Selection") MUI_XY("HR", 0,13) MUI_STYLE(0) MUI_LABEL(5,24, "Color:") MUI_XYAT("IC",80, 24, 12, "red|orange|yellow|green|cyan|azure|blue|violet|magenta|rose") /* jump to sub form 12 */ MUI_LABEL(5,42, "We need:") MUI_STYLE(3) MUI_XYAT("ID",80, 49, 21, "\x30|\x31|\x32|\x33|\x34|\x35|\x36|\x37|\x38|\x39|\x40|\x41|\x42|\x43") MUI_STYLE(0) MUI_XYAT("G1",64, 60, 0, " OK ") /* combo box color selection */ MUI_FORM(12) MUI_STYLE(1) MUI_LABEL(5,10, "Color Selection") MUI_XY("HR", 0,13) MUI_STYLE(0) MUI_XYA("OC", 5, 30, 0) /* takeover the selection text from calling field ("red") */ MUI_XYA("OC", 5, 42, 1) /* takeover the selection text from calling field ("green") */ MUI_XYA("OC", 5, 54, 2) /* */ /* no ok required, clicking on the selection, will jump back */ /* Checkbox demo */ MUI_FORM(13) MUI_STYLE(1) MUI_LABEL(5,10, "Checkbox Menu") MUI_XY("HR", 0,13) MUI_STYLE(0) MUI_LABEL(5,30, "Checkbox:") MUI_XY("CB",60, 30) MUI_XYAT("G1",64, 59, 0, " OK ") /* Radio selection demo */ MUI_FORM(14) MUI_STYLE(1) MUI_LABEL(5,10, "Radio Selection Menu") MUI_XY("HR", 0,13) MUI_STYLE(0) MUI_XYAT("RS",10, 28,0,"North") MUI_XYAT("RS",10, 40,1,"South") MUI_XYAT("RS",65, 28,2,"East") MUI_XYAT("RS",65, 40,3,"West") MUI_XYAT("G1",64, 59, 0, " OK ") /* text demo */ MUI_FORM(15) MUI_STYLE(1) MUI_LABEL(5,10, "Enter Text Menu") MUI_XY("HR", 0,13) MUI_STYLE(0) MUI_LABEL(5,30, "Text:") MUI_STYLE(2) MUI_XY("T0",40, 30) MUI_XY("T1",48, 30) MUI_XY("T2",56, 30) MUI_XY("T3",64, 30) MUI_STYLE(0) MUI_XYAT("G1",64, 59, 0, " OK ") /* single line selection */ MUI_FORM(16) MUI_STYLE(1) MUI_LABEL(5,10, "Single Line Selection") MUI_XY("HR", 0,13) MUI_STYLE(0) MUI_LABEL(5,29, "Fruit [mse]:") MUI_XYAT("IF",60, 29, 60, "Banana|Apple|Melon|Cranberry") MUI_LABEL(5,43, "Fruit [mud]:") MUI_XYAT("IG",60, 43, 60, "Banana|Apple|Melon|Cranberry") MUI_XYAT("G1",64, 59, 0, " OK ") /* long list example with list callback functions */ MUI_FORM(17) MUI_STYLE(1) MUI_LABEL(5,10, "List Line Selection") MUI_XY("HR", 0,13) MUI_STYLE(0) MUI_LABEL(5,29, "List [mse]:") MUI_XYA("L1",60, 29, 60) MUI_LABEL(5,43, "List [mud]:") MUI_XYA("L2",60, 43, 60) MUI_XYAT("G1",64, 59, 0, " OK ") /* parent / child selection */ MUI_FORM(18) MUI_STYLE(1) MUI_LABEL(5,10, "Parent/Child List") MUI_XY("HR", 0,13) MUI_STYLE(0) MUI_LABEL(5,29, "Animal:") MUI_XYA("LP",50, 29, 19) /* jump to sub form 19 */ MUI_XYAT("G1",64, 59, 0, " OK ") /* combo box color selection */ MUI_FORM(19) MUI_STYLE(1) MUI_LABEL(5,10, "Animal Selection") MUI_XY("HR", 0,13) MUI_STYLE(0) MUI_XYA("LC", 5, 30, 0) /* takeover the selection text from calling field ("red") */ MUI_XYA("LC", 5, 42, 1) /* takeover the selection text from calling field ("green") */ MUI_XYA("LC", 5, 54, 2) /* */ /* no ok required, clicking on the selection, will jump back */ MUI_FORM(20) MUI_STYLE(1) MUI_LABEL(5,10, "Array Edit") MUI_XY("HR", 0,13) MUI_STYLE(0) MUI_LABEL(5,24, "Position:") MUI_XY("AP",76, 24) MUI_LABEL(5,35, "LED off:") MUI_XY("AF",76, 35) MUI_LABEL(5,46, "LED on:") MUI_XY("AN",76, 46) MUI_XYAT("G1",64, 59, 0, " OK ") /* combo box Food & Drink Selection, called from form 11 */ MUI_FORM(21) MUI_STYLE(1) MUI_LABEL(5,10, "Food & Drink Selection") MUI_XY("HR", 0,13) MUI_STYLE(3) MUI_XYA("OD", 3, 45, 0) MUI_XYA("OD", 28, 45, 1) MUI_XYA("OD", 53, 45, 2) MUI_XYA("OD", 78, 45, 3) MUI_XYA("OD", 103, 45, 4) ; uint8_t mui_hrule(mui_t *ui, uint8_t msg) { u8g2_t *u8g2 = mui_get_U8g2(ui); switch(msg) { case MUIF_MSG_DRAW: u8g2_DrawHLine(u8g2, 0, mui_get_y(ui), u8g2_GetDisplayWidth(u8g2)); break; } return 0; } //注册表 muif_t muif_list[] = { /* normal text style */ MUIF_U8G2_FONT_STYLE(0, u8g2_font_helvR08_tr), /* bold text style */ MUIF_U8G2_FONT_STYLE(1, u8g2_font_helvB08_tr), /* monospaced font */ MUIF_U8G2_FONT_STYLE(2, u8g2_font_profont12_tr), /* food and drink */ MUIF_U8G2_FONT_STYLE(3, u8g2_font_streamline_food_drink_t), /* horizontal line (hrule) */ MUIF_RO("HR", mui_hrule), /* main menu */ MUIF_RO("GP",mui_u8g2_goto_data), MUIF_BUTTON("GC", mui_u8g2_goto_form_w1_pi), /* Goto Form Button where the width is equal to the size of the text, spaces can be used to extend the size */ MUIF_BUTTON("G1", mui_u8g2_btn_goto_wm_fi), /* input for a number between 0 to 9 */ MUIF_U8G2_U8_MIN_MAX("IN", &number_input, 0, 9, mui_u8g2_u8_min_max_wm_mse_pi), /* input for a number between 0 to 100 */ MUIF_U8G2_U8_MIN_MAX("IH", &number_input2, 0, 100, mui_u8g2_u8_min_max_wm_mud_pi), /* input for text with four chars */ MUIF_VARIABLE("T0", text_input+0, mui_u8g2_u8_char_wm_mud_pi), MUIF_VARIABLE("T1", text_input+1, mui_u8g2_u8_char_wm_mud_pi), MUIF_VARIABLE("T2", text_input+2, mui_u8g2_u8_char_wm_mud_pi), MUIF_VARIABLE("T3", text_input+3, mui_u8g2_u8_char_wm_mud_pi), /* input for a fruit (0..3), implements a selection, where the user can cycle through the options */ MUIF_VARIABLE("IF",&fruit_input,mui_u8g2_u8_opt_line_wa_mse_pi), MUIF_VARIABLE("IG",&fruit_input2,mui_u8g2_u8_opt_line_wa_mud_pi), /* checkbox */ MUIF_VARIABLE("CB",&checkbox_input,mui_u8g2_u8_chkbox_wm_pi), /* the following two fields belong together and implement a single selection combo box to select a color */ MUIF_VARIABLE("IC",&color_input,mui_u8g2_u8_opt_parent_wm_pi), MUIF_VARIABLE("OC",&color_input,mui_u8g2_u8_opt_radio_child_w1_pi), MUIF_VARIABLE("ID",&food_input,mui_u8g2_u8_opt_parent_wm_pi), MUIF_VARIABLE("OD",&food_input,mui_u8g2_u8_opt_child_wm_pi), /* radio button style */ MUIF_VARIABLE("RS",&direction_input,mui_u8g2_u8_radio_wm_pi), MUIF_U8G2_U16_LIST("L1", &list_selection, NULL, list_get_str, list_get_cnt, mui_u8g2_u16_list_line_wa_mse_pi), MUIF_U8G2_U16_LIST("L2", &list_selection2, NULL, list_get_str, list_get_cnt, mui_u8g2_u16_list_line_wa_mud_pi), MUIF_U8G2_U16_LIST("LP", &list_selection3, NULL, list_get_str, list_get_cnt, mui_u8g2_u16_list_parent_wm_pi), MUIF_U8G2_U16_LIST("LC", &list_selection3, NULL, list_get_str, list_get_cnt, mui_u8g2_u16_list_child_w1_pi), MUIF_U8G2_U16_LIST("LG", &list_selection4, NULL, menu_get_str, menu_get_cnt, mui_u8g2_u16_list_goto_w1_pi), /* MUI_GOTO uses the fixed ".G" id and is intended for goto buttons. This is a full display width style button */ MUIF_GOTO(mui_u8g2_btn_goto_w1_pi), /* register MUI_LABEL, which is used to place read only text on a form */ MUIF_U8G2_LABEL(), /* array example */ MUIF_U8G2_U8_MIN_MAX("AP", &array_pos, 0, 3, muif_array_pos_selection), MUIF_U8G2_U8_MIN_MAX("AF", &led_off_time, 0, 20, mui_u8g2_u8_min_max_wm_mse_pi), MUIF_U8G2_U8_MIN_MAX("AN", &led_on_time, 0, 20, mui_u8g2_u8_min_max_wm_mse_pi), }; void my_mui_init(u8g2_t *u8g2) { u8g2_SetFontMode(u8g2,1); // 设置字体,1表示正常,0应该是反色 //初始化 mui_Init(&mui,u8g2,fds_data,muif_list,sizeof(muif_list)/sizeof(muif_t)); // 初始进哪个表。 mui_GotoForm(&mui,0,0); } void draw_menu(u8g2_t *u8g2,char key) { switch(key) { case 'D': // 确认按键 mui_SendSelect(&mui); break; case 'B': // 下一个按键处理 mui_NextField(&mui); break; case 'A': // 上一个按键 mui_PrevField(&mui); break; default: // 不处理 break; } if(mui_IsFormActive(&mui)) { u8g2_ClearBuffer(u8g2); // 每次画图前最好清屏 mui_Draw(&mui); // 画菜单。 u8g2_SendBuffer(u8g2); // 发送给屏幕 } }效果展示: