HAL库-4.3寸电容式接触屏LCD驱动(未完成)

目录

1.LCD介绍:

工作原理

显示特性

优缺点

2.LCD与OLED的区别:

1. 工作原理

2. 显示效果

3. 屏幕厚度与重量

4. 能耗

5. 寿命与稳定性

6. 应用场景

实验所用模块:ATK-4.3’ TFTLCD

原理图模块与数据手册介绍:

LCD驱动:

正点原子提供的参考代码:

颜色:

扫描方向:

函数声明:

lcd.c

lcd.h


1.LCD介绍:

视频链接:【硬核科普】全网最简洁易懂的OLED与LCD屏幕工作原理与优劣科普_哔哩哔哩_bilibili

液晶层发生偏转,就可以调节亮度,通过比例大小的改变导致输出的颜色不同

        目前LCD是用一整个背光板

工作原理

LCD本身并不发光,而是需要背光层(通常是LED或CCFL)来提供光源。液晶分子被夹在两片玻璃基板之间,这些液晶分子在电场的作用下会发生排列变化,从而改变光线的透过率。通过控制每个像素点上的电场强度,可以精确控制该像素点的亮度,进而形成图像。

显示特性

  • 色彩:LCD通过彩色滤光片(Color Filter)来实现彩色显示。每个像素点由红、绿、蓝三个子像素组成,通过控制这三个子像素的亮度,可以混合出各种颜色。
  • 对比度:LCD的对比度受到背光层亮度和液晶分子透光率的影响。虽然现代LCD技术已经大大提高了对比度,但与OLED相比,其黑色表现仍然不够深沉。
  • 视角:LCD的可视角度相对较大,但随着观看角度的偏离,色彩和亮度可能会有所变化。不过,现代IPS(In-Plane Switching)和VA(Vertical Alignment)等技术的LCD屏幕已经大大改善了这一问题。

优缺点

优点

  • 技术成熟,制造成本相对较低。
  • 色彩表现稳定,适合长时间观看。
  • 分辨率和尺寸灵活,可以满足不同设备和应用场景的需求。

缺点

  • 依赖背光层,能耗相对较高。
  • 黑色表现不够深沉,对比度相对较低。
  • 在极端温度下,液晶分子的性能可能会受到影响

2.LCD与OLED的区别:

oled:有机自放光二极管organic - light emitting diode

oled没有背光层,可以独立控制每一个LED的开关

1. 工作原理

  • LCD:LCD不具备自发光特性,需要背光层来提供光源。液晶分子在特定电场作用下会发生排列变化,从而控制光线的穿透程度,进而显示出不同的颜色和亮度。
  • OLED:OLED则采用了自发光技术,当电流通过时,有机材料会直接发光。每个像素点都可以独立控制发光,因此在显示黑色时能做到几乎完全不发光。

2. 显示效果

  • 色彩与对比度:OLED屏幕能够显示出更加鲜艳的色彩和更深的黑色,因为每个像素点都可以独立发光,且黑色表现几乎不发光,所以对比度更高。而LCD屏幕的色彩表现相对稳定,但受限于背光层的影响,黑色表现不够深沉,对比度相对较低。
  • 视角:OLED屏幕的可视角度更大,观众从不同角度观看时仍能保持图像清晰可见。LCD屏幕虽然也有较好的可视角度,但相比OLED略有逊色。

3. 屏幕厚度与重量

  • OLED:由于无需背光层,OLED屏幕可以做到更薄更轻,这为未来可弯曲屏、折叠屏的出现提供了可能。
  • LCD:由于有背光层和液晶层的存在,LCD屏幕的厚度相对较大,重量也较重。

4. 能耗

  • OLED:OLED屏幕在显示黑色或深色内容时几乎不耗电,因此整体能耗较低。
  • LCD:由于需要持续的背光照明,LCD屏幕在能耗方面相对较高。

5. 寿命与稳定性

  • OLED:OLED屏幕的寿命可能会受到屏幕亮度和使用时间的影响。长时间显示同一内容可能会导致像素点老化,出现“烧屏”现象。然而,随着技术的进步,OLED屏幕的寿命已经得到了显著提升。
  • LCD:LCD屏幕的技术相对成熟,制造成本较低,且老化速度相对较慢。因此,在寿命和稳定性方面表现较好。

6. 应用场景

  • OLED:因其卓越的色彩表现和对比度而逐渐受到高端市场的青睐。在高端电视、手机和平板电脑等领域的应用越来越广泛。
  • LCD:由于技术成熟、制造成本相对较低,LCD屏幕在中低端电子产品中仍有广泛应用。此外,LCD屏幕在尺寸和分辨率方面具有较大的灵活性,可以满足不同设备和应用场景的需求。

未来的发展方向microLCD

实验所用模块:ATK-4.3’ TFTLCD

        ATK-4.3’ TFTLCD 模块是 ALIENTEK 推出的一款高性能 TFTLCD 显示模块,外观漂亮、 性能优异、结构紧凑。模块通过 1 个 2*17P 的 2.54mm 间距排针与外部连接

原理图模块与数据手册介绍:

        ATK-4.3’ TFTLCD 电容触摸屏模块通过 2*17 的排针(2.54mm 间距)同外部连接,模块可以与 ALIENTEK 的 STM32 开发板直接对接。模块通过 34(2*17)个引脚同外部连接,对外接口 原理图

        从上表可以看出,LCD 控制器总共需要 21 个 IO 口,背光控制需要 1 个 IO 口,电容触 摸屏需要 4 个 IO 口,这样整个模块需要 26 个 IO 口驱动

LCD驱动:

         ATK-4.3’ TFTLCD 模块采用 NT35510 作为 LCD 驱动器,该驱动器自带 LCD GRAM, 无需外加独立驱动器,并且,在指令上,基本兼容 ILI9341,使用非常方便。模块采用 16 位 8080 并口与外部连接(不支持其他接口方式,仅支持 16 位 8080 并口),在 8080 并口模 式下,

        LCD 驱动需要用到的信号线如下: CS:LCD 片选信号。 WR:向 LCD 写入数据。 RD:从 LCD 读取数据。 D[15:0]:16 位双向数据线。 RST:硬复位 LCD。 RS:命令/数据标志(0,读写命令;1,读写数据)。

         除了以上信号,我们一般还需要用到这 2 个信号:RST 和 BL_CTR,其中 RST 是液晶 的硬复位脚,低电平有效,用于复位 NT35510 芯片,实现液晶复位,在每次初始化之前, 我们强烈建议大家先执行硬复位,再做初始化。BL_CTR 则是背光控制引脚,高电平有效, BL_CTR 自带了 100K 下拉电阻,所以如果这个引脚悬空,背光是不会亮的。必须接高电平, 背光才会亮,另外可以用 PWM 控制 BL_CTR 脚,从而控制背光的亮度。

         NT35510 自带 LCD GRAM(480*864*3 字节),并且最高支持 24 位颜色深度(1600 万 色),不过,我们一般使用 16 位颜色深度(65K 色),RGB565 格式,这样,在 16 位模式下, 可以达到最快的速度。 在 16 位模式下,NT35510 采用 RGB565 格式存储颜色数据,此时 NT35510 的低 16 位 数据总线(高 8 位没有用到)与 MCU 的 16 位数据线以及 24 位 LCD GRAM 的对应关系如 表

正点原子提供的参考代码:

颜色:

/* 常用画笔颜色 */
#define WHITE           0xFFFF      /* 白色 */
#define BLACK           0x0000      /* 黑色 */
#define RED             0xF800      /* 红色 */
#define GREEN           0x07E0      /* 绿色 */
#define BLUE            0x001F      /* 蓝色 */ 
#define MAGENTA         0xF81F      /* 品红色/紫红色 = BLUE + RED */
#define YELLOW          0xFFE0      /* 黄色 = GREEN + RED */
#define CYAN            0x07FF      /* 青色 = GREEN + BLUE */  

/* 非常用颜色 */
#define BROWN           0xBC40      /* 棕色 */
#define BRRED           0xFC07      /* 棕红色 */
#define GRAY            0x8430      /* 灰色 */ 
#define DARKBLUE        0x01CF      /* 深蓝色 */
#define LIGHTBLUE       0x7D7C      /* 浅蓝色 */ 
#define GRAYBLUE        0x5458      /* 灰蓝色 */ 
#define LIGHTGREEN      0x841F      /* 浅绿色 */  
#define LGRAY           0xC618      /* 浅灰色(PANNEL),窗体背景色 */ 
#define LGRAYBLUE       0xA651      /* 浅灰蓝色(中间层颜色) */ 
#define LBBLUE          0x2B12      /* 浅棕蓝色(选择条目的反色) */ 

扫描方向:

/* 扫描方向定义 */
#define L2R_U2D         0           /* 从左到右,从上到下 */
#define L2R_D2U         1           /* 从左到右,从下到上 */
#define R2L_U2D         2           /* 从右到左,从上到下 */
#define R2L_D2U         3           /* 从右到左,从下到上 */

#define U2D_L2R         4           /* 从上到下,从左到右 */
#define U2D_R2L         5           /* 从上到下,从右到左 */
#define D2U_L2R         6           /* 从下到上,从左到右 */
#define D2U_R2L         7           /* 从下到上,从右到左 */

#define DFT_SCAN_DIR    L2R_U2D     /* 默认的扫描方向 */

函数声明:

