单片机常见的屏幕驱动移植

目录

一个驱动.c文件的典型模块划分(5)

1. Include files

2. Local type definitions ('typedef')

3. Local pre-processor symbols/macros ('#define')

4. Local variable definitions ('static')

5. Function implementation - global ('extern') and local ('static')

5.1 驱动初始化

5.2 底层函数


一个驱动.c文件的典型模块划分(5)

1. Include files

/*******************************************************************************
 * Include files
 ******************************************************************************/

在文件的顶部列出,.C文件所要包含的头文件

2. Local type definitions ('typedef')

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/
 
/**
 * @brief LCD Device Structure Definition
 */
typedef struct {
    uint16_t u16Dir;        /*!< Direction: 0, Vertical; 1, Horizontal */
    uint16_t u16ID;         /*!< LCD ID */
    uint16_t u16Width;      /*!< LCD Width */
    uint16_t u16Height;     /*!< LCD Heigth */
    uint16_t u16WRamCmd;    /*!< Start to write GRAM */
    uint16_t u16SetXCmd;    /*!< Set X axis */
    uint16_t u16SetYCmd;    /*!< Set Y axis */
} stc_lcd_device_t;

这是基本通用的屏幕参数:

屏幕方向:水平显示,垂直显示

屏幕ID:通过不同方式读取屏幕ID,根据不同ID兼容不同型号的屏幕

屏幕宽度和高度:屏幕真实像素点个数

屏幕写指令:将像素点数据写入GRAM

屏幕坐标设置指令:将指针设置到指定像素点位置

对于屏幕宽高,指的是像素大小,如240*320屏幕,表示屏幕为240*320像素 即76800个像素点,而不是屏幕尺寸。

对于用过的 NT35310 / 9341 / 5310 / 7789 ,其写指令、坐标设置指令都一样,分别是0X2C,0X2A,0X2B。

/**
 * @brief LCD Device Controller Structure Definition
 */
typedef struct {
    volatile uint16_t u16REG;
    volatile uint16_t u16RAM;
} stc_lcd_controller_t;

这是最常用的LCD通讯实现方式,EXMC或FSMC。

EXMC接口一般用 D[0:18] 进行通讯,一般用RGB565即16根数据线进行通讯。

有WR\RD\CS\RS\RST五根控制线,CS用于片选器件,RS用于控制发送数据还是命令,WR和RS分别是写使能和读使能(基本都是低电平有效)。

用EXMC接口驱动LCD屏幕时,其实是把它当成SRAM来用,只不过这个SRAM有2个地址,一个是写寄存器地址,一个是写数据地址。具体可搜索“FSMC驱动LCD的原理”去理解。

具体地址定义如下:

/* Use EXMC A16 as the RS signal */
#define BSP_NT35510_BASE                (0x70000000UL | ((1UL << 17U) - 2UL))

对应到最终写寄存器、写数据,就是直接修改指定地址的数据。

FSMC一般有普通IO模拟、SPI和I2C等方式替代,FMC速率最高,普通IO并行模拟次之。

除此之外,高分辨率屏幕,一般用RGB LCD接口,RGB分别是8位以上的数据线,还有DE数据使能线、VS垂直同步线、HS水平同步线、和DCLK像素时钟线。STM32可以用LTDC接口直接驱动,还有DMA2D图形加速。

3. Local pre-processor symbols/macros ('#define')

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/

/* LCD Scan Direction */
#define LCD_SCAN_DIR                (LCD_SCAN_DIR_L2R_U2D)

定义屏幕扫描方向为从左到右,从上到下

4. Local variable definitions ('static')

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/


static stc_lcd_device_t m_stcLcdDevice;

5. Function implementation - global ('extern') and local ('static')

5.1 驱动初始化

static void LCD_XXXX_Config(stc_lcd_controller_t *pstcLCD)

{

        NT35510_WriteReg(pstcLCD, 0xE0); //Set Gamma

        NT35510_WriteData(pstcLCD, 0x0F);

        ...

        NT35510_WriteData(pstcLCD, 0XE1); //Set Gamma

        NT35510_WriteData(pstcLCD, 0x00);

        ...

}

屏幕驱动初始化设置大同小异,也主要是屏幕的寄存器定义基本类似。

基本流程:

1.  复位

2.  进入正常模式或设置模式

3.  显示颜色和方向设置

4.  帧率设置

5.  电源设置

6.  gama设置

不同屏幕的指令可能相同也有可能不一样,

相同驱动ID的屏幕,厂家不同,gama设置以及其它设置也有可能不一样。

