MCUXpresso_MIMXRT1052xxxxB/boards/evkbimxrt1050/display_examples/sd_jpeg/sd_jpeg.c
2022-04-08 22:46:35 +08:00

478 lines
12 KiB
C

/*
* Copyright 2017-2019 NXP
* All rights reserved.
*
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <string.h>
#include "fsl_common.h"
#include "fsl_debug_console.h"
#include "fsl_cache.h"
#include "ff.h"
#include "diskio.h"
#include "fsl_sd_disk.h"
#include "jpeglib.h"
#include "display_support.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "board.h"
#include "sdmmc_config.h"
#include "fsl_gpio.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/* SD power control function */
#define DEMO_SDCARD_POWER_CTRL_FUNCTION_EXIST
#define APP_FB_HEIGHT DEMO_BUFFER_HEIGHT
#define APP_FB_WIDTH DEMO_BUFFER_WIDTH
#define APP_FB_START_X DEMO_BUFFER_START_X
#define APP_FB_START_Y DEMO_BUFFER_START_Y
/*
* For better performance, by default, three frame buffers are used in this demo.
* If the memory region is not large enough, user can define to 2 to override
* this value.
*/
#ifndef APP_FB_NUM
#define APP_FB_NUM 3 /* LCD frame buffer number. */
#endif
/* Use RGB888 format by default, for some display controllers don't support
* RGB888, use XRGB8888
*/
#ifndef APP_USE_XRGB8888
#define APP_USE_XRGB8888 0
#endif
#if APP_USE_XRGB8888
#define APP_FB_BPP 4 /* LCD frame buffer byte per pixel, XRGB888 format, 32-bit. */
#define APP_FB_FORMAT kVIDEO_PixelFormatXRGB8888
#else
#define APP_FB_BPP 3 /* LCD frame buffer byte per pixel, RGB888 format, 24-bit. */
#define APP_FB_FORMAT kVIDEO_PixelFormatRGB888
#endif
/* Cache line size. */
#ifndef FSL_FEATURE_L2CACHE_LINESIZE_BYTE
#define FSL_FEATURE_L2CACHE_LINESIZE_BYTE 0
#endif
#ifndef FSL_FEATURE_L1DCACHE_LINESIZE_BYTE
#define FSL_FEATURE_L1DCACHE_LINESIZE_BYTE 0
#endif
#if (FSL_FEATURE_L2CACHE_LINESIZE_BYTE > FSL_FEATURE_L1DCACHE_LINESIZE_BYTE)
#define APP_CACHE_LINE_SIZE FSL_FEATURE_L2CACHE_LINESIZE_BYTE
#else
#define APP_CACHE_LINE_SIZE FSL_FEATURE_L1DCACHE_LINESIZE_BYTE
#endif
/*
* For better performance, the frame buffers are located in cachable region and
* the cache line maintanance is handled in this demo. The start address of
* frame buffer, and the size of frame buffer, are aligned to the cache line
* size.
*/
#ifndef APP_FB_ALIGN
#if (APP_CACHE_LINE_SIZE > 0)
#define APP_FB_ALIGN APP_CACHE_LINE_SIZE
#else
#define APP_FB_ALIGN 1
#endif
#endif /* APP_FB_ALIGN */
#ifndef APP_FB_STRIDE_BYTE
#define APP_FB_STRIDE_BYTE (APP_FB_WIDTH * APP_FB_BPP)
#endif
#define APP_FB_SIZE_BYTE (SDK_SIZEALIGN(APP_FB_STRIDE_BYTE * APP_FB_HEIGHT, APP_FB_ALIGN))
#ifndef APP_FB_USE_FIXED_ADDRESS
#define APP_FB_USE_FIXED_ADDRESS 0
#endif
/*******************************************************************************
* Prototypes
******************************************************************************/
void BOARD_PowerOffSDCARD(void);
void BOARD_PowerOnSDCARD(void);
static status_t sdcardWaitCardInsert(void);
static void APP_BufferSwitchOffCallback(void *param, void *switchOffBuffer);
/*******************************************************************************
* Variables
******************************************************************************/
static dc_fb_info_t fbInfo;
/*
* When new frame buffer sent to display, it might not be shown immediately.
* Application could use callback to get new frame shown notification, at the
* same time, when this flag is set, application could write to the older
* frame buffer.
*/
static volatile bool s_newFrameShown = false;
static void *volatile s_fbList = NULL; /* List to the frame buffers. */
/* This struct contains the JPEG decompression parameters */
static struct jpeg_decompress_struct cinfo;
/* This struct represents a JPEG error handler */
static struct jpeg_error_mgr jerr;
#if APP_FB_USE_FIXED_ADDRESS
static void *g_frameBuffer[APP_FB_NUM] = {
(void *)APP_FB0_ADDR,
#if (APP_FB_NUM > 1)
(void *)APP_FB1_ADDR,
#endif
#if (APP_FB_NUM > 2)
(void *)APP_FB2_ADDR,
#endif
};
#else
SDK_ALIGN(static uint8_t g_frameBuffer[APP_FB_NUM][APP_FB_SIZE_BYTE], APP_FB_ALIGN);
#endif
AT_NONCACHEABLE_SECTION(static FATFS g_fileSystem); /* File system object */
AT_NONCACHEABLE_SECTION(static FIL jpgFil);
/*******************************************************************************
* Code
******************************************************************************/
/* Get the empty frame buffer from the s_fbList. */
static void *APP_GetFrameBuffer(void)
{
void *fb;
fb = s_fbList;
if (NULL != fb)
{
s_fbList = *(void **)fb;
}
return fb;
}
/* Put the unused frame buffer to the s_fbList. */
static void APP_PutFrameBuffer(void *fb)
{
*(void **)fb = s_fbList;
s_fbList = fb;
}
/*!
* @brief Jpeg Decode
* @param file: pointer to the jpg file
* @param buff: pointer to the image line
*/
void jpeg_decode(FIL *file, uint8_t *buffer)
{
uint8_t *jpg_buffer;
uint8_t *jpg_buffer_aligned;
uint8_t *read_pos;
UINT jpg_size;
UINT bytesRead;
UINT bytesRemain;
// Decode JPEG Image
JSAMPROW row_pointer[1] = {0}; /* Output row buffer */
uint32_t row_stride = 0; /* physical row width in image buffer */
// Step 1: allocate and initialize JPEG decompression object
cinfo.err = jpeg_std_error(&jerr);
// Step 2: Initialize the JPEG decompression object
jpeg_create_decompress(&cinfo);
jpg_size = f_size(file);
jpg_buffer = (unsigned char *)malloc(jpg_size + 2 * APP_CACHE_LINE_SIZE);
if (jpg_buffer == NULL)
{
PRINTF("Error: memory allocation error\r\n");
assert(false);
}
#if APP_CACHE_LINE_SIZE
jpg_buffer_aligned = (void *)(SDK_SIZEALIGN((uint32_t)jpg_buffer, APP_CACHE_LINE_SIZE));
#else
jpg_buffer_aligned = jpg_buffer;
#endif
bytesRemain = jpg_size;
read_pos = jpg_buffer_aligned;
DCACHE_CleanInvalidateByRange((uint32_t)jpg_buffer_aligned, jpg_size);
while (bytesRemain > 0)
{
f_read(file, read_pos, bytesRemain, &bytesRead);
bytesRemain -= bytesRead;
read_pos += bytesRead;
}
jpeg_mem_src(&cinfo, jpg_buffer_aligned, jpg_size);
// Step 3: read image parameters with jpeg_read_header()
jpeg_read_header(&cinfo, true);
// Step 4: set parameters for decompression
cinfo.dct_method = JDCT_FLOAT;
/*
* Resize to fit the screen, the actual resize rate is:
* cinfo.scale_num / 8, the cinfo.scale_num must be in the range of 1 ~ 16
*/
if ((cinfo.image_width * APP_FB_HEIGHT) > (cinfo.image_height * APP_FB_WIDTH))
{
cinfo.scale_num = APP_FB_WIDTH * 8 / cinfo.image_width;
}
else
{
cinfo.scale_num = APP_FB_HEIGHT * 8 / cinfo.image_height;
}
if (cinfo.scale_num < 1)
{
cinfo.scale_num = 1;
}
else if (cinfo.scale_num > 16)
{
cinfo.scale_num = 16;
}
// Step 5: start decompressor
jpeg_start_decompress(&cinfo);
row_stride = APP_FB_STRIDE_BYTE;
/* Place the output image to the center of the screen. */
buffer += row_stride * ((APP_FB_HEIGHT - cinfo.output_height) / 2);
buffer += APP_FB_BPP * ((APP_FB_WIDTH - cinfo.output_width) / 2);
while (cinfo.output_scanline < cinfo.output_height)
{
row_pointer[0] = &buffer[cinfo.output_scanline * row_stride];
jpeg_read_scanlines(&cinfo, row_pointer, 1);
}
// Step 6: Finish decompression
jpeg_finish_decompress(&cinfo);
// Step 7: Release JPEG decompression object
jpeg_destroy_decompress(&cinfo);
free(jpg_buffer);
}
static int MOUNT_SDCard(void)
{
FRESULT error;
const TCHAR driverName[3U] = {SDDISK + '0', ':', '/'};
// clear FATFS manually
memset((void *)&g_fileSystem, 0, sizeof(g_fileSystem));
/* Wait for the card insert. */
if (sdcardWaitCardInsert() != kStatus_Success)
{
PRINTF("Card not inserted.\r\n");
return -1;
}
// Mount the driver
if (f_mount(&g_fileSystem, driverName, 0))
{
PRINTF("Mount volume failed.\r\n");
return -2;
}
#if (FF_FS_RPATH >= 2U)
if (f_chdrive((char const *)&driverName[0U]))
{
PRINTF("Change drive failed.\r\n");
return -3;
}
#endif
// Open file to check
error = f_open(&jpgFil, _T("/000.jpg"), FA_OPEN_EXISTING);
if (error != FR_OK)
{
PRINTF("No demo jpeg file!\r\n");
return -4;
}
f_close(&jpgFil);
return 0;
}
void APP_InitDisplay(void)
{
uint8_t i;
void *freeFb;
status_t status;
for (i = 0; i < APP_FB_NUM; i++)
{
memset(g_frameBuffer[i], 0, APP_FB_SIZE_BYTE);
APP_PutFrameBuffer(g_frameBuffer[i]);
}
BOARD_PrepareDisplayController();
status = g_dc.ops->init(&g_dc);
if (kStatus_Success != status)
{
PRINTF("Display initialization failed\r\n");
assert(false);
}
g_dc.ops->getLayerDefaultConfig(&g_dc, 0, &fbInfo);
fbInfo.pixelFormat = APP_FB_FORMAT;
fbInfo.width = APP_FB_WIDTH;
fbInfo.height = APP_FB_HEIGHT;
fbInfo.startX = APP_FB_START_X;
fbInfo.startY = APP_FB_START_Y;
fbInfo.strideBytes = APP_FB_STRIDE_BYTE;
if (kStatus_Success != g_dc.ops->setLayerConfig(&g_dc, 0, &fbInfo))
{
PRINTF("Error: Could not configure the display controller\r\n");
assert(false);
}
g_dc.ops->setCallback(&g_dc, 0, APP_BufferSwitchOffCallback, NULL);
s_newFrameShown = false;
/* Get the first frame buffer and send to display controller. */
freeFb = APP_GetFrameBuffer();
if (freeFb == NULL)
{
PRINTF("Error: Could not get frame buffer\r\n");
assert(false);
}
memset(freeFb, 0, APP_FB_SIZE_BYTE);
g_dc.ops->setFrameBuffer(&g_dc, 0, freeFb);
/* For the DBI interface display, application must wait for the first
* frame buffer sent to the panel.
*/
if ((g_dc.ops->getProperty(&g_dc) & kDC_FB_ReserveFrameBuffer) == 0)
{
while (s_newFrameShown == false)
{
}
}
s_newFrameShown = true;
g_dc.ops->enableLayer(&g_dc, 0);
}
/*!
* @brief Main function
*/
int main(void)
{
int i = 0;
FRESULT error;
char jpgFileName[10];
void *freeFb;
uint32_t oldIntStat;
BOARD_ConfigMPU();
BOARD_InitPins();
BOARD_BootClockRUN();
BOARD_InitDebugConsole();
PRINTF("SD JPEG demo start:\r\n");
APP_InitDisplay();
// Init the SD card
if (0 != MOUNT_SDCard())
{
PRINTF("SD card mount error. Demo stopped!");
return -1;
}
while (1)
{
// format the filename
sprintf(jpgFileName, "/%03d.jpg", i++);
// open it
error = f_open(&jpgFil, jpgFileName, FA_READ);
if (error != FR_OK)
{
i = 0;
continue;
}
/* Get free frame buffer and convert the jpeg output to it. */
do
{
oldIntStat = DisableGlobalIRQ();
freeFb = APP_GetFrameBuffer();
EnableGlobalIRQ(oldIntStat);
} while (NULL == freeFb);
PRINTF("Decoding %s...", jpgFileName);
jpeg_decode(&jpgFil, freeFb);
PRINTF("done!\r\n", jpgFileName);
f_close(&jpgFil);
DCACHE_CleanInvalidateByRange((uint32_t)freeFb, APP_FB_SIZE_BYTE);
/*
* Wait for the previous set frame buffer active.
*/
while (s_newFrameShown == false)
{
}
/* Now new frame is ready, pass it to LCDIF. */
s_newFrameShown = false;
g_dc.ops->setFrameBuffer(&g_dc, 0, freeFb);
}
}
static status_t sdcardWaitCardInsert(void)
{
BOARD_SD_Config(&g_sd, NULL, BOARD_SDMMC_SD_HOST_IRQ_PRIORITY, NULL);
/* SD host init function */
if (SD_HostInit(&g_sd) != kStatus_Success)
{
PRINTF("\r\nSD host init fail\r\n");
return kStatus_Fail;
}
/* power off card */
SD_SetCardPower(&g_sd, false);
/* wait card insert */
if (SD_PollingCardInsert(&g_sd, kSD_Inserted) == kStatus_Success)
{
PRINTF("\r\nCard inserted.\r\n");
/* power on the card */
SD_SetCardPower(&g_sd, true);
}
else
{
PRINTF("\r\nCard detect fail.\r\n");
return kStatus_Fail;
}
return kStatus_Success;
}
static void APP_BufferSwitchOffCallback(void *param, void *switchOffBuffer)
{
s_newFrameShown = true;
APP_PutFrameBuffer(switchOffBuffer);
}