void lcd_wr_data(volatile uint16_t data);            /* LCD写数据 */
void lcd_wr_regno(volatile uint16_t regno);          /* LCD写寄存器编号/地址 */
void lcd_write_reg(uint16_t regno, uint16_t data);   /* LCD写寄存器的值 */

void lcd_init(void);                        /* 初始化LCD */ 
void lcd_display_on(void);                  /* 开显示 */ 
void lcd_display_off(void);                 /* 关显示 */
void lcd_scan_dir(uint8_t dir);             /* 设置屏扫描方向 */ 
void lcd_display_dir(uint8_t dir);          /* 设置屏幕显示方向 */ 
void lcd_ssd_backlight_set(uint8_t pwm);    /* SSD1963 背光控制 */ 

void lcd_write_ram_prepare(void);                           /* 准备写GRAM */ 
void lcd_set_cursor(uint16_t x, uint16_t y);                /* 设置光标 */ 
uint32_t lcd_read_point(uint16_t x, uint16_t y);            /* 读点(32位颜色,兼容LTDC) */
void lcd_draw_point(uint16_t x, uint16_t y, uint32_t color);/* 画点(32位颜色,兼容LTDC) */

void lcd_clear(uint16_t color);                                                             /* LCD清屏 */
void lcd_fill_circle(uint16_t x, uint16_t y, uint16_t r, uint16_t color);                   /* 填充实心圆 */
void lcd_draw_circle(uint16_t x0, uint16_t y0, uint8_t r, uint16_t color);                  /* 画圆 */
void lcd_draw_hline(uint16_t x, uint16_t y, uint16_t len, uint16_t color);                  /* 画水平线 */
void lcd_set_window(uint16_t sx, uint16_t sy, uint16_t width, uint16_t height);             /* 设置窗口 */
void lcd_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t color);          /* 纯色填充矩形(32位颜色,兼容LTDC) */
void lcd_color_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *color);   /* 彩色填充矩形 */
void lcd_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);     /* 画直线 */
void lcd_draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);/* 画矩形 */

void lcd_show_char(uint16_t x, uint16_t y, char chr, uint8_t size, uint8_t mode, uint16_t color);                       /* 显示一个字符 */
void lcd_show_num(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint16_t color);                     /* 显示数字 */
void lcd_show_xnum(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode, uint16_t color);      /* 扩展显示数字 */
void lcd_show_string(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t size, char *p, uint16_t color);   /* 显示字符串 */

lcd.c

