sdk例程:st77903 qspi video屏移植freertos lvgl - minichao9901/TangNano-20k-Zynq-7020 GitHub Wiki
- 参考文献: 参考是CSDN上一个大佬写的笔记。原笔记基于STM32H750,我将其移植到zynq7020上面
- 只移植了显示部分,触控部分由于手头这款屏不带触控,所以暂时没有触控功能。
- 屏幕购买的是淘宝上的
- 仍然是基于之前玩的例程:axi4_stream_qspi3, 这里不再重复了。可以参考以前写的文章。
- 开了2个线程,一个线程用于qspi video刷屏,这个对实时性要求高。
- 另一个线程用于切换图片,每500ms切换一次,这个对实时性要求低一些。
- 请看这个例程
/*
Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Copyright (C) 2012 - 2018 Xilinx, Inc. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. If you wish to use our Amazon
FreeRTOS name, please do so in a fair use way that does not cause confusion.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
http://www.FreeRTOS.org
http://aws.amazon.com/freertos
***************************************************************************
* *
* Having a problem? Start by reading the FAQ "My application does *
* not run, what could be wrong?". Have you defined configASSERT()? *
* *
* http://www.FreeRTOS.org/FAQHelp.html *
* *
***************************************************************************
***************************************************************************
* *
* FreeRTOS provides completely free yet professionally developed, *
* robust, strictly quality controlled, supported, and cross *
* platform software that is more than just the market leader, it *
* is the industry's de facto standard. *
* *
* Help yourself get started quickly while simultaneously helping *
* to support the FreeRTOS project by purchasing a FreeRTOS *
* tutorial book, reference manual, or both: *
* http://www.FreeRTOS.org/Documentation *
* *
***************************************************************************
***************************************************************************
* *
* Investing in training allows your team to be as productive as *
* possible as early as possible, lowering your overall development *
* cost, and enabling you to bring a more robust product to market *
* earlier than would otherwise be possible. Richard Barry is both *
* the architect and key author of FreeRTOS, and so also the world's *
* leading authority on what is the world's most popular real time *
* kernel for deeply embedded MCU designs. Obtaining your training *
* from Richard ensures your team will gain directly from his in-depth *
* product knowledge and years of usage experience. Contact Real Time *
* Engineers Ltd to enquire about the FreeRTOS Masterclass, presented *
* by Richard Barry: http://www.FreeRTOS.org/contact
* *
***************************************************************************
***************************************************************************
* *
* You are receiving this top quality software for free. Please play *
* fair and reciprocate by reporting any suspected issues and *
* participating in the community forum: *
* http://www.FreeRTOS.org/support *
* *
* Thank you! *
* *
***************************************************************************
http://www.FreeRTOS.org - Documentation, books, training, latest versions,
license and Real Time Engineers Ltd. contact details.
http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
including FreeRTOS+Trace - an indispensable productivity tool, a DOS
compatible FAT file system, and our tiny thread aware UDP/IP stack.
http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.
http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High
Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS
licenses offer ticketed support, indemnification and commercial middleware.
http://www.SafeRTOS.com - High Integrity Systems also provide a safety
engineered and independently SIL3 certified version for use in safety and
mission critical applications that require provable dependability.
1 tab == 4 spaces!
*/
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "timers.h"
/* Xilinx includes. */
#include "xil_printf.h"
#include "xparameters.h"
#include <stdio.h>
//#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xil_io.h"
#include "xil_mmu.h"
#include "sleep.h"
//#include "ff.h"
#include "xil_cache.h"
#include "xtime_l.h"
#include "xgpiops.h"
//#include <xscugic.h>
//#include <xscutimer.h>
//#include "lvgl/lvgl.h"
//#include "lv_examples/lv_examples.h"
//#include "lvgl/port/lv_port_disp.h"
//#include "lvgl/port/lv_port_indev.h"
#include "COMMON.h"
#define TIMER_ID 1
#define DELAY_10_SECONDS 10000UL
#define DELAY_1_SECOND 1000UL
#define TIMER_CHECK_THRESHOLD 9
/*-----------------------------------------------------------*/
/* The Tx and Rx tasks as described at the top of this file. */
static void prvTxTask( void *pvParameters );
static void prvRxTask( void *pvParameters );
static void vTimerCallback( TimerHandle_t pxTimer );
/*-----------------------------------------------------------*/
/* The queue used by the Tx and Rx tasks, as described at the top of this
file. */
static TaskHandle_t xTxTask;
static TaskHandle_t xRxTask;
static QueueHandle_t xQueue = NULL;
static TimerHandle_t xTimer = NULL;
char HWstring[15] = "Hello World";
long RxtaskCntr = 0;
#define PS_KEY (54 + 0) //EMIO0,对应的GPIO编号为54+0=54
#define PIC_SEL_PIN (54 + 1) //EMIO1,对应的GPIO编号为54+1=55
#define EMPTY_PIN (54 + 2) //EMIO2,对应的GPIO编号为54+2=56
#define INPUT 1
#define OUTPUT 0
XGpioPs GpioPs; //GPIO实例对象
//static void ScuTimer_InterruptHandler(void *data)
//{
// XScuTimer *timer = (XScuTimer *)data;
// lv_tick_inc(1);
// XScuTimer_ClearInterruptStatus(timer);
//}
void gpio_init()
{
PS_GPIO_Init(); //初始化PS端MIO和EMIO
PS_GPIO_SetMode(PS_KEY, OUTPUT, 0);
//PS_GPIO_SetMode(PIC_SEL_PIN, INPUT, 0);
PS_GPIO_SetMode(EMPTY_PIN, INPUT, 1);
}
void qspi_mode_set(u8 mode)
{
PS_GPIO_SetPort(PS_KEY, mode);
}
//u32 read_full()
//{
// return PS_GPIO_GetPort(FULL_PIN);
//}
u32 read_empty()
{
return PS_GPIO_GetPort(EMPTY_PIN);
}
/***********************************************************************************/
#define RED 0xF800
#define ORANGE 0xFC00
#define YELLOW 0xFFE0
#define GREEN 0x07E0
#define CYAN 0x07FF
#define BLUE 0x001F
#define PURPPLE 0xF81F
#define BLACK 0x0000
#define WHITE 0xFFFF
#define GRAY 0xD69A
const u16 colors[] = {
RED,
GREEN,
BLUE,
ORANGE,
YELLOW,
CYAN,
PURPPLE,
BLACK,
WHITE,
GRAY
};
typedef struct{
u8 txbuf[50];
u8 rxbuf[50];
u8 length;
}t_buf;
//#define BIST_MODE
t_buf WriteBuffer[]={
{{0xf0, 0xc3}, {0x00},2},
{{0xf0, 0x96}, {0x00},2},
{{0xf0, 0xa5}, {0x00},2},
{{0xe9, 0x20}, {0x00},2},
{{0xe7, 0x80, 0x77, 0x1f, 0xcc}, {0x00}, 5},
{{0xc1, 0x77, 0x07, 0xcf, 0x16}, {0x00},5},//Normal:VGHS/VGLS/VSP/VSN电压
{{0xc2, 0x77, 0x07, 0xcf, 0x16}, {0x00},5},//Idle: VGHS/VGLS/VSP/VSN电压
{{0xc3, 0x22, 0x02, 0x22, 0x04}, {0x00},5},//Normal: VGH/VGL/VSP/VSN Clk
{{0xc4, 0x22, 0x02, 0x22, 0x04}, {0x00},5},//Idle: VGH/VGL/VSP/VSN Clk
{{0xc5, 0xed},{0x00},2}, //VCOM
{{0xe0, 0x87, 0x09, 0x0c, 0x06, 0x05, 0x03, 0x29, 0x32, 0x49, 0x0f, 0x1b, 0x17, 0x2a, 0x2f},{0x00},15},
{{0xe1, 0x87, 0x09, 0x0c, 0x06, 0x05, 0x03, 0x29, 0x32, 0x49, 0x0f, 0x1b, 0x17, 0x2a, 0x2f},{0x00},15},
{{0xe5, 0xbe, 0xf5, 0xb1, 0x22, 0x22, 0x25, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22},{0x00},15},
{{0xe6, 0xbe, 0xf5, 0xb1, 0x22, 0x22, 0x25, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22},{0x00},15},
{{0xec, 0x40, 0x03},{0x00},3},
{{0x36, 0x0c},{0x00},2},
{{0x3a, 0x07},{0x00},2},
{{0xb2, 0x00},{0x00},2},//GIP pattern
{{0xb3, 0x01},{0x00},2},//video mode dot-inversion
{{0xb4, 0x01},{0x00},2},
{{0xb5, 0x00, 0x08, 0x00, 0x08},{0x00},5}, //vfp
{{0xb6, 0xc7, 0x31},{0x00},3},
{{0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x2a, 0x8a, 0x02},{0x00},10},
{{0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x2a, 0x8a, 0x02},{0x00},10},
{{0xba, 0x0a, 0x5a, 0x23, 0x10, 0x25, 0x02, 0x00},{0x00},8},
{{0xbb, 0x00, 0x30, 0x00, 0x2c, 0x82, 0x87, 0x18, 0x00},{0x00},9},
{{0xbc, 0x00, 0x30, 0x00, 0x2c, 0x82, 0x87, 0x18, 0x00},{0x00},9},
{{0xbd, 0xa1, 0xb2, 0x2b, 0x1a, 0x56, 0x43, 0x34, 0x65, 0xff, 0xff, 0x0f},{0x00},12},
{{0x35, 0x00},{0x00},2},
{{0x21},{0x00},1},
{{0x11},{0x00},1},
{{0xff},{0x00},0},
{{0x29},{0x00},1},
{{0xff},{0x00},0},
#ifdef BIST_MODE
{{0xb0, 0xa5},{0x00},2},
{{0xcc, 0x40, 0x00, 0x3f, 0x00, 0x14, 0x14, 0x20, 0x20, 0x03},{0x00},10},
#endif
};
/*************************************************
* reg rw functions
************************************************/
#define DMA_BASE_ADRR 0x40000000
#define DDR_BASE_ADDR 0x10000000
void wait_for_idle()
{
u32 tmp;
while(1){
tmp=Xil_In32(DMA_BASE_ADRR+04);
if(((tmp>>1)&0x1) == 1){
break;
}
}
}
void dma_xfer(u32 mem_addr, u32 length)
{/*用于初始化阶段,需要刷新Cache,确保初始化成功*/
Xil_DCacheFlush();
//Xil_DCacheFlushRange(mem_addr, length);
Xil_Out32(DMA_BASE_ADRR, 0x00010003);
Xil_Out32(DMA_BASE_ADRR+0x30, 0x00010003);
Xil_Out32(DMA_BASE_ADRR+0x18, mem_addr);
Xil_Out32(DMA_BASE_ADRR+0x28, length); /*length is bytes*/
wait_for_idle();
//usleep(10);
}
void dma_xfer2(u32 mem_addr, u32 length)
{/*发送数据阶段,可以不用Cache*/
//Xil_DCacheFlush();
Xil_DCacheFlushRange(mem_addr, length);
Xil_Out32(DMA_BASE_ADRR, 0x00010003);
Xil_Out32(DMA_BASE_ADRR+0x30, 0x00010003);
Xil_Out32(DMA_BASE_ADRR+0x18, mem_addr);
Xil_Out32(DMA_BASE_ADRR+0x28, length); /*length is bytes*/
wait_for_idle();
//usleep(10);
}
void set_cfg(u32 mem_addr, u8 cmd, u8 addr, u8 mode, u8 work_mode, u16 length)
{
u32 tmp=cmd;
tmp|=addr<<8;
tmp|=mode<<16;
tmp|=length<<20;
// tmp|=work_mode<<30;
Xil_Out32(mem_addr, tmp);
}
u32 get_cfg(u8 cmd, u8 addr, u8 mode, u8 work_mode, u16 length)
{
u32 tmp=cmd;
tmp|=addr<<8;
tmp|=mode<<16;
tmp|=length<<20;
// tmp|=work_mode<<30;
return tmp;
}
void set_npdata8(u32 mem_addr, u8 *pdata, u32 length){
u32 addr=mem_addr;
while(length--){
Xil_Out8(addr++, *pdata++);
}
}
/*************************************************
* basic xfer functions
************************************************/
void xfer_cmd_pdata8(u32 mem_addr, u8 cmd, u8* pdata, u32 length){
if(length==0)
set_cfg(mem_addr, 0xde, cmd, 0b0101, 0b00, length); /*byte_mode=0, cs_mode=1, data_mode=0b01*/
else
set_cfg(mem_addr, 0xde, cmd, 0b0110, 0b00, length); /*byte_mode=0, cs_mode=1, data_mode=0b10*/
set_npdata8(mem_addr+4, pdata,length);
dma_xfer(mem_addr, 4+length);
}
void xfer_cmd_pdata16_dma(u32 mem_addr, u32 length){
dma_xfer2(mem_addr, length);
}
void seqs_init() {
for (int i = 0; i < sizeof(WriteBuffer) / sizeof(*WriteBuffer); i++) {
if (WriteBuffer[i].length == 0) {
usleep(WriteBuffer[i].txbuf[0] * 1000);
continue;
}
u8 cmd=WriteBuffer[i].txbuf[0];
u8 *pdata=WriteBuffer[i].txbuf+1;
u16 length=WriteBuffer[i].length-1;
xfer_cmd_pdata8(DDR_BASE_ADDR, cmd, pdata, length);
usleep(10); //this delay is need
//xil_printf("Exec CMD=%x\r\n\r\n", cmd);
}
}
/*************************************************
* application functions
************************************************/
#define LCD_BPP (24)
#define LCD_X_SIZE (400U) /* available x pixel size */
#define LCD_Y_SIZE (400U) /* available y pixle size */
#define LCD_PBYTE ((LCD_BPP + 0) / 8) /* bytes in pixel unit */
#define LCD_HBYTE (LCD_X_SIZE * LCD_PBYTE) /* bytes in horizontal line */
#define LCD_VSW (1U)
#define LCD_HFP (8U)
#define LCD_HBP (8U)
#define LCD_TE_OFT (25U)
typedef u8 (*ArrayPtr)[400][20 * 60];
ArrayPtr frame_buffer=(ArrayPtr)0x10100000;
ArrayPtr p_frame_cache=(ArrayPtr)0x10200000;
//static uint8_t frame_cache[LCD_Y_SIZE][LCD_HBYTE] __attribute__ ((aligned (64)));
void lcdqspi_set_pixel(uint32_t x, uint32_t y, uint32_t color)
{
if ((x < LCD_X_SIZE)&&(y < LCD_Y_SIZE))
{
uint32_t xoft = x * 3;
(*p_frame_cache)[y][xoft + 2] = color & 0xff;
(*p_frame_cache)[y][xoft + 1] = (color & 0xff00) >> 8;
(*p_frame_cache)[y][xoft + 0] = (color & 0xff0000) >> 16;
}
}
void lcdqspi_fill_block(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t color)
{
if ((x1 < LCD_X_SIZE)&&(y1 < LCD_Y_SIZE)&&(x1 >= x0)&&(y1 >= y0))
{
uint32_t x,y;
for (y = y0; y <= y1; y++)
{
uint32_t oft = x0 * 3;
for (x = x0; x <= x1; x++)
{
(*p_frame_cache)[y][oft + 2] = color & 0xff;
(*p_frame_cache)[y][oft + 1] = (color & 0xff00) >> 8;
(*p_frame_cache)[y][oft + 0] = (color & 0xff0000) >> 16;
oft += 3;
}
//xil_printf("3\r\n");
}
}
}
void lcdqspi_clear(uint32_t color)
{
lcdqspi_fill_block(0, 0, LCD_X_SIZE - 1, LCD_Y_SIZE - 1, color);
}
void lcdqspi_draw_line(uint32_t x0, uint32_t x1, uint32_t y, uint32_t *pixel)
{
if ((x1 < LCD_X_SIZE)&&(y < LCD_Y_SIZE)&&(x1 >= x0))
{
uint32_t oft = x0 * 3;
uint32_t *dat = pixel;
for (uint32_t x = x0; x <= x1; x++)
{
(*p_frame_cache)[y][oft + 2] = *dat & 0xff;
(*p_frame_cache)[y][oft + 1] = (*dat & 0xff00) >> 8;
(*p_frame_cache)[y][oft + 0] = (*dat & 0xff0000) >> 16;
oft += 3;
dat++;
}
}
}
static void prvRxTask( void *pvParameters )
{
// lcdqspi_clear(0xff0000);
u32 color_index=0;
while(1){
xil_printf("1\r\n");
color_index++;
color_index%=5;
switch(color_index){
case 0: lcdqspi_clear(0xff0000); break;
case 1: lcdqspi_clear(0x00ff00); break;
case 2: lcdqspi_clear(0x0000ff); break;
case 3: lcdqspi_clear(0x000000); break;
case 4: lcdqspi_clear(0xffffff); break;
}
memcpy((void *)0x10100000, (void *)0x10200000, 400*400*3);
Xil_DCacheFlush();
//Xil_DCacheFlushRange(0x10100000, 400*400*3);
vTaskDelay( pdMS_TO_TICKS( 500 ) );
// Xil_DCacheFlushRange(0x10100000, 400*400*3);
}
}
static void prvTxTask( void *pvParameters )
{
u32 loop_cnt=0;
while (1)
{
xil_printf("2\r\n");
Xil_DCacheInvalidateRange(0x10100000, 400*400*3);
/* vs(0x61) packet */
for (int i = 0; i < LCD_VSW; i++)
{
xfer_cmd_pdata8(DDR_BASE_ADDR, 0x61,NULL,0);
usleep(40);
}
/* hbp(0x60) packet */
for (int i = 0; i < LCD_HBP; i++)
{
xfer_cmd_pdata8(DDR_BASE_ADDR, 0x60,NULL,0);
usleep(40);
}
qspi_mode_set(1);
/* transmit display cache data to lcd line by line */
for (int i = 0; i < LCD_Y_SIZE; i+=1)
{
//xfer_cmd_enter_stream(DDR_BASE_ADDR);
xfer_cmd_pdata16_dma((u32)(*frame_buffer)[i],20*60); /*这个地方是*frame_buffer,不要搞错了!!*/
usleep(50);
}
qspi_mode_set(0);
/* hfp(0x60) packet */
for (int i = 0; i < LCD_HFP; i++)
{
xfer_cmd_pdata8(DDR_BASE_ADDR, 0x60,NULL,0);
usleep(40);
}
vTaskDelay( pdMS_TO_TICKS( 15 ) );
}
}
int main( void )
{
const TickType_t x10seconds = pdMS_TO_TICKS( DELAY_10_SECONDS );
gpio_init();
qspi_mode_set(0);
for(int i=0; i<2; i++){ //固化到flash启动不成功,后发现把这个地方次数设置为多次,就可以稳定启动
seqs_init();
}
xil_printf( "Hello from Freertos example main\r\n" );
/* Create the two tasks. The Tx task is given a lower priority than the
Rx task, so the Rx task will leave the Blocked state and pre-empt the Tx
task as soon as the Tx task places an item in the queue. */
xTaskCreate( prvTxTask, /* The function that implements the task. */
( const char * ) "Tx", /* Text name for the task, provided to assist debugging only. */
configMINIMAL_STACK_SIZE, /* The stack allocated to the task. */
NULL, /* The task parameter is not used, so set to NULL. */
tskIDLE_PRIORITY+1, /* The task runs at the idle priority. */
&xTxTask );
xTaskCreate( prvRxTask,
( const char * ) "GB",
configMINIMAL_STACK_SIZE,
NULL,
tskIDLE_PRIORITY,
&xRxTask );
/* Create the queue used by the tasks. The Rx task has a higher priority
than the Tx task, so will preempt the Tx task and remove values from the
queue as soon as the Tx task writes to the queue - therefore the queue can
never have more than one item in it. */
// xQueue = xQueueCreate( 1, /* There is only one space in the queue. */
// sizeof( HWstring ) ); /* Each space in the queue is large enough to hold a uint32_t. */
//
// /* Check the queue was created. */
// configASSERT( xQueue );
/* Create a timer with a timer expiry of 10 seconds. The timer would expire
after 10 seconds and the timer call back would get called. In the timer call back
checks are done to ensure that the tasks have been running properly till then.
The tasks are deleted in the timer call back and a message is printed to convey that
the example has run successfully.
The timer expiry is set to 10 seconds and the timer set to not auto reload. */
// xTimer = xTimerCreate( (const char *) "Timer",
// x10seconds,
// pdFALSE,
// (void *) TIMER_ID,
// vTimerCallback);
// /* Check the timer was created. */
// configASSERT( xTimer );
/* start the timer with a block time of 0 ticks. This means as soon
as the schedule starts the timer will start running and will expire after
10 seconds */
// xTimerStart( xTimer, 0 );
/* Start the tasks and timer running. */
vTaskStartScheduler();
/* If all is well, the scheduler will now be running, and the following line
will never be reached. If the following line does execute, then there was
insufficient FreeRTOS heap memory available for the idle and/or timer tasks
to be created. See the memory management section on the FreeRTOS web site
for more details. */
for( ;; );
}
/*-----------------------------------------------------------*/
//static void prvTxTask( void *pvParameters )
//{
//const TickType_t x1second = pdMS_TO_TICKS( DELAY_1_SECOND );
//
// for( ;; )
// {
// /* Delay for 1 second. */
// vTaskDelay( x1second );
//
// /* Send the next value on the queue. The queue should always be
// empty at this point so a block time of 0 is used. */
// xQueueSend( xQueue, /* The queue being written to. */
// HWstring, /* The address of the data being sent. */
// 0UL ); /* The block time. */
// }
//}
/*-----------------------------------------------------------*/
//static void prvRxTask( void *pvParameters )
//{
//char Recdstring[15] = "";
//
// for( ;; )
// {
// /* Block to wait for data arriving on the queue. */
// xQueueReceive( xQueue, /* The queue being read. */
// Recdstring, /* Data is read into this address. */
// portMAX_DELAY ); /* Wait without a timeout for data. */
//
// /* Print the received data. */
// xil_printf( "Rx task received string from Tx task: %s\r\n", Recdstring );
// RxtaskCntr++;
// }
//}
/*-----------------------------------------------------------*/
//static void vTimerCallback( TimerHandle_t pxTimer )
//{
// long lTimerId;
// configASSERT( pxTimer );
//
// lTimerId = ( long ) pvTimerGetTimerID( pxTimer );
//
// if (lTimerId != TIMER_ID) {
// xil_printf("FreeRTOS Hello World Example FAILED");
// }
//
// /* If the RxtaskCntr is updated every time the Rx task is called. The
// Rx task is called every time the Tx task sends a message. The Tx task
// sends a message every 1 second.
// The timer expires after 10 seconds. We expect the RxtaskCntr to at least
// have a value of 9 (TIMER_CHECK_THRESHOLD) when the timer expires. */
// if (RxtaskCntr >= TIMER_CHECK_THRESHOLD) {
// xil_printf("FreeRTOS Hello World Example PASSED");
// } else {
// xil_printf("FreeRTOS Hello World Example FAILED");
// }
//
// vTaskDelete( xRxTask );
// vTaskDelete( xTxTask );
//}
-
1)首先创建工程的时候,选择freertos的hello world模板。这个功能非常好,大部分功能都有了,我们在此基础上修改。
-
2)将test_dma已经验证好的裸机刷屏功能,移植过来。将hell world中static void prvRxTask( void *pvParameters )函数注释掉,将static void prvTxTask( void *pvParameters )注释掉。
-
3)将st77903的刷图功能的函数,重新命名为static void prvTxTask( void *pvParameters ), 将st77903的切图功能,重新命名为static void prvRxTask( void *pvParameters )。注意要用vTaskDelay( pdMS_TO_TICKS( 15 ) )进行调度。每个task必须带有阻塞功能,否则就是真的死循环了。
-
4)调整优先级,让prvTxTask的优先级比prvRxTask的优先级高
-
5)配置bsp的freertos设置项,设置tick rate等参数。我们也开启了hook函数功能,为后面使用lvgl做准备。
-
- 有一个特别需要注意的点,其实就是细节了,搞错了就一直不能出结果。我在这个地方卡了几个小时,浪费了不少时间。
这个地方,我之前一直用的是(u32)frame_buffer[i],这就错了,屏幕异常。因为这里的frame_buffer其实是一个指向数组的指针,需要先解引用转成数组,才能索引。
- 整个刷屏的效果非常好,切换速度快。跟裸机刷屏几乎没有差别。我们还可以看到串口打印的2个线程的执行情况。
- 这样子freertos就调通了
-
开了2个线程,一个线程用于qspi video刷屏,这个对实时性要求高。
-
另一个线程用于用于运行lvgl,这个对实时性要求低一些。
-
lvgl还需要一个心跳函数,周期是1ms。这个我们将freertos的tick_rate修改为1kHz即可,并且需要开启tick Hook功能。lvgl的心跳是在vApplicationTickHook实现的
-
为了让2个线程实现同步,避免切图产生tear_effect效应,我们使用了一个semphore。在qspi刷图的vblank期间发送信号量,在lv_port_disp.c中的disp_flush函数中接收信号量,进行图片的切换。
-
lvgl的移植其实非常简单,只需要实现disp_flush函数即可。其实我们只需要写出lcd屏的lcdqspi_draw_line函数就可以了。
-
请看这个例程
/*
Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Copyright (C) 2012 - 2018 Xilinx, Inc. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. If you wish to use our Amazon
FreeRTOS name, please do so in a fair use way that does not cause confusion.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
http://www.FreeRTOS.org
http://aws.amazon.com/freertos
***************************************************************************
* *
* Having a problem? Start by reading the FAQ "My application does *
* not run, what could be wrong?". Have you defined configASSERT()? *
* *
* http://www.FreeRTOS.org/FAQHelp.html *
* *
***************************************************************************
***************************************************************************
* *
* FreeRTOS provides completely free yet professionally developed, *
* robust, strictly quality controlled, supported, and cross *
* platform software that is more than just the market leader, it *
* is the industry's de facto standard. *
* *
* Help yourself get started quickly while simultaneously helping *
* to support the FreeRTOS project by purchasing a FreeRTOS *
* tutorial book, reference manual, or both: *
* http://www.FreeRTOS.org/Documentation *
* *
***************************************************************************
***************************************************************************
* *
* Investing in training allows your team to be as productive as *
* possible as early as possible, lowering your overall development *
* cost, and enabling you to bring a more robust product to market *
* earlier than would otherwise be possible. Richard Barry is both *
* the architect and key author of FreeRTOS, and so also the world's *
* leading authority on what is the world's most popular real time *
* kernel for deeply embedded MCU designs. Obtaining your training *
* from Richard ensures your team will gain directly from his in-depth *
* product knowledge and years of usage experience. Contact Real Time *
* Engineers Ltd to enquire about the FreeRTOS Masterclass, presented *
* by Richard Barry: http://www.FreeRTOS.org/contact
* *
***************************************************************************
***************************************************************************
* *
* You are receiving this top quality software for free. Please play *
* fair and reciprocate by reporting any suspected issues and *
* participating in the community forum: *
* http://www.FreeRTOS.org/support *
* *
* Thank you! *
* *
***************************************************************************
http://www.FreeRTOS.org - Documentation, books, training, latest versions,
license and Real Time Engineers Ltd. contact details.
http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
including FreeRTOS+Trace - an indispensable productivity tool, a DOS
compatible FAT file system, and our tiny thread aware UDP/IP stack.
http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.
http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High
Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS
licenses offer ticketed support, indemnification and commercial middleware.
http://www.SafeRTOS.com - High Integrity Systems also provide a safety
engineered and independently SIL3 certified version for use in safety and
mission critical applications that require provable dependability.
1 tab == 4 spaces!
*/
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "timers.h"
#include "semphr.h"
/* Xilinx includes. */
#include "xil_printf.h"
#include "xparameters.h"
#include <stdio.h>
//#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xil_io.h"
#include "xil_mmu.h"
#include "sleep.h"
//#include "ff.h"
#include "xil_cache.h"
#include "xtime_l.h"
#include "xgpiops.h"
#include <xscugic.h>
#include <xscutimer.h>
#include "lvgl/lvgl.h"
#include "lv_examples/lv_examples.h"
#include "lvgl/port/lv_port_disp.h"
//#include "lvgl/port/lv_port_indev.h"
#include "COMMON.h"
#define TIMER_ID 1
#define DELAY_10_SECONDS 10000UL
#define DELAY_1_SECOND 1000UL
#define TIMER_CHECK_THRESHOLD 9
/*-----------------------------------------------------------*/
/* The Tx and Rx tasks as described at the top of this file. */
static void prvTxTask( void *pvParameters );
static void prvRxTask( void *pvParameters );
static void vTimerCallback( TimerHandle_t pxTimer );
/*-----------------------------------------------------------*/
/* The queue used by the Tx and Rx tasks, as described at the top of this
file. */
static TaskHandle_t xTxTask;
static TaskHandle_t xRxTask;
static QueueHandle_t xQueue = NULL;
static TimerHandle_t xTimer = NULL;
SemaphoreHandle_t xSemaphore=NULL;
char HWstring[15] = "Hello World";
long RxtaskCntr = 0;
#define PS_KEY (54 + 0) //EMIO0,对应的GPIO编号为54+0=54
#define PIC_SEL_PIN (54 + 1) //EMIO1,对应的GPIO编号为54+1=55
#define EMPTY_PIN (54 + 2) //EMIO2,对应的GPIO编号为54+2=56
#define INPUT 1
#define OUTPUT 0
XGpioPs GpioPs; //GPIO实例对象
//static void ScuTimer_InterruptHandler(void *data)
//{
// XScuTimer *timer = (XScuTimer *)data;
// lv_tick_inc(1);
// XScuTimer_ClearInterruptStatus(timer);
//}
void gpio_init()
{
PS_GPIO_Init(); //初始化PS端MIO和EMIO
PS_GPIO_SetMode(PS_KEY, OUTPUT, 0);
//PS_GPIO_SetMode(PIC_SEL_PIN, INPUT, 0);
PS_GPIO_SetMode(EMPTY_PIN, INPUT, 1);
}
void qspi_mode_set(u8 mode)
{
PS_GPIO_SetPort(PS_KEY, mode);
}
//u32 read_full()
//{
// return PS_GPIO_GetPort(FULL_PIN);
//}
u32 read_empty()
{
return PS_GPIO_GetPort(EMPTY_PIN);
}
/***********************************************************************************/
#define RED 0xF800
#define ORANGE 0xFC00
#define YELLOW 0xFFE0
#define GREEN 0x07E0
#define CYAN 0x07FF
#define BLUE 0x001F
#define PURPPLE 0xF81F
#define BLACK 0x0000
#define WHITE 0xFFFF
#define GRAY 0xD69A
const u16 colors[] = {
RED,
GREEN,
BLUE,
ORANGE,
YELLOW,
CYAN,
PURPPLE,
BLACK,
WHITE,
GRAY
};
typedef struct{
u8 txbuf[50];
u8 rxbuf[50];
u8 length;
}t_buf;
//#define BIST_MODE
t_buf WriteBuffer[]={
{{0xf0, 0xc3}, {0x00},2},
{{0xf0, 0x96}, {0x00},2},
{{0xf0, 0xa5}, {0x00},2},
{{0xe9, 0x20}, {0x00},2},
{{0xe7, 0x80, 0x77, 0x1f, 0xcc}, {0x00}, 5},
{{0xc1, 0x77, 0x07, 0xcf, 0x16}, {0x00},5},//Normal:VGHS/VGLS/VSP/VSN电压
{{0xc2, 0x77, 0x07, 0xcf, 0x16}, {0x00},5},//Idle: VGHS/VGLS/VSP/VSN电压
{{0xc3, 0x22, 0x02, 0x22, 0x04}, {0x00},5},//Normal: VGH/VGL/VSP/VSN Clk
{{0xc4, 0x22, 0x02, 0x22, 0x04}, {0x00},5},//Idle: VGH/VGL/VSP/VSN Clk
{{0xc5, 0xed},{0x00},2}, //VCOM
{{0xe0, 0x87, 0x09, 0x0c, 0x06, 0x05, 0x03, 0x29, 0x32, 0x49, 0x0f, 0x1b, 0x17, 0x2a, 0x2f},{0x00},15},
{{0xe1, 0x87, 0x09, 0x0c, 0x06, 0x05, 0x03, 0x29, 0x32, 0x49, 0x0f, 0x1b, 0x17, 0x2a, 0x2f},{0x00},15},
{{0xe5, 0xbe, 0xf5, 0xb1, 0x22, 0x22, 0x25, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22},{0x00},15},
{{0xe6, 0xbe, 0xf5, 0xb1, 0x22, 0x22, 0x25, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22},{0x00},15},
{{0xec, 0x40, 0x03},{0x00},3},
{{0x36, 0x0c},{0x00},2},
{{0x3a, 0x07},{0x00},2},
{{0xb2, 0x00},{0x00},2},//GIP pattern
{{0xb3, 0x01},{0x00},2},//video mode dot-inversion
{{0xb4, 0x01},{0x00},2},
{{0xb5, 0x00, 0x08, 0x00, 0x08},{0x00},5}, //vfp
{{0xb6, 0xc7, 0x31},{0x00},3},
{{0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x2a, 0x8a, 0x02},{0x00},10},
{{0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x2a, 0x8a, 0x02},{0x00},10},
{{0xba, 0x0a, 0x5a, 0x23, 0x10, 0x25, 0x02, 0x00},{0x00},8},
{{0xbb, 0x00, 0x30, 0x00, 0x2c, 0x82, 0x87, 0x18, 0x00},{0x00},9},
{{0xbc, 0x00, 0x30, 0x00, 0x2c, 0x82, 0x87, 0x18, 0x00},{0x00},9},
{{0xbd, 0xa1, 0xb2, 0x2b, 0x1a, 0x56, 0x43, 0x34, 0x65, 0xff, 0xff, 0x0f},{0x00},12},
{{0x35, 0x00},{0x00},2},
{{0x21},{0x00},1},
{{0x11},{0x00},1},
{{0xff},{0x00},0},
{{0x29},{0x00},1},
{{0xff},{0x00},0},
#ifdef BIST_MODE
{{0xb0, 0xa5},{0x00},2},
{{0xcc, 0x40, 0x00, 0x3f, 0x00, 0x14, 0x14, 0x20, 0x20, 0x03},{0x00},10},
#endif
};
/*************************************************
* reg rw functions
************************************************/
#define DMA_BASE_ADRR 0x40000000
#define DDR_BASE_ADDR 0x10000000
void wait_for_idle()
{
u32 tmp;
while(1){
tmp=Xil_In32(DMA_BASE_ADRR+04);
if(((tmp>>1)&0x1) == 1){
break;
}
}
}
void dma_xfer(u32 mem_addr, u32 length)
{/*用于初始化阶段,需要刷新Cache,确保初始化成功*/
Xil_DCacheFlush();
//Xil_DCacheFlushRange(mem_addr, length);
Xil_Out32(DMA_BASE_ADRR, 0x00010003);
Xil_Out32(DMA_BASE_ADRR+0x30, 0x00010003);
Xil_Out32(DMA_BASE_ADRR+0x18, mem_addr);
Xil_Out32(DMA_BASE_ADRR+0x28, length); /*length is bytes*/
wait_for_idle();
//usleep(10);
}
void dma_xfer2(u32 mem_addr, u32 length)
{/*发送数据阶段,可以不用Cache*/
//Xil_DCacheFlush();
Xil_DCacheFlushRange(mem_addr, length);
Xil_Out32(DMA_BASE_ADRR, 0x00010003);
Xil_Out32(DMA_BASE_ADRR+0x30, 0x00010003);
Xil_Out32(DMA_BASE_ADRR+0x18, mem_addr);
Xil_Out32(DMA_BASE_ADRR+0x28, length); /*length is bytes*/
wait_for_idle();
//usleep(10);
}
void set_cfg(u32 mem_addr, u8 cmd, u8 addr, u8 mode, u8 work_mode, u16 length)
{
u32 tmp=cmd;
tmp|=addr<<8;
tmp|=mode<<16;
tmp|=length<<20;
// tmp|=work_mode<<30;
Xil_Out32(mem_addr, tmp);
}
u32 get_cfg(u8 cmd, u8 addr, u8 mode, u8 work_mode, u16 length)
{
u32 tmp=cmd;
tmp|=addr<<8;
tmp|=mode<<16;
tmp|=length<<20;
// tmp|=work_mode<<30;
return tmp;
}
void set_npdata8(u32 mem_addr, u8 *pdata, u32 length){
u32 addr=mem_addr;
while(length--){
Xil_Out8(addr++, *pdata++);
}
}
/*************************************************
* basic xfer functions
************************************************/
void xfer_cmd_pdata8(u32 mem_addr, u8 cmd, u8* pdata, u32 length){
if(length==0)
set_cfg(mem_addr, 0xde, cmd, 0b0101, 0b00, length); /*byte_mode=0, cs_mode=1, data_mode=0b01*/
else
set_cfg(mem_addr, 0xde, cmd, 0b0110, 0b00, length); /*byte_mode=0, cs_mode=1, data_mode=0b10*/
set_npdata8(mem_addr+4, pdata,length);
dma_xfer(mem_addr, 4+length);
}
void xfer_cmd_pdata16_dma(u32 mem_addr, u32 length){
dma_xfer2(mem_addr, length);
}
void seqs_init() {
for (int i = 0; i < sizeof(WriteBuffer) / sizeof(*WriteBuffer); i++) {
if (WriteBuffer[i].length == 0) {
usleep(WriteBuffer[i].txbuf[0] * 1000);
continue;
}
u8 cmd=WriteBuffer[i].txbuf[0];
u8 *pdata=WriteBuffer[i].txbuf+1;
u16 length=WriteBuffer[i].length-1;
xfer_cmd_pdata8(DDR_BASE_ADDR, cmd, pdata, length);
usleep(10); //this delay is need
//xil_printf("Exec CMD=%x\r\n\r\n", cmd);
}
}
/*************************************************
* application functions
************************************************/
#define LCD_BPP (24)
#define LCD_X_SIZE (400U) /* available x pixel size */
#define LCD_Y_SIZE (400U) /* available y pixle size */
#define LCD_PBYTE ((LCD_BPP + 0) / 8) /* bytes in pixel unit */
#define LCD_HBYTE (LCD_X_SIZE * LCD_PBYTE) /* bytes in horizontal line */
#define LCD_VSW (1U)
#define LCD_HFP (8U)
#define LCD_HBP (8U)
#define LCD_TE_OFT (25U)
typedef u8 (*ArrayPtr)[400][20 * 60];
ArrayPtr frame_buffer=(ArrayPtr)0x10100000;
ArrayPtr p_frame_cache=(ArrayPtr)0x10100000;
//static uint8_t frame_cache[LCD_Y_SIZE][LCD_HBYTE] __attribute__ ((aligned (64)));
void lcdqspi_set_pixel(uint32_t x, uint32_t y, uint32_t color)
{
if ((x < LCD_X_SIZE)&&(y < LCD_Y_SIZE))
{
uint32_t xoft = x * 3;
(*p_frame_cache)[y][xoft + 2] = color & 0xff;
(*p_frame_cache)[y][xoft + 1] = (color & 0xff00) >> 8;
(*p_frame_cache)[y][xoft + 0] = (color & 0xff0000) >> 16;
}
}
void lcdqspi_fill_block(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t color)
{
if ((x1 < LCD_X_SIZE)&&(y1 < LCD_Y_SIZE)&&(x1 >= x0)&&(y1 >= y0))
{
uint32_t x,y;
for (y = y0; y <= y1; y++)
{
uint32_t oft = x0 * 3;
for (x = x0; x <= x1; x++)
{
(*p_frame_cache)[y][oft + 2] = color & 0xff;
(*p_frame_cache)[y][oft + 1] = (color & 0xff00) >> 8;
(*p_frame_cache)[y][oft + 0] = (color & 0xff0000) >> 16;
oft += 3;
}
}
}
}
void lcdqspi_clear(uint32_t color)
{
lcdqspi_fill_block(0, 0, LCD_X_SIZE - 1, LCD_Y_SIZE - 1, color);
}
//void __attribute__((section(".my_section"))) lcdqspi_draw_line(uint32_t x0, uint32_t x1, uint32_t y, uint32_t *pixel)
void lcdqspi_draw_line(uint32_t x0, uint32_t x1, uint32_t y, uint32_t *pixel)
{
if ((x1 < LCD_X_SIZE)&&(y < LCD_Y_SIZE)&&(x1 >= x0))
{
uint32_t oft = x0 * 3;
uint32_t *dat = pixel;
for (uint32_t x = x0; x <= x1; x++)
{
(*p_frame_cache)[y][oft + 2] = *dat & 0xff;
(*p_frame_cache)[y][oft + 1] = (*dat & 0xff00) >> 8;
(*p_frame_cache)[y][oft + 0] = (*dat & 0xff0000) >> 16;
oft += 3;
dat++;
}
}
}
void lcdqspi_set_te(uint32_t offset)
{
xSemaphoreGive( xSemaphore );
}
void lcdqspi_wait_te(void)
{
xSemaphoreTake( xSemaphore, portMAX_DELAY );
}
//static void prvRxTask_bak( void *pvParameters )
//{
//// lcdqspi_clear(0xff0000);
// u32 color_index=0;
//
//
// while(1){
// xil_printf("1\r\n");
// color_index++;
// color_index%=5;
//
// switch(color_index){
// case 0: lcdqspi_clear(0xff0000); break;
// case 1: lcdqspi_clear(0x00ff00); break;
// case 2: lcdqspi_clear(0x0000ff); break;
// case 3: lcdqspi_clear(0x000000); break;
// case 4: lcdqspi_clear(0xffffff); break;
// }
//
// memcpy((void *)0x10100000, (void *)0x10200000, 400*400*3);
// Xil_DCacheFlush();
// //Xil_DCacheFlushRange(0x10100000, 400*400*3);
// vTaskDelay( pdMS_TO_TICKS( 500 ) );
// // Xil_DCacheFlushRange(0x10100000, 400*400*3);
// }
//}
#define OCM_BASE_ADDR 0x10500000
#define LOCK_ADDR (OCM_BASE_ADDR)
u8 color_index=0;
static void prvTxTask( void *pvParameters )
{
while (1)
{
//Xil_DCacheInvalidateRange(0x10100000, 400*400*3);
/* vs(0x61) packet */
for (int i = 0; i < LCD_VSW; i++)
{
xfer_cmd_pdata8(DDR_BASE_ADDR, 0x61,NULL,0);
usleep(40);
}
/* hbp(0x60) packet */
for (int i = 0; i < LCD_HBP; i++)
{
xfer_cmd_pdata8(DDR_BASE_ADDR, 0x60,NULL,0);
usleep(40);
}
qspi_mode_set(1);
/* transmit display cache data to lcd line by line */
for (int i = 0; i < LCD_Y_SIZE; i+=1)
{
//xfer_cmd_enter_stream(DDR_BASE_ADDR);
xfer_cmd_pdata16_dma((u32)(*frame_buffer)[i],20*60); /*这个地方是*frame_buffer,不要搞错了!!*/
usleep(47);
}
qspi_mode_set(0);
/* hfp(0x60) packet */
for (int i = 0; i < LCD_HFP; i++)
{
xfer_cmd_pdata8(DDR_BASE_ADDR, 0x60,NULL,0);
usleep(40);
}
xSemaphoreGive( xSemaphore );
vTaskDelay( pdMS_TO_TICKS( 15 ) );
}
}
static lv_obj_t * gauge1;
void lv_ex_gauge_2(void)
{
/*Describe the color for the needles*/
static lv_color_t needle_colors[3];
needle_colors[0] = LV_COLOR_BLUE;
needle_colors[1] = LV_COLOR_RED;
needle_colors[2] = LV_COLOR_LIME;
LV_IMG_DECLARE(img_hand);
/*Create a gauge*/
gauge1 = lv_gauge_create(lv_scr_act(), NULL);
lv_gauge_set_scale(gauge1, 360, 60, 12);
lv_gauge_set_needle_count(gauge1, 3, needle_colors);
lv_gauge_set_range(gauge1, 0, 59);
lv_obj_set_size(gauge1, 400, 400);
lv_obj_align(gauge1, NULL, LV_ALIGN_CENTER, 0, 0);
//lv_gauge_set_formatter_cb(lv_obj_t * gauge, lv_gauge_format_cb_t format_cb)
//lv_gauge_set_needle_img(gauge1, &img_hand, 4, 4);
/*Allow recoloring of the images according to the needles' color*/
//lv_obj_set_style_local_image_recolor_opa(gauge1, LV_GAUGE_PART_NEEDLE, LV_STATE_DEFAULT, LV_OPA_COVER);
// lv_obj_set_style_local_line_width(gauge1, LV_GAUGE_PART_MAJOR, LV_STATE_DEFAULT, 3);
//lv_obj_set_style_local_line_width(gauge1, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 3);
/* 表盘长刻度线条宽度 */
lv_obj_set_style_local_line_width(gauge1, LV_GAUGE_PART_MAJOR, LV_STATE_DEFAULT, 0);
/* 表盘长刻度线条颜色 */
lv_obj_set_style_local_line_color(gauge1, LV_GAUGE_PART_MAJOR, LV_STATE_DEFAULT, LV_COLOR_RED);
/* 表盘短刻度线条宽度 */
lv_obj_set_style_local_line_width(gauge1, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 5);
/* 表盘短刻度线条颜色 */
lv_obj_set_style_local_line_color(gauge1, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
/* 表盘内LABEL字体颜色 LABEL数量和长刻度数量角度一致 */
lv_obj_set_style_local_text_color(gauge1, LV_GAUGE_PART_MAJOR, LV_STATE_DEFAULT, LV_COLOR_LIME);
/* 表盘内刻度盘与外框边缘的距离 */
//lv_obj_set_style_local_pad_all (gauge1, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, 10);
/* 表盘背景颜色 */
lv_obj_set_style_local_bg_color(gauge1, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
/* 表盘外框边缘颜色 */
//lv_obj_set_style_local_border_color(gauge1, LV_GAUGE_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
lv_obj_set_style_local_line_width(gauge1, LV_GAUGE_PART_NEEDLE, LV_STATE_DEFAULT, 10);
/*Set the values*/
lv_gauge_set_value(gauge1, 0, 10);
lv_gauge_set_value(gauge1, 1, 20);
lv_gauge_set_value(gauge1, 2, 30);
}
void gauge_run(void)
{
static uint32_t hh = 30;
static uint32_t mm = 0;
static uint32_t ss = 0;
ss += 1;
if (ss > 59){ss = 0;mm++;}
if (mm > 59){mm = 0;hh++;}
if (hh > 59){hh = 0;}
lv_gauge_set_value(gauge1, 0, hh);
lv_gauge_set_value(gauge1, 1, mm);
lv_gauge_set_value(gauge1, 2, ss);
}
static void prvRxTask( void *pvParameters )
{
/* lvgl初始化 */
lv_init();
lv_port_disp_init();
//lv_port_indev_init();
/* 创建demo */
//lv_demo_printer();
//lv_demo_widgets();
lv_ex_gauge_2();
u32 loop_cnt=0;
while (1)
{
//xil_printf("1\r\n");
//gauge_run();
lv_task_handler();
vTaskDelay( pdMS_TO_TICKS( 10 ) );
loop_cnt++;
if(loop_cnt%20==0){
gauge_run();
}
}
}
void vApplicationTickHook()
{
//xil_printf("4\r\n");
lv_tick_inc(1);
}
int main( void )
{
const TickType_t x10seconds = pdMS_TO_TICKS( DELAY_10_SECONDS );
Xil_DCacheDisable();
//Xil_MemMap(0x10100000,0x300000, NORM_NONCACHE);
//Xil_MemMap(0x100000,0x800000, NORM_NONCACHE);
gpio_init();
qspi_mode_set(0);
for(int i=0; i<2; i++){ //固化到flash启动不成功,后发现把这个地方次数设置为多次,就可以稳定启动
seqs_init();
}
xil_printf( "Hello from Freertos example main\r\n" );
/* Create the two tasks. The Tx task is given a lower priority than the
Rx task, so the Rx task will leave the Blocked state and pre-empt the Tx
task as soon as the Tx task places an item in the queue. */
xTaskCreate( prvTxTask, /* The function that implements the task. */
( const char * ) "Tx", /* Text name for the task, provided to assist debugging only. */
2000, /* The stack allocated to the task. */
NULL, /* The task parameter is not used, so set to NULL. */
tskIDLE_PRIORITY+1, /* The task runs at the idle priority. */
&xTxTask );
xTaskCreate( prvRxTask,
( const char * ) "GB",
2000,
NULL,
tskIDLE_PRIORITY,
&xRxTask );
xSemaphore = xSemaphoreCreateBinary();
/* Create the queue used by the tasks. The Rx task has a higher priority
than the Tx task, so will preempt the Tx task and remove values from the
queue as soon as the Tx task writes to the queue - therefore the queue can
never have more than one item in it. */
// xQueue = xQueueCreate( 1, /* There is only one space in the queue. */
// sizeof( HWstring ) ); /* Each space in the queue is large enough to hold a uint32_t. */
//
// /* Check the queue was created. */
// configASSERT( xQueue );
/* Create a timer with a timer expiry of 10 seconds. The timer would expire
after 10 seconds and the timer call back would get called. In the timer call back
checks are done to ensure that the tasks have been running properly till then.
The tasks are deleted in the timer call back and a message is printed to convey that
the example has run successfully.
The timer expiry is set to 10 seconds and the timer set to not auto reload. */
// xTimer = xTimerCreate( (const char *) "Timer",
// x10seconds,
// pdFALSE,
// (void *) TIMER_ID,
// vTimerCallback);
// /* Check the timer was created. */
// configASSERT( xTimer );
/* start the timer with a block time of 0 ticks. This means as soon
as the schedule starts the timer will start running and will expire after
10 seconds */
// xTimerStart( xTimer, 0 );
/* Start the tasks and timer running. */
vTaskStartScheduler();
/* If all is well, the scheduler will now be running, and the following line
will never be reached. If the following line does execute, then there was
insufficient FreeRTOS heap memory available for the idle and/or timer tasks
to be created. See the memory management section on the FreeRTOS web site
for more details. */
for( ;; );
}
/*-----------------------------------------------------------*/
//static void prvTxTask( void *pvParameters )
//{
//const TickType_t x1second = pdMS_TO_TICKS( DELAY_1_SECOND );
//
// for( ;; )
// {
// /* Delay for 1 second. */
// vTaskDelay( x1second );
//
// /* Send the next value on the queue. The queue should always be
// empty at this point so a block time of 0 is used. */
// xQueueSend( xQueue, /* The queue being written to. */
// HWstring, /* The address of the data being sent. */
// 0UL ); /* The block time. */
// }
//}
/*-----------------------------------------------------------*/
//static void prvRxTask( void *pvParameters )
//{
//char Recdstring[15] = "";
//
// for( ;; )
// {
// /* Block to wait for data arriving on the queue. */
// xQueueReceive( xQueue, /* The queue being read. */
// Recdstring, /* Data is read into this address. */
// portMAX_DELAY ); /* Wait without a timeout for data. */
//
// /* Print the received data. */
// xil_printf( "Rx task received string from Tx task: %s\r\n", Recdstring );
// RxtaskCntr++;
// }
//}
/*-----------------------------------------------------------*/
//static void vTimerCallback( TimerHandle_t pxTimer )
//{
// long lTimerId;
// configASSERT( pxTimer );
//
// lTimerId = ( long ) pvTimerGetTimerID( pxTimer );
//
// if (lTimerId != TIMER_ID) {
// xil_printf("FreeRTOS Hello World Example FAILED");
// }
//
// /* If the RxtaskCntr is updated every time the Rx task is called. The
// Rx task is called every time the Tx task sends a message. The Tx task
// sends a message every 1 second.
// The timer expires after 10 seconds. We expect the RxtaskCntr to at least
// have a value of 9 (TIMER_CHECK_THRESHOLD) when the timer expires. */
// if (RxtaskCntr >= TIMER_CHECK_THRESHOLD) {
// xil_printf("FreeRTOS Hello World Example PASSED");
// } else {
// xil_printf("FreeRTOS Hello World Example FAILED");
// }
//
// vTaskDelete( xRxTask );
// vTaskDelete( xTxTask );
//}
/**
* @file lv_port_disp_templ.c
*
*/
/*Copy this file as "lv_port_disp.c" and set this value to "1" to enable content*/
#if 1
/*********************
* INCLUDES
*********************/
#include "lv_port_disp.h"
#include "xil_cache.h"
#include "xtime_l.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
#if LV_USE_GPU
static void gpu_blend(lv_disp_drv_t * disp_drv, lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa);
static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width,
const lv_area_t * fill_area, lv_color_t color);
#endif
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
//extern unsigned int frame_buffer_addr; // LCD显存地址
#define LCD_DOUBLE_BUFFER_ADDR 0x10200000
#define LCD_DOUBLE_BUFFER_ADDR2 0x10300000
void lv_port_disp_init(void)
{
static lv_disp_buf_t disp_buf;
lv_disp_buf_init(&disp_buf, (void *)LCD_DOUBLE_BUFFER_ADDR,
(void *)LCD_DOUBLE_BUFFER_ADDR2,
LV_HOR_RES_MAX * LV_VER_RES_MAX); /*Initialize the display buffer*/
/*-----------------------------------
* Register the display in LVGL
*----------------------------------*/
lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
/*Set up the functions to access to your display*/
/*Set the resolution of the display*/
disp_drv.hor_res = LV_HOR_RES_MAX;
disp_drv.ver_res = LV_VER_RES_MAX;
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = disp_flush;
/*Set a display buffer*/
disp_drv.buffer = &disp_buf;
#if LV_USE_GPU
/*Optionally add functions to access the GPU. (Only in buffered mode, LV_VDB_SIZE != 0)*/
/*Blend two color array using opacity*/
disp_drv.gpu_blend_cb = gpu_blend;
/*Fill a memory array with a color*/
disp_drv.gpu_fill_cb = gpu_fill;
#endif
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/* Flush the content of the internal buffer the specific area on the display
* You can use DMA or any hardware acceleration to do this operation in the background but
* 'lv_disp_flush_ready()' has to be called when finished. */
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
int16_t x;
int16_t y;
uint8_t *ptrBase;
uint8_t *ptr;
/* 优化后的结果 */
// ptrBase = (area->y1 * LV_HOR_RES_MAX + area->x1) * 2 + (uint8_t *)frame_buffer_addr;
// for(y = area->y1; y <= area->y2; y++, ptrBase += (LV_HOR_RES_MAX * 2)) {
//
// for(x = area->x1, ptr = ptrBase; x <= area->x2; x++, ptr += 2) {
//
// *(uint16_t *)ptr = color_p->full;
// color_p++;
// }
// }
XTime t1,t2;
extern void lcdqspi_wait_te(void);
extern void lcdqspi_draw_line(uint32_t x0, uint32_t x1, uint32_t y, uint32_t *pixel);
lcdqspi_wait_te();
XTime_GetTime(&t1);
xil_printf("x0=%d, x1=%d, y0=%d, y1=%d\r\n", area->x1, area->x2, area->y1, area->y2);
for (uint32_t y = area->y1; y <= area->y2; y++)
{
lcdqspi_draw_line(area->x1, area->x2, y, (uint32_t *)color_p);
color_p = color_p + area->x2 - area->x1 + 1;
//rt_kprintf("draw %d\r\n", y);
}
XTime_GetTime(&t2);
int dt = (u32)(t2-t1) * (1000000.0/ COUNTS_PER_SECOND);
xil_printf("dt=%d\r\n", dt);
/* IMPORTANT!!!
* Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
/*OPTIONAL: GPU INTERFACE*/
#if LV_USE_GPU
/* If your MCU has hardware accelerator (GPU) then you can use it to blend to memories using opacity
* It can be used only in buffered mode (LV_VDB_SIZE != 0 in lv_conf.h)*/
static void gpu_blend(lv_disp_drv_t * disp_drv, lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa)
{
/*It's an example code which should be done by your GPU*/
uint32_t i;
for(i = 0; i < length; i++) {
dest[i] = lv_color_mix(dest[i], src[i], opa);
}
}
/* If your MCU has hardware accelerator (GPU) then you can use it to fill a memory with a color
* It can be used only in buffered mode (LV_VDB_SIZE != 0 in lv_conf.h)*/
static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width,
const lv_area_t * fill_area, lv_color_t color)
{
/*It's an example code which should be done by your GPU*/
int32_t x, y;
dest_buf += dest_width * fill_area->y1; /*Go to the first line*/
for(y = fill_area->y1; y <= fill_area->y2; y++) {
for(x = fill_area->x1; x <= fill_area->x2; x++) {
dest_buf[x] = color;
}
dest_buf+=dest_width; /*Go to the next line*/
}
}
#endif /*LV_USE_GPU*/
#else /* Enable this file at the top */
/* This dummy typedef exists purely to silence -Wpedantic. */
typedef int keep_pedantic_happy;
#endif
-
lv_conf.h文件中的分辨率和色彩深度,是必须要检查的。由于色彩深度我没有修改为32(st77903是rgb888屏),导致显示异常,我花了2个小时折腾排查原因
-
注意lv_disp_port.c中这个buffer的地址,不是gram的地址。我们gram的地址是0x10100000,这个地址我设置为0x10200000。如果把这个地址搞错,设置成与gram地址一样,显示异常。我在这个地方折腾了1个小时排查原因。 另外,这个地方我如果设置双Buffer模式,显示也异常,原因不清楚。
-
关于Dcache,影响非常大,而且飘忽不定。如果打开Dcache,直接没有显示,我尝试了各种DcacheFlush, DcacheInvalidate等等操作,都不行。最后,无奈之下,我只能把Dcache关闭了。效果上,由于lvgl的disp_flush执行时间非常长(不开Dcache,这个函数运行时间800ms;开Dcache,这个函数运行时间40ms),导致能看到表盘指针转动的时候,有很大拖影。因为这个函数的执行过程,相当于是跨越了60帧的刷新时间。
-
下面这个图,是关闭了Dcache后,对disp_flush函数执行时间的测量。可见大约100ms。
- 另一种方式,是配置mmu的属性,在"xil_mmu.h"库中有相应的函数,其实就是设置TLB。我们尝试了一下,如果将整个程序设置为non_chache模式,也是可以运行的,效果类似于关闭Dcache。
- 显示效果色彩非常鲜艳,很好
- 指针转动的过程,有拖影。主要原因是disp_flush函数的时间还是有点长。
-
第一步,创建内存区域,创建自定义section,并将自定义section分配到指定内存区域
-
用__attribute__((section(".my_section")))将某个函数放到my_section中,编译后,查看生成的elf文件,可以看到这个函数确实放到了指定的内存区域。
-
这样做的好处是,可以用void* Xil_MemMap(UINTPTR PhysAddr, size_t size, u32 flags)函数,对指定的区域设置存储属性。比如,我想让这个函数或这个变量所在的内存区域,关闭cache,那么我就可以这样做: myfunc()-->my_section-->my_addr_range-->Xil_Memmap进行属性设置。
-
虽然可以这样做,但我们将disp_flush函数放到指定的内存区域,并对这段内存区域设置排除在NORM_NONCACHE属性外,屏幕就点不亮了,这个原因不清楚。