具体都需要厂家提高参考代码。

5.2 底层函数

必备的底层函数一览:

void NT35510_Init(stc_lcd_controller_t *pstcLCD);
void NT35510_WriteData(stc_lcd_controller_t *pstcLCD, uint16_t u16Data);
void NT35510_WriteReg(stc_lcd_controller_t *pstcLCD, uint16_t u16Reg);
uint16_t NT35510_ReadData(stc_lcd_controller_t *pstcLCD);
void NT35510_WriteRegData(stc_lcd_controller_t *pstcLCD, uint16_t u16Reg, uint16_t u16Data);
uint16_t NT35510_ReadRegData(stc_lcd_controller_t *pstcLCD, uint16_t u16Reg);
uint16_t NT35510_ReadID(stc_lcd_controller_t *pstcLCD);
void NT35510_DisplayOn(stc_lcd_controller_t *pstcLCD);
void NT35510_DisplayOff(stc_lcd_controller_t *pstcLCD);
uint16_t NT35510_GetPixelWidth(void);
uint16_t NT35510_GetPixelHeight(void);
void NT35510_SetScanDir(stc_lcd_controller_t *pstcLCD, uint16_t u16Dir);
void NT35510_SetDisplayDir(stc_lcd_controller_t *pstcLCD, uint16_t u16Dir);
void NT35510_PrepareWriteRAM(stc_lcd_controller_t *pstcLCD);
void NT35510_SetBackLight(stc_lcd_controller_t *pstcLCD, uint8_t u8PWM);
void NT35510_SetCursor(stc_lcd_controller_t *pstcLCD, uint16_t u16Xpos, uint16_t u16Ypos);
void NT35510_WritePixel(stc_lcd_controller_t *pstcLCD, uint16_t u16Xpos, uint16_t u16Ypos, uint16_t u16RGBCode);
void NT35510_Clear(stc_lcd_controller_t *pstcLCD, uint16_t u16RGBCode);

具体实现:

需要做兼容处理的部分:

NT35510_Init                         驱动初始化

NT35510_ReadID                 读取驱动芯片ID

NT35510_SetScanDir           设置扫描方向 | 宽 高 写指令