/******************************************************************************************************* @file        lcd.c* @author      正点原子团队(ALIENTEK)* @version     V1.1* @date        2023-05-29* @brief       2.8寸/3.5寸/4.3寸/7寸 TFTLCD(MCU屏) 驱动代码*              支持驱动IC型号包括:ILI9341/NT35310/NT35510/SSD1963/ST7789/ST7796/ILI9806等** @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司***************************************************************************************************** @attention** 实验平台:正点原子 探索者 F407开发板* 在线视频:www.yuanzige.com* 技术论坛:www.openedv.com* 公司网址:www.alientek.com* 购买地址:openedv.taobao.com** 修改说明* V1.0 20211016* 第一次发布* V1.1 20230529* 1,新增对ST7796和ILI9806 IC支持* 2,简化部分代码,避免长判定*****************************************************************************************************/#include "stdlib.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/LCD/lcdfont.h"
#include "./SYSTEM/usart/usart.h"/* lcd_ex.c存放各个LCD驱动IC的寄存器初始化部分代码,以简化lcd.c,该.c文件* 不直接加入到工程里面,只有lcd.c会用到,所以通过include的形式添加.(不要在* 其他文件再包含该.c文件!!否则会报错!)*/
#include "./BSP/LCD/lcd_ex.c"SRAM_HandleTypeDef g_sram_handle;   /* SRAM句柄(用于控制LCD) *//* LCD的画笔颜色和背景色 */
uint32_t g_point_color = 0xF800;    /* 画笔颜色 */
uint32_t g_back_color  = 0xFFFF;    /* 背景色 *//* 管理LCD重要参数 */
_lcd_dev lcddev;/*** @brief       LCD写数据* @param       data: 要写入的数据* @retval      无*/
void lcd_wr_data(volatile uint16_t data)
{data = data;            /* 使用-O2优化的时候,必须插入的延时 */LCD->LCD_RAM = data;
}/*** @brief       LCD写寄存器编号/地址函数* @param       regno: 寄存器编号/地址* @retval      无*/
void lcd_wr_regno(volatile uint16_t regno)
{regno = regno;          /* 使用-O2优化的时候,必须插入的延时 */LCD->LCD_REG = regno;   /* 写入要写的寄存器序号 */
}/*** @brief       LCD写寄存器* @param       regno:寄存器编号/地址* @param       data:要写入的数据* @retval      无*/
void lcd_write_reg(uint16_t regno, uint16_t data)
{LCD->LCD_REG = regno;   /* 写入要写的寄存器序号 */LCD->LCD_RAM = data;    /* 写入数据 */
}/*** @brief       LCD延时函数,仅用于部分在mdk -O1时间优化时需要设置的地方* @param       t:延时的数值* @retval      无*/
static void lcd_opt_delay(uint32_t i)
{while (i--); /* 使用AC6时空循环可能被优化,可使用while(1) __asm volatile(""); */
}/*** @brief       LCD读数据* @param       无* @retval      读取到的数据*/
static uint16_t lcd_rd_data(void)
{volatile uint16_t ram;  /* 防止被优化 */lcd_opt_delay(2);ram = LCD->LCD_RAM;return ram;
}/*** @brief       准备写GRAM* @param       无* @retval      无*/
void lcd_write_ram_prepare(void)
{LCD->LCD_REG = lcddev.wramcmd;
}/*** @brief       读取个某点的颜色值* @param       x,y:坐标* @retval      此点的颜色(32位颜色,方便兼容LTDC)*/
uint32_t lcd_read_point(uint16_t x, uint16_t y)
{uint16_t r = 0, g = 0, b = 0;if (x >= lcddev.width || y >= lcddev.height){return 0;   /* 超过了范围,直接返回 */}lcd_set_cursor(x, y);       /* 设置坐标 */if (lcddev.id == 0x5510){lcd_wr_regno(0x2E00);   /* 5510 发送读GRAM指令 */}else{lcd_wr_regno(0x2E);     /* 9341/5310/1963/7789/7796/9806 等发送读GRAM指令 */}r = lcd_rd_data();          /* 假读(dummy read) */if (lcddev.id == 0x1963){return r;   /* 1963直接读就可以 */}r = lcd_rd_data();          /* 实际坐标颜色 */if (lcddev.id == 0x7796)    /* 7796 一次读取一个像素值 */{return r;}/* 9341/5310/5510/7789/9806要分2次读出 */b = lcd_rd_data();g = r & 0xFF;               /* 对于9341/5310/5510/7789/9806,第一次读取的是RG的值,R在前,G在后,各占8位 */g <<= 8;return (((r >> 11) << 11) | ((g >> 10) << 5) | (b >> 11));  /* ILI9341/NT35310/NT35510/ST7789/ILI9806需要公式转换一下 */
}/*** @brief       LCD开启显示* @param       无* @retval      无*/
void lcd_display_on(void)
{if (lcddev.id == 0x5510){lcd_wr_regno(0x2900);   /* 开启显示 */}else                        /* 9341/5310/1963/7789/7796/9806 等发送开启显示指令 */{lcd_wr_regno(0x29);     /* 开启显示 */}
}/*** @brief       LCD关闭显示* @param       无* @retval      无*/
void lcd_display_off(void)
{if (lcddev.id == 0x5510){lcd_wr_regno(0x2800);   /* 关闭显示 */}else                        /* 9341/5310/1963/7789/7796/9806 等发送关闭显示指令 */{lcd_wr_regno(0x28);     /* 关闭显示 */}
}/*** @brief       设置光标位置(对RGB屏无效)* @param       x,y: 坐标* @retval      无*/
void lcd_set_cursor(uint16_t x, uint16_t y)
{if (lcddev.id == 0x1963){if (lcddev.dir == 0)    /* 竖屏模式, x坐标需要变换 */{x = lcddev.width - 1 - x;lcd_wr_regno(lcddev.setxcmd);lcd_wr_data(0);lcd_wr_data(0);lcd_wr_data(x >> 8);lcd_wr_data(x & 0xFF);}else                    /* 横屏模式 */{lcd_wr_regno(lcddev.setxcmd);lcd_wr_data(x >> 8);lcd_wr_data(x & 0xFF);lcd_wr_data((lcddev.width - 1) >> 8);lcd_wr_data((lcddev.width - 1) & 0xFF);}lcd_wr_regno(lcddev.setycmd);lcd_wr_data(y >> 8);lcd_wr_data(y & 0xFF);lcd_wr_data((lcddev.height - 1) >> 8);lcd_wr_data((lcddev.height - 1) & 0xFF);}else if (lcddev.id == 0x5510){lcd_wr_regno(lcddev.setxcmd);lcd_wr_data(x >> 8);lcd_wr_regno(lcddev.setxcmd + 1);lcd_wr_data(x & 0xFF);lcd_wr_regno(lcddev.setycmd);lcd_wr_data(y >> 8);lcd_wr_regno(lcddev.setycmd + 1);lcd_wr_data(y & 0xFF);}else    /* 9341/5310/7789/7796/9806 等 设置坐标 */{lcd_wr_regno(lcddev.setxcmd);lcd_wr_data(x >> 8);lcd_wr_data(x & 0xFF);lcd_wr_regno(lcddev.setycmd);lcd_wr_data(y >> 8);lcd_wr_data(y & 0xFF);}
}/*** @brief       设置LCD的自动扫描方向(对RGB屏无效)*   @note*              9341/5310/5510/1963/7789/7796/9806等IC已经实际测试*              注意:其他函数可能会受到此函数设置的影响(尤其是9341),*              所以,一般设置为L2R_U2D即可,如果设置为其他扫描方式,可能导致显示不正常.** @param       dir:0~7,代表8个方向(具体定义见lcd.h)* @retval      无*/
void lcd_scan_dir(uint8_t dir)
{uint16_t regval = 0;uint16_t dirreg = 0;uint16_t temp;/* 横屏时,对1963不改变扫描方向!竖屏时1963改变方向(这里仅用于1963的特殊处理,对其他驱动IC无效) */if ((lcddev.dir == 1 && lcddev.id != 0x1963) || (lcddev.dir == 0 && lcddev.id == 0x1963)){switch (dir)   /* 方向转换 */{case 0:dir = 6;break;case 1:dir = 7;break;case 2:dir = 4;break;case 3:dir = 5;break;case 4:dir = 1;break;case 5:dir = 0;break;case 6:dir = 3;break;case 7:dir = 2;break;}}/* 根据扫描方式 设置 0x36/0x3600 寄存器 bit 5,6,7 位的值 */switch (dir){case L2R_U2D:   /* 从左到右,从上到下 */regval |= (0 << 7) | (0 << 6) | (0 << 5);break;case L2R_D2U:   /* 从左到右,从下到上 */regval |= (1 << 7) | (0 << 6) | (0 << 5);break;case R2L_U2D:   /* 从右到左,从上到下 */regval |= (0 << 7) | (1 << 6) | (0 << 5);break;case R2L_D2U:   /* 从右到左,从下到上 */regval |= (1 << 7) | (1 << 6) | (0 << 5);break;case U2D_L2R:   /* 从上到下,从左到右 */regval |= (0 << 7) | (0 << 6) | (1 << 5);break;case U2D_R2L:   /* 从上到下,从右到左 */regval |= (0 << 7) | (1 << 6) | (1 << 5);break;case D2U_L2R:   /* 从下到上,从左到右 */regval |= (1 << 7) | (0 << 6) | (1 << 5);break;case D2U_R2L:   /* 从下到上,从右到左 */regval |= (1 << 7) | (1 << 6) | (1 << 5);break;}dirreg = 0x36;  /* 对绝大部分驱动IC, 由0x36寄存器控制 */if (lcddev.id == 0x5510){dirreg = 0x3600;    /* 对于5510, 和其他驱动ic的寄存器有差异 */}/* 9341 & 7789 & 7796 要设置BGR位 */if (lcddev.id == 0x9341 || lcddev.id == 0x7789 || lcddev.id == 0x7796){regval |= 0x08;}lcd_write_reg(dirreg, regval);if (lcddev.id != 0x1963)                    /* 1963不做坐标处理 */{if (regval & 0x20){if (lcddev.width < lcddev.height)   /* 交换X,Y */{temp = lcddev.width;lcddev.width = lcddev.height;lcddev.height = temp;}}else{if (lcddev.width > lcddev.height)   /* 交换X,Y */{temp = lcddev.width;lcddev.width = lcddev.height;lcddev.height = temp;}}}/* 设置显示区域(开窗)大小 */if (lcddev.id == 0x5510){lcd_wr_regno(lcddev.setxcmd);lcd_wr_data(0);lcd_wr_regno(lcddev.setxcmd + 1);lcd_wr_data(0);lcd_wr_regno(lcddev.setxcmd + 2);lcd_wr_data((lcddev.width - 1) >> 8);lcd_wr_regno(lcddev.setxcmd + 3);lcd_wr_data((lcddev.width - 1) & 0xFF);lcd_wr_regno(lcddev.setycmd);lcd_wr_data(0);lcd_wr_regno(lcddev.setycmd + 1);lcd_wr_data(0);lcd_wr_regno(lcddev.setycmd + 2);lcd_wr_data((lcddev.height - 1) >> 8);lcd_wr_regno(lcddev.setycmd + 3);lcd_wr_data((lcddev.height - 1) & 0xFF);}else{lcd_wr_regno(lcddev.setxcmd);lcd_wr_data(0);lcd_wr_data(0);lcd_wr_data((lcddev.width - 1) >> 8);lcd_wr_data((lcddev.width - 1) & 0xFF);lcd_wr_regno(lcddev.setycmd);lcd_wr_data(0);lcd_wr_data(0);lcd_wr_data((lcddev.height - 1) >> 8);lcd_wr_data((lcddev.height - 1) & 0xFF);}
}/*** @brief       画点* @param       x,y: 坐标* @param       color: 点的颜色(32位颜色,方便兼容LTDC)* @retval      无*/
void lcd_draw_point(uint16_t x, uint16_t y, uint32_t color)
{lcd_set_cursor(x, y);       /* 设置光标位置 */lcd_write_ram_prepare();    /* 开始写入GRAM */LCD->LCD_RAM = color;
}/*** @brief       SSD1963背光亮度设置函数* @param       pwm: 背光等级,0~100.越大越亮.* @retval      无*/
void lcd_ssd_backlight_set(uint8_t pwm)
{lcd_wr_regno(0xBE);         /* 配置PWM输出 */lcd_wr_data(0x05);          /* 1设置PWM频率 */lcd_wr_data(pwm * 2.55);    /* 2设置PWM占空比 */lcd_wr_data(0x01);          /* 3设置C */lcd_wr_data(0xFF);          /* 4设置D */lcd_wr_data(0x00);          /* 5设置E */lcd_wr_data(0x00);          /* 6设置F */
}/*** @brief       设置LCD显示方向* @param       dir:0,竖屏; 1,横屏* @retval      无*/
void lcd_display_dir(uint8_t dir)
{lcddev.dir = dir;   /* 竖屏/横屏 */if (dir == 0)       /* 竖屏 */{lcddev.width = 240;lcddev.height = 320;if (lcddev.id == 0x5510){lcddev.wramcmd = 0x2C00;lcddev.setxcmd = 0x2A00;lcddev.setycmd = 0x2B00;lcddev.width = 480;lcddev.height = 800;}else if (lcddev.id == 0x1963){lcddev.wramcmd = 0x2C;  /* 设置写入GRAM的指令 */lcddev.setxcmd = 0x2B;  /* 设置写X坐标指令 */lcddev.setycmd = 0x2A;  /* 设置写Y坐标指令 */lcddev.width = 480;     /* 设置宽度480 */lcddev.height = 800;    /* 设置高度800 */}else   /* 其他IC, 包括: 9341/5310/7789/7796/9806等IC */{lcddev.wramcmd = 0x2C;lcddev.setxcmd = 0x2A;lcddev.setycmd = 0x2B;}if (lcddev.id == 0x5310 || lcddev.id == 0x7796)     /* 如果是5310/7796 则表示是 320*480分辨率 */{lcddev.width = 320;lcddev.height = 480;}if (lcddev.id == 0X9806)    /* 如果是9806 则表示是 480*800 分辨率 */{lcddev.width = 480;lcddev.height = 800;}  }else        /* 横屏 */{lcddev.width = 320;         /* 默认宽度 */lcddev.height = 240;        /* 默认高度 */if (lcddev.id == 0x5510){lcddev.wramcmd = 0x2C00;lcddev.setxcmd = 0x2A00;lcddev.setycmd = 0x2B00;lcddev.width = 800;lcddev.height = 480;}else if (lcddev.id == 0x1963 || lcddev.id == 0x9806){lcddev.wramcmd = 0x2C;  /* 设置写入GRAM的指令 */lcddev.setxcmd = 0x2A;  /* 设置写X坐标指令 */lcddev.setycmd = 0x2B;  /* 设置写Y坐标指令 */lcddev.width = 800;     /* 设置宽度800 */lcddev.height = 480;    /* 设置高度480 */}else   /* 其他IC, 包括:9341/5310/7789/7796等IC */{lcddev.wramcmd = 0x2C;lcddev.setxcmd = 0x2A;lcddev.setycmd = 0x2B;}if (lcddev.id == 0x5310 || lcddev.id == 0x7796)     /* 如果是5310/7796 则表示是 320*480分辨率 */{lcddev.width = 480;lcddev.height = 320;}}lcd_scan_dir(DFT_SCAN_DIR);     /* 默认扫描方向 */
}/*** @brief       设置窗口(对RGB屏无效), 并自动设置画点坐标到窗口左上角(sx,sy).* @param       sx,sy:窗口起始坐标(左上角)* @param       width,height:窗口宽度和高度,必须大于0!!*   @note      窗体大小:width*height.** @retval      无*/
void lcd_set_window(uint16_t sx, uint16_t sy, uint16_t width, uint16_t height)
{uint16_t twidth, theight;twidth = sx + width - 1;theight = sy + height - 1;if (lcddev.id == 0x1963 && lcddev.dir != 1)     /* 1963竖屏特殊处理 */{sx = lcddev.width - width - sx;height = sy + height - 1;lcd_wr_regno(lcddev.setxcmd);lcd_wr_data(sx >> 8);lcd_wr_data(sx & 0xFF);lcd_wr_data((sx + width - 1) >> 8);lcd_wr_data((sx + width - 1) & 0xFF);lcd_wr_regno(lcddev.setycmd);lcd_wr_data(sy >> 8);lcd_wr_data(sy & 0xFF);lcd_wr_data(height >> 8);lcd_wr_data(height & 0xFF);}else if (lcddev.id == 0x5510){lcd_wr_regno(lcddev.setxcmd);lcd_wr_data(sx >> 8);lcd_wr_regno(lcddev.setxcmd + 1);lcd_wr_data(sx & 0xFF);lcd_wr_regno(lcddev.setxcmd + 2);lcd_wr_data(twidth >> 8);lcd_wr_regno(lcddev.setxcmd + 3);lcd_wr_data(twidth & 0xFF);lcd_wr_regno(lcddev.setycmd);lcd_wr_data(sy >> 8);lcd_wr_regno(lcddev.setycmd + 1);lcd_wr_data(sy & 0xFF);lcd_wr_regno(lcddev.setycmd + 2);lcd_wr_data(theight >> 8);lcd_wr_regno(lcddev.setycmd + 3);lcd_wr_data(theight & 0xFF);}else    /* 9341/5310/7789/1963/7796/9806横屏 等 设置窗口 */{lcd_wr_regno(lcddev.setxcmd);lcd_wr_data(sx >> 8);lcd_wr_data(sx & 0xFF);lcd_wr_data(twidth >> 8);lcd_wr_data(twidth & 0xFF);lcd_wr_regno(lcddev.setycmd);lcd_wr_data(sy >> 8);lcd_wr_data(sy & 0xFF);lcd_wr_data(theight >> 8);lcd_wr_data(theight & 0xFF);}
}/*** @brief       SRAM底层驱动,时钟使能,引脚分配* @note        此函数会被HAL_SRAM_Init()调用,初始化读写总线引脚* @param       hsram:SRAM句柄* @retval      无*/
void HAL_SRAM_MspInit(SRAM_HandleTypeDef *hsram)
{GPIO_InitTypeDef gpio_init_struct;__HAL_RCC_FSMC_CLK_ENABLE();            /* 使能FSMC时钟 */__HAL_RCC_GPIOD_CLK_ENABLE();           /* 使能GPIOD时钟 */__HAL_RCC_GPIOE_CLK_ENABLE();           /* 使能GPIOE时钟 *//* 初始化PD0,1, 8,9,10,14,15 */gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 \| GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15;gpio_init_struct.Mode = GPIO_MODE_AF_PP;            /* 推挽复用 */gpio_init_struct.Pull = GPIO_PULLUP;                /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;      /* 高速 */gpio_init_struct.Alternate = GPIO_AF12_FSMC;        /* 复用为FSMC */HAL_GPIO_Init(GPIOD, &gpio_init_struct);            /* 初始化 *//* 初始化PE7,8,9,10,11,12,13,14,15 */gpio_init_struct.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 \| GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;HAL_GPIO_Init(GPIOE, &gpio_init_struct);
}/*** @brief       初始化LCD*   @note      该初始化函数可以初始化各种型号的LCD(详见本.c文件最前面的描述)** @param       无* @retval      无*/
void lcd_init(void)
{GPIO_InitTypeDef gpio_init_struct;FSMC_NORSRAM_TimingTypeDef fsmc_read_handle;FSMC_NORSRAM_TimingTypeDef fsmc_write_handle;LCD_CS_GPIO_CLK_ENABLE();   /* LCD_CS脚时钟使能 */LCD_WR_GPIO_CLK_ENABLE();   /* LCD_WR脚时钟使能 */LCD_RD_GPIO_CLK_ENABLE();   /* LCD_RD脚时钟使能 */LCD_RS_GPIO_CLK_ENABLE();   /* LCD_RS脚时钟使能 */LCD_BL_GPIO_CLK_ENABLE();   /* LCD_BL脚时钟使能 */gpio_init_struct.Pin = LCD_CS_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_AF_PP;                /* 推挽复用 */gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* 高速 */gpio_init_struct.Alternate = GPIO_AF12_FSMC;            /* 复用为FSMC */HAL_GPIO_Init(LCD_CS_GPIO_PORT, &gpio_init_struct);     /* 初始化LCD_CS引脚 */gpio_init_struct.Pin = LCD_WR_GPIO_PIN;HAL_GPIO_Init(LCD_WR_GPIO_PORT, &gpio_init_struct);     /* 初始化LCD_WR引脚 */gpio_init_struct.Pin = LCD_RD_GPIO_PIN;HAL_GPIO_Init(LCD_RD_GPIO_PORT, &gpio_init_struct);     /* 初始化LCD_RD引脚 */gpio_init_struct.Pin = LCD_RS_GPIO_PIN;HAL_GPIO_Init(LCD_RS_GPIO_PORT, &gpio_init_struct);     /* 初始化LCD_RS引脚 */gpio_init_struct.Pin = LCD_BL_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽输出 */HAL_GPIO_Init(LCD_BL_GPIO_PORT, &gpio_init_struct);     /* LCD_BL引脚模式设置(推挽输出) */g_sram_handle.Instance = FSMC_NORSRAM_DEVICE;g_sram_handle.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;g_sram_handle.Init.NSBank = FSMC_NORSRAM_BANK4;                        /* 使用NE4 */g_sram_handle.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;     /* 地址/数据线不复用 */g_sram_handle.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;    /* 16位数据宽度 */g_sram_handle.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;   /* 是否使能突发访问,仅对同步突发存储器有效,此处未用到 */g_sram_handle.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW; /* 等待信号的极性,仅在突发模式访问下有用 */g_sram_handle.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;      /* 存储器是在等待周期之前的一个时钟周期还是等待周期期间使能NWAIT */g_sram_handle.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;       /* 存储器写使能 */g_sram_handle.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;              /* 等待使能位,此处未用到 */g_sram_handle.Init.ExtendedMode = FSMC_EXTENDED_MODE_ENABLE;           /* 读写使用不同的时序 */g_sram_handle.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;  /* 是否使能同步传输模式下的等待信号,此处未用到 */g_sram_handle.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;              /* 禁止突发写 *//* FSMC读时序控制寄存器 */fsmc_read_handle.AddressSetupTime = 0x0F;           /* 地址建立时间(ADDSET)为15个fsmc_ker_ck(1/168=6)即6*15=90ns */fsmc_read_handle.AddressHoldTime = 0x00;            /* 地址保持时间(ADDHLD) 模式A是没有用到 */fsmc_read_handle.DataSetupTime = 60;                /* 数据保存时间(DATAST)为60个fsmc_ker_ck=6*60=360ns *//* 因为液晶驱动IC的读数据的时候,速度不能太快,尤其是个别奇葩芯片 */fsmc_read_handle.AccessMode = FSMC_ACCESS_MODE_A;   /* 模式A *//* FSMC写时序控制寄存器 */fsmc_write_handle.AddressSetupTime = 9;             /* 地址建立时间(ADDSET)为9个fsmc_ker_ck=6*9=54ns */fsmc_write_handle.AddressHoldTime = 0x00;           /* 地址保持时间(ADDHLD) 模式A是没有用到 */fsmc_write_handle.DataSetupTime = 9;                /* 数据保存时间(DATAST)为9个fsmc_ker_ck=6*9=54ns *//* 注意:某些液晶驱动IC的写信号脉宽,最少也得50ns */fsmc_write_handle.AccessMode = FSMC_ACCESS_MODE_A;  /* 模式A */HAL_SRAM_Init(&g_sram_handle, &fsmc_read_handle, &fsmc_write_handle);delay_ms(50);/* 尝试9341 ID的读取 */lcd_wr_regno(0xD3);lcddev.id = lcd_rd_data();  /* dummy read */lcddev.id = lcd_rd_data();  /* 读到0x00 */lcddev.id = lcd_rd_data();  /* 读取93 */lcddev.id <<= 8;lcddev.id |= lcd_rd_data(); /* 读取41 */if (lcddev.id != 0x9341)    /* 不是 9341 , 尝试看看是不是 ST7789 */{lcd_wr_regno(0x04);lcddev.id = lcd_rd_data();      /* dummy read */lcddev.id = lcd_rd_data();      /* 读到0x85 */lcddev.id = lcd_rd_data();      /* 读取0x85 */lcddev.id <<= 8;lcddev.id |= lcd_rd_data();     /* 读取0x52 */if (lcddev.id == 0x8552)        /* 将8552的ID转换成7789 */{lcddev.id = 0x7789;}if (lcddev.id != 0x7789)        /* 也不是ST7789, 尝试是不是 NT35310 */{lcd_wr_regno(0xD4);lcddev.id = lcd_rd_data();  /* dummy read */lcddev.id = lcd_rd_data();  /* 读回0x01 */lcddev.id = lcd_rd_data();  /* 读回0x53 */lcddev.id <<= 8;lcddev.id |= lcd_rd_data(); /* 这里读回0x10 */if (lcddev.id != 0x5310)    /* 也不是NT35310,尝试看看是不是ST7796 */{lcd_wr_regno(0XD3);lcddev.id = lcd_rd_data();  /* dummy read */lcddev.id = lcd_rd_data();  /* 读到0X00 */lcddev.id = lcd_rd_data();  /* 读取0X77 */lcddev.id <<= 8;lcddev.id |= lcd_rd_data(); /* 读取0X96 */if (lcddev.id != 0x7796)    /* 也不是ST7796,尝试看看是不是NT35510 */{/* 发送密钥(厂家提供) */lcd_write_reg(0xF000, 0x0055);lcd_write_reg(0xF001, 0x00AA);lcd_write_reg(0xF002, 0x0052);lcd_write_reg(0xF003, 0x0008);lcd_write_reg(0xF004, 0x0001);lcd_wr_regno(0xC500);       /* 读取ID低八位 */lcddev.id = lcd_rd_data();  /* 读回0x80 */lcddev.id <<= 8;lcd_wr_regno(0xC501);       /* 读取ID高八位 */lcddev.id |= lcd_rd_data(); /* 读回0x00 */delay_ms(5);                /* 等待5ms, 因为0XC501指令对1963来说就是软件复位指令, 等待5ms让1963复位完成再操作 */if (lcddev.id != 0x5510)    /* 也不是NT5510,尝试看看是不是ILI9806 */{lcd_wr_regno(0XD3);lcddev.id = lcd_rd_data();  /* dummy read */lcddev.id = lcd_rd_data();  /* 读回0X00 */lcddev.id = lcd_rd_data();  /* 读回0X98 */lcddev.id <<= 8;lcddev.id |= lcd_rd_data(); /* 读回0X06 */if (lcddev.id != 0x9806)    /* 也不是ILI9806,尝试看看是不是SSD1963 */{lcd_wr_regno(0xA1);lcddev.id = lcd_rd_data();lcddev.id = lcd_rd_data();  /* 读回0x57 */lcddev.id <<= 8;lcddev.id |= lcd_rd_data(); /* 读回0x61 */if (lcddev.id == 0x5761) lcddev.id = 0x1963; /* SSD1963读回的ID是5761H,为方便区分,我们强制设置为1963 */}}}}}}/* 特别注意, 如果在main函数里面屏蔽串口1初始化, 则会卡死在printf* 里面(卡死在f_putc函数), 所以, 必须初始化串口1, 或者屏蔽掉下面* 这行 printf 语句 !!!!!!!*/printf("LCD ID:%x\r\n", lcddev.id); /* 打印LCD ID */if (lcddev.id == 0x7789){lcd_ex_st7789_reginit();    /* 执行ST7789初始化 */}else if (lcddev.id == 0x9341){lcd_ex_ili9341_reginit();   /* 执行ILI9341初始化 */}else if (lcddev.id == 0x5310){lcd_ex_nt35310_reginit();   /* 执行NT35310初始化 */}else if (lcddev.id == 0x7796){lcd_ex_st7796_reginit();    /* 执行ST7796初始化 */}else if (lcddev.id == 0x5510){lcd_ex_nt35510_reginit();   /* 执行NT35510初始化 */}else if (lcddev.id == 0x9806){lcd_ex_ili9806_reginit();   /* 执行ILI9806初始化 */}else if (lcddev.id == 0x1963){lcd_ex_ssd1963_reginit();   /* 执行SSD1963初始化 */lcd_ssd_backlight_set(100); /* 背光设置为最亮 */}/* 由于不同屏幕的写时序不同,这里的时序可以根据自己的屏幕进行修改(若插上长排线对时序也会有影响,需要自己根据情况修改) *//* 初始化完成以后,提速 */if (lcddev.id == 0x7789){/* 重新配置写时序控制寄存器的时序 */fsmc_write_handle.AddressSetupTime = 3; /* 地址建立时间(ADDSET)为3个fsmc_ker_ck=6*3=18ns */fsmc_write_handle.DataSetupTime = 3;    /* 数据保持时间(DATAST)为3个fsmc_ker_ck=6*3=18ns */FSMC_NORSRAM_Extended_Timing_Init(g_sram_handle.Extended, &fsmc_write_handle, g_sram_handle.Init.NSBank, g_sram_handle.Init.ExtendedMode);}else if (lcddev.id == 0x9806 || lcddev.id == 0x9341 || lcddev.id == 0x5510){/* 重新配置写时序控制寄存器的时序 */fsmc_write_handle.AddressSetupTime = 2; /* 地址建立时间(ADDSET)为2个fsmc_ker_ck=6*2=12ns */fsmc_write_handle.DataSetupTime = 2;    /* 数据保持时间(DATAST)为2个fsmc_ker_ck=6*2=12ns */FSMC_NORSRAM_Extended_Timing_Init(g_sram_handle.Extended, &fsmc_write_handle, g_sram_handle.Init.NSBank, g_sram_handle.Init.ExtendedMode);}else if (lcddev.id == 0x5310 || lcddev.id == 0x7796 || lcddev.id == 0x1963){/* 重新配置写时序控制寄存器的时序 */fsmc_write_handle.AddressSetupTime = 1; /* 地址建立时间(ADDSET)为1个fsmc_ker_ck=6*1=6ns */fsmc_write_handle.DataSetupTime = 1;    /* 数据保持时间(DATAST)为1个fsmc_ker_ck=6*1=6ns */FSMC_NORSRAM_Extended_Timing_Init(g_sram_handle.Extended, &fsmc_write_handle, g_sram_handle.Init.NSBank, g_sram_handle.Init.ExtendedMode);}lcd_display_dir(0); /* 默认为竖屏 */LCD_BL(1);          /* 点亮背光 */lcd_clear(WHITE);
}/*** @brief       清屏函数* @param       color: 要清屏的颜色* @retval      无*/
void lcd_clear(uint16_t color)
{uint32_t index = 0;uint32_t totalpoint = lcddev.width;totalpoint *= lcddev.height;    /* 得到总点数 */lcd_set_cursor(0x00, 0x0000);   /* 设置光标位置 */lcd_write_ram_prepare();        /* 开始写入GRAM */for (index = 0; index < totalpoint; index++){LCD->LCD_RAM = color;}
}/*** @brief       在指定区域内填充单个颜色* @param       (sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex - sx + 1) * (ey - sy + 1)* @param       color:  要填充的颜色(32位颜色,方便兼容LTDC)* @retval      无*/
void lcd_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t color)
{uint16_t i, j;uint16_t xlen = 0;xlen = ex - sx + 1;for (i = sy; i <= ey; i++){lcd_set_cursor(sx, i);      /* 设置光标位置 */lcd_write_ram_prepare();    /* 开始写入GRAM */for (j = 0; j < xlen; j++){LCD->LCD_RAM = color;   /* 显示颜色 */}}
}/*** @brief       在指定区域内填充指定颜色块* @param       (sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex - sx + 1) * (ey - sy + 1)* @param       color: 要填充的颜色数组首地址* @retval      无*/
void lcd_color_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *color)
{uint16_t height, width;uint16_t i, j;width = ex - sx + 1;            /* 得到填充的宽度 */height = ey - sy + 1;           /* 高度 */for (i = 0; i < height; i++){lcd_set_cursor(sx, sy + i); /* 设置光标位置 */lcd_write_ram_prepare();    /* 开始写入GRAM */for (j = 0; j < width; j++){LCD->LCD_RAM = color[i * width + j]; /* 写入数据 */}}
}/*** @brief       画线* @param       x1,y1: 起点坐标* @param       x2,y2: 终点坐标* @param       color: 线的颜色* @retval      无*/
void lcd_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)
{uint16_t t;int xerr = 0, yerr = 0, delta_x, delta_y, distance;int incx, incy, row, col;delta_x = x2 - x1;      /* 计算坐标增量 */delta_y = y2 - y1;row = x1;col = y1;if (delta_x > 0){incx = 1;       /* 设置单步方向 */}else if (delta_x == 0){incx = 0;       /* 垂直线 */}else{incx = -1;delta_x = -delta_x;}if (delta_y > 0){incy = 1;}else if (delta_y == 0){incy = 0;       /* 水平线 */}else{incy = -1;delta_y = -delta_y;}if ( delta_x > delta_y){distance = delta_x;  /* 选取基本增量坐标轴 */}else{distance = delta_y;}for (t = 0; t <= distance + 1; t++)     /* 画线输出 */{lcd_draw_point(row, col, color);    /* 画点 */xerr += delta_x;yerr += delta_y;if (xerr > distance){xerr -= distance;row += incx;}if (yerr > distance){yerr -= distance;col += incy;}}
}/*** @brief       画水平线* @param       x,y   : 起点坐标* @param       len   : 线长度* @param       color : 矩形的颜色* @retval      无*/
void lcd_draw_hline(uint16_t x, uint16_t y, uint16_t len, uint16_t color)
{if ((len == 0) || (x > lcddev.width) || (y > lcddev.height)){return;}lcd_fill(x, y, x + len - 1, y, color);
}/*** @brief       画矩形* @param       x1,y1: 起点坐标* @param       x2,y2: 终点坐标* @param       color: 矩形的颜色* @retval      无*/
void lcd_draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)
{lcd_draw_line(x1, y1, x2, y1, color);lcd_draw_line(x1, y1, x1, y2, color);lcd_draw_line(x1, y2, x2, y2, color);lcd_draw_line(x2, y1, x2, y2, color);
}/*** @brief       画圆* @param       x0,y0 : 圆中心坐标* @param       r     : 半径* @param       color : 圆的颜色* @retval      无*/
void lcd_draw_circle(uint16_t x0, uint16_t y0, uint8_t r, uint16_t color)
{int a, b;int di;a = 0;b = r;di = 3 - (r << 1);       /* 判断下个点位置的标志 */while (a <= b){lcd_draw_point(x0 + a, y0 - b, color);  /* 5 */lcd_draw_point(x0 + b, y0 - a, color);  /* 0 */lcd_draw_point(x0 + b, y0 + a, color);  /* 4 */lcd_draw_point(x0 + a, y0 + b, color);  /* 6 */lcd_draw_point(x0 - a, y0 + b, color);  /* 1 */lcd_draw_point(x0 - b, y0 + a, color);lcd_draw_point(x0 - a, y0 - b, color);  /* 2 */lcd_draw_point(x0 - b, y0 - a, color);  /* 7 */a++;/* 使用Bresenham算法画圆 */if (di < 0){di += 4 * a + 6;}else{di += 10 + 4 * (a - b);b--;}}
}/*** @brief       填充实心圆* @param       x,y  : 圆中心坐标* @param       r    : 半径* @param       color: 圆的颜色* @retval      无*/
void lcd_fill_circle(uint16_t x, uint16_t y, uint16_t r, uint16_t color)
{uint32_t i;uint32_t imax = ((uint32_t)r * 707) / 1000 + 1;uint32_t sqmax = (uint32_t)r * (uint32_t)r + (uint32_t)r / 2;uint32_t xr = r;lcd_draw_hline(x - r, y, 2 * r, color);for (i = 1; i <= imax; i++){if ((i * i + xr * xr) > sqmax){/* draw lines from outside */if (xr > imax){lcd_draw_hline (x - i + 1, y + xr, 2 * (i - 1), color);lcd_draw_hline (x - i + 1, y - xr, 2 * (i - 1), color);}xr--;}/* draw lines from inside (center) */lcd_draw_hline(x - xr, y + i, 2 * xr, color);lcd_draw_hline(x - xr, y - i, 2 * xr, color);}
}/*** @brief       在指定位置显示一个字符* @param       x,y  : 坐标* @param       chr  : 要显示的字符:" "--->"~"* @param       size : 字体大小 12/16/24/32* @param       mode : 叠加方式(1); 非叠加方式(0);* @param       color : 字符的颜色;* @retval      无*/
void lcd_show_char(uint16_t x, uint16_t y, char chr, uint8_t size, uint8_t mode, uint16_t color)
{uint8_t temp, t1, t;uint16_t y0 = y;uint8_t csize = 0;uint8_t *pfont = 0;csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size / 2); /* 得到字体一个字符对应点阵集所占的字节数 */chr = chr - ' ';    /* 得到偏移后的值(ASCII字库是从空格开始取模,所以-' '就是对应字符的字库) */switch (size){case 12:pfont = (uint8_t *)asc2_1206[chr];  /* 调用1206字体 */break;case 16:pfont = (uint8_t *)asc2_1608[chr];  /* 调用1608字体 */break;case 24:pfont = (uint8_t *)asc2_2412[chr];  /* 调用2412字体 */break;case 32:pfont = (uint8_t *)asc2_3216[chr];  /* 调用3216字体 */break;default:return ;}for (t = 0; t < csize; t++){temp = pfont[t];                            /* 获取字符的点阵数据 */for (t1 = 0; t1 < 8; t1++)                  /* 一个字节8个点 */{if (temp & 0x80)                        /* 有效点,需要显示 */{lcd_draw_point(x, y, color);        /* 画点出来,要显示这个点 */}else if (mode == 0)                     /* 无效点,不显示 */{lcd_draw_point(x, y, g_back_color); /* 画背景色,相当于这个点不显示(注意背景色由全局变量控制) */}temp <<= 1;                             /* 移位, 以便获取下一个位的状态 */y++;if (y >= lcddev.height)return;          /* 超区域了 */if ((y - y0) == size)                   /* 显示完一列了? */{y = y0; /* y坐标复位 */x++;    /* x坐标递增 */if (x >= lcddev.width){return;       /* x坐标超区域了 */}break;}}}
}/*** @brief       平方函数, m^n* @param       m: 底数* @param       n: 指数* @retval      m的n次方*/
static uint32_t lcd_pow(uint8_t m, uint8_t n)
{uint32_t result = 1;while (n--){result *= m;}return result;
}/*** @brief       显示len个数字* @param       x,y : 起始坐标* @param       num : 数值(0 ~ 2^32)* @param       len : 显示数字的位数* @param       size: 选择字体 12/16/24/32* @param       color : 数字的颜色;* @retval      无*/
void lcd_show_num(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint16_t color)
{uint8_t t, temp;uint8_t enshow = 0;for (t = 0; t < len; t++)   /* 按总显示位数循环 */{temp = (num / lcd_pow(10, len - t - 1)) % 10;   /* 获取对应位的数字 */if (enshow == 0 && t < (len - 1))               /* 没有使能显示,且还有位要显示 */{if (temp == 0){lcd_show_char(x + (size / 2) * t, y, ' ', size, 0, color);  /* 显示空格,占位 */continue;       /* 继续下个一位 */}else{enshow = 1;     /* 使能显示 */}}lcd_show_char(x + (size / 2) * t, y, temp + '0', size, 0, color);   /* 显示字符 */}
}/*** @brief       扩展显示len个数字(高位是0也显示)* @param       x,y : 起始坐标* @param       num : 数值(0 ~ 2^32)* @param       len : 显示数字的位数* @param       size: 选择字体 12/16/24/32* @param       mode: 显示模式*              [7]:0,不填充;1,填充0.*              [6:1]:保留*              [0]:0,非叠加显示;1,叠加显示.* @param       color : 数字的颜色;* @retval      无*/
void lcd_show_xnum(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode, uint16_t color)
{uint8_t t, temp;uint8_t enshow = 0;for (t = 0; t < len; t++)       /* 按总显示位数循环 */{temp = (num / lcd_pow(10, len - t - 1)) % 10;    /* 获取对应位的数字 */if (enshow == 0 && t < (len - 1))   /* 没有使能显示,且还有位要显示 */{if (temp == 0){if (mode & 0x80)    /* 高位需要填充0 */{lcd_show_char(x + (size / 2) * t, y, '0', size, mode & 0x01, color);    /* 用0占位 */}else{lcd_show_char(x + (size / 2) * t, y, ' ', size, mode & 0x01, color);    /* 用空格占位 */}continue;}else{enshow = 1;         /* 使能显示 */}}lcd_show_char(x + (size / 2) * t, y, temp + '0', size, mode & 0x01, color);}
}/*** @brief       显示字符串* @param       x,y         : 起始坐标* @param       width,height: 区域大小* @param       size        : 选择字体 12/16/24/32* @param       p           : 字符串首地址* @param       color       : 字符串的颜色;* @retval      无*/
void lcd_show_string(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t size, char *p, uint16_t color)
{uint8_t x0 = x;width += x;height += y;while ((*p <= '~') && (*p >= ' '))   /* 判断是不是非法字符! */{if (x >= width){x = x0;y += size;}if (y >= height){break;      /* 退出 */}lcd_show_char(x, y, *p, size, 0, color);x += size / 2;p++;}
}

