Skip to content

Commit

Permalink
Merge pull request #198 from espressif/examples/esp_jpeg_example
Browse files Browse the repository at this point in the history
esp_jpeg: Added example
  • Loading branch information
suda-morris authored Jul 13, 2023
2 parents f97d732 + aa11a20 commit 8593068
Show file tree
Hide file tree
Showing 13 changed files with 400 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .build-test-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ sh2lib/examples/http2_request:
enable:
- if: IDF_VERSION_MAJOR > 4 and INCLUDE_DEFAULT == 1
reason: Example uses sh2lib component which was introduced in IDF v5.0

esp_jpeg/examples/tjpgd:
enable:
- if: IDF_VERSION_MAJOR > 4 and IDF_TARGET in ["esp32", "esp32s2", "esp32s3"]
reason: Example depends on BSP, which is supported only for IDF >= 5.0 and limited targets
6 changes: 6 additions & 0 deletions esp_jpeg/examples/tjpgd/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(lcd_tjpgd)
57 changes: 57 additions & 0 deletions esp_jpeg/examples/tjpgd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |

## LCD tjpgd example

This example shows how to decode a jpeg image and display it on an SPI-interfaced LCD, and rotates the image periodically.

Example using initialization of the LCD from [ESP-BSP](https://github.com/espressif/esp-bsp) project. For change the Espressif's board, go to [idf_component.yml](main/idf_component.yml) and change `esp-box` to another board from BSP.

## How to Use Example

### Hardware Required

* An ESP development board
* An SPI-interfaced LCD
* An USB cable for power supply and programming

### Hardware Connection

The connection between ESP Board and the LCD is as follows:

```
ESP Board LCD Screen
+---------+ +---------------------------------+
| | | |
| 3V3 +--------------+ VCC +----------------------+ |
| | | | | |
| GND +--------------+ GND | | |
| | | | | |
| DATA0 +--------------+ MOSI | | |
| | | | | |
| PCLK +--------------+ SCK | | |
| | | | | |
| CS +--------------+ CS | | |
| | | | | |
| D/C +--------------+ D/C | | |
| | | | | |
| RST +--------------+ RST | | |
| | | | | |
|BK_LIGHT +--------------+ BCKL +----------------------+ |
| | | |
+---------+ +---------------------------------+
```

The GPIO numbers used by this example is taken from BSP.

### Build and Flash

Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. A flowing picture will be shown on the LCD screen.

(To exit the serial monitor, type ``Ctrl-]``.)

See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.

## Troubleshooting

For any technical queries, please open an [issue] (https://github.com/espressif/idf-extra-components/issues) on GitHub. We will get back to you soon.
8 changes: 8 additions & 0 deletions esp_jpeg/examples/tjpgd/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
set(srcs "pretty_effect.c"
"lcd_tjpgd_example_main.c"
"decode_image.c"
)

idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "."
EMBED_FILES image.jpg)
9 changes: 9 additions & 0 deletions esp_jpeg/examples/tjpgd/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
menu "Example Configuration"
config EXAMPLE_LCD_FLUSH_PARALLEL_LINES
int "LCD flush parallel lines"
default 12 if IDF_TARGET_ESP32C2
default 16
help
To speed up transfers, every SPI transfer sends a bunch of lines.

endmenu
68 changes: 68 additions & 0 deletions esp_jpeg/examples/tjpgd/main/decode_image.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/

/*
The image used for the effect on the LCD in the SPI master example is stored in flash
as a jpeg file. This file contains the decode_image routine, which uses the tiny JPEG
decoder library to decode this JPEG into a format that can be sent to the display.
Keep in mind that the decoder library cannot handle progressive files (will give
``Image decoder: jd_prepare failed (8)`` as an error) so make sure to save in the correct
format if you want to use a different image file.
*/

#include <string.h>
#include "decode_image.h"
#include "jpeg_decoder.h"
#include "esp_log.h"
#include "esp_check.h"
#include "freertos/FreeRTOS.h"

//Reference the binary-included jpeg file
extern const uint8_t image_jpg_start[] asm("_binary_image_jpg_start");
extern const uint8_t image_jpg_end[] asm("_binary_image_jpg_end");
//Define the height and width of the jpeg file. Make sure this matches the actual jpeg
//dimensions.

const char *TAG = "ImageDec";