void NT35510_Init(stc_lcd_controller_t *pstcLCD)
{/* NOP */NT35510_WriteRegData(pstcLCD, 0x0000U, 0x00U);/* Read ID */m_stcLcdDevice.u16ID = NT35510_ReadID(pstcLCD);/* Chip Init */if(m_stcLcdDevice.u16ID == 0x9341){LCD_9341_Config(pstcLCD);} else if(m_stcLcdDevice.u16ID == 0x7789){LCD_7789_Config(pstcLCD);} else {/* Unsupported LCD */}/* Set LCD cursor */NT35510_SetDisplayDir(pstcLCD, LCD_DISPLAY_VERTICAL);/* Set cursor */NT35510_SetCursor(pstcLCD, 0U, 0U);/* Prepare to write to LCD RAM */NT35510_PrepareWriteRAM(pstcLCD);
}/*** @brief  Read LCD ID.* @param  [in] pstcLCD:                LCD controller @ref stc_lcd_controller_t structure.* @retval LCD Register Value.*/
uint16_t NT35510_ReadID(stc_lcd_controller_t *pstcLCD)
{uint16_t u16ID;/* Try to read ID: 0x9341 */NT35510_WriteReg(pstcLCD, 0xD3U);(void)NT35510_ReadData(pstcLCD);                /* dummy read */(void)NT35510_ReadData(pstcLCD);                /* read: 0x00 */u16ID  = NT35510_ReadData(pstcLCD) << 8;        /* read: 0x93 */u16ID |= NT35510_ReadData(pstcLCD);             /* read: 0x41 */if (u16ID != 0x9341U) {/* Try to read ID: 0x8552 */NT35510_WriteReg(pstcLCD, 0x04U);(void)NT35510_ReadData(pstcLCD);            /* dummy read */(void)NT35510_ReadData(pstcLCD);            /* read: 0x85 */u16ID  = NT35510_ReadData(pstcLCD) << 8;    /* read: 0x85 */u16ID |= NT35510_ReadData(pstcLCD);         /* read: 0x41 */if (u16ID == 0x8552U) {u16ID = 0x7789U;                        /* ID convert to: 0x7789 */}else {u16ID = 0U;                         	/* Unsupported LCD */}}return u16ID;
}/*** @brief  Set scan direction.* @param  [in] pstcLCD:                LCD controller* @param  [in] u16Dir:                 Scan direction*         This parameter can be one of the following values:*           @arg LCD_SCAN_DIR_L2R_U2D: From left to right && from up to down*           @arg LCD_SCAN_DIR_L2R_D2U: From left to right && from down to up*           @arg LCD_SCAN_DIR_R2L_U2D: From right to left && from up to down*           @arg LCD_SCAN_DIR_R2L_D2U: From right to left && from down to up*           @arg LCD_SCAN_DIR_U2D_L2R: From up to down && from left to right*           @arg LCD_SCAN_DIR_U2D_R2L: From up to down && from right to left*           @arg LCD_SCAN_DIR_D2U_L2R: From down to up && from left to right*           @arg LCD_SCAN_DIR_D2U_R2L: From down to up && from right to left* @retval None*/
void NT35510_SetScanDir(stc_lcd_controller_t *pstcLCD, uint16_t u16Dir)
{uint16_t u16Temp;uint16_t dirreg;uint16_t regval = 0U;/* when display dir is VERTICAL, 1963 IC change scan-direction, other IC don't changewhen display dir is HORIZONTAL, 1963 IC don't change scan-direction, other IC change */if (((0U == m_stcLcdDevice.u16Dir) && (m_stcLcdDevice.u16ID == 0x1963U)) || \((1U == m_stcLcdDevice.u16Dir) && (m_stcLcdDevice.u16ID != 0x1963U))) {if (0U == u16Dir) {u16Dir = 6U;} else if (1U == u16Dir) {u16Dir = 7U;} else if (2U == u16Dir) {u16Dir = 4U;} else if (3UL == u16Dir) {u16Dir = 5U;} else if (4U == u16Dir) {u16Dir = 1U;} else if (5U == u16Dir) {u16Dir = 0U;} else if (6U == u16Dir) {u16Dir = 3U;} else if (7U == u16Dir) {u16Dir = 2U;} else {u16Dir = 6U;}}switch (u16Dir) {case LCD_SCAN_DIR_L2R_U2D:regval |= ((0U << 7) | (0U << 6) | (0U << 5));break;case LCD_SCAN_DIR_L2R_D2U:regval |= ((1U << 7) | (0U << 6) | (0U << 5));break;case LCD_SCAN_DIR_R2L_U2D:regval |= ((0U << 7) | (1U << 6) | (0U << 5));break;case LCD_SCAN_DIR_R2L_D2U:regval |= ((1U << 7) | (1U << 6) | (0U << 5));break;case LCD_SCAN_DIR_U2D_L2R:regval |= ((0U << 7) | (0U << 6) | (1U << 5));break;case LCD_SCAN_DIR_U2D_R2L:regval |= ((0U << 7) | (1U << 6) | (1U << 5));break;case LCD_SCAN_DIR_D2U_L2R:regval |= ((1U << 7) | (0U << 6) | (1U << 5));break;case LCD_SCAN_DIR_D2U_R2L:regval |= ((1U << 7) | (1U << 6) | (1U << 5));break;default:break;}if (0x5510U == m_stcLcdDevice.u16ID) {dirreg = 0x3600U;} else {dirreg = 0x36U;}/* 0x9341 & 0x7789 set BGR bit */if ((0x9341U == m_stcLcdDevice.u16ID) || (0x7789U == m_stcLcdDevice.u16ID)) {regval |= 0x08U;}NT35510_WriteRegData(pstcLCD, dirreg, regval);/* 1963 don't handle coordinate */if (m_stcLcdDevice.u16ID != 0x1963U) {if ((regval & 0x20U) > 0U) {/* swap X,Y */if (m_stcLcdDevice.u16Width < m_stcLcdDevice.u16Height) {u16Temp = m_stcLcdDevice.u16Width;m_stcLcdDevice.u16Width = m_stcLcdDevice.u16Height;m_stcLcdDevice.u16Height = u16Temp;}} else {/* swap X,Y */if (m_stcLcdDevice.u16Width > m_stcLcdDevice.u16Height) {u16Temp = m_stcLcdDevice.u16Width;m_stcLcdDevice.u16Width = m_stcLcdDevice.u16Height;m_stcLcdDevice.u16Height = u16Temp;}}}/* Set display window size */NT35510_WriteReg(pstcLCD, m_stcLcdDevice.u16SetXCmd);NT35510_WriteData(pstcLCD, 0U);NT35510_WriteData(pstcLCD, 0U);NT35510_WriteData(pstcLCD, (m_stcLcdDevice.u16Width - 1U) >> 8);NT35510_WriteData(pstcLCD, (m_stcLcdDevice.u16Width - 1U) & 0xFFU);NT35510_WriteReg(pstcLCD, m_stcLcdDevice.u16SetYCmd);NT35510_WriteData(pstcLCD, 0U);NT35510_WriteData(pstcLCD, 0U);NT35510_WriteData(pstcLCD, (m_stcLcdDevice.u16Height - 1U) >> 8);NT35510_WriteData(pstcLCD, (m_stcLcdDevice.u16Height - 1U) & 0xFFU);
}/*** @brief  Set screen direction.* @param  [in] pstcLCD:                LCD controller* @param  [in] u16Dir:                 Screen direction*         This parameter can be one of the following values:*           @arg LCD_DISPLAY_VERTICAL:   LCD vertical display*           @arg LCD_DISPLAY_HORIZONTAL: LCD horizontal display* @retval None*/
void NT35510_SetDisplayDir(stc_lcd_controller_t *pstcLCD, uint16_t u16Dir)
{/* NT35310 / 9341 / 5310 / 7789 etc */m_stcLcdDevice.u16WRamCmd = 0x2CU;m_stcLcdDevice.u16SetXCmd = 0x2AU;m_stcLcdDevice.u16SetYCmd = 0x2BU;m_stcLcdDevice.u16Width  = 320U;m_stcLcdDevice.u16Height = 240U;m_stcLcdDevice.u16Dir = u16Dir;NT35510_SetScanDir(pstcLCD, LCD_SCAN_DIR);
}