lcd.h

/******************************************************************************************************* @file        lcd.h* @author      正点原子团队(ALIENTEK)* @version     V1.1* @date        2023-05-29* @brief       2.8寸/3.5寸/4.3寸/7寸 TFTLCD(MCU屏) 驱动代码*              支持驱动IC型号包括:ILI9341/NT35310/NT35510/SSD1963/ST7789/ST7796/ILI9806等** @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司***************************************************************************************************** @attention** 实验平台:正点原子 探索者 F407开发板* 在线视频:www.yuanzige.com* 技术论坛:www.openedv.com* 公司网址:www.alientek.com* 购买地址:openedv.taobao.com** 修改说明* V1.0 20211016* 第一次发布* V1.1 20230529* 1,新增对ST7796和ILI9806 IC支持* 2,简化部分代码,避免长判定*****************************************************************************************************/#ifndef __LCD_H
#define __LCD_H#include "stdlib.h"
#include "./SYSTEM/sys/sys.h"/******************************************************************************************/
/* LCD RST/WR/RD/BL/CS/RS 引脚 定义 * LCD_D0~D15,由于引脚太多,就不在这里定义了,直接在lcd_init里面修改.所以在移植的时候,除了改* 这6个IO口, 还得改LCD_Init里面的D0~D15所在的IO口.*//* RESET 和系统复位脚共用 所以这里不用定义 RESET引脚 */
//#define LCD_RST_GPIO_PORT               GPIOx
//#define LCD_RST_GPIO_PIN                SYS_GPIO_PINx
//#define LCD_RST_GPIO_CLK_ENABLE()       do{ __HAL_RCC_GPIOx_CLK_ENABLE(); }while(0)   /* 所在IO口时钟使能 */#define LCD_WR_GPIO_PORT                GPIOD
#define LCD_WR_GPIO_PIN                 GPIO_PIN_5
#define LCD_WR_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)     /* 所在IO口时钟使能 */#define LCD_RD_GPIO_PORT                GPIOD
#define LCD_RD_GPIO_PIN                 GPIO_PIN_4
#define LCD_RD_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)     /* 所在IO口时钟使能 */#define LCD_BL_GPIO_PORT                GPIOB
#define LCD_BL_GPIO_PIN                 GPIO_PIN_15
#define LCD_BL_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)     /* 背光所在IO口时钟使能 *//* LCD_CS(需要根据LCD_FSMC_NEX设置正确的IO口) 和 LCD_RS(需要根据LCD_FSMC_AX设置正确的IO口) 引脚 定义 */
#define LCD_CS_GPIO_PORT                GPIOG
#define LCD_CS_GPIO_PIN                 GPIO_PIN_12
#define LCD_CS_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0)     /* 所在IO口时钟使能 */#define LCD_RS_GPIO_PORT                GPIOF
#define LCD_RS_GPIO_PIN                 GPIO_PIN_12
#define LCD_RS_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0)     /* 所在IO口时钟使能 *//* FSMC相关参数 定义 * 注意: 我们默认是通过FSMC块1来连接LCD, 块1有4个片选: FSMC_NE1~4** 修改LCD_FSMC_NEX, 对应的LCD_CS_GPIO相关设置也得改* 修改LCD_FSMC_AX , 对应的LCD_RS_GPIO相关设置也得改*/
#define LCD_FSMC_NEX         4              /* 使用FSMC_NE4接LCD_CS,取值范围只能是: 1~4 */
#define LCD_FSMC_AX          6              /* 使用FSMC_A6接LCD_RS,取值范围是: 0 ~ 25 */#define LCD_FSMC_BCRX        FSMC_Bank1->BTCR[(LCD_FSMC_NEX - 1) * 2]       /* BCR寄存器,根据LCD_FSMC_NEX自动计算 */
#define LCD_FSMC_BTRX        FSMC_Bank1->BTCR[(LCD_FSMC_NEX - 1) * 2 + 1]   /* BTR寄存器,根据LCD_FSMC_NEX自动计算 */
#define LCD_FSMC_BWTRX       FSMC_Bank1E->BWTR[(LCD_FSMC_NEX - 1) * 2]      /* BWTR寄存器,根据LCD_FSMC_NEX自动计算 *//******************************************************************************************//* LCD重要参数集 */
typedef struct
{uint16_t width;     /* LCD 宽度 */uint16_t height;    /* LCD 高度 */uint16_t id;        /* LCD ID */uint8_t dir;        /* 横屏还是竖屏控制:0,竖屏;1,横屏。 */uint16_t wramcmd;   /* 开始写gram指令 */uint16_t setxcmd;   /* 设置x坐标指令 */uint16_t setycmd;   /* 设置y坐标指令 */
} _lcd_dev;/* LCD参数 */
extern _lcd_dev lcddev; /* 管理LCD重要参数 *//* LCD的画笔颜色和背景色 */
extern uint32_t  g_point_color;     /* 默认红色 */
extern uint32_t  g_back_color;      /* 背景颜色.默认为白色 *//* LCD背光控制 */
#define LCD_BL(x)   do{ x ? \HAL_GPIO_WritePin(LCD_BL_GPIO_PORT, LCD_BL_GPIO_PIN, GPIO_PIN_SET) : \HAL_GPIO_WritePin(LCD_BL_GPIO_PORT, LCD_BL_GPIO_PIN, GPIO_PIN_RESET); \}while(0)/* LCD地址结构体 */
typedef struct
{volatile uint16_t LCD_REG;volatile uint16_t LCD_RAM;
} LCD_TypeDef;/* LCD_BASE的详细解算方法:* 我们一般使用FSMC的块1(BANK1)来驱动TFTLCD液晶屏(MCU屏), 块1地址范围总大小为256MB,均分成4块:* 存储块1(FSMC_NE1)地址范围: 0x6000 0000 ~ 0x63FF FFFF* 存储块2(FSMC_NE2)地址范围: 0x6400 0000 ~ 0x67FF FFFF* 存储块3(FSMC_NE3)地址范围: 0x6800 0000 ~ 0x6BFF FFFF* 存储块4(FSMC_NE4)地址范围: 0x6C00 0000 ~ 0x6FFF FFFF** 我们需要根据硬件连接方式选择合适的片选(连接LCD_CS)和地址线(连接LCD_RS)* 探索者F407开发板使用FSMC_NE4连接LCD_CS, FSMC_A6连接LCD_RS ,16位数据线,计算方法如下:* 首先FSMC_NE4的基地址为: 0x6C00 0000;     NEX的基址为(x=1/2/3/4): 0x6000 0000 + (0x400 0000 * (x - 1))* FSMC_A6对应地址值: 2^6 * 2 = 0x80;    FSMC_Ay对应的地址为(y = 0 ~ 25): 2^y * 2** LCD->LCD_REG,对应LCD_RS = 0(LCD寄存器); LCD->LCD_RAM,对应LCD_RS = 1(LCD数据)* 则 LCD->LCD_RAM的地址为:  0x6C00 0000 + 2^6 * 2 = 0x6C00 0080*    LCD->LCD_REG的地址可以为 LCD->LCD_RAM之外的任意地址.* 由于我们使用结构体管理LCD_REG 和 LCD_RAM(REG在前,RAM在后,均为16位数据宽度)* 因此 结构体的基地址(LCD_BASE) = LCD_RAM - 2 = 0x6C00 0080 -2** 更加通用的计算公式为((片选脚FSMC_NEX)X=1/2/3/4, (RS接地址线FSMC_Ay)y=0~25):*          LCD_BASE = (0x6000 0000 + (0x400 0000 * (x - 1))) | (2^y * 2 -2)*          等效于(使用移位操作)*          LCD_BASE = (0x6000 0000 + (0x400 0000 * (x - 1))) | ((1 << y) * 2 -2)*/
#define LCD_BASE        (uint32_t)((0x60000000 + (0x4000000 * (LCD_FSMC_NEX - 1))) | (((1 << LCD_FSMC_AX) * 2) -2))
#define LCD             ((LCD_TypeDef *) LCD_BASE)/******************************************************************************************/
/* LCD扫描方向和颜色 定义 *//* 扫描方向定义 */
#define L2R_U2D         0           /* 从左到右,从上到下 */
#define L2R_D2U         1           /* 从左到右,从下到上 */
#define R2L_U2D         2           /* 从右到左,从上到下 */
#define R2L_D2U         3           /* 从右到左,从下到上 */#define U2D_L2R         4           /* 从上到下,从左到右 */
#define U2D_R2L         5           /* 从上到下,从右到左 */
#define D2U_L2R         6           /* 从下到上,从左到右 */
#define D2U_R2L         7           /* 从下到上,从右到左 */#define DFT_SCAN_DIR    L2R_U2D     /* 默认的扫描方向 *//* 常用画笔颜色 */
#define WHITE           0xFFFF      /* 白色 */
#define BLACK           0x0000      /* 黑色 */
#define RED             0xF800      /* 红色 */
#define GREEN           0x07E0      /* 绿色 */
#define BLUE            0x001F      /* 蓝色 */ 
#define MAGENTA         0xF81F      /* 品红色/紫红色 = BLUE + RED */
#define YELLOW          0xFFE0      /* 黄色 = GREEN + RED */
#define CYAN            0x07FF      /* 青色 = GREEN + BLUE */  /* 非常用颜色 */
#define BROWN           0xBC40      /* 棕色 */
#define BRRED           0xFC07      /* 棕红色 */
#define GRAY            0x8430      /* 灰色 */ 
#define DARKBLUE        0x01CF      /* 深蓝色 */
#define LIGHTBLUE       0x7D7C      /* 浅蓝色 */ 
#define GRAYBLUE        0x5458      /* 灰蓝色 */ 
#define LIGHTGREEN      0x841F      /* 浅绿色 */  
#define LGRAY           0xC618      /* 浅灰色(PANNEL),窗体背景色 */ 
#define LGRAYBLUE       0xA651      /* 浅灰蓝色(中间层颜色) */ 
#define LBBLUE          0x2B12      /* 浅棕蓝色(选择条目的反色) */ /******************************************************************************************/
/* SSD1963相关配置参数(一般不用改) *//* LCD分辨率设置 */ 
#define SSD_HOR_RESOLUTION      800     /* LCD水平分辨率 */ 
#define SSD_VER_RESOLUTION      480     /* LCD垂直分辨率 */ /* LCD驱动参数设置 */ 
#define SSD_HOR_PULSE_WIDTH     1       /* 水平脉宽 */ 
#define SSD_HOR_BACK_PORCH      46      /* 水平前廊 */ 
#define SSD_HOR_FRONT_PORCH     210     /* 水平后廊 */ #define SSD_VER_PULSE_WIDTH     1       /* 垂直脉宽 */ 
#define SSD_VER_BACK_PORCH      23      /* 垂直前廊 */ 
#define SSD_VER_FRONT_PORCH     22      /* 垂直前廊 */ /* 如下几个参数,自动计算 */ 
#define SSD_HT          (SSD_HOR_RESOLUTION + SSD_HOR_BACK_PORCH + SSD_HOR_FRONT_PORCH)
#define SSD_HPS         (SSD_HOR_BACK_PORCH)
#define SSD_VT          (SSD_VER_RESOLUTION + SSD_VER_BACK_PORCH + SSD_VER_FRONT_PORCH)
#define SSD_VPS         (SSD_VER_BACK_PORCH)/******************************************************************************************/
/* 函数声明 */void lcd_wr_data(volatile uint16_t data);            /* LCD写数据 */
void lcd_wr_regno(volatile uint16_t regno);          /* LCD写寄存器编号/地址 */
void lcd_write_reg(uint16_t regno, uint16_t data);   /* LCD写寄存器的值 */void lcd_init(void);                        /* 初始化LCD */ 
void lcd_display_on(void);                  /* 开显示 */ 
void lcd_display_off(void);                 /* 关显示 */
void lcd_scan_dir(uint8_t dir);             /* 设置屏扫描方向 */ 
void lcd_display_dir(uint8_t dir);          /* 设置屏幕显示方向 */ 
void lcd_ssd_backlight_set(uint8_t pwm);    /* SSD1963 背光控制 */ void lcd_write_ram_prepare(void);                           /* 准备写GRAM */ 
void lcd_set_cursor(uint16_t x, uint16_t y);                /* 设置光标 */ 
uint32_t lcd_read_point(uint16_t x, uint16_t y);            /* 读点(32位颜色,兼容LTDC) */
void lcd_draw_point(uint16_t x, uint16_t y, uint32_t color);/* 画点(32位颜色,兼容LTDC) */void lcd_clear(uint16_t color);                                                             /* LCD清屏 */
void lcd_fill_circle(uint16_t x, uint16_t y, uint16_t r, uint16_t color);                   /* 填充实心圆 */
void lcd_draw_circle(uint16_t x0, uint16_t y0, uint8_t r, uint16_t color);                  /* 画圆 */
void lcd_draw_hline(uint16_t x, uint16_t y, uint16_t len, uint16_t color);                  /* 画水平线 */
void lcd_set_window(uint16_t sx, uint16_t sy, uint16_t width, uint16_t height);             /* 设置窗口 */
void lcd_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t color);          /* 纯色填充矩形(32位颜色,兼容LTDC) */
void lcd_color_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *color);   /* 彩色填充矩形 */
void lcd_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);     /* 画直线 */
void lcd_draw_rectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);/* 画矩形 */void lcd_show_char(uint16_t x, uint16_t y, char chr, uint8_t size, uint8_t mode, uint16_t color);                       /* 显示一个字符 */
void lcd_show_num(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint16_t color);                     /* 显示数字 */
void lcd_show_xnum(uint16_t x, uint16_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode, uint16_t color);      /* 扩展显示数字 */
void lcd_show_string(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t size, char *p, uint16_t color);   /* 显示字符串 */#endif

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