//Decode the embedded image into pixel lines that can be used with the rest of the logic.
esp_err_t decode_image(uint16_t **pixels)
{
*pixels = NULL;
esp_err_t ret = ESP_OK;

//Alocate pixel memory. Each line is an array of IMAGE_W 16-bit pixels; the `*pixels` array itself contains pointers to these lines.
*pixels = calloc(IMAGE_H * IMAGE_W, sizeof(uint16_t));
ESP_GOTO_ON_FALSE((*pixels), ESP_ERR_NO_MEM, err, TAG, "Error allocating memory for lines");

//JPEG decode config
esp_jpeg_image_cfg_t jpeg_cfg = {
.indata = (uint8_t *)image_jpg_start,
.indata_size = image_jpg_end - image_jpg_start,
.outbuf = (uint8_t *)(*pixels),
.outbuf_size = IMAGE_W * IMAGE_H * sizeof(uint16_t),
.out_format = JPEG_IMAGE_FORMAT_RGB565,
.out_scale = JPEG_IMAGE_SCALE_0,
.flags = {
.swap_color_bytes = 1,
}
};

//JPEG decode
esp_jpeg_image_output_t outimg;
esp_jpeg_decode(&jpeg_cfg, &outimg);

ESP_LOGI(TAG, "JPEG image decoded! Size of the decoded image is: %dpx x %dpx", outimg.width, outimg.height);

return ret;
err:
//Something went wrong! Exit cleanly, de-allocating everything we allocated.
if (*pixels != NULL) {
free(*pixels);
}
return ret;
}
31 changes: 31 additions & 0 deletions esp_jpeg/examples/tjpgd/main/decode_image.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/

#pragma once
#include <stdint.h>
#include "esp_err.h"

#define IMAGE_W 320
#define IMAGE_H 240

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Decode the jpeg ``image.jpg`` embedded into the program file into pixel data.
*
* @param pixels A pointer to a pointer for an array of rows, which themselves are an array of pixels.
* Effectively, you can get the pixel data by doing ``decode_image(&myPixels); pixelval=myPixels[ypos][xpos];``
* @return - ESP_ERR_NOT_SUPPORTED if image is malformed or a progressive jpeg file
* - ESP_ERR_NO_MEM if out of memory
* - ESP_OK on succesful decode
*/
esp_err_t decode_image(uint16_t **pixels);

#ifdef __cplusplus
}
#endif
17 changes: 17 additions & 0 deletions esp_jpeg/examples/tjpgd/main/idf_component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
dependencies:
idf: ">=5.0"
esp_jpeg:
version: ">=1.0.2"
override_path: "../../../"
esp-box:
version: "^2.4"
rules:
- if: "target == esp32s3"
esp32_s2_kaluga_kit:
version: "^2.2"
rules:
- if: "target == esp32s2"
esp_wrover_kit:
version: "^1.4"
rules:
- if: "target == esp32"
Binary file added esp_jpeg/examples/tjpgd/main/image.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
98 changes: 98 additions & 0 deletions esp_jpeg/examples/tjpgd/main/lcd_tjpgd_example_main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_lcd_panel_ops.h"
#include "esp_heap_caps.h"
#include "pretty_effect.h"
#include "bsp/esp-bsp.h"
#include "bsp/display.h"

// Using SPI2 in the example, as it also supports octal modes on some targets
#define LCD_HOST SPI2_HOST
// To speed up transfers, every SPI transfer sends a bunch of lines. This define specifies how many.
// More means more memory use, but less overhead for setting up / finishing transfers. Make sure 240
// is dividable by this.
#define PARALLEL_LINES CONFIG_EXAMPLE_LCD_FLUSH_PARALLEL_LINES
// The number of frames to show before rotate the graph
#define ROTATE_FRAME 30

#if BSP_LCD_H_RES > BSP_LCD_V_RES
#define EXAMPLE_LCD_SWAP 0
#define EXAMPLE_LCD_H_RES BSP_LCD_H_RES
#define EXAMPLE_LCD_V_RES BSP_LCD_V_RES
#else
#define EXAMPLE_LCD_SWAP 1
#define EXAMPLE_LCD_H_RES BSP_LCD_V_RES
#define EXAMPLE_LCD_V_RES BSP_LCD_H_RES
#endif