底层通用函数:

NT35510_WriteData               写数据

NT35510_WriteReg                写寄存器

NT35510_ReadData               读数据

NT35510_WriteRegData         写寄存器数据

NT35510_ReadRegData         读寄存器数据

NT35510_DisplayOn                打开显示

NT35510_DisplayOff                关闭显示

NT35510_GetPixelWidth         获取像素宽度

NT35510_GetPixelHeight        获取像素高度

NT35510_PrepareWriteRAM   写RAM

NT35510_SetCursor                设置坐标

NT35510_WritePixel                写单个像素点颜色

NT35510_Clear                        清屏

/*** @brief  Write data on LCD data register.* @param  [in] pstcLCD:                LCD controller @ref stc_lcd_controller_t structure.* @param  [in] u16Data:                Data to be written* @retval None*/
void NT35510_WriteData(stc_lcd_controller_t *pstcLCD, uint16_t u16Data)
{pstcLCD->u16RAM = u16Data;
}/*** @brief  Write register on LCD register.* @param  [in] pstcLCD:                LCD controller @ref stc_lcd_controller_t structure.* @param  [in] u16Reg:                 Address of the selected register.* @retval None*/
void NT35510_WriteReg(stc_lcd_controller_t *pstcLCD, uint16_t u16Reg)
{pstcLCD->u16REG = u16Reg;
}/*** @brief  Read data from LCD data register.* @param  [in] pstcLCD:                LCD controller @ref stc_lcd_controller_t structure.* @retval Read data.*/
uint16_t NT35510_ReadData(stc_lcd_controller_t *pstcLCD)
{return pstcLCD->u16RAM;
}/*** @brief  Write to the selected LCD register.* @param  [in] pstcLCD:                LCD controller @ref stc_lcd_controller_t structure.* @param  [in] u16Reg:                 Address of the selected register.* @param  [in] u16Data:                Data to be written* @retval None*/
void NT35510_WriteRegData(stc_lcd_controller_t *pstcLCD, uint16_t u16Reg, uint16_t u16Data)
{/* Write 16-bit index */pstcLCD->u16REG = u16Reg;/* Write 16-bit Reg */pstcLCD->u16RAM = u16Data;
}/*** @brief  Read the selected LCD register.* @param  [in] pstcLCD:                LCD controller @ref stc_lcd_controller_t structure.* @param  [in] u16Reg:                 Address of the selected register.* @retval Register value*/
uint16_t NT35510_ReadRegData(stc_lcd_controller_t *pstcLCD, uint16_t u16Reg)
{/* Write 16-bit index*/pstcLCD->u16REG = u16Reg;return pstcLCD->u16RAM;
}/*** @brief  Enable the Display.* @param  [in] pstcLCD:                LCD controller* @retval None*/
void NT35510_DisplayOn(stc_lcd_controller_t *pstcLCD)
{if (m_stcLcdDevice.u16ID == 0x5510U) {NT35510_WriteReg(pstcLCD, 0x2900U);     /* 5510 */} else {NT35510_WriteReg(pstcLCD, 0x29U);       /* 9341/5310/1963/7789 */}
}/*** @brief  Disable the Display.* @param  [in] pstcLCD:                LCD controller* @retval None*/
void NT35510_DisplayOff(stc_lcd_controller_t *pstcLCD)
{if (m_stcLcdDevice.u16ID == 0x5510U) {NT35510_WriteReg(pstcLCD, 0x2800U);     /* 5510 */} else {NT35510_WriteReg(pstcLCD, 0x28U);       /* 9341/5310/1963/7789 */}
}/*** @brief  Get LCD PIXEL WIDTH.* @param  None* @retval LCD PIXEL WIDTH.*/
uint16_t NT35510_GetPixelWidth(void)
{return m_stcLcdDevice.u16Width;
}/*** @brief  Get LCD PIXEL HEIGHT.* @param  None* @retval LCD PIXEL HEIGHT.*/
uint16_t NT35510_GetPixelHeight(void)
{return m_stcLcdDevice.u16Height;
}/*** @brief  Prepare to write LCD RAM.* @param  [in] pstcLCD:                LCD controller* @retval None*/
void NT35510_PrepareWriteRAM(stc_lcd_controller_t *pstcLCD)
{NT35510_WriteReg(pstcLCD, m_stcLcdDevice.u16WRamCmd);
}/*** @brief  Set screen backlight.* @param  [in] pstcLCD:                LCD controller* @param  [in] u8PWM:                  PWM levelThis parameter can be a value between Min_Data = 0 and Max_Data = 100* @retval None*/
void NT35510_SetBackLight(stc_lcd_controller_t *pstcLCD, uint8_t u8PWM)
{float32_t f32PWM = ((float32_t)u8PWM * 2.55F);NT35510_WriteReg(pstcLCD, 0xBEU);NT35510_WriteData(pstcLCD, 0x05U);NT35510_WriteData(pstcLCD, (uint16_t)f32PWM);NT35510_WriteData(pstcLCD, 0x01U);NT35510_WriteData(pstcLCD, 0xFFU);NT35510_WriteData(pstcLCD, 0x00U);NT35510_WriteData(pstcLCD, 0x00U);
}/*** @brief  Set Cursor position.* @param  [in] pstcLCD:                LCD controller* @param  u16Xpos:                     Specifies the X position.* @param  u16Ypos:                     Specifies the Y position.* @retval None*/
void NT35510_SetCursor(stc_lcd_controller_t *pstcLCD, uint16_t u16Xpos, uint16_t u16Ypos)
{NT35510_WriteReg(pstcLCD, m_stcLcdDevice.u16SetXCmd);NT35510_WriteData(pstcLCD, (u16Xpos >> 8));NT35510_WriteData(pstcLCD, (u16Xpos & 0xFFU));NT35510_WriteReg(pstcLCD, m_stcLcdDevice.u16SetYCmd);NT35510_WriteData(pstcLCD, (u16Ypos >> 8));NT35510_WriteData(pstcLCD, (u16Ypos & 0xFFU));
}/*** @brief  Write pixel.* @param  [in] pstcLCD:                LCD controller* @param  u16Xpos:                     Specifies the X position.* @param  u16Ypos:                     Specifies the Y position.* @param  u16RGBCode:                  The RGB pixel color in RGB565 format* @retval None*/
void NT35510_WritePixel(stc_lcd_controller_t *pstcLCD, uint16_t u16Xpos, uint16_t u16Ypos, uint16_t u16RGBCode)
{/* Set cursor */NT35510_SetCursor(pstcLCD, u16Xpos, u16Ypos);/* Prepare to write to LCD RAM */NT35510_PrepareWriteRAM(pstcLCD);NT35510_WriteData(pstcLCD, u16RGBCode);
}/*** @brief  Clear screen.* @param  [in] pstcLCD:                LCD controller* @param  u16RGBCode:                  The RGB pixel color in RGB565 format* @retval None*/
void NT35510_Clear(stc_lcd_controller_t *pstcLCD, uint16_t u16RGBCode)
{uint32_t i;uint32_t u32TotalPoint;/* Set cursor */NT35510_SetCursor(pstcLCD, 0U, 0U);/* Prepare to write to LCD RAM */NT35510_PrepareWriteRAM(pstcLCD);u32TotalPoint = (uint32_t)m_stcLcdDevice.u16Width * (uint32_t)m_stcLcdDevice.u16Height;for (i = 0UL; i < u32TotalPoint; i++) {NT35510_WriteData(pstcLCD, u16RGBCode);}
}