OpenAI GPT o1技术报告阅读(5)-安全性对齐以及思维链等的综合评估与思考

✨继续阅读报告&#xff1a;使用大模型来学习推理(Reason) 原文链接&#xff1a;https://openai.com/index/learning-to-reason-with-llms/ 编码 我们训练了一个模型&#xff0c;在2024年国际信息学奥林匹克竞赛&#xff08;IOI&#xff09;中得分213分&#xff0c;排名在第…

大数据实验一: Linux系统安装和使用

一、实验目的 描述Ubuntu的安装过程&#xff1b;使用命令完成Ubuntu中的基础操作&#xff1b; 二、实验平台 操作系统&#xff1a;window系统&#xff1b;内存&#xff1a;4G以上&#xff1b;硬盘&#xff1a;100GB以上&#xff1b;Virtual Box或者VMware&#xff1b;Ubuntu…

当大语言模型应用到教育领域时会有什么火花出现?

当大语言模型应用到教育领域时会有什么火花出现&#xff1f; LLM Education会出现哪些机遇与挑战? 今天笔者分享一篇来自New York University大学的研究论文&#xff0c;另外一篇则是来自Michigan State University与浙江师范大学的研究论文&#xff0c;希望对这个话题感兴趣…

Java反序列化利用链篇 | CC6链分析(通用版CC链)

