/**
 * \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);
	}
}
/**
 * \internal
 * \brief Helper function that draws a character from a font in progmem
 *        to the display
 *
 * This function will first calculate the start offset in the font character
 * data before iterating over the specific character data.
 *
 * Only pixels in the character that should be enabled are done so, the caller
 * is required to prepare the drawing area before printing a character to it.
 * This is done by the gfx_mono_draw_string() and
 * gfx_mono_draw_progmem_string() functions.
 *
 * \param ch       Character to be drawn
 * \param x        X coordinate on screen.
 * \param y        Y coordinate on screen.
 * \param font     Font to draw character in
 */
static void gfx_mono_draw_char_progmem(const char ch, const gfx_coord_t x,
		const gfx_coord_t y, const struct font *font)
{
	uint8_t PROGMEM_PTR_T glyph_data;
	uint16_t glyph_data_offset;
	uint8_t char_row_size;
	uint8_t rows_left;
	uint8_t i;

	/* Sanity check on parameters, assert if font is NULL. */
	Assert(font != NULL);

	gfx_coord_t inc_x = x;
	gfx_coord_t inc_y = y;

	char_row_size = font->width / CONFIG_FONT_PIXELS_PER_BYTE;
	if (font->width % CONFIG_FONT_PIXELS_PER_BYTE) {
		char_row_size++;
	}

	glyph_data_offset = char_row_size * font->height *
			((uint8_t)ch - font->first_char);
	glyph_data = font->data.progmem + glyph_data_offset;
	rows_left = font->height;

	do {
		uint8_t glyph_byte = 0;
		uint8_t pixelsToDraw = font->width;

		for (i = 0; i < pixelsToDraw; i++) {
			if (i % CONFIG_FONT_PIXELS_PER_BYTE == 0) {
				glyph_byte = PROGMEM_READ_BYTE(glyph_data);
				glyph_data++;
			}

			if ((glyph_byte & 0x80)) {
				gfx_mono_draw_pixel(inc_x, inc_y,
						GFX_PIXEL_SET);
			}

			inc_x += 1;
			glyph_byte <<= 1;
		}

		inc_y += 1;
		inc_x = x;
		rows_left--;
	} while (rows_left > 0);
}
Exemplo n.º 3
0
/**
 * \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 Helper function that draws a character from a font in hugemem
 *        to the display
 *
 * This function will first calculate the start offset in the font character
 * data before iterating over the specific character data.
 *
 * Only pixels in the character that should be enabled are done so, the caller
 * is required to prepare the drawing area before printing a character to it.
 * This is done by the gfx_mono_draw_string() and
 * gfx_mono_draw_progmem_string() functions.
 *
 * \param ch       Character to be drawn
 * \param x        X coordinate on screen.
 * \param y        Y coordinate on screen.
 * \param font     Font to draw character in
 */
static void gfx_mono_draw_char_hugemem(const char ch, const gfx_coord_t x,
		const gfx_coord_t y, const struct font *font)
{
	uint8_t i;
	uint8_t char_row_size;
	uint8_t glyph_size;
	uint16_t glyph_data_offset;
	uint8_t char_buff[EXTMEM_BUF_SIZE];
	uint8_t buffer_pos;
	uint8_t rows_left;

	/* Sanity check on parameters, assert if font is NULL. */
	Assert(font != NULL);

	gfx_coord_t inc_x = x;
	gfx_coord_t inc_y = y;

	char_row_size = font->width / CONFIG_FONT_PIXELS_PER_BYTE;
	if (font->width % CONFIG_FONT_PIXELS_PER_BYTE) {
		char_row_size++;
	}

	glyph_size = char_row_size * font->height;
	glyph_data_offset = glyph_size * ((uint8_t)ch - font->first_char);
	buffer_pos = EXTMEM_BUF_SIZE;
	rows_left = font->height;

	do {
		static uint8_t glyph_byte = 0;
		uint8_t pixelsToDraw = font->width;

		for (i = 0; i < pixelsToDraw; i++) {
			if (i % CONFIG_FONT_PIXELS_PER_BYTE == 0) {
				/* Read another byte from hugemem */
				if (buffer_pos >= EXTMEM_BUF_SIZE) {
					hugemem_ptr_t source
						= font->data.hugemem;
					source = (hugemem_ptr_t)
							((uint32_t)source +
							glyph_data_offset);

					hugemem_read_block(char_buff, source,
							EXTMEM_BUF_SIZE);

					glyph_data_offset += EXTMEM_BUF_SIZE;
					buffer_pos = 0;
				}

				glyph_byte = char_buff[buffer_pos];
				buffer_pos++;
			}

			/* Draw bit of glyph to screen */
			if ((glyph_byte & 0x80)) {
				gfx_mono_draw_pixel(inc_x, inc_y,
						GFX_PIXEL_SET);
			}

			inc_x += 1;
			glyph_byte <<= 1;
		}

		inc_y += 1;
		inc_x = x;
	} while (--rows_left > 0);
}