5.3 画线

利用差值法画线。包括水平线、垂直线、斜线。

画线是最基本的函数,基于画线函数可以衍生出画圆、画弧线、画矩形等多种图形。

/**
 * @brief  Draw line.
 * @param  [in] pstcLCD:                LCD controller
 * @param  u16X1:                       Specifies the X position 1.
 * @param  u16X2:                       Specifies the X position 2.
 * @param  u16Y1:                       Specifies the Y position 1.
 * @param  u16Y2:                       Specifies the Y position 2.
 * @param  u16RGBCode:                  The RGB pixel color in RGB565 format
 * @retval None
 */
void NT35510_DrawLine(stc_lcd_controller_t *pstcLCD, uint16_t u16X1, uint16_t u16Y1,
                      uint16_t u16X2, uint16_t u16Y2, uint16_t u16RGBCode)
{
    int16_t t;
    int16_t xerr = 0;
    int16_t yerr = 0;
    int16_t delta_x;
    int16_t delta_y;
    int16_t distance;
    int16_t incx;
    int16_t incy;
    int16_t Row;
    int16_t Col;

    Row = (int16_t)u16X1;
    Col = (int16_t)u16Y1;
    delta_x = ((int16_t)u16X2 - (int16_t)u16X1);      /* calc delta X, Y*/
    delta_y = ((int16_t)u16Y2 - (int16_t)u16Y1);

    if (delta_x > 0) {
        incx = 1;           /* forward u8Direction */
    } else if (delta_x == 0) {
        incx = 0;           /* vertical line */
    } else {
        incx = -1;          /* reverse direction */
        delta_x = -delta_x;
    }

    if (delta_y > 0) {
        incy = 1;             /* downward direction */
    } else if (delta_y == 0) {
        incy = 0;             /* horizontal line */
    } else {
        incy = -1;            /* upward direction */
        delta_y = -delta_y;
    }

    if (delta_x > delta_y) {
        distance = delta_x; /* set axis */
    } else {
        distance = delta_y;
    }

    for (t = 0; t <= (distance + 1); t++) {
        NT35510_WritePixel(pstcLCD, (uint16_t)Row, (uint16_t)Col, u16RGBCode);   /* draw pixel */

        xerr += delta_x ;
        yerr += delta_y ;

        if (xerr > distance) {
            xerr -= distance;
            Row += incx;
        }

        if (yerr > distance) {
            yerr -= distance;
            Col += incy;
        }
    }
}
 