文章目录 CC6和CC1之间的区别CC6的调用链构造CC6的payload完成TiedMapEntry.getValue()完成TiedMapEntry.hashCode()完成HashMap.hash()及HashMap.readObject()解决hash()方法提前触发的问题 系列篇其他文章&#xff0c;推荐顺序观看~ Java反序列化利用链篇 | JdbcRowSetImpl利…

FastAPI 的隐藏宝石:自动生成 TypeScript 客户端

在现代 Web 开发中&#xff0c;前后端分离已成为标准做法。这种架构允许前端和后端独立开发和扩展&#xff0c;但同时也带来了如何高效交互的问题。FastAPI&#xff0c;作为一个新兴的 Python Web 框架&#xff0c;提供了一个优雅的解决方案&#xff1a;自动生成客户端代码。本…

C语言-文件操作-一些我想到的、见到的奇怪的问题

博客主页&#xff1a;【夜泉_ly】 本文专栏&#xff1a;【C语言】 欢迎点赞&#x1f44d;收藏⭐关注❤️ C语言-文件操作-一些我想到的、见到的奇怪的问题 前言1.在不关闭文件的情况下&#xff0c;连续多次调用 fopen() 打开同一个文件&#xff0c;会发生什么&#xff1f;1.1过…

简单多状态dp第三弹 leetcode -买卖股票的最佳时机问题

