因为之前的H750内部空间太小,无法放入UI资源,所以这次直接买了块NUCLEO-H743ZI2,驱动上做了指令1LANE和4LANE的兼容
驱动文件如下:
#include "board.h"
#include "sram.h"
#include "rtthread.h"
#include "rtdevice.h"
#include "lcdqspi.h"
#define DBG
#define DBG_TAG "lcdqspi"
#include "log.h"
#define FRAME_BLANKING_TIME (10U)
#define FRAME_MEM_SIZE (LCD_HBYTE * LCD_Y_SIZE)
#define FRAME_MEM_BASE SRAM_AXI_BASE
#define QSPI_CLK_MHZ (25)
#if (FRAME_MEM_SIZE > SRAM_AXI_SIZE)
#error "lcdqspi frame cache size is not enough!"
#endif
static uint8_t frame_cache[LCD_Y_SIZE][LCD_HBYTE] __attribute__ ((at(FRAME_MEM_BASE)));
void rt_hw_us_delay(rt_uint32_t us);
static QSPI_HandleTypeDef QSPI_Handler;
static QSPI_CommandTypeDef QSPI_Cmdhandler;
static struct rt_semaphore trans_sem = {0};
static struct rt_completion frame_sync = {0};
static struct rt_timer te_timer = {0};
static __IO uint32_t te_offset = LCD_TE_OFT;
void QUADSPI_IRQHandler(void)
{
IRQ_ENTER();
HAL_QSPI_IRQHandler(&QSPI_Handler);
IRQ_LEAVE();
}
void MDMA_IRQHandler(void)
{
IRQ_ENTER();
HAL_MDMA_IRQHandler(QSPI_Handler.hmdma);
IRQ_LEAVE();
}
void HAL_QSPI_CmdCpltCallback(QSPI_HandleTypeDef *hqspi)
{
rt_sem_release(&trans_sem);
}
void HAL_QSPI_TxCpltCallback(QSPI_HandleTypeDef *hqspi)
{
rt_sem_release(&trans_sem);
}
void HAL_QSPI_MspInit(QSPI_HandleTypeDef *hqspi)
{
static MDMA_HandleTypeDef hmdma;
__HAL_RCC_QSPI_CLK_ENABLE();
__HAL_RCC_QSPI_FORCE_RESET();
__HAL_RCC_QSPI_RELEASE_RESET();
__HAL_RCC_MDMA_CLK_ENABLE();
__HAL_RCC_MDMA_FORCE_RESET();
__HAL_RCC_MDMA_RELEASE_RESET();
HAL_NVIC_SetPriority(QUADSPI_IRQn, NVIC_PRIORITY_QSPI, 0);
HAL_NVIC_EnableIRQ(QUADSPI_IRQn);
hmdma.Init.Request = MDMA_REQUEST_QUADSPI_FIFO_TH;
hmdma.Init.TransferTriggerMode = MDMA_BUFFER_TRANSFER;
hmdma.Init.Priority = MDMA_PRIORITY_HIGH;
hmdma.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE;
hmdma.Init.SourceInc = MDMA_SRC_INC_BYTE;
hmdma.Init.DestinationInc = MDMA_DEST_INC_DISABLE;
hmdma.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE;
hmdma.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE;
hmdma.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE;
hmdma.Init.BufferTransferLength = 16;
hmdma.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE;
hmdma.Init.DestBurst = MDMA_DEST_BURST_SINGLE;
hmdma.Init.SourceBlockAddressOffset = 0;
hmdma.Init.DestBlockAddressOffset = 0;
hmdma.Instance = MDMA_Channel1;
__HAL_LINKDMA(hqspi, hmdma, hmdma);
HAL_MDMA_DeInit(&hmdma);
HAL_MDMA_Init(&hmdma);
HAL_NVIC_SetPriority(MDMA_IRQn, 0x00, 0);
HAL_NVIC_EnableIRQ(MDMA_IRQn);
}
void HAL_QSPI_MspDeInit(QSPI_HandleTypeDef *hqspi)
{
__HAL_RCC_QSPI_FORCE_RESET();
__HAL_RCC_QSPI_RELEASE_RESET();
HAL_NVIC_DisableIRQ(QUADSPI_IRQn);
__HAL_RCC_MDMA_FORCE_RESET();
__HAL_RCC_MDMA_RELEASE_RESET();
HAL_NVIC_DisableIRQ(MDMA_IRQn);
}
/* QSPI initialize */
static void ll_qspi_init(void)
{
static DMA_HandleTypeDef hdma;
__HAL_RCC_QSPI_CLK_ENABLE();
__HAL_RCC_DMA1_CLK_ENABLE();
QSPI_Handler.Instance = QUADSPI;
/* D1HCLK(200MHz) / (prescaler + 1) = 200/5 = 40MHZ */
QSPI_Handler.Init.ClockPrescaler = 200 / QSPI_CLK_MHZ - 1;
QSPI_Handler.Init.FifoThreshold = 16;
QSPI_Handler.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_NONE;
QSPI_Handler.Init.FlashSize = POSITION_VAL(0X800000)-1;
QSPI_Handler.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_5_CYCLE;
/* clock keep high level in idle time cs inactive */
QSPI_Handler.Init.ClockMode = QSPI_CLOCK_MODE_3;
QSPI_Handler.Init.FlashID = QSPI_FLASH_ID_1;
QSPI_Handler.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
HAL_QSPI_Init(&QSPI_Handler);
/* 0xde is the first byte every transmit */
QSPI_Cmdhandler.Instruction = 0xde;
/* qspi 24bit address, lcd command is set in A15~A8 */
QSPI_Cmdhandler.Address = 0x000000;
QSPI_Cmdhandler.DummyCycles = 0;
QSPI_Cmdhandler.InstructionMode = QSPI_INSTRUCTION_1_LINE;
QSPI_Cmdhandler.AddressMode = QSPI_ADDRESS_1_LINE;
QSPI_Cmdhandler.AddressSize = QSPI_ADDRESS_24_BITS;
QSPI_Cmdhandler.DataMode = QSPI_DATA_NONE;
QSPI_Cmdhandler.NbData = 0;
QSPI_Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
QSPI_Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
QSPI_Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE;
QSPI_Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
}
/* lcd delay millisecond */
__INLINE static void lcd_dlyms(uint32_t ms)
{
rt_thread_delay(ms);
}
__INLINE static void lcd_dlyus(uint32_t us)
{
rt_hw_us_delay(us);
}
/* lcd transmit, write command, write display data */
static void lcd_transmit(uint32_t cmd, uint32_t len, uint8_t *dat)
{
if (len == 0)
{ /* write command, no parameter */
#if (CMD_LANE == CMD_LANE_1)
QSPI_Cmdhandler.Instruction = 0xd8;
QSPI_Cmdhandler.InstructionMode = QSPI_INSTRUCTION_1_LINE;
QSPI_Cmdhandler.AddressMode = QSPI_ADDRESS_1_LINE;
QSPI_Cmdhandler.DataMode = QSPI_DATA_NONE;
#endif
#if (CMD_LANE == CMD_LANE_4)
QSPI_Cmdhandler.Instruction = 0xde;
QSPI_Cmdhandler.InstructionMode = QSPI_INSTRUCTION_4_LINES;
QSPI_Cmdhandler.AddressMode = QSPI_ADDRESS_4_LINES;
QSPI_Cmdhandler.DataMode = QSPI_DATA_NONE;
#endif
QSPI_Cmdhandler.Address = cmd << 8;
QSPI_Cmdhandler.NbData = 0;
/* interrupt mode */
HAL_QSPI_Command_IT(&QSPI_Handler, &QSPI_Cmdhandler);
}
else if (len <= INIT_DAT_LEN)
{ /* write command with parameter */
#if (CMD_LANE == CMD_LANE_1)
QSPI_Cmdhandler.Instruction = 0xd8;
QSPI_Cmdhandler.InstructionMode = QSPI_INSTRUCTION_1_LINE;
QSPI_Cmdhandler.AddressMode = QSPI_ADDRESS_1_LINE;
QSPI_Cmdhandler.DataMode = QSPI_DATA_1_LINE;
#endif
#if (CMD_LANE == CMD_LANE_4)
QSPI_Cmdhandler.Instruction = 0xde;
QSPI_Cmdhandler.InstructionMode = QSPI_INSTRUCTION_4_LINES;
QSPI_Cmdhandler.AddressMode = QSPI_ADDRESS_4_LINES;
QSPI_Cmdhandler.DataMode = QSPI_DATA_4_LINES;
#endif
QSPI_Cmdhandler.Address = cmd << 8;
QSPI_Cmdhandler.NbData = len;
/* interrupt mode */
HAL_QSPI_Command_IT(&QSPI_Handler, &QSPI_Cmdhandler);
HAL_QSPI_Transmit_IT(&QSPI_Handler, dat);
}
else
{ /* write display data by hbyte length */
#if (CMD_LANE == CMD_LANE_1)
QSPI_Cmdhandler.Instruction = 0xde;
QSPI_Cmdhandler.InstructionMode = QSPI_INSTRUCTION_1_LINE;
QSPI_Cmdhandler.AddressMode = QSPI_ADDRESS_1_LINE;
QSPI_Cmdhandler.DataMode = QSPI_DATA_4_LINES;
#endif
#if (CMD_LANE == CMD_LANE_4)
QSPI_Cmdhandler.Instruction = 0xde;
QSPI_Cmdhandler.InstructionMode = QSPI_INSTRUCTION_4_LINES;
QSPI_Cmdhandler.AddressMode = QSPI_ADDRESS_4_LINES;
QSPI_Cmdhandler.DataMode = QSPI_DATA_4_LINES;
#endif
QSPI_Cmdhandler.Address = cmd << 8;
QSPI_Cmdhandler.NbData = len;
/* mdma mode */
HAL_QSPI_Command(&QSPI_Handler, &QSPI_Cmdhandler, 1000);
HAL_QSPI_Transmit_DMA(&QSPI_Handler, dat);
}
/* wait trans complete */
rt_sem_take(&trans_sem, RT_WAITING_FOREVER);
}
/* LCD initialize */
static void lcd_initialize(void)
{
uint32_t i;
LCD_RST_H;
lcd_dlyms(10);
LCD_RST_L;
lcd_dlyms(10);
LCD_RST_H;
lcd_dlyms(120);
init_line_t *init = (init_line_t *)&init_table[0];
for (i = 0; i < sizeof(init_table)/sizeof(init_line_t); i++)
{ /* transmit initialize code */
if (init->cmd == 0xff)
{ /* delay */
lcd_dlyms(init->dat[0]);
}
else
{ /* command - parameter */
lcd_transmit(init->cmd, init->len, init->dat);
}
init++;
}
}
/* lcd te signal generate */
static void lcdqspi_timer_entry(void * parameter)
{
rt_completion_done(&frame_sync);
}
/* lcd display frame thread */
static void lcdqspi_thread_entry(void * parameter)
{
__IO uint32_t i = 0;
/* hardware qspi initialize */
ll_qspi_init();
/* lcd driver st77903 initialize */
lcd_initialize();
/* lcd display frame with cache pixel data */
while (1)
{
/* TE signal */
rt_timer_start(&te_timer);
/* vs(0x61) packet */
for (i = 0; i < LCD_VSW; i++)
{
lcd_transmit(0x61, 0, NULL);
lcd_dlyus(40);
}
/* hbp(0x60) packet */
for (i = 0; i < LCD_HBP; i++)
{
lcd_transmit(0x60, 0, NULL);
lcd_dlyus(40);
}
/* transmit display cache data to lcd line by line */
for (i = 0; i < LCD_Y_SIZE; i++)
{
lcd_transmit(0x60, LCD_HBYTE, (uint8_t *)&frame_cache[i][0]);
}
/* hfp(0x60) packet */
for (i = 0; i < LCD_HFP; i++)
{
lcd_transmit(0x60, 0, NULL);
lcd_dlyus(40);
}
/* transmit is complet, can update frame cache in blanking time */
lcd_dlyms(FRAME_BLANKING_TIME);
}
}
static int lcdqspi_app(void)
{
rt_thread_t thread;
rt_sem_init(&trans_sem, "lcdtrans", 0, RT_IPC_FLAG_PRIO);
rt_timer_init(&te_timer, "lcdte", lcdqspi_timer_entry, RT_NULL, te_offset, RT_TIMER_FLAG_ONE_SHOT);
rt_completion_init(&frame_sync);
thread = rt_thread_create("lcdqspi", lcdqspi_thread_entry, RT_NULL, 1024, 5, 10);
if (thread != RT_NULL)rt_thread_startup(thread);
return 0;
}
INIT_APP_EXPORT(lcdqspi_app);
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;
frame_cache[y][xoft + 2] = color & 0xff;
frame_cache[y][xoft + 1] = (color & 0xff00) >> 8;
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++)
{
frame_cache[y][oft + 2] = color & 0xff;
frame_cache[y][oft + 1] = (color & 0xff00) >> 8;
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 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++)
{
frame_cache[y][oft + 2] = *dat & 0xff;
frame_cache[y][oft + 1] = (*dat & 0xff00) >> 8;
frame_cache[y][oft + 0] = (*dat & 0xff0000) >> 16;
oft += 3;
dat++;
}
}
}
void lcdqspi_draw_line_rbg888(uint32_t x0, uint32_t x1, uint32_t y, uint8_t *dat)
{
if ((x1 < LCD_X_SIZE)&&(y < LCD_Y_SIZE)&&(x1 >= x0))
{
uint32_t oft = x0 * 3;
for (uint32_t x = x0; x <= x1; x++)
{
frame_cache[y][oft + 2] = *dat++;
frame_cache[y][oft + 1] = *dat++;
frame_cache[y][oft + 0] = *dat++;
oft += 3;
}
}
}
void lcdqspi_set_te(uint32_t offset)
{
te_offset = offset + 1;
rt_timer_stop(&te_timer);
rt_timer_control(&te_timer, RT_TIMER_CTRL_SET_TIME, (void *)&te_offset);
}
void lcdqspi_wait_te(void)
{
/* if already done, clear flag */
rt_completion_wait(&frame_sync, 0);
/* wait a new flag for update sync */
rt_completion_wait(&frame_sync, RT_WAITING_FOREVER);
}
#ifdef FINSH_USING_MSH
#include "stdlib.h"
static void lcdqspi_ctl(int argc, char **argv)
{
if (argc == 3)
{
if (rt_strcmp("te", argv[1]) == RT_EOK)
{
lcdqspi_set_te(atoi(argv[2]));
}
}
}
MSH_CMD_EXPORT_ALIAS(lcdqspi_ctl, lcdctl, lcdqspi control);
#endif
头文件如下
#ifndef __LCD_QSPI_H__
#define __LCD_QSPI_H__
#include "stdint.h"
#define CMD_LANE_4 (4)
#define CMD_LANE_1 (1)
#define INIT_DAT_LEN (30)
/* ST77903 bist mode, test command is transmit correctly.
if can't see color bar on lcd panel, check the hardware issure */
//#define BIST_MODE
typedef struct
{
uint8_t cmd;
uint8_t len;
uint8_t dat[INIT_DAT_LEN];
}init_line_t;
#include "ST77903-BOE1D3-320X320.H"
//#include "ST77903-IVO1D6-400X400.H"
#ifndef CMD_LANE
#define CMD_LANE CMD_LANE_1
#endif
#define LCD_BPP (24)
#define LCD_X_SIZE LCM_X_SIZE /* available x pixel size */
#define LCD_Y_SIZE LCM_Y_SIZE /* available y pixle size */
#define LCD_PBYTE ((LCD_BPP + 7) / 8) /* bytes in pixel unit */
#define LCD_HBYTE (LCD_X_SIZE * LCD_PBYTE) /* bytes in horizontal line */
void lcdqspi_clear(uint32_t color);
void lcdqspi_set_pixel(uint32_t x, uint32_t y, uint32_t color);
void lcdqspi_fill_block(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t color);
void lcdqspi_draw_line(uint32_t x0, uint32_t x1, uint32_t y, uint32_t *pixel);
void lcdqspi_set_te(uint32_t offset);
void lcdqspi_wait_te(void);
#endif
BOE 320X320初始化
#define CMD_LANE CMD_LANE_1
#define LCM_X_SIZE (320)
#define LCM_Y_SIZE (320)
#define LCD_VSW (1U)
#define LCD_HFP (8U)
#define LCD_HBP (8U)
#define LCD_TE_OFT (25U)
/* ST77903 LCD Initialize table */
/* command - parameter length - parameter data */
/* if command is 0xff, means a delay time unit millisecond */
static const init_line_t init_table[] = {
{0xf0, 1, 0xc3},
{0xf0, 1, 0x96},
{0xf0, 1, 0xa5},
{0xe7, 4, 0x80, 0x77, 0x1f, 0xcc},
{0xc1, 4, 0x44, 0x06, 0xac, 0x1b},
{0xc2, 4, 0x44, 0x06, 0xac, 0x1b},
{0xc3, 4, 0x42, 0x04, 0x24, 0x03},
{0xc4, 4, 0x42, 0x02, 0x24, 0x03},
{0xc5, 1, 0x5a},
{0xe0, 14, 0xf3, 0x0f, 0x12, 0x05, 0x07, 0x03, 0x32, 0x44, 0x4a, 0x09, 0x14, 0x14, 0x2e, 0x33},
{0xe1, 14, 0xf3, 0x0f, 0x13, 0x05, 0x07, 0x03, 0x32, 0x33, 0x4a, 0x08, 0x14, 0x14, 0x2e, 0x33},
{0xe5, 14, 0x9a, 0xf5, 0x95, 0x34, 0x22, 0x25, 0x10, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25},
{0xe6, 14, 0x9a, 0xf5, 0x95, 0x57, 0x22, 0x25, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22},
{0xec, 2, 0x40, 0x07},
{0x36, 1, 0x0c},
{0x3a, 1, 0x07},
{0xb2, 1, 0x00},
{0xb3, 1, 0x01},
{0xb4, 1, 0x00},
{0xb5, 4, 0x00, 0x54, 0x00, 0x54},
{0xa5, 9, 0x00, 0x01, 0x40, 0x00, 0x00, 0x17, 0x2a, 0x0a, 0x02},
{0xa6, 9, 0xa0, 0x12, 0x40, 0x01, 0x00, 0x11, 0x2a, 0x0a, 0x02},
{0xba, 7, 0x59, 0x02, 0x03, 0x00, 0x22, 0x02, 0x00},
{0xbb, 8, 0x00, 0x35, 0x00, 0x33, 0x88, 0x87, 0x07, 0x00},
{0xbc, 8, 0x00, 0x3b, 0x00, 0x33, 0x08, 0x87, 0x07, 0x00},
{0xbd, 11, 0x33, 0xff, 0xff, 0xff, 0x67, 0x76, 0xff, 0xff, 0x14, 0xff, 0x02},
{0xf9, 1, 0x3c},
{0xb6, 2, 0x9f, 0x27},
{0x35, 1, 0x00},
{0x21, 0, 0x00},
{0x11, 0, 0x00},
{0xff, 1, 120},
{0x29, 0, 0x00},
{0xff, 1, 120},
#ifdef BIST_MODE
{0xb0, 1, 0xa5},
{0xcc, 9, 0x40, 0x00, 0x3f, 0x00, 0x14, 0x14, 0x20, 0x20, 0x03},
#endif
};
IVO 400X400初始化
#define CMD_LANE CMD_LANE_4
#define LCM_X_SIZE (400)
#define LCM_Y_SIZE (400)
#define LCD_VSW (1U)
#define LCD_HFP (8U)
#define LCD_HBP (8U)
#define LCD_TE_OFT (25U)
/* ST77903 LCD Initialize table */
/* command - parameter length - parameter data */
/* if command is 0xff, means a delay time unit millisecond */
static const init_line_t init_table[] = {
{0xf0, 1, 0xc3},
{0xf0, 1, 0x96},
{0xf0, 1, 0xa5},
{0xe9, 1, 0x20},
{0xe7, 4, 0x80, 0x77, 0x1f, 0xcc},
{0xc1, 4, 0x77, 0x07, 0xc2, 0x07},
{0xc2, 4, 0x77, 0x07, 0xc2, 0x07},
{0xc3, 4, 0x22, 0x02, 0x22, 0x04},
{0xc4, 4, 0x22, 0x02, 0x22, 0x04},
{0xc5, 1, 0x71},
{0xe0, 14, 0x87, 0x09, 0x0c, 0x06, 0x05, 0x03, 0x29, 0x32, 0x49, 0x0f, 0x1b, 0x17, 0x2a, 0x2f},
{0xe1, 14, 0x87, 0x09, 0x0c, 0x06, 0x05, 0x03, 0x29, 0x32, 0x49, 0x0f, 0x1b, 0x17, 0x2a, 0x2f},
{0xe5, 14, 0xb2, 0xf5, 0xbd, 0x24, 0x22, 0x25, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22},
{0xe6, 14, 0xb2, 0xf5, 0xbd, 0x24, 0x22, 0x25, 0x10, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22},
{0xec, 2, 0x40, 0x03},
{0x36, 1, 0x0c},
{0x3a, 1, 0x07},
{0xb2, 1, 0x00},
{0xb3, 1, 0x01},
{0xb4, 1, 0x00},
{0xb5, 4, 0x00, 0x08, 0x00, 0x08},
{0xb6, 2, 0xc7, 0x31},
{0xa5, 9, 0x00, 0x00, 0x00, 0x00, 0x20, 0x15, 0x2a, 0x8a, 0x02},
{0xa6, 9, 0x00, 0x00, 0x00, 0x00, 0x20, 0x15, 0x2a, 0x8a, 0x02},
{0xba, 7, 0x0a, 0x5a, 0x23, 0x10, 0x25, 0x02, 0x00},
{0xbb, 8, 0x00, 0x30, 0x00, 0x29, 0x88, 0x87, 0x18, 0x00},
{0xbc, 8, 0x00, 0x30, 0x00, 0x29, 0x88, 0x87, 0x18, 0x00},
{0xbd, 11, 0xa1, 0xb2, 0x2b, 0x1a, 0x56, 0x43, 0x34, 0x65, 0xff, 0xff, 0x0f},
{0x35, 1, 0x00},
{0x21, 0, 0x00},
{0x11, 0, 0x00},
{0xff, 1, 120},
{0x29, 0, 0x00},
{0xff, 1, 120},
#ifdef BIST_MODE
{0xb0, 1, 0xa5},
{0xcc, 9, 0x40, 0x00, 0x3f, 0x00, 0x14, 0x14, 0x20, 0x20, 0x03},
#endif
};
实际效果
指针表盘资源从以下地址链接,感谢作者提供
https://blog.csdn.net/mygod2008ok/article/details/111143232