Bresenham算法画圆


/**
 * @brief  Draw a circle.
 * @param  [in] pstcLCD:                LCD controller
 * @param  [in]                         u16Xpos: X position
 * @param  [in]                         u16Ypos: Y position
 * @param  [in]                         u16Radius: Circle radius
 * @param  u16RGBCode:                  The RGB pixel color in RGB565 format
 * @retval None
 */
void NT35510_DrawCircle(stc_lcd_controller_t *pstcLCD, uint16_t u16Xpos, uint16_t u16Ypos,
                        uint16_t u16Radius, uint16_t u16RGBCode)
{
    int32_t  decision;       /* Decision Variable */
    uint32_t current_x;      /* Current X Value */
    uint32_t current_y;      /* Current Y Value */

    decision = 3 - ((int32_t)u16Radius * 2);
    current_x = 0U;
    current_y = u16Radius;

    while (current_x <= current_y) {
        NT35510_WritePixel(pstcLCD, (u16Xpos + (uint16_t)current_x), (u16Ypos - (uint16_t)current_y), u16RGBCode);
        NT35510_WritePixel(pstcLCD, (u16Xpos + (uint16_t)current_y), (u16Ypos - (uint16_t)current_x), u16RGBCode);
        NT35510_WritePixel(pstcLCD, (u16Xpos + (uint16_t)current_y), (u16Ypos + (uint16_t)current_x), u16RGBCode);
        NT35510_WritePixel(pstcLCD, (u16Xpos + (uint16_t)current_x), (u16Ypos + (uint16_t)current_y), u16RGBCode);
        NT35510_WritePixel(pstcLCD, (u16Xpos - (uint16_t)current_x), (u16Ypos + (uint16_t)current_y), u16RGBCode);
        NT35510_WritePixel(pstcLCD, (u16Xpos - (uint16_t)current_y), (u16Ypos + (uint16_t)current_x), u16RGBCode);
        NT35510_WritePixel(pstcLCD, (u16Xpos - (uint16_t)current_x), (u16Ypos - (uint16_t)current_y), u16RGBCode);
        NT35510_WritePixel(pstcLCD, (u16Xpos - (uint16_t)current_y), (u16Ypos - (uint16_t)current_x), u16RGBCode);
        current_x++;
        /* Bresenham algorithm */
        if (decision < 0) {
            decision += ((4 * (int32_t)current_x) + 6);
        } else {
            decision += (10 + (4 * ((int32_t)current_x - (int32_t)current_y)));
            current_y--;
        }
    }
}