309. 买卖股票的最佳时机含冷冻期 买卖股票的最佳时机含冷冻期 分析: 使用动态规划解决 状态表示: 由于有「买入」「可交易」「冷冻期」三个状态&#xff0c;因此我们可以选择用三个数组&#xff0c;其中&#xff1a; ▪ dp[i][0] 表示&#xff1a;第 i 天结束后&#xff0c…

基于主从Reactor模型实现高并发服务器

目录 1. 项目简介1.1 环境介绍1.2 项目定位1.3 功能模块整体划分 2. Reactor简介2.1 Reactor模型分析2.2 多Reactor多线程分析&#xff1a;多I/O多路复用线程池&#xff08;业务处理&#xff09; 3. 日志宏的编写4. Server模块4.1 Buffer模块4.1.1 Buffer的功能4.1.2 Buffer的实…

AI健身之俯卧撑计数和姿态矫正-角度估计

在本项目中&#xff0c;实现了Yolov7-Pose用于人体姿态估计。以下是如何在Windows 11操作系统上设置和运行该项目的详细步骤。 环境准备 首先&#xff0c;确保您的计算机已经安装了Anaconda。Anaconda是一个开源的Python发行版本&#xff0c;它包含了conda、Python以及众多科…

Python基于TensorFlow实现时间序列循环神经网络回归模型(LSTM时间序列回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 随着信息技术的发展和传感器设备的广泛应用&#xff0c;时间序列数据的产生量急剧增加。无论是股市价格…

Windows本地连接远程服务器并创建新用户详细记录

前提可知&#xff1a; &#xff08;1&#xff09;服务器IP地址&#xff1a;x.x.x.x &#xff08;2&#xff09;服务器名称&#xff1a;root&#xff08;一般默认为root&#xff0c;当然也有别的名称&#xff09; &#xff08;3&#xff09;服务器登陆密码&#xff1a;**** 一、…

优化下载性能:使用Python多线程与异步并发提升下载效率

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 文章内容 📒📝 普通请求下载📝 使用多线程加速下载📝 使用异步编程加速下载📝 总结 📝⚓️ 相关链接 ⚓️📖 介绍 📖 你是否因为下载速度慢而感到焦虑?特别是在下载大型文件时,等待进度条慢慢移动的感觉真的很…

西圣、吉玛仕、绿联电容笔好不好用?热门平替电容笔超真实测评!

电容笔在数字化学习与办公环境中扮演着举足轻重的角色&#xff0c;它不仅是绘写的基本工具&#xff0c;更是提高创造效率的重要手段。随着平替电容笔的市场不断扩大&#xff0c;涌现了很多品牌&#xff0c;使得很多消费者不知道如何选择。此外&#xff0c;还有掺杂了一些性能不…

浅谈Spring Cloud:OpenFeign

RestTemplate 方式调用存在的问题&#xff1a; String url "http://userservice/user/" order.getUserId(); User user restTemplate.getForObject(url, User.class); 这是通过URL地址来访问的。但是&#xff1a; 代码可读性差&#xff0c;编程体验不统一参数复…

CSGHub开源版本v0.9.0更新

CSGHub开源版本v0.9.0更新现已发布&#xff01; 00 重大更新&#x1f50a;&#x1f50a;&#x1f50a; golang 重写 Rails 服务端API git server增加gitaly的支持&#xff0c;且新版本默认使用 gitaly 本地运行应用空间、推理、微调不再需要域名 01 代码仓库&#xff08;模型…

在线骑行网站设计与实现

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装在线骑行网站软件来发挥其高效地信息处理的作用&#xff0c…

灾备技术演进之路 | 虚拟化无代理备份只能挂载验证和容灾吗?只能无代理恢复吗?且看科力锐升级方案

灾备技术演进之路系列 虚拟化备份技术演进 摆脱束缚&#xff0c;加速前行 无代理备份仅能挂载/恢复验证吗&#xff1f; ——科力锐极简验证演练无代理备份来了 无代理备份无法应对平台级故障吗&#xff1f; ——科力锐应急接管无代理备份来了 无代理备份仅能同平台挂载吗&a…

Java反序列化利用链篇 | URLDNS链

文章目录 URLDNS链调用链分析Payload编写 系列篇其他文章&#xff0c;推荐顺序观看~ Java反序列化利用链篇 | JdbcRowSetImpl利用链分析Java反序列化利用链篇 | CC1链_全网最菜的分析思路Java反序列化利用链篇 | CC1链的第二种方式-LazyMap版调用链Java反序列化利用链篇 | URLD…

thinkphp 做分布式服务+读写分离+分库分表(分区)(后续接着写)

thinkphp 做分布式服务读写分离分库分表&#xff08;分区&#xff09; 引言 thinkphp* 大道至简一、分库分表分表php 分库分表hash算法0、分表的方法&#xff08;thinkphp&#xff09;1、ThinkPHP6 业务分表之一&#xff1a;UID 发号器2、ThinkPHP6 业务分表之二&#xff1a;用…

【数据结构与算法 | 灵神题单 | 二叉搜索树篇】力扣653

1. 力扣653&#xff1a;两数之和IV - 输入二叉搜索树 1.1 题目&#xff1a; 给定一个二叉搜索树 root 和一个目标结果 k&#xff0c;如果二叉搜索树中存在两个元素且它们的和等于给定的目标结果&#xff0c;则返回 true。 示例 1&#xff1a; 输入: root [5,3,6,2,4,null,7…