// Simple routine to generate some patterns and send them to the LCD. Because the
// SPI driver handles transactions in the background, we can calculate the next line
// while the previous one is being sent.
static uint16_t *s_lines[2];
static void display_pretty_colors(esp_lcd_panel_handle_t panel_handle)
{
int frame = 0;
// Indexes of the line currently being sent to the LCD and the line we're calculating
int sending_line = 0;
int calc_line = 0;

// After ROTATE_FRAME frames, the image will be rotated
while (frame <= ROTATE_FRAME) {
frame++;
for (int y = 0; y < EXAMPLE_LCD_V_RES; y += PARALLEL_LINES) {
// Calculate a line
pretty_effect_calc_lines(s_lines[calc_line], y, frame, PARALLEL_LINES);
sending_line = calc_line;
calc_line = !calc_line;
// Send the calculated data
esp_lcd_panel_draw_bitmap(panel_handle, 0, y, 0 + EXAMPLE_LCD_H_RES, y + PARALLEL_LINES, s_lines[sending_line]);
}
}
}

void app_main(void)
{
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_handle_t panel_handle = NULL;

bsp_display_config_t disp_cfg = {
.max_transfer_sz = EXAMPLE_LCD_H_RES * PARALLEL_LINES * sizeof(uint16_t),
};
// Display initialize from BSP
bsp_display_new(&disp_cfg, &panel_handle, &io_handle);
esp_lcd_panel_disp_on_off(panel_handle, true);
bsp_display_backlight_on();

// Initialize the effect displayed
ESP_ERROR_CHECK(pretty_effect_init());

// "Rotate or not" flag
bool is_rotated = false;

// Allocate memory for the pixel buffers
for (int i = 0; i < 2; i++) {
s_lines[i] = heap_caps_malloc(EXAMPLE_LCD_H_RES * PARALLEL_LINES * sizeof(uint16_t), MALLOC_CAP_DMA);
assert(s_lines[i] != NULL);
}

#if EXAMPLE_LCD_SWAP
esp_lcd_panel_swap_xy(panel_handle, true);
#endif

// Start and rotate
while (1) {
// Set driver configuration to rotate 180 degrees each time
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, is_rotated, is_rotated));
// Display
display_pretty_colors(panel_handle);
is_rotated = !is_rotated;
}
}
61 changes: 61 additions & 0 deletions esp_jpeg/examples/tjpgd/main/pretty_effect.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/

#include <math.h>
#include "pretty_effect.h"
#include "decode_image.h"

uint16_t *pixels;

//Grab a rgb16 pixel from the esp32_tiles image
static inline uint16_t get_bgnd_pixel(int x, int y)
{
//Get color of the pixel on x,y coords
return (uint16_t) * (pixels + (y * IMAGE_W) + x);
}

//This variable is used to detect the next frame.
static int prev_frame = -1;

//Instead of calculating the offsets for each pixel we grab, we pre-calculate the valueswhenever a frame changes, then re-use
//these as we go through all the pixels in the frame. This is much, much faster.
static int8_t xofs[320], yofs[240];
static int8_t xcomp[320], ycomp[240];

//Calculate the pixel data for a set of lines (with implied line size of 320). Pixels go in dest, line is the Y-coordinate of the
//first line to be calculated, linect is the amount of lines to calculate. Frame increases by one every time the entire image
//is displayed; this is used to go to the next frame of animation.
void pretty_effect_calc_lines(uint16_t *dest, int line, int frame, int linect)
{
if (frame != prev_frame) {
//We need to calculate a new set of offset coefficients. Take some random sines as offsets to make everything
//look pretty and fluid-y.
for (int x = 0; x < 320; x++) {
xofs[x] = sin(frame * 0.15 + x * 0.06) * 4;
}
for (int y = 0; y < 240; y++) {
yofs[y] = sin(frame * 0.1 + y * 0.05) * 4;
}
for (int x = 0; x < 320; x++) {
xcomp[x] = sin(frame * 0.11 + x * 0.12) * 4;
}
for (int y = 0; y < 240; y++) {
ycomp[y] = sin(frame * 0.07 + y * 0.15) * 4;
}
prev_frame = frame;
}
for (int y = line; y < line + linect; y++) {
for (int x = 0; x < 320; x++) {
*dest++ = get_bgnd_pixel(x + yofs[y] + xcomp[x], y + xofs[x] + ycomp[y]);
}
}
}


esp_err_t pretty_effect_init(void)
{
return decode_image(&pixels);
}
Loading

0 comments on commit 8593068

Please sign in to comment.