5.4 显示图片

像素点其实就是:

1)事先将要显示的图,转化成位图,确定每个像素点要显示的颜色

2)依次显示这些点

基于这种方法,可以衍生出显示文字符合、特殊图像等等。

不过该方法对内存要求高,需要占用RAM或ROM 来存储这些像素点颜色数据。

/**
 * @brief  Show Picture.
 * @param  [in] pstcLCD:                LCD controller
 * @param  u16RGBCode:                  The RGB pixel color in RGB565 format
 * @retval None
 */
void NT35510_Picture(stc_lcd_controller_t *pstcLCD, uint16_t *pu16RGBData)
{
    uint32_t i;
    uint32_t u32TotalPoint;

    /* Set cursor */
    NT35510_SetCursor(pstcLCD, 0U, 0U);

    /* Prepare to write to LCD RAM */
    NT35510_PrepareWriteRAM(pstcLCD);

    u32TotalPoint = (uint32_t)m_stcLcdDevice.u16Width * (uint32_t)m_stcLcdDevice.u16Height;

    for (i = 0UL; i < u32TotalPoint; i++) {
        NT35510_WriteData(pstcLCD, pu16RGBData[i]);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/104184.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux之/etc/skel目录

/etc/skel目录是在使用useradd添加用户时&#xff0c;一个需要用到的目录&#xff0c;该目录用来存放新建用户时需要拷贝到新建用户家目录下的文件。即&#xff1a;当我们新建新用户时&#xff0c;这个目录下的所有文件会自动被复制到新建用户的家目录下&#xff0c;默认情况下…

go语言基础之变量

目录 视频学习地址&#xff1a;Go零基础入门_在线视频教程-CSDN程序员研修院 一. 单变量声明和赋值 1、变量的声明 2、变量赋值 3、声明并赋值 二. 多变量声明和赋值 1、多变量声明 2、多变量赋值 三. 变量声明赋值的简易写法 1、单变量简易写法 2、多变量简易写法 …

DOSBox和MASM汇编开发环境搭建

DOSBox和MASM汇编开发环境搭建 1 安装DOSBox2 安装MASM3 编译测试代码4 运行测试代码5 调试测试代码 本文属于《 X86指令基础系列教程》之一&#xff0c;欢迎查看其它文章。 1 安装DOSBox 下载DOSBox和MASM&#xff1a;https://download.csdn.net/download/u011832525/884180…

uniapp封装loading 的动画动态加载

实现效果 html代码 <view class"loadBox" v-if"loading"><img :src"logo" class"logo"> </view> css代码 .loadBox {width: 180rpx;min-height: 180rpx;border-radius: 50%;display: flex;align-items: center;j…

【面试经典150 | 哈希表】存在重复元素 II

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;哈希表方法二&#xff1a;滑动窗口 其他语言python3哈希表python3滑动窗口 写在最后 Tag 【哈希表】【滑动窗口】【数组】 题目来源 219. 存在重复元素 II 题目解读 判断在数组中有没有相同的元素小于一定的距离。 解…

Java面试题-Java核心基础-第三天(基本数据类型)

目录 一、Java的基本数据类型了解吗&#xff1f; 二、基本类型和包装类型的区别 三、包装类型的缓存机制了解吗&#xff1f; 四、自动拆箱和自动装箱了解吗&#xff1f; 五、为什么浮点数在运算的时候会有精度损失的风险&#xff1f; 六、如何解决浮点数在运算时存在的精度…

Win10 系统中用户环境变量和系统环境变量是什么作用和区别?

环境&#xff1a; Win10专业版 问题描述&#xff1a; Win10 系统中用户环境变量和系统环境变量是什么作用和区别&#xff1f; 解答&#xff1a; 在Windows 10系统中&#xff0c;用户环境变量和系统环境变量是两个不同的环境变量&#xff0c;它们具有不同的作用和区别 1.用…

双向链表C语言版本

1、声明链表节点操作函数 linklist.h #ifndef LINKLIST_H__ #define LINKLIST_H__ #include <stdio.h> #include <stdlib.h> #include <stdbool.h>//#define TAIL_ADD #define HEAD_ADD typedef int LinkDataType; // 构造节点 struct LinkNode {LinkDataTy…

【Debian】报错:su: Authentication failure

项目场景&#xff1a; 今天我重新刷了一个debian系统。 系统版本&#xff1a; # 查看系统版本 lsb_release -a 我的系统版本&#xff1a; No LSB modules are available. Distributor ID&#xff1a;Debian Description: Debian GNU/Linux 12 &#xff08;bookworm&#xff…

目录启示:PHP 与命名空间的声明

文章目录 参考环境命名空间概念版本支持影响范围 全局命名空间概念魔术常量 \_\_NAMESPACE\_\_声明全局命名空间 声明命名空间为空间命名命名规则核心命名空间 子命名空间的声明在同一文件中定义多个命名空间无括号命名空间声明有括号命名空间声明禁止混合使用推荐使用有括号命…

统信UOS1060设置自动关机01

原文链接&#xff1a;统信UOS1060设置自动关机01 hello&#xff0c;大家好啊&#xff0c;今天给大家介绍一篇如何在统信UOS 1060上实现自动关机的文章&#xff0c;本篇文章采用两种方式&#xff0c;第一种使用的是crontab定时任务的方式&#xff0c;第二种是使用at命令的方式&a…

服务器系统和普通系统的区别

随着信息技术的迅猛发展&#xff0c;服务器系统和普通系统在计算机领域扮演着重要的角色。虽然它们都是计算机操作系统的一种形式&#xff0c;但在设计、功能和用途上存在着显著的区别。本文将以服务器系统和普通系统的区别为方向&#xff0c;探讨它们之间的特点和应用。 首先…

实体解析实施的复杂性

实体的艺术表现斯特凡伯克纳 一、说明 实体解析是确定数据集中的两条或多条记录是否引用同一现实世界实体&#xff08;通常是个人或公司&#xff09;的过程。乍一看&#xff0c;实体分辨率可能看起来像一个相对简单的任务&#xff1a;例如&#xff0c;给定一张人物的两张照片&a…

natapp内网穿透-将本地运行的程序/服务器通过公网IP供其它人访问

文章目录 1.几个基本概念1.1 局域网1.2 内网1.3 内网穿透1.4 Natapp 2.搭建内网穿透环境3.本地服务测试 1.几个基本概念 1.1 局域网 LAN&#xff08;Local Area Network&#xff0c;局域网&#xff09;是一个可连接住宅&#xff0c;学校&#xff0c;实验室&#xff0c;大学校…

2核4G游戏服务器推荐(阿里云/腾讯云/华为云)

2核4G游戏服务器推荐&#xff0c;首选腾讯云2核4G5M带宽轻量应用服务器218元一年、阿里云2核4G4M带宽轻量应用服务器297元一年&#xff0c;华为云2核2G3M云耀L服务器95元一年&#xff0c;阿腾云来详细说下2核4G游戏服务器推荐配置大全&#xff1a; 目录 2核4G游戏服务器推荐 …

vue axios封装

Vue.js 是一款前端框架&#xff0c;而 Axios 是一个基于 Promise 的 HTTP 请求客户端&#xff0c;通常用于发送 Ajax 请求。在Vue.js开发中&#xff0c;经常需要使用 Axios 来进行 HTTP 数据请求&#xff0c;为了更好的维护和使用 Axios&#xff0c;我们可以对其进行封装。下面…

前端 js导出excel

一、导出效果 二、导出代码 download() {let data [{name: aaaa,age: 10,address: 广东,phone: 1,wechart: 1},{name: bbbb,age: 11,address: 广西,phone: 2,wechart: 2},{name: cccc,age: 12,address: 山东,phone: 3,wechart: 3}]if (!data.length) {this.$Message.warnin…

暴力递归转动态规划(九)

题目 题有点难&#xff0c;但还挺有趣 有一个咖啡机数组arr[]&#xff0c;其中arr[i]代表每一个咖啡机冲泡咖啡所需的时间&#xff0c;有整数N&#xff0c;代表着准备冲咖啡的N个人&#xff08;假设这个人拿到咖啡后喝完的时间为0&#xff0c;拿手里咖啡杯即变空&#xff09;&a…

postman和jmete接口测试的用法与区别

前言 前阶段做了一个小调查&#xff0c;发现软件测试行业做功能测试和接口测试的人相对比较多。在测试工作中&#xff0c;有高手&#xff0c;自然也会有小白&#xff0c;但有一点我们无法否认&#xff0c;就是每一个高手都是从小白开始的&#xff0c;所以今天我们就来谈谈一大…