/** * \brief Graph task * * This task runs in the background to draw a pseudo-random graph to a dedicated * display buffer. If the user selects a different screen than the graph, it * will continue to update even though it is not visible until the graph screen * is selected again. * * \param params Parameters for the task. (Not used.) */ static void graph_task(void *params) { gfx_coord_t x, y, old_y; uint8_t current_value; EventBits_t event_bits; const TickType_t ticks_to_wait = 10 / portTICK_PERIOD_MS; x = 0; current_value = graph_noise; for(;;) { /* Wait a maximum of 10ms for either bit 0 or bit 1 to be set within the event group. Not clear the bits before exiting. */ event_bits = xEventGroupWaitBits( event_group, /* The event group being tested. */ EVENT_DISPLAY_INIT | EVENT_DISPLAY_GRAPH, /* The bits within the event group to wait for. */ pdFALSE, /* Bit should not be cleared before returning. */ pdFALSE, /* Don't wait for both bits, either bit will do. */ ticks_to_wait);/* Wait a maximum of 10ms for either bit to be set. */ if((event_bits & EVENT_DISPLAY_INIT) || (event_bits & EVENT_DISPLAY_GRAPH)) { oled1_set_led_state(&oled1, OLED1_LED1_ID, true); // Compute new noise sample and value of current graph sample graph_noise = (graph_noise * 173) + 1; current_value = ((uint16_t)graph_noise + current_value) / 2; xSemaphoreTake(display_mutex, portMAX_DELAY); // Scale graph value so it fits within the canvas y = CANVAS_GRAPH_Y_OFFSET + ((uint16_t)CANVAS_HEIGHT * current_value) / 256; // Clear previous graph point.. gfx_mono_draw_vertical_line(x, CANVAS_GRAPH_Y_OFFSET, CANVAS_HEIGHT, GFX_PIXEL_CLR); // ..and draw a continuous graph using lines if (x == 0) { gfx_mono_draw_pixel(x, y, GFX_PIXEL_SET); } else { gfx_mono_draw_line(x - 1, old_y, x, y, GFX_PIXEL_SET); } xSemaphoreGive(display_mutex); old_y = y; if (++x >= CANVAS_WIDTH) { x = 0; } oled1_set_led_state(&oled1, OLED1_LED1_ID, false); } vTaskDelay(GRAPH_TASK_DELAY); } }
int main(void){ struct gfx_mono_bitmap smiley; struct gfx_mono_bitmap smiley_flash; board_init(); sysclk_init(); /* Initialize GFX lib. Will configure the display controller and * create a framebuffer in RAM if the controller lack two-way communication */ gfx_mono_init(); // Setup bitmap struct for bitmap stored in RAM smiley.type = GFX_MONO_BITMAP_RAM; smiley.width = 8; smiley.height = 16; smiley.data.pixmap = smiley_data; // Setup bitmap for bitmap stored in FLASH smiley_flash.type = GFX_MONO_BITMAP_PROGMEM; smiley_flash.width = 8; smiley_flash.height = 16; smiley_flash.data.progmem = flash_bitmap; // Output bitmaps to display gfx_mono_put_bitmap(&smiley, 50, 0); gfx_mono_put_bitmap(&smiley_flash, 0, 0); //Draw horizontal an vertical lines with length 128 and 32 pixels gfx_mono_draw_vertical_line(1, 0, 32, GFX_PIXEL_SET); gfx_mono_draw_horizontal_line(0, 2, 128, GFX_PIXEL_SET); // Draw a line from the top-left corner to the bottom right corner gfx_mono_draw_line(0, 0, 127, 31, GFX_PIXEL_SET); // Draw a rectangle with upper left corner at x=20,y=10 - width=height=10px gfx_mono_draw_rect(20, 10, 10, 10,GFX_PIXEL_SET); // Draw a 10x10 filled rectangle at x=10,y=10 gfx_mono_draw_filled_rect(10, 10, 10, 10, GFX_PIXEL_SET); // Draw a whole circle at x=50,y=16 with radios=8 and all octants drawn gfx_mono_draw_circle(50, 16, 8, GFX_PIXEL_SET,GFX_WHOLE); // Draw a filled circle with all quadrant drawn gfx_mono_draw_filled_circle(80, 16, 10, GFX_PIXEL_SET, GFX_WHOLE); while(true) { // This while left intentionally blank to halt execution. } }
/** * \brief Graph task * * This task runs in the background to draw a pseudo-random graph to a dedicated * display buffer. If the user selects a different screen than the graph, it * will continue to update even though it is not visible until the graph screen * is selected again. * * \param params Parameters for the task. (Not used.) */ static void graph_task(void *params) { gfx_coord_t x, y, old_y; uint8_t current_value; x = 0; current_value = graph_noise; for(;;) { oled1_set_led_state(&oled1, OLED1_LED1_ID, true); // Compute new noise sample and value of current graph sample graph_noise = (graph_noise * 173) + 1; current_value = ((uint16_t)graph_noise + current_value) / 2; xSemaphoreTake(display_mutex, portMAX_DELAY); // Scale graph value so it fits within the canvas y = CANVAS_GRAPH_Y_OFFSET + ((uint16_t)CANVAS_HEIGHT * current_value) / 256; // Clear previous graph point.. gfx_mono_draw_vertical_line(x, CANVAS_GRAPH_Y_OFFSET, CANVAS_HEIGHT, GFX_PIXEL_CLR); // ..and draw a continuous graph using lines if (x == 0) { gfx_mono_draw_pixel(x, y, GFX_PIXEL_SET); } else { gfx_mono_draw_line(x - 1, old_y, x, y, GFX_PIXEL_SET); } xSemaphoreGive(display_mutex); old_y = y; if (++x >= CANVAS_WIDTH) { x = 0; } oled1_set_led_state(&oled1, OLED1_LED1_ID, false); vTaskDelay(GRAPH_TASK_DELAY); } }
/** * \internal * \brief Test drawing a vertical line * * This test draws a vertical line and checks that this is done. * * \param test Current test case. */ static void run_draw_vertical_line_test(const struct test_case *test) { uint8_t actual[GFX_MONO_LCD_WIDTH]; uint8_t expected_page1[GFX_MONO_LCD_WIDTH]; uint8_t expected_page2[GFX_MONO_LCD_WIDTH]; uint8_t expected_empty[GFX_MONO_LCD_WIDTH]; uint8_t page; // Clear entire display gfx_mono_draw_filled_rect(0, 0, GFX_MONO_LCD_WIDTH, GFX_MONO_LCD_HEIGHT, GFX_PIXEL_CLR); // Fill page buffer holding the top of the line set_buffer(expected_page1, 0x00); expected_page1[4] = 0xFC; // Fill page buffer holding the bottom of the line set_buffer(expected_page2, 0x00); expected_page2[4] = 0x1F; // Fill empty page buffer set_buffer(expected_empty, 0x00); // Draw a vertical line at position 4, 2 with length 11 gfx_mono_draw_vertical_line(4, 2, 11, GFX_PIXEL_SET); // Get all pages from display and check that the line is drawn for (page = 0; page < GFX_MONO_LCD_PAGES; page++) { gfx_mono_get_page(actual, page, 0, GFX_MONO_LCD_WIDTH); if (page == 0) { test_assert_true(test, is_page_correct(actual, expected_page1), "Page %d with line not matching expected page", page); } else if (page == 1) { test_assert_true(test, is_page_correct(actual, expected_page2), "Page %d with line not matching expected page", page); } else { test_assert_true(test, is_page_correct(actual, expected_empty), "Empty page %d not matching expected page", page); } } }
/** * \brief Main demo task * * This task keeps track of which screen the user has selected, which tasks * to resume/suspend to draw the selected screen, and also draws the menu bar. * * The menu bar shows which screens the user can select by clicking the * corresponding buttons on the OLED1 Xplained Pro: * - \ref graph_task() "graph" (selected at start-up) * - \ref terminal_task() "term." * - \ref about_task() "about" * * \param params Parameters for the task. (Not used.) */ static void main_task(void *params) { bool graph_buffer_initialized = false; bool selection_changed = true; bool select_graph_buffer; enum menu_items current_selection = MENU_ITEM_GRAPH; gfx_coord_t x, y, display_y_offset; xTaskHandle temp_task_handle = NULL; for(;;) { // Show that task is executing oled1_set_led_state(&oled1, OLED1_LED3_ID, true); // Check buttons to see if user changed the selection if (oled1_get_button_state(&oled1, OLED1_BUTTON1_ID) && (current_selection != MENU_ITEM_GRAPH)) { current_selection = MENU_ITEM_GRAPH; selection_changed = true; } else if (oled1_get_button_state(&oled1, OLED1_BUTTON2_ID) && (current_selection != MENU_ITEM_TERMINAL)) { current_selection = MENU_ITEM_TERMINAL; selection_changed = true; } else if (oled1_get_button_state(&oled1, OLED1_BUTTON3_ID) && (current_selection != MENU_ITEM_ABOUT)) { current_selection = MENU_ITEM_ABOUT; selection_changed = true; } // If selection changed, handle the selection if (selection_changed) { // Wait for and take the display semaphore before doing any changes. xSemaphoreTake(display_mutex, portMAX_DELAY); // We can now safely suspend the previously resumed task if (temp_task_handle) { vTaskSuspend(temp_task_handle); temp_task_handle = NULL; } // Select the new drawing task and corresponding display buffer switch (current_selection) { case MENU_ITEM_GRAPH: // Graph task runs continuously, no need to set task handle select_graph_buffer = true; break; case MENU_ITEM_TERMINAL: temp_task_handle = terminal_task_handle; select_graph_buffer = false; break; default: case MENU_ITEM_ABOUT: temp_task_handle = about_task_handle; select_graph_buffer = false; } // Select and initialize display buffer to use. display_y_offset = select_graph_buffer ? CANVAS_GRAPH_Y_OFFSET : 0; // Draw the menu bar (only needs to be done once for graph) if (!select_graph_buffer || !graph_buffer_initialized) { // Clear the selected display buffer first gfx_mono_draw_filled_rect(0, display_y_offset, GFX_MONO_LCD_WIDTH, GFX_MONO_LCD_HEIGHT / 2, GFX_PIXEL_CLR); // Draw menu lines, each item with height MENU_HEIGHT pixels y = display_y_offset + CANVAS_HEIGHT; gfx_mono_draw_horizontal_line(0, y, GFX_MONO_LCD_WIDTH, GFX_PIXEL_SET); x = MENU_ITEM_WIDTH; y++; for (uint8_t i = 0; i < (MENU_NUM_ITEMS - 1); i++) { gfx_mono_draw_vertical_line(x, y, MENU_HEIGHT, GFX_PIXEL_SET); x += 1 + MENU_ITEM_WIDTH; } // Highlight the current selection gfx_mono_draw_rect(current_selection * (1 + MENU_ITEM_WIDTH), y, MENU_ITEM_WIDTH, MENU_HEIGHT, GFX_PIXEL_SET); // Draw the menu item text x = (MENU_ITEM_WIDTH / 2) - ((5 * SYSFONT_WIDTH) / 2); y += (MENU_HEIGHT / 2) - (SYSFONT_HEIGHT / 2); for (uint8_t i = 0; i < MENU_NUM_ITEMS; i++) { gfx_mono_draw_string(menu_items_text[i], x, y, &sysfont); x += 1 + MENU_ITEM_WIDTH; } graph_buffer_initialized = true; } // Set display controller to output the new buffer ssd1306_set_display_start_line_address(display_y_offset); // We are done modifying the display, so give back the mutex xSemaphoreGive(display_mutex); selection_changed = false; // If a task handle was specified, resume it now if (temp_task_handle) { vTaskResume(temp_task_handle); } } // Show that task is done oled1_set_led_state(&oled1, OLED1_LED3_ID, false); vTaskDelay(MAIN_TASK_DELAY); } }